<commit_before>package onthefly

import (
	"fmt"
	"log"
)

// For generating IDs
var (
	geometryCounter = 0
	materialCounter = 0
	meshCounter     = 0
)

// Unique prefixes when generating IDs
const (
	geometryPrefix = "g"
	materialPrefix = "ma"
	meshPrefix     = "m"
)

type (
	// For Three.JS elements, like a mesh or material
	Element struct {
		ID string // name of the variable
		JS string // javascript code for creating the element
	}
	// The Three.JS render function, where heand and tail are standard
	RenderFunc struct {
		head, mid, tail string
	}
	// Different types of elements
	Geometry Element
	Material Element
	Mesh     Element
)

// Create a HTML5 page that links with Three.JS and sets up a scene
func NewThreeJS(titleText string) (*Page, *Tag) {
	page := NewHTML5Page(titleText)

	// Style the page for showing a fullscreen canvas
	page.FullCanvas()

	// Link to Three.JS
	page.LinkToJSInBody("http://threejs.org/build/three.min.js")

	// Add a scene
	script, _ := page.AddScriptToBody("var scene = new THREE.Scene();")

	// Return the sript tag that can be used for adding additional javascript/Three.JS code
	return page, script
}

// Add a camera with default settings
// todo: create an AddCustomCamera function
func (three *Tag) AddCamera() {
	three.AddContent("var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);")
}

// Add a WebGL renderer with default settings
func (three *Tag) AddRenderer() {
	three.AddContent("var renderer = new THREE.WebGLRenderer();")
	three.AddContent("renderer.setSize(window.innerWidth, window.innerHeight);")
	three.AddContent("document.body.appendChild(renderer.domElement);")
}

func (three *Tag) AddToScene(mesh *Mesh) {
	three.AddContent(mesh.JS)
	three.AddContent("scene.add(" + mesh.ID + ");")
}

func NewMesh(geometry *Geometry, material *Material) *Mesh {
	id := fmt.Sprintf("%s%d", meshPrefix, meshCounter)
	meshCounter++
	js := geometry.JS + material.JS
	js += "var " + id + " = new THREE.Mesh(" + geometry.ID + ", " + material.ID + ");"
	return &Mesh{id, js}
}

func (three *Tag) CameraPos(axis string, value int) {
	if (axis != "x") && (axis != "y") && (axis != "z") {
		log.Fatalln("camera axis must be x, y or z")
	}
	three.AddContent(fmt.Sprintf("camera.position.%s = %d;", axis, value))
}

// Very simple type of material
func NewMaterial(color string) *Material {
	id := fmt.Sprintf("%s%d", materialPrefix, materialCounter)
	materialCounter++
	js := "var " + id + " = new THREE.MeshBasicMaterial({color: " + color + "});"
	return &Material{id, js}
}

func NewNormalMaterial() *Material {
	id := fmt.Sprintf("%s%d", materialPrefix, materialCounter)
	materialCounter++
	js := "var " + id + " = new THREE.MeshNormalMaterial();"
	return &Material{id, js}
}

func NewBoxGeometry(w, h, d int) *Geometry {
	id := fmt.Sprintf("%s%d", geometryPrefix, geometryCounter)
	geometryCounter++
	js := fmt.Sprintf("var %s = new THREE.BoxGeometry(%d, %d, %d);", id, w, h, d)
	return &Geometry{id, js}
}

// Add a test cube to the scene
// todo: create functions for adding geometry, material and creating meshes
func (three *Tag) AddTestCube() *Mesh {
	//material := NewMaterial(color)
	material := NewNormalMaterial()
	geometry := NewBoxGeometry(1, 1, 1)
	cube := NewMesh(geometry, material)
	three.AddToScene(cube)
	return cube
}

func NewRenderFunction() *RenderFunc {
	head := "var render = function() { requestAnimationFrame(render);"
	tail := "renderer.render(scene, camera); };"
	return &RenderFunc{head, "", tail}
}

func (r *RenderFunc) AddJS(s string) {
	r.mid += s
}

func (three *Tag) AddRenderFunction(r *RenderFunc, call bool) {
	three.AddContent(r.head + r.mid + r.tail)
	if call {
		three.AddContent("render();")
	}
}
<commit_msg>Additional comments<commit_after>package onthefly

import (
	"fmt"
	"log"
)

// For generating IDs
var (
	geometryCounter = 0
	materialCounter = 0
	meshCounter     = 0
)

// Unique prefixes when generating IDs
const (
	geometryPrefix = "g"
	materialPrefix = "ma"
	meshPrefix     = "m"
)

type (
	// For Three.JS elements, like a mesh or material
	Element struct {
		ID string // name of the variable
		JS string // javascript code for creating the element
	}
	// The Three.JS render function, where heand and tail are standard
	RenderFunc struct {
		head, mid, tail string
	}
	// Different types of elements
	Geometry Element
	Material Element
	Mesh     Element
)

// Create a HTML5 page that links with Three.JS and sets up a scene
func NewThreeJS(titleText string) (*Page, *Tag) {
	page := NewHTML5Page(titleText)

	// Style the page for showing a fullscreen canvas
	page.FullCanvas()

	// Link to Three.JS
	page.LinkToJSInBody("http://threejs.org/build/three.min.js")

	// Add a scene
	script, _ := page.AddScriptToBody("var scene = new THREE.Scene();")

	// Return the sript tag that can be used for adding additional javascript/Three.JS code
	return page, script
}

// Add a camera with default settings
// todo: create an AddCustomCamera function
func (three *Tag) AddCamera() {
	three.AddContent("var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);")
}

// Add a WebGL renderer with default settings
func (three *Tag) AddRenderer() {
	three.AddContent("var renderer = new THREE.WebGLRenderer();")
	three.AddContent("renderer.setSize(window.innerWidth, window.innerHeight);")
	three.AddContent("document.body.appendChild(renderer.domElement);")
}

// Add a mesh to the current scene.
func (three *Tag) AddToScene(mesh *Mesh) {
	three.AddContent(mesh.JS)
	three.AddContent("scene.add(" + mesh.ID + ");")
}

// Create a new mesh, given geometry and material.
// The geometry and material will be instanciated together with the mesh.
func NewMesh(geometry *Geometry, material *Material) *Mesh {
	id := fmt.Sprintf("%s%d", meshPrefix, meshCounter)
	meshCounter++
	js := geometry.JS + material.JS
	js += "var " + id + " = new THREE.Mesh(" + geometry.ID + ", " + material.ID + ");"
	return &Mesh{id, js}
}

// Set the camera position. Axis must be "x", "y", or "z".
func (three *Tag) CameraPos(axis string, value int) {
	if (axis != "x") && (axis != "y") && (axis != "z") {
		log.Fatalln("camera axis must be x, y or z")
	}
	three.AddContent(fmt.Sprintf("camera.position.%s = %d;", axis, value))
}

// Very simple type of material
func NewMaterial(color string) *Material {
	id := fmt.Sprintf("%s%d", materialPrefix, materialCounter)
	materialCounter++
	js := "var " + id + " = new THREE.MeshBasicMaterial({color: " + color + "});"
	return &Material{id, js}
}

// Create a material which reflects the normals of the geometry
func NewNormalMaterial() *Material {
	id := fmt.Sprintf("%s%d", materialPrefix, materialCounter)
	materialCounter++
	js := "var " + id + " = new THREE.MeshNormalMaterial();"
	return &Material{id, js}
}

// Create geometry for a box
func NewBoxGeometry(w, h, d int) *Geometry {
	id := fmt.Sprintf("%s%d", geometryPrefix, geometryCounter)
	geometryCounter++
	js := fmt.Sprintf("var %s = new THREE.BoxGeometry(%d, %d, %d);", id, w, h, d)
	return &Geometry{id, js}
}

// Add a test cube to the scene
// todo: create functions for adding geometry, material and creating meshes
func (three *Tag) AddTestCube() *Mesh {
	//material := NewMaterial(color)
	material := NewNormalMaterial()
	geometry := NewBoxGeometry(1, 1, 1)
	cube := NewMesh(geometry, material)
	three.AddToScene(cube)
	return cube
}

// Create a new render function, which is called at every animation frame
func NewRenderFunction() *RenderFunc {
	head := "var render = function() { requestAnimationFrame(render);"
	tail := "renderer.render(scene, camera); };"
	return &RenderFunc{head, "", tail}
}

// Add javascript code to the body of a render function
func (r *RenderFunc) AddJS(s string) {
	r.mid += s
}

// Add a render function.
// If call is true, the render function is called at the end of the script.
func (three *Tag) AddRenderFunction(r *RenderFunc, call bool) {
	three.AddContent(r.head + r.mid + r.tail)
	if call {
		three.AddContent("render();")
	}
}
<|endoftext|>


<commit_before>// Package timeout is for handling timeout invocation of external command
package timeout

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"runtime"

	"syscall"
	"time"
)

// Timeout is main struct of timeout package
type Timeout struct {
	Duration  time.Duration
	KillAfter time.Duration
	Signal    os.Signal
	Cmd       *exec.Cmd
}

var defaultSignal os.Signal

func init() {
	switch runtime.GOOS {
	case "windows":
		defaultSignal = os.Interrupt
	default:
		defaultSignal = syscall.SIGTERM
	}
}

// exit statuses are same with GNU timeout
const (
	exitNormal            = 0
	exitTimedOut          = 124
	exitUnknownErr        = 125
	exitCommandNotInvoked = 126
	exitCommandNotFound   = 127
	exitKilled            = 137
)

// Error is error of timeout
type Error struct {
	ExitCode int
	Err      error
}

func (err *Error) Error() string {
	return fmt.Sprintf("exit code: %d, %s", err.ExitCode, err.Err.Error())
}

// ExitStatus stores exit information of the command
type ExitStatus struct {
	Code int
	typ  exitType
}

// IsTimedOut returns the command timed out or not
func (ex ExitStatus) IsTimedOut() bool {
	return ex.typ == exitTypeTimedOut || ex.typ == exitTypeKilled
}

// IsKilled returns the command is killed or not
func (ex ExitStatus) IsKilled() bool {
	return ex.typ == exitTypeKilled
}

// GetExitCode gets the exit code for command line tools
func (ex ExitStatus) GetExitCode() int {
	switch {
	case ex.IsKilled():
		return exitKilled
	case ex.IsTimedOut():
		return exitTimedOut
	default:
		return ex.Code
	}
}

// GetChildExitCode gets the exit code of the Cmd itself
func (ex ExitStatus) GetChildExitCode() int {
	return ex.Code
}

type exitType int

// exit types
const (
	exitTypeNormal exitType = iota + 1
	exitTypeTimedOut
	exitTypeKilled
)

func (tio *Timeout) signal() os.Signal {
	if tio.Signal == nil {
		return defaultSignal
	}
	return tio.Signal
}

// Run is synchronous interface of executing command and returning information
func (tio *Timeout) Run() (ExitStatus, string, string, error) {
	cmd := tio.getCmd()
	var outBuffer, errBuffer bytes.Buffer
	cmd.Stdout = &outBuffer
	cmd.Stderr = &errBuffer

	ch, err := tio.RunCommand()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return ExitStatus{}, string(outBuffer.Bytes()), string(errBuffer.Bytes()), err
	}
	exitSt := <-ch
	return exitSt, string(outBuffer.Bytes()), string(errBuffer.Bytes()), nil
}

// RunSimple executes command and only returns integer as exit code. It is mainly for go-timeout command
func (tio *Timeout) RunSimple(preserveStatus bool) int {
	cmd := tio.getCmd()

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return exitUnknownErr
	}
	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return exitUnknownErr
	}

	ch, err := tio.RunCommand()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return getExitCodeFromErr(err)
	}

	go func() {
		defer stdoutPipe.Close()
		io.Copy(os.Stdout, stdoutPipe)
	}()

	go func() {
		defer stderrPipe.Close()
		io.Copy(os.Stderr, stderrPipe)
	}()

	exitSt := <-ch
	if preserveStatus {
		return exitSt.GetChildExitCode()
	}
	return exitSt.GetExitCode()
}

func getExitCodeFromErr(err error) int {
	if err != nil {
		if tmerr, ok := err.(*Error); ok {
			return tmerr.ExitCode
		}
		return -1
	}
	return exitNormal
}

// RunCommand is executing the command and handling timeout. This is primitive interface of Timeout
func (tio *Timeout) RunCommand() (chan ExitStatus, error) {
	cmd := tio.getCmd()

	if err := cmd.Start(); err != nil {
		switch {
		case os.IsNotExist(err):
			return nil, &Error{
				ExitCode: exitCommandNotFound,
				Err:      err,
			}
		case os.IsPermission(err):
			return nil, &Error{
				ExitCode: exitCommandNotInvoked,
				Err:      err,
			}
		default:
			return nil, &Error{
				ExitCode: exitUnknownErr,
				Err:      err,
			}
		}
	}

	exitChan := make(chan ExitStatus)
	go func() {
		exitChan <- tio.handleTimeout()
	}()

	return exitChan, nil
}

func (tio *Timeout) handleTimeout() (ex ExitStatus) {
	cmd := tio.getCmd()
	exitChan := getExitChan(cmd)
	select {
	case exitCode := <-exitChan:
		ex.Code = exitCode
		ex.typ = exitTypeNormal
		return ex
	case <-time.After(tio.Duration):
		cmd.Process.Signal(tio.signal()) // XXX error handling
		ex.typ = exitTypeTimedOut
	}

	if tio.KillAfter > 0 {
		select {
		case ex.Code = <-exitChan:
		case <-time.After(tio.KillAfter):
			tio.killall()
			ex.Code = exitKilled
			ex.typ = exitTypeKilled
		}
	} else {
		ex.Code = <-exitChan
	}

	return ex
}

func getExitChan(cmd *exec.Cmd) chan int {
	ch := make(chan int)
	go func() {
		err := cmd.Wait()
		ch <- resolveExitCode(err)
	}()
	return ch
}

func resolveExitCode(err error) int {
	if err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				return status.ExitStatus()
			}
		}
		// The exit codes in some platforms aren't integer. e.g. plan9.
		return -1
	}
	return exitNormal
}
<commit_msg>call cmd.Procell.Kill after calling tio.killall() just to make sure<commit_after>// Package timeout is for handling timeout invocation of external command
package timeout

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"os/exec"
	"runtime"

	"syscall"
	"time"
)

// Timeout is main struct of timeout package
type Timeout struct {
	Duration  time.Duration
	KillAfter time.Duration
	Signal    os.Signal
	Cmd       *exec.Cmd
}

var defaultSignal os.Signal

func init() {
	switch runtime.GOOS {
	case "windows":
		defaultSignal = os.Interrupt
	default:
		defaultSignal = syscall.SIGTERM
	}
}

// exit statuses are same with GNU timeout
const (
	exitNormal            = 0
	exitTimedOut          = 124
	exitUnknownErr        = 125
	exitCommandNotInvoked = 126
	exitCommandNotFound   = 127
	exitKilled            = 137
)

// Error is error of timeout
type Error struct {
	ExitCode int
	Err      error
}

func (err *Error) Error() string {
	return fmt.Sprintf("exit code: %d, %s", err.ExitCode, err.Err.Error())
}

// ExitStatus stores exit information of the command
type ExitStatus struct {
	Code int
	typ  exitType
}

// IsTimedOut returns the command timed out or not
func (ex ExitStatus) IsTimedOut() bool {
	return ex.typ == exitTypeTimedOut || ex.typ == exitTypeKilled
}

// IsKilled returns the command is killed or not
func (ex ExitStatus) IsKilled() bool {
	return ex.typ == exitTypeKilled
}

// GetExitCode gets the exit code for command line tools
func (ex ExitStatus) GetExitCode() int {
	switch {
	case ex.IsKilled():
		return exitKilled
	case ex.IsTimedOut():
		return exitTimedOut
	default:
		return ex.Code
	}
}

// GetChildExitCode gets the exit code of the Cmd itself
func (ex ExitStatus) GetChildExitCode() int {
	return ex.Code
}

type exitType int

// exit types
const (
	exitTypeNormal exitType = iota + 1
	exitTypeTimedOut
	exitTypeKilled
)

func (tio *Timeout) signal() os.Signal {
	if tio.Signal == nil {
		return defaultSignal
	}
	return tio.Signal
}

// Run is synchronous interface of executing command and returning information
func (tio *Timeout) Run() (ExitStatus, string, string, error) {
	cmd := tio.getCmd()
	var outBuffer, errBuffer bytes.Buffer
	cmd.Stdout = &outBuffer
	cmd.Stderr = &errBuffer

	ch, err := tio.RunCommand()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return ExitStatus{}, string(outBuffer.Bytes()), string(errBuffer.Bytes()), err
	}
	exitSt := <-ch
	return exitSt, string(outBuffer.Bytes()), string(errBuffer.Bytes()), nil
}

// RunSimple executes command and only returns integer as exit code. It is mainly for go-timeout command
func (tio *Timeout) RunSimple(preserveStatus bool) int {
	cmd := tio.getCmd()

	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return exitUnknownErr
	}
	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return exitUnknownErr
	}

	ch, err := tio.RunCommand()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return getExitCodeFromErr(err)
	}

	go func() {
		defer stdoutPipe.Close()
		io.Copy(os.Stdout, stdoutPipe)
	}()

	go func() {
		defer stderrPipe.Close()
		io.Copy(os.Stderr, stderrPipe)
	}()

	exitSt := <-ch
	if preserveStatus {
		return exitSt.GetChildExitCode()
	}
	return exitSt.GetExitCode()
}

func getExitCodeFromErr(err error) int {
	if err != nil {
		if tmerr, ok := err.(*Error); ok {
			return tmerr.ExitCode
		}
		return -1
	}
	return exitNormal
}

// RunCommand is executing the command and handling timeout. This is primitive interface of Timeout
func (tio *Timeout) RunCommand() (chan ExitStatus, error) {
	cmd := tio.getCmd()

	if err := cmd.Start(); err != nil {
		switch {
		case os.IsNotExist(err):
			return nil, &Error{
				ExitCode: exitCommandNotFound,
				Err:      err,
			}
		case os.IsPermission(err):
			return nil, &Error{
				ExitCode: exitCommandNotInvoked,
				Err:      err,
			}
		default:
			return nil, &Error{
				ExitCode: exitUnknownErr,
				Err:      err,
			}
		}
	}

	exitChan := make(chan ExitStatus)
	go func() {
		exitChan <- tio.handleTimeout()
	}()

	return exitChan, nil
}

func (tio *Timeout) handleTimeout() (ex ExitStatus) {
	cmd := tio.getCmd()
	exitChan := getExitChan(cmd)
	select {
	case exitCode := <-exitChan:
		ex.Code = exitCode
		ex.typ = exitTypeNormal
		return ex
	case <-time.After(tio.Duration):
		cmd.Process.Signal(tio.signal()) // XXX error handling
		ex.typ = exitTypeTimedOut
	}

	if tio.KillAfter > 0 {
		select {
		case ex.Code = <-exitChan:
		case <-time.After(tio.KillAfter):
			tio.killall()
			// just to make sure
			cmd.Process.Kill()
			ex.Code = exitKilled
			ex.typ = exitTypeKilled
		}
	} else {
		ex.Code = <-exitChan
	}

	return ex
}

func getExitChan(cmd *exec.Cmd) chan int {
	ch := make(chan int)
	go func() {
		err := cmd.Wait()
		ch <- resolveExitCode(err)
	}()
	return ch
}

func resolveExitCode(err error) int {
	if err != nil {
		if exiterr, ok := err.(*exec.ExitError); ok {
			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
				return status.ExitStatus()
			}
		}
		// The exit codes in some platforms aren't integer. e.g. plan9.
		return -1
	}
	return exitNormal
}
<|endoftext|>


<commit_before>package exif

import "github.com/jonathanpittman/tiff"

const ExifIFDTagID = 34665

var (
	exifTags     = tiff.NewTagSet("Exif", 0, 65535)
	ExifTagSpace = tiff.NewTagSpace("Exif")
	exifIFDTag   = tiff.NewTag(ExifIFDTagID, "ExifIFD", nil)
)

// TODO: Pass in the slice of valid FieldType for each tag.
func init() {
	tiff.PrivateTags.Register(exifIFDTag)

	// http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html
	exifTags.Register(tiff.NewTag(33434, "ExposureTime", nil))
	exifTags.Register(tiff.NewTag(33437, "FNumber", nil))
	exifTags.Register(tiff.NewTag(34850, "ExposureProgram", nil))
	exifTags.Register(tiff.NewTag(34852, "SpectralSensitivity", nil))
	exifTags.Register(tiff.NewTag(34855, "ISOSpeedRatings", nil))
	exifTags.Register(tiff.NewTag(34856, "OECF", nil))
	exifTags.Register(tiff.NewTag(34864, "SensitivityType", nil))
	exifTags.Register(tiff.NewTag(36864, "ExifVersion", nil))
	exifTags.Register(tiff.NewTag(36867, "DateTimeOriginal", nil))
	exifTags.Register(tiff.NewTag(36868, "DateTimeDigitized", nil))
	exifTags.Register(tiff.NewTag(37121, "ComponentsConfiguration", nil))
	exifTags.Register(tiff.NewTag(37122, "CompressedBitsPerPixel", nil))
	exifTags.Register(tiff.NewTag(37377, "ShutterSpeedValue", nil))
	exifTags.Register(tiff.NewTag(37378, "ApertureValue", nil))
	exifTags.Register(tiff.NewTag(37379, "BrightnessValue", nil))
	exifTags.Register(tiff.NewTag(37380, "ExposureBiasValue", nil))
	exifTags.Register(tiff.NewTag(37381, "MaxApertureValue", nil))
	exifTags.Register(tiff.NewTag(37382, "SubjectDistance", nil))
	exifTags.Register(tiff.NewTag(37383, "MeteringMode", nil))
	exifTags.Register(tiff.NewTag(37384, "LightSource", nil))
	exifTags.Register(tiff.NewTag(37385, "Flash", nil))
	exifTags.Register(tiff.NewTag(37386, "FocalLength", nil))
	exifTags.Register(tiff.NewTag(37396, "SubjectArea", nil))
	exifTags.Register(tiff.NewTag(37500, "MakerNote", nil))
	exifTags.Register(tiff.NewTag(37510, "UserComment", nil))
	exifTags.Register(tiff.NewTag(37520, "SubsecTime", nil))
	exifTags.Register(tiff.NewTag(37521, "SubsecTimeOriginal", nil))
	exifTags.Register(tiff.NewTag(37522, "SubsecTimeDigitized", nil))
	exifTags.Register(tiff.NewTag(40960, "FlashpixVersion", nil))
	exifTags.Register(tiff.NewTag(40961, "ColorSpace", nil))
	exifTags.Register(tiff.NewTag(40962, "PixelXDimension", nil))
	exifTags.Register(tiff.NewTag(40963, "PixelYDimension", nil))
	exifTags.Register(tiff.NewTag(40964, "RelatedSoundFile", nil))
	exifTags.Register(tiff.NewTag(41483, "FlashEnergy", nil))
	exifTags.Register(tiff.NewTag(41484, "SpatialFrequencyResponse", nil))
	exifTags.Register(tiff.NewTag(41486, "FocalPlaneXResolution", nil))
	exifTags.Register(tiff.NewTag(41487, "FocalPlaneYResolution", nil))
	exifTags.Register(tiff.NewTag(41488, "FocalPlaneResolutionUnit", nil))
	exifTags.Register(tiff.NewTag(41492, "SubjectLocation", nil))
	exifTags.Register(tiff.NewTag(41493, "ExposureIndex", nil))
	exifTags.Register(tiff.NewTag(41495, "SensingMethod", nil))
	exifTags.Register(tiff.NewTag(41728, "FileSource", nil))
	exifTags.Register(tiff.NewTag(41729, "SceneType", nil))
	exifTags.Register(tiff.NewTag(41730, "CFAPattern", nil))
	exifTags.Register(tiff.NewTag(41985, "CustomRendered", nil))
	exifTags.Register(tiff.NewTag(41986, "ExposureMode", nil))
	exifTags.Register(tiff.NewTag(41987, "WhiteBalance", nil))
	exifTags.Register(tiff.NewTag(41988, "DigitalZoomRatio", nil))
	exifTags.Register(tiff.NewTag(41989, "FocalLengthIn35mmFilm", nil))
	exifTags.Register(tiff.NewTag(41990, "SceneCaptureType", nil))
	exifTags.Register(tiff.NewTag(41991, "GainControl", nil))
	exifTags.Register(tiff.NewTag(41992, "Contrast", nil))
	exifTags.Register(tiff.NewTag(41993, "Saturation", nil))
	exifTags.Register(tiff.NewTag(41994, "Sharpness", nil))
	exifTags.Register(tiff.NewTag(41995, "DeviceSettingDescription", nil))
	exifTags.Register(tiff.NewTag(41996, "SubjectDistanceRange", nil))
	exifTags.Register(tiff.NewTag(42016, "ImageUniqueID", nil))

	// Tags that indicate the offsets to the respective IFDs.
	exifTags.Register(exifIFDTag)
	exifTags.Register(gpsIFDTag)
	exifTags.Register(iopIFDTag)

	// Not sure if this actually belongs in Exif, but it has shown up in an ExifIFD.
	exifTags.Register(tiff.NewTag(18246, "Rating", nil))

	// Prevent further registration in exif.  If tags are missing, they
	// should be added here instead of added from the outside.
	exifTags.Lock()

	tiff.DefaultTagSpace.RegisterTagSet(exifTags)

	ExifTagSpace.RegisterTagSet(tiff.BaselineTags)
	ExifTagSpace.RegisterTagSet(tiff.ExtendedTags)
	ExifTagSpace.RegisterTagSet(exifTags)

	tiff.RegisterTagSpace(ExifTagSpace)
}
<commit_msg>Add some exif tags<commit_after>package exif

import "github.com/jonathanpittman/tiff"

// http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif.html
// http://www.exiv2.org/tags.html
// http://www.cipa.jp/exifprint/index_e.html
// http://www.jeita.or.jp/cgi-bin/standard_e/list.cgi?cateid=1&subcateid=4

const ExifIFDTagID = 34665

var (
	exifTags     = tiff.NewTagSet("Exif", 0, 65535)
	ExifTagSpace = tiff.NewTagSpace("Exif")
	exifIFDTag   = tiff.NewTag(ExifIFDTagID, "ExifIFD", nil)
)

// TODO: Break up these exif tags into sets based on the exif version.  They
//       still all likely belong in the same space though.  For an example, take
//       a look at the way DNG was broken up.  Tags introduced in newer versions
//       are added to a set named for the version.  They still all get put into
//       the same space, just the sets are identified separately.
func init() {
	tiff.PrivateTags.Register(exifIFDTag)

	exifTags.Register(tiff.NewTag(33434, "ExposureTime", nil))
	exifTags.Register(tiff.NewTag(33437, "FNumber", nil))
	exifTags.Register(tiff.NewTag(34850, "ExposureProgram", nil))
	exifTags.Register(tiff.NewTag(34852, "SpectralSensitivity", nil))
	exifTags.Register(tiff.NewTag(34855, "ISOSpeedRatings", nil))
	exifTags.Register(tiff.NewTag(34856, "OECF", nil))
	exifTags.Register(tiff.NewTag(34864, "SensitivityType", nil))
	exifTags.Register(tiff.NewTag(34866, "RecommendedExposureIndex", nil))
	exifTags.Register(tiff.NewTag(36864, "ExifVersion", nil))
	exifTags.Register(tiff.NewTag(36867, "DateTimeOriginal", nil))
	exifTags.Register(tiff.NewTag(36868, "DateTimeDigitized", nil))
	exifTags.Register(tiff.NewTag(37121, "ComponentsConfiguration", nil))
	exifTags.Register(tiff.NewTag(37122, "CompressedBitsPerPixel", nil))
	exifTags.Register(tiff.NewTag(37377, "ShutterSpeedValue", nil))
	exifTags.Register(tiff.NewTag(37378, "ApertureValue", nil))
	exifTags.Register(tiff.NewTag(37379, "BrightnessValue", nil))
	exifTags.Register(tiff.NewTag(37380, "ExposureBiasValue", nil))
	exifTags.Register(tiff.NewTag(37381, "MaxApertureValue", nil))
	exifTags.Register(tiff.NewTag(37382, "SubjectDistance", nil))
	exifTags.Register(tiff.NewTag(37383, "MeteringMode", nil))
	exifTags.Register(tiff.NewTag(37384, "LightSource", nil))
	exifTags.Register(tiff.NewTag(37385, "Flash", nil))
	exifTags.Register(tiff.NewTag(37386, "FocalLength", nil))
	exifTags.Register(tiff.NewTag(37396, "SubjectArea", nil))
	exifTags.Register(tiff.NewTag(37500, "MakerNote", nil))
	exifTags.Register(tiff.NewTag(37510, "UserComment", nil))
	exifTags.Register(tiff.NewTag(37520, "SubsecTime", nil))
	exifTags.Register(tiff.NewTag(37521, "SubsecTimeOriginal", nil))
	exifTags.Register(tiff.NewTag(37522, "SubsecTimeDigitized", nil))
	exifTags.Register(tiff.NewTag(40960, "FlashpixVersion", nil))
	exifTags.Register(tiff.NewTag(40961, "ColorSpace", nil))
	exifTags.Register(tiff.NewTag(40962, "PixelXDimension", nil))
	exifTags.Register(tiff.NewTag(40963, "PixelYDimension", nil))
	exifTags.Register(tiff.NewTag(40964, "RelatedSoundFile", nil))
	exifTags.Register(tiff.NewTag(41483, "FlashEnergy", nil))
	exifTags.Register(tiff.NewTag(41484, "SpatialFrequencyResponse", nil))
	exifTags.Register(tiff.NewTag(41486, "FocalPlaneXResolution", nil))
	exifTags.Register(tiff.NewTag(41487, "FocalPlaneYResolution", nil))
	exifTags.Register(tiff.NewTag(41488, "FocalPlaneResolutionUnit", nil))
	exifTags.Register(tiff.NewTag(41492, "SubjectLocation", nil))
	exifTags.Register(tiff.NewTag(41493, "ExposureIndex", nil))
	exifTags.Register(tiff.NewTag(41495, "SensingMethod", nil))
	exifTags.Register(tiff.NewTag(41728, "FileSource", nil))
	exifTags.Register(tiff.NewTag(41729, "SceneType", nil))
	exifTags.Register(tiff.NewTag(41730, "CFAPattern", nil))
	exifTags.Register(tiff.NewTag(41985, "CustomRendered", nil))
	exifTags.Register(tiff.NewTag(41986, "ExposureMode", nil))
	exifTags.Register(tiff.NewTag(41987, "WhiteBalance", nil))
	exifTags.Register(tiff.NewTag(41988, "DigitalZoomRatio", nil))
	exifTags.Register(tiff.NewTag(41989, "FocalLengthIn35mmFilm", nil))
	exifTags.Register(tiff.NewTag(41990, "SceneCaptureType", nil))
	exifTags.Register(tiff.NewTag(41991, "GainControl", nil))
	exifTags.Register(tiff.NewTag(41992, "Contrast", nil))
	exifTags.Register(tiff.NewTag(41993, "Saturation", nil))
	exifTags.Register(tiff.NewTag(41994, "Sharpness", nil))
	exifTags.Register(tiff.NewTag(41995, "DeviceSettingDescription", nil))
	exifTags.Register(tiff.NewTag(41996, "SubjectDistanceRange", nil))
	exifTags.Register(tiff.NewTag(42016, "ImageUniqueID", nil))
	exifTags.Register(tiff.NewTag(42032, "CameraOwnerName", nil))
	exifTags.Register(tiff.NewTag(42033, "BodySerialNumber", nil))
	exifTags.Register(tiff.NewTag(42034, "LensSpecification", nil))
	exifTags.Register(tiff.NewTag(42035, "LensMake", nil))
	exifTags.Register(tiff.NewTag(42036, "LensModel", nil))
	exifTags.Register(tiff.NewTag(42037, "LensSerialNumber", nil))

	// Tags that indicate the offsets to the respective IFDs.
	exifTags.Register(exifIFDTag)
	exifTags.Register(gpsIFDTag)
	exifTags.Register(iopIFDTag)

	// Not sure if this actually belongs in Exif, but it has shown up in an ExifIFD.
	exifTags.Register(tiff.NewTag(18246, "Rating", nil))

	// Prevent further registration in exif.  If tags are missing, they
	// should be added here instead of added from the outside.
	exifTags.Lock()

	tiff.DefaultTagSpace.RegisterTagSet(exifTags)

	ExifTagSpace.RegisterTagSet(tiff.BaselineTags)
	ExifTagSpace.RegisterTagSet(tiff.ExtendedTags)
	ExifTagSpace.RegisterTagSet(exifTags)

	tiff.RegisterTagSpace(ExifTagSpace)
}
<|endoftext|>


<commit_before>package todotxt

import (
        "time"
        "os"
        "bufio"
        "strings"
        "regexp"
)

type Task struct {
        todo string
        priority string
        create_date time.Time
        contexts []string
        projects []string
}

type TaskList []Task

func LoadTaskList (filename string) (TaskList) {

        var f, err = os.Open(filename)

        if err != nil {
                panic(err)
        }

        defer f.Close()

        var tasklist = TaskList{}

        scanner := bufio.NewScanner(f)

        for scanner.Scan() {
                var task = Task{}
                text := scanner.Text()
                splits := strings.Split(text, " ")
                date_regexp := "([\\d]{4})-([\\d]{2})-([\\d]{2})"

                if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                        if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                                panic(e)
                        } else {
                                task.create_date = date
                        }

                        task.todo = strings.Join(splits[1:], " ")
                } else {
                        task.todo = text
                }

                context_regexp, _ := regexp.Compile("@[[:word:]]+")
                contexts := context_regexp.FindAllStringSubmatch(text, -1)
                if len(contexts) != 0 {
                        task.contexts = contexts[0]
                }

                project_regexp, _ := regexp.Compile("\\+[[:word:]]+")
                projects := project_regexp.FindAllStringSubmatch(text, -1)
                if len(projects) != 0 {
                        task.projects = projects[0]
                }

                tasklist = append(tasklist, task)
        }

        if err := scanner.Err(); err != nil {
                panic(scanner.Err())
        }

        return tasklist
}

func (tasks TaskList) Count() int {
        return len(tasks)
}

func (task Task) Text() string {
        return task.todo
}

func (task Task) Priority() string {
        return task.priority
}

func (task Task) Contexts() []string {
        return task.contexts
}

func (task Task) Projects() []string {
        return task.projects
}

func (task Task) CreateDate() time.Time {
        return task.create_date
}
<commit_msg>Parsing priorities<commit_after>package todotxt

import (
        "time"
        "os"
        "bufio"
        "strings"
        "regexp"
)

type Task struct {
        todo string
        priority byte
        create_date time.Time
        contexts []string
        projects []string
}

type TaskList []Task

func LoadTaskList (filename string) (TaskList) {

        var f, err = os.Open(filename)

        if err != nil {
                panic(err)
        }

        defer f.Close()

        var tasklist = TaskList{}

        scanner := bufio.NewScanner(f)

        for scanner.Scan() {
                var task = Task{}
                text := scanner.Text()
                splits := strings.Split(text, " ")

                head := splits[0]

                if (len(head) == 3) &&
                   (head[0] == '(') &&
                   (head[2] == ')') {
                        task.priority = head[1]
                        splits = splits[1:]
                }

                date_regexp := "([\\d]{4})-([\\d]{2})-([\\d]{2})"
                if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                        if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                                panic(e)
                        } else {
                                task.create_date = date
                        }

                        task.todo = strings.Join(splits[1:], " ")
                } else {
                        task.todo = strings.Join(splits[0:], " ")
                }

                context_regexp, _ := regexp.Compile("@[[:word:]]+")
                contexts := context_regexp.FindAllStringSubmatch(text, -1)
                if len(contexts) != 0 {
                        task.contexts = contexts[0]
                }

                project_regexp, _ := regexp.Compile("\\+[[:word:]]+")
                projects := project_regexp.FindAllStringSubmatch(text, -1)
                if len(projects) != 0 {
                        task.projects = projects[0]
                }

                tasklist = append(tasklist, task)
        }

        if err := scanner.Err(); err != nil {
                panic(scanner.Err())
        }

        return tasklist
}

func (tasks TaskList) Count() int {
        return len(tasks)
}

func (task Task) Text() string {
        return task.todo
}

func (task Task) Priority() byte {
        return task.priority
}

func (task Task) Contexts() []string {
        return task.contexts
}

func (task Task) Projects() []string {
        return task.projects
}

func (task Task) CreateDate() time.Time {
        return task.create_date
}
<|endoftext|>


<commit_before>package todotxt

import (
        "time"
        "os"
        "bufio"
        "strings"
        "regexp"
        "sort"
        "unicode"
        "fmt"
)

type Task struct {
        id int
        todo string
        priority byte
        create_date time.Time
        contexts []string
        projects []string
        raw_todo string
        finished bool
        id_padding int
}

type TaskList []Task

func CreateTask(id int, text string) (Task) {
        var task = Task{}
        task.id = id
        task.raw_todo = text

        splits := strings.Split(text, " ")

        if text[0] == 'x' &&
           text[1] == ' ' &&
           !unicode.IsSpace(rune(text[2])) {
                task.finished = true
                splits = splits[1:]
        }

        head := splits[0]

        if (len(head) == 3) &&
           (head[0] == '(') &&
           (head[2] == ')') &&
           (head[1] >= 65 && head[1] <= 90) { // checking if it's in range [A-Z]
                task.priority = head[1]
                splits = splits[1:]
        }

        date_regexp := "([\\d]{4})-([\\d]{2})-([\\d]{2})"
        if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                        panic(e)
                } else {
                        task.create_date = date
                }

                task.todo = strings.Join(splits[1:], " ")
        } else {
                task.todo = strings.Join(splits[0:], " ")
        }

        context_regexp, _ := regexp.Compile("@[[:word:]]+")
        contexts := context_regexp.FindAllStringSubmatch(text, -1)
        if len(contexts) != 0 {
                task.contexts = contexts[0]
        }

        project_regexp, _ := regexp.Compile("\\+[[:word:]]+")
        projects := project_regexp.FindAllStringSubmatch(text, -1)
        if len(projects) != 0 {
                task.projects = projects[0]
        }

        return task
}

func LoadTaskList (filename string) (TaskList) {

        var f, err = os.Open(filename)

        if err != nil {
                panic(err)
        }

        defer f.Close()

        var tasklist = TaskList{}

        scanner := bufio.NewScanner(f)

        for scanner.Scan() {
                text := scanner.Text()
                tasklist.Add(text)
        }

        if err := scanner.Err(); err != nil {
                panic(scanner.Err())
        }

        return tasklist
}

type By func(t1, t2 Task) bool

func (by By) Sort(tasks TaskList) {
        ts := &taskSorter{
                tasks: tasks,
                by:    by,
        }
        sort.Sort(ts)
}

type taskSorter struct {
        tasks TaskList
        by func(t1, t2 Task) bool
}

func (s *taskSorter) Len() int {
        return len(s.tasks)
}

func (s *taskSorter) Swap(i, j int) {
        s.tasks[i], s.tasks[j] = s.tasks[j], s.tasks[i]
}

func (s *taskSorter) Less(i, j int) bool {
        return s.by(s.tasks[i], s.tasks[j])
}

func (tasks TaskList) Len() int {
        return len(tasks)
}

func prioCmp(t1, t2 Task) bool {
        return t1.Priority() < t2.Priority()
}

func prioRevCmp(t1, t2 Task) bool {
        return t1.Priority() > t2.Priority()
}

func dateCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 > tm2
        }
}

func dateRevCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 < tm2
        }
}

func lenCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 < tl2
        }
}

func lenRevCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 > tl2
        }
}

func idCmp(t1, t2 Task) bool {
        return t1.Id() < t2.Id()
}

func (tasks TaskList) Sort(by string) {
        switch by {
        default:
        case "prio":
                By(prioCmp).Sort(tasks)
        case "prio-rev":
                By(prioRevCmp).Sort(tasks)
        case "date":
                By(dateCmp).Sort(tasks)
        case "date-rev":
                By(dateRevCmp).Sort(tasks)
        case "len":
                By(lenCmp).Sort(tasks)
        case "len-rev":
                By(lenRevCmp).Sort(tasks)
        case "id":
                By(idCmp).Sort(tasks)
        }
}

func (tasks TaskList) Save(filename string) {
        tasks.Sort("id")

        f, err := os.Create(filename)
        if err != nil {
                panic(err)
        }

        defer f.Close()

        for _, task := range tasks {
                if task.Finished() {
                        f.WriteString("x ")
                }
                f.WriteString(task.RawText() + "\n")
        }
        f.Sync()
}

func (tasks *TaskList) Add(todo string) {
        task := CreateTask(tasks.Len(), todo)
        *tasks = append(*tasks, task)
}

func (tasks TaskList) Done(id int) error {
        if id > tasks.Len() || id < 0 {
                return fmt.Errorf("Error: id is %v", id)
        }

        tasks[id].finished = true

        return nil
}

func (task Task) Id() int {
        return task.id
}

func (task Task) Text() string {
        return task.todo
}

func (task Task) RawText() string {
        return task.raw_todo
}

func (task Task) Priority() byte {
        // if priority is not from [A-Z], let it be 94 (^)
        if task.priority < 65 || task.priority > 90 {
                return 94 // you know, ^
        } else {
                return task.priority
        }
}

func (task Task) Contexts() []string {
        return task.contexts
}

func (task Task) Projects() []string {
        return task.projects
}

func (task Task) CreateDate() time.Time {
        return task.create_date
}

func (task Task) Finished() bool {
        return task.finished
}

func (task *Task) SetIdPaddingBy(tasklist TaskList) {
        l := tasklist.Len()

        if l >= 10000 {
                task.id_padding = 5
        } else if l >= 1000 {
                task.id_padding = 4
        } else if l >= 100 {
                task.id_padding = 3
        } else if l >= 10 {
                task.id_padding = 2
        } else {
                task.id_padding = 1
        }
}

func (task Task) IdPadding() int {
        return task.id_padding
}

func (task Task) PrettyPrint(pretty string) string {
        rp := regexp.MustCompile("(%[a-zA-Z])")
        out := rp.ReplaceAllStringFunc(pretty, func(s string) string {

                switch s{
                case "%i":
                        str := fmt.Sprintf("%%0%dd", task.IdPadding())
                        return fmt.Sprintf(str, task.Id())
                case "%t":
                        return task.Text()
                case "%T":
                        return task.RawText()
                case "%p":
                        return string(task.Priority())
                default:
                        return s
                }
        })
        return out
}
<commit_msg>no finish date<commit_after>package todotxt

import (
        "time"
        "os"
        "bufio"
        "strings"
        "regexp"
        "sort"
        "unicode"
        "fmt"
)

type Task struct {
        id int
        todo string
        priority byte
        create_date time.Time
        contexts []string
        projects []string
        raw_todo string
        finished bool
        id_padding int
}

type TaskList []Task

func CreateTask(id int, text string) (Task) {
        var task = Task{}
        task.id = id
        task.raw_todo = text

        splits := strings.Split(text, " ")

        if text[0] == 'x' &&
           text[1] == ' ' &&
           !unicode.IsSpace(rune(text[2])) {
                task.finished = true
                splits = splits[1:]
        }

        head := splits[0]

        if (len(head) == 3) &&
           (head[0] == '(') &&
           (head[2] == ')') &&
           (head[1] >= 65 && head[1] <= 90) { // checking if it's in range [A-Z]
                task.priority = head[1]
                splits = splits[1:]
        }

        date_regexp := "([\\d]{4})-([\\d]{2})-([\\d]{2})"
        if match, _ := regexp.MatchString(date_regexp, splits[0]); match {
                if date, e := time.Parse("2006-01-02", splits[0]); e != nil {
                        panic(e)
                } else {
                        task.create_date = date
                }

                task.todo = strings.Join(splits[1:], " ")
        } else {
                task.todo = strings.Join(splits[0:], " ")
        }

        context_regexp, _ := regexp.Compile("@[[:word:]]+")
        contexts := context_regexp.FindAllStringSubmatch(text, -1)
        if len(contexts) != 0 {
                task.contexts = contexts[0]
        }

        project_regexp, _ := regexp.Compile("\\+[[:word:]]+")
        projects := project_regexp.FindAllStringSubmatch(text, -1)
        if len(projects) != 0 {
                task.projects = projects[0]
        }

        return task
}

func LoadTaskList (filename string) (TaskList) {

        var f, err = os.Open(filename)

        if err != nil {
                panic(err)
        }

        defer f.Close()

        var tasklist = TaskList{}

        scanner := bufio.NewScanner(f)

        for scanner.Scan() {
                text := scanner.Text()
                tasklist.Add(text)
        }

        if err := scanner.Err(); err != nil {
                panic(scanner.Err())
        }

        return tasklist
}

type By func(t1, t2 Task) bool

func (by By) Sort(tasks TaskList) {
        ts := &taskSorter{
                tasks: tasks,
                by:    by,
        }
        sort.Sort(ts)
}

type taskSorter struct {
        tasks TaskList
        by func(t1, t2 Task) bool
}

func (s *taskSorter) Len() int {
        return len(s.tasks)
}

func (s *taskSorter) Swap(i, j int) {
        s.tasks[i], s.tasks[j] = s.tasks[j], s.tasks[i]
}

func (s *taskSorter) Less(i, j int) bool {
        return s.by(s.tasks[i], s.tasks[j])
}

func (tasks TaskList) Len() int {
        return len(tasks)
}

func prioCmp(t1, t2 Task) bool {
        return t1.Priority() < t2.Priority()
}

func prioRevCmp(t1, t2 Task) bool {
        return t1.Priority() > t2.Priority()
}

func dateCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 > tm2
        }
}

func dateRevCmp(t1, t2 Task) bool {
        tm1 := t1.CreateDate().Unix()
        tm2 := t2.CreateDate().Unix()

        // if the dates equal, let's use priority
        if tm1 == tm2 {
                return prioCmp(t1, t2)
        } else {
                return tm1 < tm2
        }
}

func lenCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 < tl2
        }
}

func lenRevCmp(t1, t2 Task) bool {
        tl1 := len(t1.raw_todo)
        tl2 := len(t2.raw_todo)
        if tl1 == tl2 {
                return prioCmp(t1, t2)
        } else {
                return tl1 > tl2
        }
}

func idCmp(t1, t2 Task) bool {
        return t1.Id() < t2.Id()
}

func (tasks TaskList) Sort(by string) {
        switch by {
        default:
        case "prio":
                By(prioCmp).Sort(tasks)
        case "prio-rev":
                By(prioRevCmp).Sort(tasks)
        case "date":
                By(dateCmp).Sort(tasks)
        case "date-rev":
                By(dateRevCmp).Sort(tasks)
        case "len":
                By(lenCmp).Sort(tasks)
        case "len-rev":
                By(lenRevCmp).Sort(tasks)
        case "id":
                By(idCmp).Sort(tasks)
        }
}

func (tasks TaskList) Save(filename string, finish_date bool) {
        tasks.Sort("id")

        f, err := os.Create(filename)
        if err != nil {
                panic(err)
        }

        defer f.Close()

        for _, task := range tasks {
                if task.Finished() && finish_date {
                        f.WriteString("x ")
                }
                f.WriteString(task.RawText() + "\n")
        }
        f.Sync()
}

func (tasks *TaskList) Add(todo string) {
        task := CreateTask(tasks.Len(), todo)
        *tasks = append(*tasks, task)
}

func (tasks TaskList) Done(id int) error {
        if id > tasks.Len() || id < 0 {
                return fmt.Errorf("Error: id is %v", id)
        }

        tasks[id].finished = true

        return nil
}

func (task Task) Id() int {
        return task.id
}

func (task Task) Text() string {
        return task.todo
}

func (task Task) RawText() string {
        return task.raw_todo
}

func (task Task) Priority() byte {
        // if priority is not from [A-Z], let it be 94 (^)
        if task.priority < 65 || task.priority > 90 {
                return 94 // you know, ^
        } else {
                return task.priority
        }
}

func (task Task) Contexts() []string {
        return task.contexts
}

func (task Task) Projects() []string {
        return task.projects
}

func (task Task) CreateDate() time.Time {
        return task.create_date
}

func (task Task) Finished() bool {
        return task.finished
}

func (task *Task) SetIdPaddingBy(tasklist TaskList) {
        l := tasklist.Len()

        if l >= 10000 {
                task.id_padding = 5
        } else if l >= 1000 {
                task.id_padding = 4
        } else if l >= 100 {
                task.id_padding = 3
        } else if l >= 10 {
                task.id_padding = 2
        } else {
                task.id_padding = 1
        }
}

func (task Task) IdPadding() int {
        return task.id_padding
}

func (task Task) PrettyPrint(pretty string) string {
        rp := regexp.MustCompile("(%[a-zA-Z])")
        out := rp.ReplaceAllStringFunc(pretty, func(s string) string {

                switch s{
                case "%i":
                        str := fmt.Sprintf("%%0%dd", task.IdPadding())
                        return fmt.Sprintf(str, task.Id())
                case "%t":
                        return task.Text()
                case "%T":
                        return task.RawText()
                case "%p":
                        return string(task.Priority())
                default:
                        return s
                }
        })
        return out
}
<|endoftext|>


<commit_before>package bitio

import (
	"bytes"
	"errors"
	"io"
	"math/rand"
	"testing"
	"time"
)

func TestReader(t *testing.T) {
	data := []byte{3, 255, 0xcc, 0x1a, 0xbc, 0xde, 0x80, 0x01, 0x02, 0xf8, 0x08, 0xf0}

	r := NewReader(bytes.NewBuffer(data))

	var nExp interface{}
	check := func(n interface{}, err error) {
		if n != nExp || err != nil {
			t.Errorf("Got %x, want %x, error: %v", n, nExp, err)
		}
	}

	nExp = byte(3)
	check(r.ReadByte())
	nExp = uint64(255)
	check(r.ReadBits(8))

	nExp = uint64(0xc)
	check(r.ReadBits(4))

	nExp = uint64(0xc1)
	check(r.ReadBits(8))

	nExp = uint64(0xabcde)
	check(r.ReadBits(20))

	if b, err := r.ReadBool(); !b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, false, err)
	}
	if b, err := r.ReadBool(); b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, true, err)
	}

	if n := r.Align(); n != 6 {
		t.Errorf("Got %v, want %v", n, 6)
	}

	s := make([]byte, 2)
	if n, err := r.Read(s); n != 2 || err != nil || !bytes.Equal(s, []byte{0x01, 0x02}) {
		t.Errorf("Got %v, want %v, error: %v", s, []byte{0x01, 0x02}, err)
	}

	if i, err := r.ReadBits(4); i != 0xf || err != nil {
		t.Errorf("Got %x, want %x, error: %v", i, 0xf, err)
	}

	if n, err := r.Read(s); n != 2 || err != nil || !bytes.Equal(s, []byte{0x80, 0x8f}) {
		t.Errorf("Got %v, want %v, error: %v", s, []byte{0x80, 0x8f}, err)
	}
}

func TestWriter(t *testing.T) {
	b := &bytes.Buffer{}

	w := NewWriter(b)

	expected := []byte{0xc1, 0x7f, 0xac, 0x89, 0x24, 0x78, 0x01, 0x02, 0xf8, 0x08, 0xf0, 0xff, 0x80}

	errs := []error{}
	errs = append(errs, w.WriteByte(0xc1))
	errs = append(errs, w.WriteBool(false))
	errs = append(errs, w.WriteBits(0x3f, 6))
	errs = append(errs, w.WriteBool(true))
	errs = append(errs, w.WriteByte(0xac))
	errs = append(errs, w.WriteBits(0x01, 1))
	errs = append(errs, w.WriteBits(0x1248f, 20))

	var nExp interface{}
	check := func(n interface{}, err error) {
		if n != nExp || err != nil {
			t.Errorf("Got %x, want %x, error: %v", n, nExp, err)
		}
	}

	nExp = byte(3)
	check(w.Align())

	nExp = int(2)
	check(w.Write([]byte{0x01, 0x02}))

	errs = append(errs, w.WriteBits(0x0f, 4))

	check(w.Write([]byte{0x80, 0x8f}))

	nExp = byte(4)
	check(w.Align())
	nExp = byte(0)
	check(w.Align())
	if err := w.WriteBits(0x01, 1); err != nil {
		t.Error("Got error:", err)
	}
	if err := w.WriteByte(0xff); err != nil {
		t.Error("Got error:", err)
	}

	errs = append(errs, w.Close())

	for _, v := range errs {
		if v != nil {
			t.Error("Got error:", v)
		}
	}

	if !bytes.Equal(b.Bytes(), expected) {
		t.Errorf("Got: %x, want: %x", b.Bytes(), expected)
	}
}

func TestReaderEOF(t *testing.T) {
	r := NewReader(bytes.NewBuffer([]byte{0x01}))

	if b, err := r.ReadByte(); b != 1 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", b, 1, err)
	}
	if _, err := r.ReadByte(); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
	if _, err := r.ReadBool(); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
	if _, err := r.ReadBits(1); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
	if n, err := r.Read(make([]byte, 2)); n != 0 || err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
}

func TestReaderEOF2(t *testing.T) {
	r := NewReader(bytes.NewBuffer([]byte{0x01}))
	if _, err := r.ReadBits(17); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}

	// Byte spreading byte boundary (readUnalignedByte)
	r = NewReader(bytes.NewBuffer([]byte{0xc1, 0x01}))
	if b, err := r.ReadBool(); !b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, false, err)
	}
	if b, err := r.ReadByte(); b != 0x82 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", b, 0x82, err)
	}
	// readUnalignedByte resulting in EOF
	if _, err := r.ReadByte(); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}

	r = NewReader(bytes.NewBuffer([]byte{0xc1, 0x01}))
	if b, err := r.ReadBool(); !b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, false, err)
	}
	if n, err := r.Read(make([]byte, 2)); n != 1 || err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
}

type nonByteReaderWriter struct {
	io.Reader
	io.Writer
}

func TestNonByteReaderWriter(t *testing.T) {
	NewReader(nonByteReaderWriter{})
	NewWriter(nonByteReaderWriter{})
}

type errWriter struct {
	limit int
}

func (e *errWriter) WriteByte(c byte) error {
	if e.limit == 0 {
		return errors.New("Can't write more!")
	}
	e.limit--
	return nil
}

func (e *errWriter) Write(p []byte) (n int, err error) {
	for i, v := range p {
		if err := e.WriteByte(v); err != nil {
			return i, err
		}
	}
	return len(p), nil
}

type errCloser struct {
	errWriter
}

func (e *errCloser) Close() error {
	return errors.New("Obliged not to close!")
}

func TestWriterError(t *testing.T) {
	w := NewWriter(&errWriter{1})
	if err := w.WriteBool(true); err != nil {
		t.Error("Got error:", err)
	}
	if n, err := w.Write([]byte{0x01, 0x02}); n != 1 || err == nil {
		t.Errorf("Got %x, want %x, error: %v", n, 2, err)
	}
	if err := w.Close(); err == nil {
		t.Error("Got no error:", err)
	}

	w = NewWriter(&errWriter{0})
	if err := w.WriteBits(0x00, 9); err == nil {
		t.Error("Got no error:", err)
	}

	w = NewWriter(&errWriter{1})
	if err := w.WriteBits(0x00, 17); err == nil {
		t.Error("Got no error:", err)
	}

	w = NewWriter(&errWriter{})
	if err := w.WriteBits(0x00, 7); err != nil {
		t.Error("Got error:", err)
	}
	if err := w.WriteBool(false); err == nil {
		t.Error("Got no error:", err)
	}

	w = NewWriter(&errWriter{})
	if err := w.WriteBool(true); err != nil {
		t.Error("Got error:", err)
	}
	if _, err := w.Align(); err == nil {
		t.Error("Got no error:", err)
	}

	w = NewWriter(&errCloser{})
	if err := w.Close(); err == nil {
		t.Error("Got no error:", err)
	}
}

func TestChain(t *testing.T) {
	b := &bytes.Buffer{}
	w := NewWriter(b)

	rand.Seed(time.Now().UnixNano())

	expected := make([]uint64, 100000)
	bits := make([]byte, len(expected))

	// Writing (generating)
	for i := range expected {
		expected[i] = uint64(rand.Int63())
		bits[i] = byte(1 + rand.Int31n(60))
		expected[i] &= uint64(1)<<bits[i] - 1
		w.WriteBits(expected[i], bits[i])
	}
	if err := w.Close(); err != nil {
		t.Error("Got error:", err)
	}

	r := NewReader(bytes.NewBuffer(b.Bytes()))

	// Reading (verifying)
	for i, v := range expected {
		if u, err := r.ReadBits(bits[i]); u != v || err != nil {
			t.Errorf("Idx: %d, Got: %x, want: %x, bits: %d, error: %v", i, u, v, bits[i], err)
		}
	}
}
<commit_msg>Code formatting / refactoring.<commit_after>package bitio

import (
	"bytes"
	"errors"
	"io"
	"math/rand"
	"testing"
	"time"
)

func TestReader(t *testing.T) {
	data := []byte{3, 255, 0xcc, 0x1a, 0xbc, 0xde, 0x80, 0x01, 0x02, 0xf8, 0x08, 0xf0}

	r := NewReader(bytes.NewBuffer(data))

	var nExp interface{}
	check := func(n interface{}, err error) {
		if n != nExp || err != nil {
			t.Errorf("Got %x, want %x, error: %v", n, nExp, err)
		}
	}

	nExp = byte(3)
	check(r.ReadByte())
	nExp = uint64(255)
	check(r.ReadBits(8))

	nExp = uint64(0xc)
	check(r.ReadBits(4))

	nExp = uint64(0xc1)
	check(r.ReadBits(8))

	nExp = uint64(0xabcde)
	check(r.ReadBits(20))

	if b, err := r.ReadBool(); !b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, false, err)
	}
	if b, err := r.ReadBool(); b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, true, err)
	}

	if n := r.Align(); n != 6 {
		t.Errorf("Got %v, want %v", n, 6)
	}

	s := make([]byte, 2)
	if n, err := r.Read(s); n != 2 || err != nil || !bytes.Equal(s, []byte{0x01, 0x02}) {
		t.Errorf("Got %v, want %v, error: %v", s, []byte{0x01, 0x02}, err)
	}

	nExp = uint64(0xf)
	check(r.ReadBits(4))

	if n, err := r.Read(s); n != 2 || err != nil || !bytes.Equal(s, []byte{0x80, 0x8f}) {
		t.Errorf("Got %v, want %v, error: %v", s, []byte{0x80, 0x8f}, err)
	}
}

func TestWriter(t *testing.T) {
	b := &bytes.Buffer{}

	w := NewWriter(b)

	expected := []byte{0xc1, 0x7f, 0xac, 0x89, 0x24, 0x78, 0x01, 0x02, 0xf8, 0x08, 0xf0, 0xff, 0x80}

	errs := []error{}
	errs = append(errs, w.WriteByte(0xc1))
	errs = append(errs, w.WriteBool(false))
	errs = append(errs, w.WriteBits(0x3f, 6))
	errs = append(errs, w.WriteBool(true))
	errs = append(errs, w.WriteByte(0xac))
	errs = append(errs, w.WriteBits(0x01, 1))
	errs = append(errs, w.WriteBits(0x1248f, 20))

	var nExp interface{}
	check := func(n interface{}, err error) {
		if n != nExp || err != nil {
			t.Errorf("Got %x, want %x, error: %v", n, nExp, err)
		}
	}

	nExp = byte(3)
	check(w.Align())

	nExp = int(2)
	check(w.Write([]byte{0x01, 0x02}))

	errs = append(errs, w.WriteBits(0x0f, 4))

	check(w.Write([]byte{0x80, 0x8f}))

	nExp = byte(4)
	check(w.Align())
	nExp = byte(0)
	check(w.Align())
	if err := w.WriteBits(0x01, 1); err != nil {
		t.Error("Got error:", err)
	}
	if err := w.WriteByte(0xff); err != nil {
		t.Error("Got error:", err)
	}

	errs = append(errs, w.Close())

	for _, v := range errs {
		if v != nil {
			t.Error("Got error:", v)
		}
	}

	if !bytes.Equal(b.Bytes(), expected) {
		t.Errorf("Got: %x, want: %x", b.Bytes(), expected)
	}
}

func TestReaderEOF(t *testing.T) {
	r := NewReader(bytes.NewBuffer([]byte{0x01}))

	if b, err := r.ReadByte(); b != 1 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", b, 1, err)
	}
	if _, err := r.ReadByte(); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
	if _, err := r.ReadBool(); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
	if _, err := r.ReadBits(1); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
	if n, err := r.Read(make([]byte, 2)); n != 0 || err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
}

func TestReaderEOF2(t *testing.T) {
	r := NewReader(bytes.NewBuffer([]byte{0x01}))
	if _, err := r.ReadBits(17); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}

	// Byte spreading byte boundary (readUnalignedByte)
	r = NewReader(bytes.NewBuffer([]byte{0xc1, 0x01}))
	if b, err := r.ReadBool(); !b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, false, err)
	}
	if b, err := r.ReadByte(); b != 0x82 || err != nil {
		t.Errorf("Got %x, want %x, error: %v", b, 0x82, err)
	}
	// readUnalignedByte resulting in EOF
	if _, err := r.ReadByte(); err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}

	r = NewReader(bytes.NewBuffer([]byte{0xc1, 0x01}))
	if b, err := r.ReadBool(); !b || err != nil {
		t.Errorf("Got %v, want %v, error: %v", b, false, err)
	}
	if n, err := r.Read(make([]byte, 2)); n != 1 || err != io.EOF {
		t.Errorf("Got %v, want %v", err, io.EOF)
	}
}

type nonByteReaderWriter struct {
	io.Reader
	io.Writer
}

func TestNonByteReaderWriter(t *testing.T) {
	NewReader(nonByteReaderWriter{})
	NewWriter(nonByteReaderWriter{})
}

type errWriter struct {
	limit int
}

func (e *errWriter) WriteByte(c byte) error {
	if e.limit == 0 {
		return errors.New("Can't write more!")
	}
	e.limit--
	return nil
}

func (e *errWriter) Write(p []byte) (n int, err error) {
	for i, v := range p {
		if err := e.WriteByte(v); err != nil {
			return i, err
		}
	}
	return len(p), nil
}

type errCloser struct {
	errWriter
}

func (e *errCloser) Close() error {
	return errors.New("Obliged not to close!")
}

func TestWriterError(t *testing.T) {
	w := NewWriter(&errWriter{1})
	if err := w.WriteBool(true); err != nil {
		t.Error("Got error:", err)
	}
	if n, err := w.Write([]byte{0x01, 0x02}); n != 1 || err == nil {
		t.Errorf("Got %x, want %x, error: %v", n, 2, err)
	}
	if err := w.Close(); err == nil {
		t.Error("Got no error:", err)
	}

	w = NewWriter(&errWriter{0})
	if err := w.WriteBits(0x00, 9); err == nil {
		t.Error("Got no error:", err)
	}

	w = NewWriter(&errWriter{1})
	if err := w.WriteBits(0x00, 17); err == nil {
		t.Error("Got no error:", err)
	}

	w = NewWriter(&errWriter{})
	if err := w.WriteBits(0x00, 7); err != nil {
		t.Error("Got error:", err)
	}
	if err := w.WriteBool(false); err == nil {
		t.Error("Got no error:", err)
	}

	w = NewWriter(&errWriter{})
	if err := w.WriteBool(true); err != nil {
		t.Error("Got error:", err)
	}
	if _, err := w.Align(); err == nil {
		t.Error("Got no error:", err)
	}

	w = NewWriter(&errCloser{})
	if err := w.Close(); err == nil {
		t.Error("Got no error:", err)
	}
}

func TestChain(t *testing.T) {
	b := &bytes.Buffer{}
	w := NewWriter(b)

	rand.Seed(time.Now().UnixNano())

	expected := make([]uint64, 100000)
	bits := make([]byte, len(expected))

	// Writing (generating)
	for i := range expected {
		expected[i] = uint64(rand.Int63())
		bits[i] = byte(1 + rand.Int31n(60))
		expected[i] &= uint64(1)<<bits[i] - 1
		w.WriteBits(expected[i], bits[i])
	}
	if err := w.Close(); err != nil {
		t.Error("Got error:", err)
	}

	r := NewReader(bytes.NewBuffer(b.Bytes()))

	// Reading (verifying)
	for i, v := range expected {
		if u, err := r.ReadBits(bits[i]); u != v || err != nil {
			t.Errorf("Idx: %d, Got: %x, want: %x, bits: %d, error: %v", i, u, v, bits[i], err)
		}
	}
}
<|endoftext|>


<commit_before>package fire

import (
	"errors"
	"fmt"
	"reflect"

	"github.com/256dpi/fire/coal"

	"gopkg.in/mgo.v2/bson"
)

// C is a short-hand function to construct a callback. It will also add tracing
// code around the execution of the callback.
func C(name string, h Handler) *Callback {
	return &Callback{
		Handler: func(ctx *Context) error {
			// begin trace
			ctx.Tracer.Push(name)

			// call handler
			err := h(ctx)
			if err != nil {
				return err
			}

			// finish trace
			ctx.Tracer.Pop()

			return nil
		},
	}
}

// A Callback is called during the request processing flow of a controller.
//
// Note: If the callback returns an error wrapped using Fatal() the API returns
// an InternalServerError status and the error will be logged. All other errors
// are serialized to an error object and returned.
type Callback struct {
	Handler Handler
}

type noDefault int

// NoDefault marks the specified field to have no default that needs to be
// enforced while executing the ProtectedFieldsValidator.
const NoDefault noDefault = iota

// Only will return a callback that runs the specified callback only when one
// of the supplied operations match.
func Only(cb *Callback, force bool, ops ...Operation) *Callback {
	// construct name
	name := fmt.Sprintf("fire/Only(%s)", joinOperations(ops, ","))

	return C(name, func(ctx *Context) error {
		// check operation
		for _, a := range ops {
			// run callback if operation is allowed
			if a == ctx.Operation {
				return cb.Handler(ctx)
			}
		}

		// check force
		if force {
			panic(fmt.Sprintf("unsupported operation"))
		}

		return nil
	})
}

// Except will return a callback that runs the specified callback only when none
// of the supplied operations match.
func Except(cb *Callback, force bool, ops ...Operation) *Callback {
	// construct name
	name := fmt.Sprintf("fire/Except(%s)", joinOperations(ops, ","))

	return C(name, func(ctx *Context) error {
		// check operation
		for _, a := range ops {
			if a == ctx.Operation {
				// check force
				if force {
					panic(fmt.Sprintf("unsupported operation"))
				}

				return nil
			}
		}

		return cb.Handler(ctx)
	})
}

// Combine will return a callback that runs all the specified callbacks in order
// until an error is returned.
func Combine(cbs ...*Callback) *Callback {
	return C("fire/Combine", func(ctx *Context) error {
		// run all callbacks
		for _, cb := range cbs {
			err := cb.Handler(ctx)
			if err != nil {
				return err
			}
		}

		return nil
	})
}

// BasicAuthorizer authorizes requests based on a simple credentials list.
func BasicAuthorizer(credentials map[string]string) *Callback {
	return C("fire/BasicAuthorizer", func(ctx *Context) error {
		// check for credentials
		user, password, ok := ctx.HTTPRequest.BasicAuth()
		if !ok {
			return errors.New("access denied")
		}

		// check if credentials match
		if val, ok := credentials[user]; !ok || val != password {
			return errors.New("access denied")
		}

		return nil
	})
}

// ModelValidator performs a validation of the model using the Validate
// function.
func ModelValidator() *Callback {
	return Only(C("fire/ModelValidator", func(ctx *Context) error {
		// TODO: Add error source pointer.
		return coal.Validate(ctx.Model)
	}), false, Create, Update)
}

// ProtectedFieldsValidator compares protected attributes against their
// default (during Create) or stored value (during Update) and returns an error
// if they have been changed.
//
// Attributes are defined by passing pairs of fields and default values:
//
//	ProtectedFieldsValidator(map[string]interface{}{
//		F(&Post{}, "Title"): NoDefault, // can only be set during Create
//		F(&Post{}, "Link"):  "",        // is fixed and cannot be changed
//	})
//
// The special NoDefault value can be provided to skip the default enforcement
// on Create.
//
func ProtectedFieldsValidator(fields map[string]interface{}) *Callback {
	return Only(C("fire/ProtectedFieldsValidator", func(ctx *Context) error {
		// handle resource creation
		if ctx.Operation == Create {
			// check all fields
			for field, def := range fields {
				// skip fields that have no default
				if def == NoDefault {
					continue
				}

				// check equality
				if !reflect.DeepEqual(ctx.Model.MustGet(field), def) {
					return errors.New("field " + field + " is protected")
				}
			}
		}

		// handle resource updates
		if ctx.Operation == Update {
			// read the original
			original, err := ctx.Original()
			if err != nil {
				return err
			}

			// check all fields
			for field := range fields {
				// check equality
				if !reflect.DeepEqual(ctx.Model.MustGet(field), original.MustGet(field)) {
					return errors.New("field " + field + " is protected")
				}
			}
		}

		return nil
	}), false, Create, Update)
}

// DependentResourcesValidator counts documents in the supplied collections
// and returns an error if some get found. This callback is meant to protect
// resources from breaking relations when requested to be deleted.
//
// Dependent resources are defined by passing pairs of collections and database
// fields that hold the current models id:
//
//	DependentResourcesValidator(map[string]string{
//		C(&Post{}): F(&Post{}, "Author"),
//		C(&Comment{}): F(&Comment{}, "Author"),
//	})
//
func DependentResourcesValidator(resources map[string]string) *Callback {
	return Only(C("DependentResourcesValidator", func(ctx *Context) error {
		// check all relations
		for coll, field := range resources {
			// prepare query
			query := bson.M{field: ctx.Model.ID()}

			// count referencing documents
			ctx.Tracer.Push("mgo/Query.Count")
			ctx.Tracer.Tag("query", query)
			n, err := ctx.Store.DB().C(coll).Find(query).Limit(1).Count()
			if err != nil {
				return Fatal(err)
			}
			ctx.Tracer.Pop()

			// return err of documents are found
			if n != 0 {
				return errors.New("resource has dependent resources")
			}
		}

		// pass validation
		return nil
	}), false, Delete)
}

// VerifyReferencesValidator makes sure all references in the document are
// existing by counting the references on the related collections.
//
// References are defined by passing pairs of database fields and collections of
// models whose ids might be referenced on the current model:
//
//	VerifyReferencesValidator(map[string]string{
//		F(&Comment{}, "Post"): C(&Post{}),
//		F(&Comment{}, "Author"): C(&User{}),
//	})
//
// The callbacks supports to-one, optional to-one and to-many relationships.
//
func VerifyReferencesValidator(references map[string]string) *Callback {
	return Only(C("fire/VerifyReferencesValidator", func(ctx *Context) error {
		// check all references
		for field, collection := range references {
			// read referenced id
			ref := ctx.Model.MustGet(field)

			// continue if reference is not set
			if oid, ok := ref.(*bson.ObjectId); ok && oid == nil {
				continue
			}

			// continue if slice is empty
			if ids, ok := ref.([]bson.ObjectId); ok && ids == nil {
				continue
			}

			// handle to-many relationships
			if ids, ok := ref.([]bson.ObjectId); ok {
				// prepare query
				query := bson.M{"_id": bson.M{"$in": ids}}

				// count entities in database
				ctx.Tracer.Push("mgo/Query.Count")
				ctx.Tracer.Tag("query", query)
				n, err := ctx.Store.DB().C(collection).Find(query).Count()
				if err != nil {
					return Fatal(err)
				}
				ctx.Tracer.Pop()

				// check for existence
				if n != len(ids) {
					return errors.New("missing references for field " + field)
				}

				continue
			}

			// handle to-one relationships

			// count entities in database
			ctx.Tracer.Push("mgo/Query.Count")
			ctx.Tracer.Tag("id", ref)
			n, err := ctx.Store.DB().C(collection).FindId(ref).Limit(1).Count()
			if err != nil {
				return Fatal(err)
			}
			ctx.Tracer.Pop()

			// check for existence
			if n != 1 {
				return errors.New("missing reference for field " + field)
			}
		}

		// pass validation
		return nil
	}), false, Create, Update)
}

// RelationshipValidator makes sure all relationships of a model are correct and
// in place. It does so by creating a DependentResourcesValidator and a
// VerifyReferencesValidator based on the specified model and catalog.
func RelationshipValidator(model coal.Model, catalog *coal.Catalog, excludedFields ...string) *Callback {
	// prepare lists
	dependentResources := make(map[string]string)
	references := make(map[string]string)

	// iterate through all fields
	for _, field := range coal.Init(model).Meta().Fields {
		// exclude field if requested
		if stringInList(field.Name, excludedFields) {
			continue
		}

		// handle has-one and has-many relationships
		if field.HasOne || field.HasMany {
			// get related model
			relatedModel := catalog.Find(field.RelType)
			if relatedModel == nil {
				panic("fire: missing model in catalog: " + field.RelType)
			}

			// get collection
			collection := relatedModel.Meta().Collection

			// get related bson field
			bsonField := ""
			for _, relatedField := range relatedModel.Meta().Fields {
				if relatedField.RelName == field.RelInverse {
					bsonField = relatedField.BSONName
				}
			}
			if bsonField == "" {
				panic("fire: missing field for inverse relationship: " + field.RelInverse)
			}

			// add relationship
			dependentResources[collection] = bsonField
		}

		// handle to-one and to-many relationships
		if field.ToOne || field.ToMany {
			// get related model
			relatedModel := catalog.Find(field.RelType)
			if relatedModel == nil {
				panic("fire: missing model in catalog: " + field.RelType)
			}

			// add relationship
			references[field.BSONName] = relatedModel.Meta().Collection
		}
	}

	// create callbacks
	cb1 := DependentResourcesValidator(dependentResources)
	cb2 := VerifyReferencesValidator(references)

	// create a combined callback
	cb := Combine(cb1, cb2)

	return cb
}

// MatchingReferencesValidator compares the model with one related model or all
// related models and checks if the specified references are exactly shared.
//
// The target model is defined by passing its collection and the referencing
// field on the current model. The matcher is defined by passing pairs of
// database fields on the target and current model:
//
//	MatchingReferencesValidator(C(&Blog{}), F(&Post{}, "Blog"), map[string]string{
//		F(&Blog{}, "Owner"): F(&Post{}, "Owner"),
//	})
//
// To-many, optional to-many and has-many relationships are supported both for
// the initial reference and in the matchers.
//
func MatchingReferencesValidator(collection, reference string, matcher map[string]string) *Callback {
	return Only(C("fire/MatchingReferencesValidator", func(ctx *Context) error {
		// prepare ids
		var ids []bson.ObjectId

		// get reference
		ref := ctx.Model.MustGet(reference)

		// handle to-one reference
		if id, ok := ref.(bson.ObjectId); ok {
			ids = []bson.ObjectId{id}
		}

		// handle optional to-one reference
		if oid, ok := ref.(*bson.ObjectId); ok {
			// return immediately if not set
			if oid == nil {
				return nil
			}

			// set id
			ids = []bson.ObjectId{*oid}
		}

		// handle to-many reference
		if list, ok := ref.([]bson.ObjectId); ok {
			// return immediately if empty
			if len(list) == 0 {
				return nil
			}

			// set list
			ids = list
		}

		// ensure list is unique
		ids = coal.Unique(ids)

		// prepare query
		query := bson.M{
			"_id": bson.M{
				"$in": ids,
			},
		}

		// add matchers as-is
		for targetField, modelField := range matcher {
			query[targetField] = ctx.Model.MustGet(modelField)
		}

		// find matching documents
		ctx.Tracer.Push("mgo/Query.Count")
		ctx.Tracer.Tag("query", query)
		n, err := ctx.Store.DB().C(collection).Find(query).Count()
		if err != nil {
			return Fatal(err)
		}
		ctx.Tracer.Pop()

		// return error if a document is missing (does not match)
		if n != len(ids) {
			return errors.New("references do not match")
		}

		return nil
	}), false, Create, Update)
}

// UniqueAttributeValidator ensures that the specified attribute of the
// controllers Model will remain unique among the specified filters.
//
// The unique attribute is defines as the first argument. Filters are defined
// by passing a list of database fields:
//
//	UniqueAttributeValidator(F(&Blog{}, "Name"), F(&Blog{}, "Creator"))
//
func UniqueAttributeValidator(uniqueAttribute string, filters ...string) *Callback {
	return Only(C("fire/UniqueAttributeValidator", func(ctx *Context) error {
		// check if field has changed
		if ctx.Operation == Update {
			// get original model
			original, err := ctx.Original()
			if err != nil {
				return err
			}

			// return if field has not been changed
			if reflect.DeepEqual(ctx.Model.MustGet(uniqueAttribute), original.MustGet(uniqueAttribute)) {
				return nil
			}
		}

		// prepare query
		query := bson.M{
			uniqueAttribute: ctx.Model.MustGet(uniqueAttribute),
		}

		// add filters
		for _, field := range filters {
			query[field] = ctx.Model.MustGet(field)
		}

		// count
		ctx.Tracer.Push("mgo/Query.Count")
		ctx.Tracer.Tag("query", query)
		n, err := ctx.Store.C(ctx.Model).Find(query).Limit(1).Count()
		if err != nil {
			return Fatal(err)
		} else if n != 0 {
			return fmt.Errorf("attribute %s is not unique", uniqueAttribute)
		}
		ctx.Tracer.Pop()

		return nil
	}), false, Create, Update)
}
<commit_msg>moved<commit_after>package fire

import (
	"errors"
	"fmt"
	"reflect"

	"github.com/256dpi/fire/coal"

	"gopkg.in/mgo.v2/bson"
)

// C is a short-hand function to construct a callback. It will also add tracing
// code around the execution of the callback.
func C(name string, h Handler) *Callback {
	return &Callback{
		Handler: func(ctx *Context) error {
			// begin trace
			ctx.Tracer.Push(name)

			// call handler
			err := h(ctx)
			if err != nil {
				return err
			}

			// finish trace
			ctx.Tracer.Pop()

			return nil
		},
	}
}

// A Callback is called during the request processing flow of a controller.
//
// Note: If the callback returns an error wrapped using Fatal() the API returns
// an InternalServerError status and the error will be logged. All other errors
// are serialized to an error object and returned.
type Callback struct {
	Handler Handler
}

// Only will return a callback that runs the specified callback only when one
// of the supplied operations match.
func Only(cb *Callback, force bool, ops ...Operation) *Callback {
	// construct name
	name := fmt.Sprintf("fire/Only(%s)", joinOperations(ops, ","))

	return C(name, func(ctx *Context) error {
		// check operation
		for _, a := range ops {
			// run callback if operation is allowed
			if a == ctx.Operation {
				return cb.Handler(ctx)
			}
		}

		// check force
		if force {
			panic(fmt.Sprintf("unsupported operation"))
		}

		return nil
	})
}

// Except will return a callback that runs the specified callback only when none
// of the supplied operations match.
func Except(cb *Callback, force bool, ops ...Operation) *Callback {
	// construct name
	name := fmt.Sprintf("fire/Except(%s)", joinOperations(ops, ","))

	return C(name, func(ctx *Context) error {
		// check operation
		for _, a := range ops {
			if a == ctx.Operation {
				// check force
				if force {
					panic(fmt.Sprintf("unsupported operation"))
				}

				return nil
			}
		}

		return cb.Handler(ctx)
	})
}

// Combine will return a callback that runs all the specified callbacks in order
// until an error is returned.
func Combine(cbs ...*Callback) *Callback {
	return C("fire/Combine", func(ctx *Context) error {
		// run all callbacks
		for _, cb := range cbs {
			err := cb.Handler(ctx)
			if err != nil {
				return err
			}
		}

		return nil
	})
}

// BasicAuthorizer authorizes requests based on a simple credentials list.
func BasicAuthorizer(credentials map[string]string) *Callback {
	return C("fire/BasicAuthorizer", func(ctx *Context) error {
		// check for credentials
		user, password, ok := ctx.HTTPRequest.BasicAuth()
		if !ok {
			return errors.New("access denied")
		}

		// check if credentials match
		if val, ok := credentials[user]; !ok || val != password {
			return errors.New("access denied")
		}

		return nil
	})
}

// ModelValidator performs a validation of the model using the Validate
// function.
func ModelValidator() *Callback {
	return Only(C("fire/ModelValidator", func(ctx *Context) error {
		// TODO: Add error source pointer.
		return coal.Validate(ctx.Model)
	}), false, Create, Update)
}

type noDefault int

// NoDefault marks the specified field to have no default that needs to be
// enforced while executing the ProtectedFieldsValidator.
const NoDefault noDefault = iota

// ProtectedFieldsValidator compares protected attributes against their
// default (during Create) or stored value (during Update) and returns an error
// if they have been changed.
//
// Attributes are defined by passing pairs of fields and default values:
//
//	ProtectedFieldsValidator(map[string]interface{}{
//		F(&Post{}, "Title"): NoDefault, // can only be set during Create
//		F(&Post{}, "Link"):  "",        // is fixed and cannot be changed
//	})
//
// The special NoDefault value can be provided to skip the default enforcement
// on Create.
//
func ProtectedFieldsValidator(fields map[string]interface{}) *Callback {
	return Only(C("fire/ProtectedFieldsValidator", func(ctx *Context) error {
		// handle resource creation
		if ctx.Operation == Create {
			// check all fields
			for field, def := range fields {
				// skip fields that have no default
				if def == NoDefault {
					continue
				}

				// check equality
				if !reflect.DeepEqual(ctx.Model.MustGet(field), def) {
					return errors.New("field " + field + " is protected")
				}
			}
		}

		// handle resource updates
		if ctx.Operation == Update {
			// read the original
			original, err := ctx.Original()
			if err != nil {
				return err
			}

			// check all fields
			for field := range fields {
				// check equality
				if !reflect.DeepEqual(ctx.Model.MustGet(field), original.MustGet(field)) {
					return errors.New("field " + field + " is protected")
				}
			}
		}

		return nil
	}), false, Create, Update)
}

// DependentResourcesValidator counts documents in the supplied collections
// and returns an error if some get found. This callback is meant to protect
// resources from breaking relations when requested to be deleted.
//
// Dependent resources are defined by passing pairs of collections and database
// fields that hold the current models id:
//
//	DependentResourcesValidator(map[string]string{
//		C(&Post{}): F(&Post{}, "Author"),
//		C(&Comment{}): F(&Comment{}, "Author"),
//	})
//
func DependentResourcesValidator(resources map[string]string) *Callback {
	return Only(C("DependentResourcesValidator", func(ctx *Context) error {
		// check all relations
		for coll, field := range resources {
			// prepare query
			query := bson.M{field: ctx.Model.ID()}

			// count referencing documents
			ctx.Tracer.Push("mgo/Query.Count")
			ctx.Tracer.Tag("query", query)
			n, err := ctx.Store.DB().C(coll).Find(query).Limit(1).Count()
			if err != nil {
				return Fatal(err)
			}
			ctx.Tracer.Pop()

			// return err of documents are found
			if n != 0 {
				return errors.New("resource has dependent resources")
			}
		}

		// pass validation
		return nil
	}), false, Delete)
}

// VerifyReferencesValidator makes sure all references in the document are
// existing by counting the references on the related collections.
//
// References are defined by passing pairs of database fields and collections of
// models whose ids might be referenced on the current model:
//
//	VerifyReferencesValidator(map[string]string{
//		F(&Comment{}, "Post"): C(&Post{}),
//		F(&Comment{}, "Author"): C(&User{}),
//	})
//
// The callbacks supports to-one, optional to-one and to-many relationships.
//
func VerifyReferencesValidator(references map[string]string) *Callback {
	return Only(C("fire/VerifyReferencesValidator", func(ctx *Context) error {
		// check all references
		for field, collection := range references {
			// read referenced id
			ref := ctx.Model.MustGet(field)

			// continue if reference is not set
			if oid, ok := ref.(*bson.ObjectId); ok && oid == nil {
				continue
			}

			// continue if slice is empty
			if ids, ok := ref.([]bson.ObjectId); ok && ids == nil {
				continue
			}

			// handle to-many relationships
			if ids, ok := ref.([]bson.ObjectId); ok {
				// prepare query
				query := bson.M{"_id": bson.M{"$in": ids}}

				// count entities in database
				ctx.Tracer.Push("mgo/Query.Count")
				ctx.Tracer.Tag("query", query)
				n, err := ctx.Store.DB().C(collection).Find(query).Count()
				if err != nil {
					return Fatal(err)
				}
				ctx.Tracer.Pop()

				// check for existence
				if n != len(ids) {
					return errors.New("missing references for field " + field)
				}

				continue
			}

			// handle to-one relationships

			// count entities in database
			ctx.Tracer.Push("mgo/Query.Count")
			ctx.Tracer.Tag("id", ref)
			n, err := ctx.Store.DB().C(collection).FindId(ref).Limit(1).Count()
			if err != nil {
				return Fatal(err)
			}
			ctx.Tracer.Pop()

			// check for existence
			if n != 1 {
				return errors.New("missing reference for field " + field)
			}
		}

		// pass validation
		return nil
	}), false, Create, Update)
}

// RelationshipValidator makes sure all relationships of a model are correct and
// in place. It does so by creating a DependentResourcesValidator and a
// VerifyReferencesValidator based on the specified model and catalog.
func RelationshipValidator(model coal.Model, catalog *coal.Catalog, excludedFields ...string) *Callback {
	// prepare lists
	dependentResources := make(map[string]string)
	references := make(map[string]string)

	// iterate through all fields
	for _, field := range coal.Init(model).Meta().Fields {
		// exclude field if requested
		if stringInList(field.Name, excludedFields) {
			continue
		}

		// handle has-one and has-many relationships
		if field.HasOne || field.HasMany {
			// get related model
			relatedModel := catalog.Find(field.RelType)
			if relatedModel == nil {
				panic("fire: missing model in catalog: " + field.RelType)
			}

			// get collection
			collection := relatedModel.Meta().Collection

			// get related bson field
			bsonField := ""
			for _, relatedField := range relatedModel.Meta().Fields {
				if relatedField.RelName == field.RelInverse {
					bsonField = relatedField.BSONName
				}
			}
			if bsonField == "" {
				panic("fire: missing field for inverse relationship: " + field.RelInverse)
			}

			// add relationship
			dependentResources[collection] = bsonField
		}

		// handle to-one and to-many relationships
		if field.ToOne || field.ToMany {
			// get related model
			relatedModel := catalog.Find(field.RelType)
			if relatedModel == nil {
				panic("fire: missing model in catalog: " + field.RelType)
			}

			// add relationship
			references[field.BSONName] = relatedModel.Meta().Collection
		}
	}

	// create callbacks
	cb1 := DependentResourcesValidator(dependentResources)
	cb2 := VerifyReferencesValidator(references)

	// create a combined callback
	cb := Combine(cb1, cb2)

	return cb
}

// MatchingReferencesValidator compares the model with one related model or all
// related models and checks if the specified references are exactly shared.
//
// The target model is defined by passing its collection and the referencing
// field on the current model. The matcher is defined by passing pairs of
// database fields on the target and current model:
//
//	MatchingReferencesValidator(C(&Blog{}), F(&Post{}, "Blog"), map[string]string{
//		F(&Blog{}, "Owner"): F(&Post{}, "Owner"),
//	})
//
// To-many, optional to-many and has-many relationships are supported both for
// the initial reference and in the matchers.
//
func MatchingReferencesValidator(collection, reference string, matcher map[string]string) *Callback {
	return Only(C("fire/MatchingReferencesValidator", func(ctx *Context) error {
		// prepare ids
		var ids []bson.ObjectId

		// get reference
		ref := ctx.Model.MustGet(reference)

		// handle to-one reference
		if id, ok := ref.(bson.ObjectId); ok {
			ids = []bson.ObjectId{id}
		}

		// handle optional to-one reference
		if oid, ok := ref.(*bson.ObjectId); ok {
			// return immediately if not set
			if oid == nil {
				return nil
			}

			// set id
			ids = []bson.ObjectId{*oid}
		}

		// handle to-many reference
		if list, ok := ref.([]bson.ObjectId); ok {
			// return immediately if empty
			if len(list) == 0 {
				return nil
			}

			// set list
			ids = list
		}

		// ensure list is unique
		ids = coal.Unique(ids)

		// prepare query
		query := bson.M{
			"_id": bson.M{
				"$in": ids,
			},
		}

		// add matchers as-is
		for targetField, modelField := range matcher {
			query[targetField] = ctx.Model.MustGet(modelField)
		}

		// find matching documents
		ctx.Tracer.Push("mgo/Query.Count")
		ctx.Tracer.Tag("query", query)
		n, err := ctx.Store.DB().C(collection).Find(query).Count()
		if err != nil {
			return Fatal(err)
		}
		ctx.Tracer.Pop()

		// return error if a document is missing (does not match)
		if n != len(ids) {
			return errors.New("references do not match")
		}

		return nil
	}), false, Create, Update)
}

// UniqueAttributeValidator ensures that the specified attribute of the
// controllers Model will remain unique among the specified filters.
//
// The unique attribute is defines as the first argument. Filters are defined
// by passing a list of database fields:
//
//	UniqueAttributeValidator(F(&Blog{}, "Name"), F(&Blog{}, "Creator"))
//
func UniqueAttributeValidator(uniqueAttribute string, filters ...string) *Callback {
	return Only(C("fire/UniqueAttributeValidator", func(ctx *Context) error {
		// check if field has changed
		if ctx.Operation == Update {
			// get original model
			original, err := ctx.Original()
			if err != nil {
				return err
			}

			// return if field has not been changed
			if reflect.DeepEqual(ctx.Model.MustGet(uniqueAttribute), original.MustGet(uniqueAttribute)) {
				return nil
			}
		}

		// prepare query
		query := bson.M{
			uniqueAttribute: ctx.Model.MustGet(uniqueAttribute),
		}

		// add filters
		for _, field := range filters {
			query[field] = ctx.Model.MustGet(field)
		}

		// count
		ctx.Tracer.Push("mgo/Query.Count")
		ctx.Tracer.Tag("query", query)
		n, err := ctx.Store.C(ctx.Model).Find(query).Limit(1).Count()
		if err != nil {
			return Fatal(err)
		} else if n != 0 {
			return fmt.Errorf("attribute %s is not unique", uniqueAttribute)
		}
		ctx.Tracer.Pop()

		return nil
	}), false, Create, Update)
}
<|endoftext|>


<commit_before>package telebot

// CallbackEndpoint is an interface any element capable
// of responding to a callback `\f<unique>`.
type CallbackEndpoint interface {
	CallbackUnique() string
}

// Callback object represents a query from a callback button in an
// inline keyboard.
type Callback struct {
	ID string `json:"id"`

	// For message sent to channels, Sender may be empty
	Sender *User `json:"from"`

	// Message will be set if the button that originated the query
	// was attached to a message sent by a bot.
	Message *Message `json:"message"`

	// MessageID will be set if the button was attached to a message
	// sent via the bot in inline mode.
	MessageID string `json:"inline_message_id"`

	// Data associated with the callback button. Be aware that
	// a bad client can send arbitrary data in this field.
	Data string `json:"data"`
}

// CallbackResponse builds a response to a Callback query.
//
// See also: https://core.telegram.org/bots/api#answerCallbackQuery
type CallbackResponse struct {
	// The ID of the callback to which this is a response.
	//
	// Note: Telebot sets this field automatically!
	CallbackID string `json:"callback_query_id"`

	// Text of the notification. If not specified, nothing will be
	// shown to the user.
	Text string `json:"text,omitempty"`

	// (Optional) If true, an alert will be shown by the client instead
	// of a notification at the top of the chat screen. Defaults to false.
	ShowAlert bool `json:"show_alert,omitempty"`

	// (Optional) URL that will be opened by the user's client.
	// If you have created a Game and accepted the conditions via
	// @BotFather, specify the URL that opens your game.
	//
	// Note: this will only work if the query comes from a game
	// callback button. Otherwise, you may use deep-linking:
	// https://telegram.me/your_bot?start=XXXX
	URL string `json:"url,omitempty"`
}

// InlineButton represents a button displayed in the message.
type InlineButton struct {
	// Unique slagish name for this kind of button,
	// try to be as specific as possible.
	//
	// It will be used as a callback endpoint.
	Unique string `json:"unique,omitempty"`

	Text        string `json:"text"`
	URL         string `json:"url,omitempty"`
	Data        string `json:"callback_data,omitempty"`
	InlineQuery string `json:"switch_inline_query,omitempty"`

	Action func(*Callback) `json:"-"`
}

// CallbackUnique returns InlineButto.Unique.
func (t *InlineButton) CallbackUnique() string {
	return "\f" + t.Unique
}

// CallbackUnique returns KeyboardButton.Text.
func (t *ReplyButton) CallbackUnique() string {
	return t.Text
}
<commit_msg>Add switch_inline_query_current_chat field to InlineButton<commit_after>package telebot

// CallbackEndpoint is an interface any element capable
// of responding to a callback `\f<unique>`.
type CallbackEndpoint interface {
	CallbackUnique() string
}

// Callback object represents a query from a callback button in an
// inline keyboard.
type Callback struct {
	ID string `json:"id"`

	// For message sent to channels, Sender may be empty
	Sender *User `json:"from"`

	// Message will be set if the button that originated the query
	// was attached to a message sent by a bot.
	Message *Message `json:"message"`

	// MessageID will be set if the button was attached to a message
	// sent via the bot in inline mode.
	MessageID string `json:"inline_message_id"`

	// Data associated with the callback button. Be aware that
	// a bad client can send arbitrary data in this field.
	Data string `json:"data"`
}

// CallbackResponse builds a response to a Callback query.
//
// See also: https://core.telegram.org/bots/api#answerCallbackQuery
type CallbackResponse struct {
	// The ID of the callback to which this is a response.
	//
	// Note: Telebot sets this field automatically!
	CallbackID string `json:"callback_query_id"`

	// Text of the notification. If not specified, nothing will be
	// shown to the user.
	Text string `json:"text,omitempty"`

	// (Optional) If true, an alert will be shown by the client instead
	// of a notification at the top of the chat screen. Defaults to false.
	ShowAlert bool `json:"show_alert,omitempty"`

	// (Optional) URL that will be opened by the user's client.
	// If you have created a Game and accepted the conditions via
	// @BotFather, specify the URL that opens your game.
	//
	// Note: this will only work if the query comes from a game
	// callback button. Otherwise, you may use deep-linking:
	// https://telegram.me/your_bot?start=XXXX
	URL string `json:"url,omitempty"`
}

// InlineButton represents a button displayed in the message.
type InlineButton struct {
	// Unique slagish name for this kind of button,
	// try to be as specific as possible.
	//
	// It will be used as a callback endpoint.
	Unique string `json:"unique,omitempty"`

	Text            string `json:"text"`
	URL             string `json:"url,omitempty"`
	Data            string `json:"callback_data,omitempty"`
	InlineQuery     string `json:"switch_inline_query,omitempty"`
	InlineQueryChat string `json:"switch_inline_query_current_chat"`

	Action func(*Callback) `json:"-"`
}

// CallbackUnique returns InlineButto.Unique.
func (t *InlineButton) CallbackUnique() string {
	return "\f" + t.Unique
}

// CallbackUnique returns KeyboardButton.Text.
func (t *ReplyButton) CallbackUnique() string {
	return t.Text
}
<|endoftext|>