<commit_before>// Adapted from http://www.ffconsultancy.com/languages/ray_tracer/comparison.html

package main
import fmt "fmt"
import io "io"
import os "os"
import math "math"

var infinity float = float(math.Inf(1));
var delta float = float(math.Sqrt(1.19209E-07)); // sqrt(float_epsilon)

func sqrtf(a float) float {
    return float(math.Sqrt(float64(a)));
}

type Vec3 struct {
    x, y, z float;
}

func (v *Vec3) add(b *Vec3) *Vec3 {
    v.x += b.x;
    v.y += b.y;
    v.z += b.z;
    return v;
}

func (v *Vec3) sub(b *Vec3) *Vec3 {
    v.x -= b.x;
    v.y -= b.y;
    v.z -= b.z;
    return v;
}

func (v *Vec3) mulf(b float) *Vec3 {
    v.x *= b;
    v.y *= b;
    v.z *= b;
    return v;
}

func (v *Vec3) normalize() *Vec3 {
    return v.mulf(1.0 / sqrtf(v.dot(v)));
}

func (v Vec3) normalized() Vec3 {
    return *v.mulf(1.0 / sqrtf(v.dot(&v)));
}

func (v *Vec3) dot(b *Vec3) float {
    return v.x * b.x + v.y * b.y + v.z * b.z;
}

func vec3add(a Vec3, b Vec3) Vec3 {
    a.x += b.x;
    a.y += b.y;
    a.z += b.z;
    return a;
}

func vec3sub(a Vec3, b Vec3) Vec3 {
    a.x -= b.x;
    a.y -= b.y;
    a.z -= b.z;
    return a;
}

func vec3mulf(a Vec3, b float) Vec3 {
    a.x *= b;
    a.y *= b;
    a.z *= b;
    return a;
}

func vec3dot(a Vec3, b Vec3) float {
    return a.x * b.x + a.y * b.y + a.z * b.z;
}

func normalize(a Vec3) Vec3 {
    return vec3mulf(a, 1.0 / sqrtf(vec3dot(a,a)));
}

var backgroundColor Vec3 = Vec3{0.1,0.1,0.1};
var diffuseSphereColor Vec3 = Vec3{0.0, 0.7, 0.0};
var ambientSphereColor Vec3 = Vec3{0.2, 0.3, 0.2};

type Sphere struct {
    center Vec3;
    radius float;
}

type Hit struct {
    distance float;
    pos Vec3;
    sphere *Sphere;
}

var hitinfinity Hit = Hit{infinity, Vec3{0, 0, 0}, nil};

type Ray struct {
    orig, dir Vec3;
}

type Geometry interface {
    Intersect(h *Hit, r *Ray) *Hit;
    Print(); // Temporary until fmt handles interfaces.
}

func (s *Sphere) RaySphere(r *Ray) float {
    v := vec3sub(s.center, r.orig);
    b := vec3dot(v, r.dir);
    disc := b*b - vec3dot(v, v) + s.radius * s.radius;
    if disc < 0.0 {
        return infinity;
    }
    d := sqrtf(disc);
    t2 := b + d;
    if t2 < 0.0 {
        return infinity;
    }
    t1 := b - d;
    if t1 > 0.0 {
        return t1;
    }
    return t2;
}

func (s *Sphere) Intersect(h *Hit, r *Ray) *Hit {
    lambda := s.RaySphere(r);
    if lambda >= h.distance {
        return h;
    }
    return &Hit{lambda, normalize(vec3add(r.orig, vec3sub(vec3mulf(r.dir, lambda), s.center))), s};
}

func (s *Sphere) Print() {
    fmt.Println("Sphere:",*s);
}

type Group struct {
    bound Sphere;
    children []Geometry;
}

func (g *Group) Print() {
    fmt.Print("Group:");
    g.bound.Print();
    for i := 0; i < len(g.children); i++ {
        fmt.Print("  ");
        g.children[i].Print();
    }
}

func (g *Group) Intersect(h *Hit, r *Ray) *Hit {
    l := g.bound.RaySphere(r);
    if l >= h.distance {
        return h;
    }
    hit2 := h;
    for i := 0; i < len(g.children); i++ {
      hit2 = g.children[i].Intersect(hit2, r);
    }
    return hit2;
}

func NewGroup(bound Sphere, children []Geometry) *Group {
    g := new(Group);
    g.bound = bound;
    g.children = children;
    return g;
}

func intersect(r *Ray, s Geometry) *Hit {
    return s.Intersect(&hitinfinity, r);
}

type Scene struct {
    light Vec3;
    g Geometry;
}

func createScene(light Vec3, g Geometry) *Scene {
    scene := new(Scene);
    scene.light = light;
    scene.g = g;
    return scene;
}

func (s *Scene) rayTrace(r *Ray) Vec3 {
    h := intersect(r, s.g);
    if h.distance == infinity {
        return backgroundColor;
    }
    g := vec3dot(h.pos, s.light);
    if g >= 0.0 {
        // The hit intersection is in shadow
        return ambientSphereColor;
    }
    p := vec3add(r.orig, vec3add(vec3mulf(r.dir, h.distance), vec3mulf(h.pos, delta)));
    if intersect(&Ray{p, vec3mulf(s.light, -1.0)}, s.g).distance < infinity {
        // There`s an object between us and the light.
        return ambientSphereColor;
    }
    litColor := vec3mulf(diffuseSphereColor, -g);
    totalColor := vec3add(ambientSphereColor, litColor);
    return totalColor;
}

func createSpherePyramid(level int, c Vec3, r float) Geometry {
    s := new(Sphere);
    s.center = c;
    s.radius = r;
    if level == 1 {
        return s;
    }
    children := make([]Geometry, 5);
    i := 0;
    children[i] = s;
    i++;
    rn := 3.0*r/sqrtf(12.0);
    for dz := -1; dz<=1; dz+=2 {
        for dx := -1; dx<=1; dx+=2 {
            newc := vec3add(c, vec3mulf(Vec3{float(dx), 1.0, float(dz)}, rn));
            children[i] = createSpherePyramid(level-1, newc, r * 0.5);
            i++;
        }
    }
    return NewGroup(Sphere{c, 3*r}, children);
}

type Texture struct {
    w, h int;
    buf []byte;
}

func NewTexture(w int, h int) *Texture {
    t := new(Texture);
    t.w = w;
    t.h = h;
    t.buf = make([]byte, w * h * 4);
    return t;
}

func (t *Texture) WritePPM(w io.Writer) {
    fmt.Fprintf(w,"P6\n%d\n%d\n%d\n", t.w, t.h, 255);
    buf := make([]byte, t.w * 3);
    i := 0;
    for y := t.h - 1; y >= 0; y-- {
        o := 0;
        for x := 0; x < t.w; x++ {
            buf[o] = t.buf[i];
            buf[o + 1] = t.buf[i + 1];
            buf[o + 2] = t.buf[i + 2];
            o += 3;
            i += 4;
        }
        w.Write(buf);
    }
}

func formatTGAShort(buf []byte, offset int, value int) {
    buf[offset] = byte(value & 0xff);
    buf[offset+1] = byte((value >> 8) & 0xff);
}

func (t *Texture) WriteTGA(w io.Writer) {
    header := make([]byte, 18);
    header[0] = 0; // ID length
    header[1] = 0; // Color map type
    header[2] = 2; // Image type (2 == uncompressed true-color image)
    header[3] = 0;
    header[4] = 0;
    header[5] = 0;
    header[6] = 0;
    header[7] = 0;
    formatTGAShort(header, 8, 0);
    formatTGAShort(header, 10, 0);
    formatTGAShort(header, 12, t.w);
    formatTGAShort(header, 14, t.h);
    header[16] = 24; // pixel depth
    header[17] = 0;

    w.Write(header);
    buf := make([]byte, t.w * 3);
    i := 4 * t.w * (t.h - 1);
    for y := 0; y < t.h; y++ {
        o := 0;
        for x := 0; x < t.w; x++ {
            buf[o] = t.buf[i+2];
            buf[o + 1] = t.buf[i + 1];
            buf[o + 2] = t.buf[i + 0];
            o += 3;
            i += 4;
        }
        i -= 2 * 4 * t.w;
        w.Write(buf);
    }
}

func (t *Texture) SetRgba(x int, y int, r byte, g byte, b byte, a byte) {
    o := 4 * (t.w  * y + x);
    t.buf[o] = r;
    t.buf[o + 1] = g;
    t.buf[o + 2] = b;
    t.buf[o + 3] = a;
}

func f2b(f float) byte {
    scaled := 0.5 + f * 255.0;
    switch {
    case scaled < 0:
        scaled = 0;
    case scaled > 255:
        scaled = 255;
    }
    return byte(scaled);
}

func (t *Texture) SetV(x int, y int, v Vec3) {
    t.SetRgba(x, y, f2b(v.x), f2b(v.y), f2b(v.z), 255);
}

type Rect struct {
    l int;
    t int;
    r int;
    b int;
}

func newRect(l, t, r, b int ) *Rect {
    rect := new(Rect);
    rect.l = l;
    rect.t = t;
    rect.r = r;
    rect.b = b;
    return rect;
}

func (r *Rect) isEmpty() bool {
    return r.l == r.r || r.t == r.b;
}

type Camera struct {
    eye Vec3;
    w int;
    h int;
}

func (c *Camera) setRayDirForPixel(r *Ray, x,y float){
	r.dir.x = x - float(c.w) * 0.5;
	r.dir.y = y - float(c.h) * 0.5;
	r.dir.z = float(c.w);
	r.dir.normalize();
}

type Renderer struct {
    scene *Scene;
    t *Texture;
    cam *Camera;
    ss int;					// oversampling
    xres, yres int;			// image resolution
    jobChan chan Rect;
    quitChan chan bool;
    joinChan chan bool;
}

func (ren *Renderer) renderRect(tint Vec3, r *Rect) {
	ray := Ray{orig:ren.cam.eye};

    for y := r.t; y < r.b; y++ {
        for x := r.l; x < r.r; x++ {
        	var g Vec3;
			for ssx := 0; ssx < ren.ss; ssx++ {
				for ssy := 0; ssy < ren.ss; ssy++ {
					var xres float = float(x) + float(ssx) / float(ren.ss);
					var yres float = float(y) + float(ssy) / float(ren.ss);

					ren.cam.setRayDirForPixel(&ray, xres, yres);
					if false {
						// Draw a rectangle around the bounds of our rendering.
						if y == r.t || y == r.b-1 || x == r.l || x == r.r-1 {
							g = tint;
						} else {
							g = vec3add(g, ren.scene.rayTrace(&ray));
						}
					} else {
						g = vec3add(g, ren.scene.rayTrace(&ray));
					}
				} // END for each y subsample
            } // END for each x subsample

            ren.t.SetV(x, ren.cam.h - (y + 1), vec3mulf(g, 1.0 / float(ren.ss * ren.ss)));

        }// END for each x pixel
    }// END for each y pixel
}

func (renderer *Renderer) worker(tint Vec3) {
    jobChan := renderer.jobChan;
    for {
        select {
		case r := <- jobChan:
		    renderer.renderRect(tint, &r);
		case <- renderer.quitChan:
			renderer.joinChan <- true;
			return;
        }
    }
}

func main() {
    level := 8;
    n := 512;
    chunkw := 16;
    chunkh := 16;
    w := n;
    h := n;
    workers := 8;
    ss := 1;			// oversampling - use 4 to get 16 samples
    t := NewTexture(w, h);
    light := normalize(Vec3{-1.0, -3.0, 2.0});
    sp := createSpherePyramid(level, Vec3{0.0, -1.0, 0.0}, 1.0);
    scene := createScene(light, sp);
    eye := Vec3{0, 0, -4.0};
    camera := Camera{eye, n, n};
    quitChan := make(chan bool);
    joinChan := make(chan bool);
    jobChan := make(chan Rect);
    renderer := Renderer{scene, t, &camera, ss, w, h, jobChan, quitChan, joinChan};
    for w := 0; w < workers; w++ {
        tint := Vec3{0.5, float(w) / float(workers), 0.5};
        go renderer.worker(tint);
    }
    for y := 0; y < h; y += chunkh {
        for x := 0; x < w; x += chunkw {
            renderer.jobChan <- Rect{x, y, x + chunkw, y + chunkh};
        }
    }
    for w := 0; w < workers; w++ {
        renderer.quitChan <- true;
    }
    for w := 0; w < workers; w++ {
        <- renderer.joinChan;
    }
    od, err := os.Open("out.tga", os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0666);
    if err == nil {
        t.WriteTGA(od);
        od.Close();
    }
}
<commit_msg>compiles with go 1<commit_after>// Adapted from http://www.ffconsultancy.com/languages/ray_tracer/comparison.html

package main
import fmt "fmt"
import io "io"
import os "os"
import math "math"

var infinity float64 = float64(math.Inf(1));
var delta float64 = float64(math.Sqrt(1.19209E-07)); // sqrt(float_epsilon)

func sqrtf(a float64) float64 {
    return float64(math.Sqrt(float64(a)));
}

type Vec3 struct {
    x, y, z float64;
}

func (v *Vec3) add(b *Vec3) *Vec3 {
    v.x += b.x;
    v.y += b.y;
    v.z += b.z;
    return v;
}

func (v *Vec3) sub(b *Vec3) *Vec3 {
    v.x -= b.x;
    v.y -= b.y;
    v.z -= b.z;
    return v;
}

func (v *Vec3) mulf(b float64) *Vec3 {
    v.x *= b;
    v.y *= b;
    v.z *= b;
    return v;
}

func (v *Vec3) normalize() *Vec3 {
    return v.mulf(1.0 / sqrtf(v.dot(v)));
}

func (v Vec3) normalized() Vec3 {
    return *v.mulf(1.0 / sqrtf(v.dot(&v)));
}

func (v *Vec3) dot(b *Vec3) float64 {
    return v.x * b.x + v.y * b.y + v.z * b.z;
}

func vec3add(a Vec3, b Vec3) Vec3 {
    a.x += b.x;
    a.y += b.y;
    a.z += b.z;
    return a;
}

func vec3sub(a Vec3, b Vec3) Vec3 {
    a.x -= b.x;
    a.y -= b.y;
    a.z -= b.z;
    return a;
}

func vec3mulf(a Vec3, b float64) Vec3 {
    a.x *= b;
    a.y *= b;
    a.z *= b;
    return a;
}

func vec3dot(a Vec3, b Vec3) float64 {
    return a.x * b.x + a.y * b.y + a.z * b.z;
}

func normalize(a Vec3) Vec3 {
    return vec3mulf(a, 1.0 / sqrtf(vec3dot(a,a)));
}

var backgroundColor Vec3 = Vec3{0.1,0.1,0.1};
var diffuseSphereColor Vec3 = Vec3{0.0, 0.7, 0.0};
var ambientSphereColor Vec3 = Vec3{0.2, 0.3, 0.2};

type Sphere struct {
    center Vec3;
    radius float64;
}

type Hit struct {
    distance float64;
    pos Vec3;
    sphere *Sphere;
}

var hitinfinity Hit = Hit{infinity, Vec3{0, 0, 0}, nil};

type Ray struct {
    orig, dir Vec3;
}

type Geometry interface {
    Intersect(h *Hit, r *Ray) *Hit;
    Print(); // Temporary until fmt handles interfaces.
}

func (s *Sphere) RaySphere(r *Ray) float64 {
    v := vec3sub(s.center, r.orig);
    b := vec3dot(v, r.dir);
    disc := b*b - vec3dot(v, v) + s.radius * s.radius;
    if disc < 0.0 {
        return infinity;
    }
    d := sqrtf(disc);
    t2 := b + d;
    if t2 < 0.0 {
        return infinity;
    }
    t1 := b - d;
    if t1 > 0.0 {
        return t1;
    }
    return t2;
}

func (s *Sphere) Intersect(h *Hit, r *Ray) *Hit {
    lambda := s.RaySphere(r);
    if lambda >= h.distance {
        return h;
    }
    return &Hit{lambda, normalize(vec3add(r.orig, vec3sub(vec3mulf(r.dir, lambda), s.center))), s};
}

func (s *Sphere) Print() {
    fmt.Println("Sphere:",*s);
}

type Group struct {
    bound Sphere;
    children []Geometry;
}

func (g *Group) Print() {
    fmt.Print("Group:");
    g.bound.Print();
    for i := 0; i < len(g.children); i++ {
        fmt.Print("  ");
        g.children[i].Print();
    }
}

func (g *Group) Intersect(h *Hit, r *Ray) *Hit {
    l := g.bound.RaySphere(r);
    if l >= h.distance {
        return h;
    }
    hit2 := h;
    for i := 0; i < len(g.children); i++ {
      hit2 = g.children[i].Intersect(hit2, r);
    }
    return hit2;
}

func NewGroup(bound Sphere, children []Geometry) *Group {
    g := new(Group);
    g.bound = bound;
    g.children = children;
    return g;
}

func intersect(r *Ray, s Geometry) *Hit {
    return s.Intersect(&hitinfinity, r);
}

type Scene struct {
    light Vec3;
    g Geometry;
}

func createScene(light Vec3, g Geometry) *Scene {
    scene := new(Scene);
    scene.light = light;
    scene.g = g;
    return scene;
}

func (s *Scene) rayTrace(r *Ray) Vec3 {
    h := intersect(r, s.g);
    if h.distance == infinity {
        return backgroundColor;
    }
    g := vec3dot(h.pos, s.light);
    if g >= 0.0 {
        // The hit intersection is in shadow
        return ambientSphereColor;
    }
    p := vec3add(r.orig, vec3add(vec3mulf(r.dir, h.distance), vec3mulf(h.pos, delta)));
    if intersect(&Ray{p, vec3mulf(s.light, -1.0)}, s.g).distance < infinity {
        // There`s an object between us and the light.
        return ambientSphereColor;
    }
    litColor := vec3mulf(diffuseSphereColor, -g);
    totalColor := vec3add(ambientSphereColor, litColor);
    return totalColor;
}

func createSpherePyramid(level int, c Vec3, r float64) Geometry {
    s := new(Sphere);
    s.center = c;
    s.radius = r;
    if level == 1 {
        return s;
    }
    children := make([]Geometry, 5);
    i := 0;
    children[i] = s;
    i++;
    rn := 3.0*r/sqrtf(12.0);
    for dz := -1; dz<=1; dz+=2 {
        for dx := -1; dx<=1; dx+=2 {
            newc := vec3add(c, vec3mulf(Vec3{float64(dx), 1.0, float64(dz)}, rn));
            children[i] = createSpherePyramid(level-1, newc, r * 0.5);
            i++;
        }
    }
    return NewGroup(Sphere{c, 3*r}, children);
}

type Texture struct {
    w, h int;
    buf []byte;
}

func NewTexture(w int, h int) *Texture {
    t := new(Texture);
    t.w = w;
    t.h = h;
    t.buf = make([]byte, w * h * 4);
    return t;
}

func (t *Texture) WritePPM(w io.Writer) {
    fmt.Fprintf(w,"P6\n%d\n%d\n%d\n", t.w, t.h, 255);
    buf := make([]byte, t.w * 3);
    i := 0;
    for y := t.h - 1; y >= 0; y-- {
        o := 0;
        for x := 0; x < t.w; x++ {
            buf[o] = t.buf[i];
            buf[o + 1] = t.buf[i + 1];
            buf[o + 2] = t.buf[i + 2];
            o += 3;
            i += 4;
        }
        w.Write(buf);
    }
}

func formatTGAShort(buf []byte, offset int, value int) {
    buf[offset] = byte(value & 0xff);
    buf[offset+1] = byte((value >> 8) & 0xff);
}

func (t *Texture) WriteTGA(w io.Writer) {
    header := make([]byte, 18);
    header[0] = 0; // ID length
    header[1] = 0; // Color map type
    header[2] = 2; // Image type (2 == uncompressed true-color image)
    header[3] = 0;
    header[4] = 0;
    header[5] = 0;
    header[6] = 0;
    header[7] = 0;
    formatTGAShort(header, 8, 0);
    formatTGAShort(header, 10, 0);
    formatTGAShort(header, 12, t.w);
    formatTGAShort(header, 14, t.h);
    header[16] = 24; // pixel depth
    header[17] = 0;

    w.Write(header);
    buf := make([]byte, t.w * 3);
    i := 4 * t.w * (t.h - 1);
    for y := 0; y < t.h; y++ {
        o := 0;
        for x := 0; x < t.w; x++ {
            buf[o] = t.buf[i+2];
            buf[o + 1] = t.buf[i + 1];
            buf[o + 2] = t.buf[i + 0];
            o += 3;
            i += 4;
        }
        i -= 2 * 4 * t.w;
        w.Write(buf);
    }
}

func (t *Texture) SetRgba(x int, y int, r byte, g byte, b byte, a byte) {
    o := 4 * (t.w  * y + x);
    t.buf[o] = r;
    t.buf[o + 1] = g;
    t.buf[o + 2] = b;
    t.buf[o + 3] = a;
}

func f2b(f float64) byte {
    scaled := 0.5 + f * 255.0;
    switch {
    case scaled < 0:
        scaled = 0;
    case scaled > 255:
        scaled = 255;
    }
    return byte(scaled);
}

func (t *Texture) SetV(x int, y int, v Vec3) {
    t.SetRgba(x, y, f2b(v.x), f2b(v.y), f2b(v.z), 255);
}

type Rect struct {
    l int;
    t int;
    r int;
    b int;
}

func newRect(l, t, r, b int ) *Rect {
    rect := new(Rect);
    rect.l = l;
    rect.t = t;
    rect.r = r;
    rect.b = b;
    return rect;
}

func (r *Rect) isEmpty() bool {
    return r.l == r.r || r.t == r.b;
}

type Camera struct {
    eye Vec3;
    w int;
    h int;
}

func (c *Camera) setRayDirForPixel(r *Ray, x,y float64){
	r.dir.x = x - float64(c.w) * 0.5;
	r.dir.y = y - float64(c.h) * 0.5;
	r.dir.z = float64(c.w);
	r.dir.normalize();
}

type Renderer struct {
    scene *Scene;
    t *Texture;
    cam *Camera;
    ss int;					// oversampling
    xres, yres int;			// image resolution
    jobChan chan Rect;
    quitChan chan bool;
    joinChan chan bool;
}

func (ren *Renderer) renderRect(tint Vec3, r *Rect) {
	ray := Ray{orig:ren.cam.eye};

    for y := r.t; y < r.b; y++ {
        for x := r.l; x < r.r; x++ {
        	var g Vec3;
			for ssx := 0; ssx < ren.ss; ssx++ {
				for ssy := 0; ssy < ren.ss; ssy++ {
					var xres float64 = float64(x) + float64(ssx) / float64(ren.ss);
					var yres float64 = float64(y) + float64(ssy) / float64(ren.ss);

					ren.cam.setRayDirForPixel(&ray, xres, yres);
					if false {
						// Draw a rectangle around the bounds of our rendering.
						if y == r.t || y == r.b-1 || x == r.l || x == r.r-1 {
							g = tint;
						} else {
							g = vec3add(g, ren.scene.rayTrace(&ray));
						}
					} else {
						g = vec3add(g, ren.scene.rayTrace(&ray));
					}
				} // END for each y subsample
            } // END for each x subsample

            ren.t.SetV(x, ren.cam.h - (y + 1), vec3mulf(g, 1.0 / float64(ren.ss * ren.ss)));

        }// END for each x pixel
    }// END for each y pixel
}

func (renderer *Renderer) worker(tint Vec3) {
    jobChan := renderer.jobChan;
    for {
        select {
		case r := <- jobChan:
		    renderer.renderRect(tint, &r);
		case <- renderer.quitChan:
			renderer.joinChan <- true;
			return;
        }
    }
}

func main() {
    level := 8;
    n := 512;
    chunkw := 16;
    chunkh := 16;
    w := n;
    h := n;
    workers := 8;
    ss := 1;			// oversampling - use 4 to get 16 samples
    t := NewTexture(w, h);
    light := normalize(Vec3{-1.0, -3.0, 2.0});
    sp := createSpherePyramid(level, Vec3{0.0, -1.0, 0.0}, 1.0);
    scene := createScene(light, sp);
    eye := Vec3{0, 0, -4.0};
    camera := Camera{eye, n, n};
    quitChan := make(chan bool);
    joinChan := make(chan bool);
    jobChan := make(chan Rect);
    renderer := Renderer{scene, t, &camera, ss, w, h, jobChan, quitChan, joinChan};
    for w := 0; w < workers; w++ {
        tint := Vec3{0.5, float64(w) / float64(workers), 0.5};
        go renderer.worker(tint);
    }
    for y := 0; y < h; y += chunkh {
        for x := 0; x < w; x += chunkw {
            renderer.jobChan <- Rect{x, y, x + chunkw, y + chunkh};
        }
    }
    for w := 0; w < workers; w++ {
        renderer.quitChan <- true;
    }
    for w := 0; w < workers; w++ {
        <- renderer.joinChan;
    }
    od, err := os.OpenFile("out.tga", os.O_WRONLY | os.O_CREATE | os.O_TRUNC, 0666);
    if err == nil {
        t.WriteTGA(od);
        od.Close();
    }
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"os"
	"os/exec"
	"path"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/archive"
)

var cmdCp = &Command{
	Exec:        runCp,
	UsageLine:   "cp [OPTIONS] SERVER:PATH HOSTDIR|-",
	Description: "Copy files/folders from a PATH on the server to a HOSTDIR on the host",
	Help:        "Copy files/folders from a PATH on the server to a HOSTDIR on the host\nrunning the command. Use '-' to write the data as a tar file to STDOUT.",
}

func init() {
	cmdCp.Flag.BoolVar(&cpHelp, []string{"h", "-help"}, false, "Print usage")
}

// Flags
var cpHelp bool // -h, --help flag

func runCp(cmd *Command, args []string) {
	if cpHelp {
		cmd.PrintUsage()
	}
	if len(args) != 2 {
		cmd.PrintShortUsage()
	}

	hostPath := args[1]

	serverParts := strings.Split(args[0], ":")
	if len(serverParts) != 2 {
		log.Fatalf("usage: scw %s", cmd.UsageLine)
	}

	serverID := cmd.API.GetServerID(serverParts[0])

	server, err := cmd.API.GetServer(serverID)
	if err != nil {
		log.Fatalf("Failed to get server information for %s: %v", serverID, err)
	}

	// remoteCommand is executed on the remote server
	// it streams a tarball raw content
	remoteCommand := []string{"tar"}
	remoteCommand = append(remoteCommand, "-C", path.Dir(serverParts[1]))
	if os.Getenv("DEBUG") == "1" {
		remoteCommand = append(remoteCommand, "-v")
	}
	remoteCommand = append(remoteCommand, "-cf", "-")
	remoteCommand = append(remoteCommand, path.Base(serverParts[1]))

	// execCmd contains the ssh connection + the remoteCommand
	execCmd := append(NewSSHExecCmd(server.PublicAddress.IP, false, remoteCommand))
	log.Debugf("Executing: ssh %s", strings.Join(execCmd, " "))
	spawn := exec.Command("ssh", execCmd...)

	tarOutputStream, err := spawn.StdoutPipe()
	if err != nil {
		log.Fatal(err)
	}
	tarErrorStream, err := spawn.StderrPipe()
	if err != nil {
		log.Fatal(err)
	}

	err = spawn.Start()
	if err != nil {
		log.Fatalf("Failed to start ssh command: %v", err)
	}

	defer spawn.Wait()

	io.Copy(os.Stderr, tarErrorStream)

	if hostPath == "-" {
		log.Debugf("Writing tarOutputStream(%v) to os.Stdout(%v)", tarOutputStream, os.Stdout)
		written, err := io.Copy(os.Stdout, tarOutputStream)
		log.Debugf("%d bytes written", written)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		err = archive.Untar(tarOutputStream, hostPath, &archive.TarOptions{NoLchown: true})
		if err != nil {
			log.Fatalf("Failed to untar the remote archive: %v", err)
		}
	}
}
<commit_msg>wip #56<commit_after>package main

import (
	"io"
	"os"
	"os/exec"
	"path"
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/docker/pkg/archive"
)

var cmdCp = &Command{
	Exec:        runCp,
	UsageLine:   "cp [OPTIONS] SERVER:PATH|HOSTPATH|- SERVER:PATH|HOSTPATH|-",
	Description: "Copy files/folders from a PATH on the server to a HOSTDIR on the host",
	Help:        "Copy files/folders from a PATH on the server to a HOSTDIR on the host\nrunning the command. Use '-' to write the data as a tar file to STDOUT.",
	Examples: `
    $ scw cp path/to/my/local/file myserver:path
    $ scw cp myserver:path path/to/my/local/file
    $ scw cp myserver:path myserver2:path
`,
}

func init() {
	cmdCp.Flag.BoolVar(&cpHelp, []string{"h", "-help"}, false, "Print usage")
}

// Flags
var cpHelp bool // -h, --help flag

func runCp(cmd *Command, args []string) {
	if cpHelp {
		cmd.PrintUsage()
	}
	if len(args) != 2 {
		cmd.PrintShortUsage()
	}

	var tarOutputStream io.ReadCloser

	// source
	source := args[0]
	if strings.Index(source, ":") > -1 { // source server address
		serverParts := strings.Split(args[0], ":")
		if len(serverParts) != 2 {
			log.Fatalf("usage: scw %s", cmd.UsageLine)
		}

		serverID := cmd.API.GetServerID(serverParts[0])

		server, err := cmd.API.GetServer(serverID)
		if err != nil {
			log.Fatalf("Failed to get server information for %s: %v", serverID, err)
		}

		// remoteCommand is executed on the remote server
		// it streams a tarball raw content
		remoteCommand := []string{"tar"}
		remoteCommand = append(remoteCommand, "-C", path.Dir(serverParts[1]))
		if os.Getenv("DEBUG") == "1" {
			remoteCommand = append(remoteCommand, "-v")
		}
		remoteCommand = append(remoteCommand, "-cf", "-")
		remoteCommand = append(remoteCommand, path.Base(serverParts[1]))

		// execCmd contains the ssh connection + the remoteCommand
		execCmd := append(NewSSHExecCmd(server.PublicAddress.IP, false, remoteCommand))
		log.Debugf("Executing: ssh %s", strings.Join(execCmd, " "))
		spawn := exec.Command("ssh", execCmd...)

		tarOutputStream, err = spawn.StdoutPipe()
		if err != nil {
			log.Fatal(err)
		}
		tarErrorStream, err := spawn.StderrPipe()
		if err != nil {
			log.Fatal(err)
		}

		err = spawn.Start()
		if err != nil {
			log.Fatalf("Failed to start ssh command: %v", err)
		}

		defer spawn.Wait()

		io.Copy(os.Stderr, tarErrorStream)
	} else if source == "-" { // stdin
		log.Fatalf("'scw cp - ...' is not yet implemented")
	} else { // source host path
		log.Fatalf("'scw cp HOSTPATH ...' is not yet implemented")
	}

	// destination
	destination := args[1]
	if strings.Index(destination, ":") > -1 { // destination server address
		log.Fatalf("'scw cp ... SERVER' is not yet implemented")
	} else if destination == "-" { // stdout
		log.Debugf("Writing tarOutputStream(%v) to os.Stdout(%v)", tarOutputStream, os.Stdout)
		written, err := io.Copy(os.Stdout, tarOutputStream)
		log.Debugf("%d bytes written", written)
		if err != nil {
			log.Fatal(err)
		}

	} else { // destination host path
		err := archive.Untar(tarOutputStream, destination, &archive.TarOptions{NoLchown: true})
		if err != nil {
			log.Fatalf("Failed to untar the remote archive: %v", err)
		}
	}
}
<|endoftext|>


<commit_before>package gotutum

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
)

var User string
var Apikey string

func AuthForUser() string {

	return "ApiKey " + User + ":" + Apikey
}

type Objects struct {
	Status string
}

func ListContainers() interface{} {

	url := "https://app.tutum.co/api/v1/container/"

	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", AuthForUser())
	req.Header.Add("Accept", "application/json")

	log.Println(url)
	// log.Println(req.Header)
	response, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}

	data, err := ioutil.ReadAll(response.Body)
	// log.Println(string(data))

	//Response has a tag named objects.  Need to unmarshal objects, then work with objects.

	var f interface{}
	err = json.Unmarshal(data, &f)

	m := f.(map[string]interface{})
	// log.Println(m)
	// for k, v := range m {
	// 	switch vv := v.(type) {
	// 	case string:
	// 		log.Println(k, "is string", vv)
	// 	case int:
	// 		log.Println(k, "is int", vv)
	// 	case []interface{}:
	// 		log.Println(k, "is an array:")
	// 		//Loops through containers.  We want an object. made out of u.
	// 		for i, u := range vv {
	// 			log.Println(i)
	// 			log.Println(u)
	// 			log.Println("Gets here")
	// 		}
	// 	default:
	// 		log.Println(k, "is of a type I don't know how to handle")
	// 	}
	// }

	// for k, v := range m {
	// 	if v.(type) == []interface{} {
	// 		log.Println(v)
	// 	}
	// }
	for k, v := range m {
		// log.Println()
		// log.Println(k)
		// log.Println(v)
		if k == "objects" {
			return v
		}
	}
	return nil
}
<commit_msg>refactoring the http call<commit_after>package gotutum

import (
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
)

var User string
var Apikey string

func AuthForUser() string {

	return "ApiKey " + User + ":" + Apikey
}

type Objects struct {
	Status string
}

func ListContainers() interface{} {

	url := "https://app.tutum.co/api/v1/container/"
	request := "GET"

	f := TutumCall(url, request)
	m := f.(map[string]interface{})

	//Relies on the array of items being named objects.
	for k, v := range m {
		if k == "objects" {
			return v
		}
	}
	return nil
}

func TutumCall(url string, requestType string) interface{} {

	client := &http.Client{}
	req, err := http.NewRequest("GET", url, nil)

	req.Header.Add("Authorization", AuthForUser())
	req.Header.Add("Accept", "application/json")

	log.Println(url)
	// log.Println(req.Header)
	response, err := client.Do(req)
	if err != nil {
		log.Println(err)
	}

	data, err := ioutil.ReadAll(response.Body)
	// log.Println(string(data))

	//Response has a tag named objects.  Need to unmarshal objects, then work with objects.

	var f interface{}
	err = json.Unmarshal(data, &f)
}
<|endoftext|>


<commit_before>package yorm

import (
	"database/sql"
	"sync"
)

const (

	//DriverMySQL driver for mysql
	DriverMySQL         = "mysql"
	defaultExecutorName = "default"

	shortSimpleTimeFormat = "2006-01-02"
	longSimpleTimeFormat  = "2006-01-02 15:04:05"
)

var (
	dbMutex         sync.RWMutex
	defaultExecutor *executor
	// stmt to prepare db conn
	stmtMap     = map[string]*sql.Stmt{}
	executorMap = map[string]*executor{}
)

type sqlExecutor interface {
	SelectByPK(i interface{}, tableName ...string) error
	Select(i interface{}, clause string, args ...interface{}) error
	Insert(i interface{}, args ...string) (int64, error)
	Update(clause string, args ...interface{}) (int64, error)
	Delete(clause string, args ...interface{}) (int64, error)
	SetMaxIdleConns(int)
	SetMaxOpenConns(int)
}

type executor struct {
	*sql.DB
}

//RegisterWithName register a database dirver with specific name.
func RegisterWithName(dsn, name string, driver ...string) (err error) {
	if executorMap[name] != nil {
		return nil
	}
	dbMutex.Lock()
	defer dbMutex.Unlock()
	var sqlDb *sql.DB
	sqlDb, err = sql.Open(append(driver, DriverMySQL)[0], dsn)
	if sqlDb == nil {
		return err
	}
	err = sqlDb.Ping()
	if err != nil {
		return err
	}
	executorMap[name] = &executor{sqlDb}
	return nil
}

//Register register a database driver.
func Register(dsn string, driver ...string) error {
	err := RegisterWithName(dsn, defaultExecutorName, driver...)
	if err == nil {
		defaultExecutor = executorMap[defaultExecutorName]
	}
	return err
}

type nilSqlExecutor struct {
}

func (n nilSqlExecutor) SelectByPK(i interface{}, args ...string) error {
	return ErrNilSqlExecutor
}
func (n nilSqlExecutor) Select(i interface{}, clause string, args ...interface{}) error {
	return ErrNilSqlExecutor
}
func (n nilSqlExecutor) Insert(i interface{}, args ...string) (int64, error) {
	return 0, ErrNilSqlExecutor
}
func (n nilSqlExecutor) Update(clause string, args ...interface{}) (int64, error) {
	return 0, ErrNilSqlExecutor
}
func (n nilSqlExecutor) Delete(clause string, args ...interface{}) (int64, error) {
	return 0, ErrNilSqlExecutor
}
func (n nilSqlExecutor) SetMaxOpenConns(int) {
}
func (n nilSqlExecutor) SetMaxIdleConns(int) {
}

//Using using the executor with name, if not exist  nilSqlExecutor instead.
func Using(name string) sqlExecutor {
	if e, ok := executorMap[name]; ok {
		return e
	}
	return nilSqlExecutor{}
}

func (ex *executor) getStmt(clause string) (*sql.Stmt, error) {

	var err error
	clause, err = validClause(clause)
	if err != nil {
		return nil, err
	}
	stmt := stmtMap[clause]
	if stmt == nil {
		stmt, err = ex.Prepare(clause)
		if stmt != nil {
			stmtMap[clause] = stmt
		}
	}
	return stmt, err
}

func (ex *executor) exec(clause string, args ...interface{}) (sql.Result, error) {
	stmt, err := ex.getStmt(clause)
	if err != nil {
		return nil, err
	}
	yogger.Debug("%s;%v", clause, args)
	return stmt.Exec(args...)
}

func validClause(clause string) (string, error) {
	return clause, nil
}
<commit_msg>如果是单句执行，不需要再使用stmt，防止过多的prepare<commit_after>package yorm

import (
	"database/sql"
	"sync"
)

const (

	//DriverMySQL driver for mysql
	DriverMySQL         = "mysql"
	defaultExecutorName = "default"

	shortSimpleTimeFormat = "2006-01-02"
	longSimpleTimeFormat  = "2006-01-02 15:04:05"
)

var (
	dbMutex         sync.RWMutex
	defaultExecutor *executor
	// stmt to prepare db conn
	stmtMap     = map[string]*sql.Stmt{}
	executorMap = map[string]*executor{}
)

type sqlExecutor interface {
	SelectByPK(i interface{}, tableName ...string) error
	Select(i interface{}, clause string, args ...interface{}) error
	Insert(i interface{}, args ...string) (int64, error)
	Update(clause string, args ...interface{}) (int64, error)
	Delete(clause string, args ...interface{}) (int64, error)
	SetMaxIdleConns(int)
	SetMaxOpenConns(int)
}

type executor struct {
	*sql.DB
}

//RegisterWithName register a database dirver with specific name.
func RegisterWithName(dsn, name string, driver ...string) (err error) {
	if executorMap[name] != nil {
		return nil
	}
	dbMutex.Lock()
	defer dbMutex.Unlock()
	var sqlDb *sql.DB
	sqlDb, err = sql.Open(append(driver, DriverMySQL)[0], dsn)
	if sqlDb == nil {
		return err
	}
	err = sqlDb.Ping()
	if err != nil {
		return err
	}
	executorMap[name] = &executor{sqlDb}
	return nil
}

//Register register a database driver.
func Register(dsn string, driver ...string) error {
	err := RegisterWithName(dsn, defaultExecutorName, driver...)
	if err == nil {
		defaultExecutor = executorMap[defaultExecutorName]
	}
	return err
}

type nilSqlExecutor struct {
}

func (n nilSqlExecutor) SelectByPK(i interface{}, args ...string) error {
	return ErrNilSqlExecutor
}
func (n nilSqlExecutor) Select(i interface{}, clause string, args ...interface{}) error {
	return ErrNilSqlExecutor
}
func (n nilSqlExecutor) Insert(i interface{}, args ...string) (int64, error) {
	return 0, ErrNilSqlExecutor
}
func (n nilSqlExecutor) Update(clause string, args ...interface{}) (int64, error) {
	return 0, ErrNilSqlExecutor
}
func (n nilSqlExecutor) Delete(clause string, args ...interface{}) (int64, error) {
	return 0, ErrNilSqlExecutor
}
func (n nilSqlExecutor) SetMaxOpenConns(int) {
}
func (n nilSqlExecutor) SetMaxIdleConns(int) {
}

//Using using the executor with name, if not exist  nilSqlExecutor instead.
func Using(name string) sqlExecutor {
	if e, ok := executorMap[name]; ok {
		return e
	}
	return nilSqlExecutor{}
}

func (ex *executor) getStmt(clause string) (*sql.Stmt, error) {

	var err error
	clause, err = validClause(clause)
	if err != nil {
		return nil, err
	}
	stmt := stmtMap[clause]
	if stmt == nil {
		stmt, err = ex.Prepare(clause)
		if stmt != nil {
			stmtMap[clause] = stmt
		}
	}
	return stmt, err
}

func (ex *executor) exec(clause string, args ...interface{}) (sql.Result, error) {
	//如果是单句执行，不需要再使用stmt，防止过多的prepare
	if len(args) == 0 {
		return ex.Exec(clause)
	}
	stmt, err := ex.getStmt(clause)
	if err != nil {
		return nil, err
	}
	yogger.Debug("%s;%v", clause, args)
	return stmt.Exec(args...)
}

func validClause(clause string) (string, error) {
	return clause, nil
}
<|endoftext|>


<commit_before>// +build mage

package main

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/gohugoio/hugo/codegen"
	"github.com/gohugoio/hugo/resources/page/page_generate"

	"github.com/magefile/mage/mg"
	"github.com/magefile/mage/sh"
)

const (
	packageName  = "github.com/gohugoio/hugo"
	noGitLdflags = "-X $PACKAGE/common/hugo.buildDate=$BUILD_DATE"
)

var ldflags = "-X $PACKAGE/common/hugo.commitHash=$COMMIT_HASH -X $PACKAGE/common/hugo.buildDate=$BUILD_DATE"

// allow user to override go executable by running as GOEXE=xxx make ... on unix-like systems
var goexe = "go"

func init() {
	if exe := os.Getenv("GOEXE"); exe != "" {
		goexe = exe
	}

	// We want to use Go 1.11 modules even if the source lives inside GOPATH.
	// The default is "auto".
	os.Setenv("GO111MODULE", "on")
}

// Build hugo binary
func Hugo() error {
	return sh.RunWith(flagEnv(), goexe, "build", "-ldflags", ldflags, "-tags", buildTags(), packageName)
}

// Build hugo binary with race detector enabled
func HugoRace() error {
	return sh.RunWith(flagEnv(), goexe, "build", "-race", "-ldflags", ldflags, "-tags", buildTags(), packageName)
}

// Install hugo binary
func Install() error {
	return sh.RunWith(flagEnv(), goexe, "install", "-ldflags", ldflags, "-tags", buildTags(), packageName)
}

func flagEnv() map[string]string {
	hash, _ := sh.Output("git", "rev-parse", "--short", "HEAD")
	return map[string]string{
		"PACKAGE":     packageName,
		"COMMIT_HASH": hash,
		"BUILD_DATE":  time.Now().Format("2006-01-02T15:04:05Z0700"),
	}
}

// Generate autogen packages
func Generate() error {
	generatorPackages := []string{
		"tpl/tplimpl/embedded/generate",
		//"resources/page/generate",
	}

	for _, pkg := range generatorPackages {
		if err := sh.RunWith(flagEnv(), goexe, "generate", path.Join(packageName, pkg)); err != nil {
			return err
		}
	}

	dir, _ := os.Getwd()
	c := codegen.NewInspector(dir)

	if err := page_generate.Generate(c); err != nil {
		return err
	}

	goFmtPatterns := []string{
		// TODO(bep) check: stat ./resources/page/*autogen*: no such file or directory
		"./resources/page/page_marshaljson.autogen.go",
		"./resources/page/page_wrappers.autogen.go",
		"./resources/page/zero_file.autogen.go",
	}

	for _, pattern := range goFmtPatterns {
		if err := sh.Run("gofmt", "-w", filepath.FromSlash(pattern)); err != nil {
			return err
		}
	}

	return nil
}

// Generate docs helper
func GenDocsHelper() error {
	return runCmd(flagEnv(), goexe, "run", "-tags", buildTags(), "main.go", "gen", "docshelper")
}

// Build hugo without git info
func HugoNoGitInfo() error {
	ldflags = noGitLdflags
	return Hugo()
}

var docker = sh.RunCmd("docker")

// Build hugo Docker container
func Docker() error {
	if err := docker("build", "-t", "hugo", "."); err != nil {
		return err
	}
	// yes ignore errors here
	docker("rm", "-f", "hugo-build")
	if err := docker("run", "--name", "hugo-build", "hugo ls /go/bin"); err != nil {
		return err
	}
	if err := docker("cp", "hugo-build:/go/bin/hugo", "."); err != nil {
		return err
	}
	return docker("rm", "hugo-build")
}

// Run tests and linters
func Check() {
	if strings.Contains(runtime.Version(), "1.8") {
		// Go 1.8 doesn't play along with go test ./... and /vendor.
		// We could fix that, but that would take time.
		fmt.Printf("Skip Check on %s\n", runtime.Version())
		return
	}

	if runtime.GOARCH == "amd64" && runtime.GOOS != "darwin" {
		mg.Deps(Test386)
	} else {
		fmt.Printf("Skip Test386 on %s and/or %s\n", runtime.GOARCH, runtime.GOOS)
	}

	mg.Deps(Fmt, Vet)

	// don't run two tests in parallel, they saturate the CPUs anyway, and running two
	// causes memory issues in CI.
	mg.Deps(TestRace)
}

func testGoFlags() string {
	if isCI() {
		return ""
	}

	return "-test.short"
}

// Run tests in 32-bit mode
// Note that we don't run with the extended tag. Currently not supported in 32 bit.
func Test386() error {
	env := map[string]string{"GOARCH": "386", "GOFLAGS": testGoFlags()}
	return runCmd(env, goexe, "test", "./...")
}

// Run tests
func Test() error {
	env := map[string]string{"GOFLAGS": testGoFlags()}
	return runCmd(env, goexe, "test", "./...", "-tags", buildTags())
}

// Run tests with race detector
func TestRace() error {
	env := map[string]string{"GOFLAGS": testGoFlags()}
	return runCmd(env, goexe, "test", "-race", "./...", "-tags", buildTags())
}

// Run gofmt linter
func Fmt() error {
	if !isGoLatest() {
		return nil
	}
	pkgs, err := hugoPackages()
	if err != nil {
		return err
	}
	failed := false
	first := true
	for _, pkg := range pkgs {
		files, err := filepath.Glob(filepath.Join(pkg, "*.go"))
		if err != nil {
			return nil
		}
		for _, f := range files {
			// gofmt doesn't exit with non-zero when it finds unformatted code
			// so we have to explicitly look for output, and if we find any, we
			// should fail this target.
			s, err := sh.Output("gofmt", "-l", f)
			if err != nil {
				fmt.Printf("ERROR: running gofmt on %q: %v\n", f, err)
				failed = true
			}
			if s != "" {
				if first {
					fmt.Println("The following files are not gofmt'ed:")
					first = false
				}
				failed = true
				fmt.Println(s)
			}
		}
	}
	if failed {
		return errors.New("improperly formatted go files")
	}
	return nil
}

var (
	pkgPrefixLen = len("github.com/gohugoio/hugo")
	pkgs         []string
	pkgsInit     sync.Once
)

func hugoPackages() ([]string, error) {
	var err error
	pkgsInit.Do(func() {
		var s string
		s, err = sh.Output(goexe, "list", "./...")
		if err != nil {
			return
		}
		pkgs = strings.Split(s, "\n")
		for i := range pkgs {
			pkgs[i] = "." + pkgs[i][pkgPrefixLen:]
		}
	})
	return pkgs, err
}

// Run golint linter
func Lint() error {
	pkgs, err := hugoPackages()
	if err != nil {
		return err
	}
	failed := false
	for _, pkg := range pkgs {
		// We don't actually want to fail this target if we find golint errors,
		// so we don't pass -set_exit_status, but we still print out any failures.
		if _, err := sh.Exec(nil, os.Stderr, nil, "golint", pkg); err != nil {
			fmt.Printf("ERROR: running go lint on %q: %v\n", pkg, err)
			failed = true
		}
	}
	if failed {
		return errors.New("errors running golint")
	}
	return nil
}

//  Run go vet linter
func Vet() error {
	if err := sh.Run(goexe, "vet", "./..."); err != nil {
		return fmt.Errorf("error running go vet: %v", err)
	}
	return nil
}

// Generate test coverage report
func TestCoverHTML() error {
	const (
		coverAll = "coverage-all.out"
		cover    = "coverage.out"
	)
	f, err := os.Create(coverAll)
	if err != nil {
		return err
	}
	defer f.Close()
	if _, err := f.Write([]byte("mode: count")); err != nil {
		return err
	}
	pkgs, err := hugoPackages()
	if err != nil {
		return err
	}
	for _, pkg := range pkgs {
		if err := sh.Run(goexe, "test", "-coverprofile="+cover, "-covermode=count", pkg); err != nil {
			return err
		}
		b, err := ioutil.ReadFile(cover)
		if err != nil {
			if os.IsNotExist(err) {
				continue
			}
			return err
		}
		idx := bytes.Index(b, []byte{'\n'})
		b = b[idx+1:]
		if _, err := f.Write(b); err != nil {
			return err
		}
	}
	if err := f.Close(); err != nil {
		return err
	}
	return sh.Run(goexe, "tool", "cover", "-html="+coverAll)
}

func runCmd(env map[string]string, cmd string, args ...string) error {
	if mg.Verbose() {
		return sh.RunWith(env, cmd, args...)
	}
	output, err := sh.OutputWith(env, cmd, args...)
	if err != nil {
		fmt.Fprint(os.Stderr, output)
	}

	return err
}

func isGoLatest() bool {
	return strings.Contains(runtime.Version(), "1.14")
}

func isCI() bool {
	return os.Getenv("CI") != ""
}

func buildTags() string {
	// To build the extended Hugo SCSS/SASS enabled version, build with
	// HUGO_BUILD_TAGS=extended mage install etc.
	if envtags := os.Getenv("HUGO_BUILD_TAGS"); envtags != "" {
		return envtags
	}
	return "none"
}
<commit_msg>mage: Add uninstall target<commit_after>// +build mage

package main

import (
	"bytes"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"runtime"
	"strings"
	"sync"
	"time"

	"github.com/gohugoio/hugo/codegen"
	"github.com/gohugoio/hugo/resources/page/page_generate"

	"github.com/magefile/mage/mg"
	"github.com/magefile/mage/sh"
)

const (
	packageName  = "github.com/gohugoio/hugo"
	noGitLdflags = "-X $PACKAGE/common/hugo.buildDate=$BUILD_DATE"
)

var ldflags = "-X $PACKAGE/common/hugo.commitHash=$COMMIT_HASH -X $PACKAGE/common/hugo.buildDate=$BUILD_DATE"

// allow user to override go executable by running as GOEXE=xxx make ... on unix-like systems
var goexe = "go"

func init() {
	if exe := os.Getenv("GOEXE"); exe != "" {
		goexe = exe
	}

	// We want to use Go 1.11 modules even if the source lives inside GOPATH.
	// The default is "auto".
	os.Setenv("GO111MODULE", "on")
}

// Build hugo binary
func Hugo() error {
	return sh.RunWith(flagEnv(), goexe, "build", "-ldflags", ldflags, "-tags", buildTags(), packageName)
}

// Build hugo binary with race detector enabled
func HugoRace() error {
	return sh.RunWith(flagEnv(), goexe, "build", "-race", "-ldflags", ldflags, "-tags", buildTags(), packageName)
}

// Install hugo binary
func Install() error {
	return sh.RunWith(flagEnv(), goexe, "install", "-ldflags", ldflags, "-tags", buildTags(), packageName)
}

// Uninstall hugo binary
func Uninstall() error {
	return sh.Run(goexe, "clean", "-i", packageName)
}

func flagEnv() map[string]string {
	hash, _ := sh.Output("git", "rev-parse", "--short", "HEAD")
	return map[string]string{
		"PACKAGE":     packageName,
		"COMMIT_HASH": hash,
		"BUILD_DATE":  time.Now().Format("2006-01-02T15:04:05Z0700"),
	}
}

// Generate autogen packages
func Generate() error {
	generatorPackages := []string{
		"tpl/tplimpl/embedded/generate",
		//"resources/page/generate",
	}

	for _, pkg := range generatorPackages {
		if err := sh.RunWith(flagEnv(), goexe, "generate", path.Join(packageName, pkg)); err != nil {
			return err
		}
	}

	dir, _ := os.Getwd()
	c := codegen.NewInspector(dir)

	if err := page_generate.Generate(c); err != nil {
		return err
	}

	goFmtPatterns := []string{
		// TODO(bep) check: stat ./resources/page/*autogen*: no such file or directory
		"./resources/page/page_marshaljson.autogen.go",
		"./resources/page/page_wrappers.autogen.go",
		"./resources/page/zero_file.autogen.go",
	}

	for _, pattern := range goFmtPatterns {
		if err := sh.Run("gofmt", "-w", filepath.FromSlash(pattern)); err != nil {
			return err
		}
	}

	return nil
}

// Generate docs helper
func GenDocsHelper() error {
	return runCmd(flagEnv(), goexe, "run", "-tags", buildTags(), "main.go", "gen", "docshelper")
}

// Build hugo without git info
func HugoNoGitInfo() error {
	ldflags = noGitLdflags
	return Hugo()
}

var docker = sh.RunCmd("docker")

// Build hugo Docker container
func Docker() error {
	if err := docker("build", "-t", "hugo", "."); err != nil {
		return err
	}
	// yes ignore errors here
	docker("rm", "-f", "hugo-build")
	if err := docker("run", "--name", "hugo-build", "hugo ls /go/bin"); err != nil {
		return err
	}
	if err := docker("cp", "hugo-build:/go/bin/hugo", "."); err != nil {
		return err
	}
	return docker("rm", "hugo-build")
}

// Run tests and linters
func Check() {
	if strings.Contains(runtime.Version(), "1.8") {
		// Go 1.8 doesn't play along with go test ./... and /vendor.
		// We could fix that, but that would take time.
		fmt.Printf("Skip Check on %s\n", runtime.Version())
		return
	}

	if runtime.GOARCH == "amd64" && runtime.GOOS != "darwin" {
		mg.Deps(Test386)
	} else {
		fmt.Printf("Skip Test386 on %s and/or %s\n", runtime.GOARCH, runtime.GOOS)
	}

	mg.Deps(Fmt, Vet)

	// don't run two tests in parallel, they saturate the CPUs anyway, and running two
	// causes memory issues in CI.
	mg.Deps(TestRace)
}

func testGoFlags() string {
	if isCI() {
		return ""
	}

	return "-test.short"
}

// Run tests in 32-bit mode
// Note that we don't run with the extended tag. Currently not supported in 32 bit.
func Test386() error {
	env := map[string]string{"GOARCH": "386", "GOFLAGS": testGoFlags()}
	return runCmd(env, goexe, "test", "./...")
}

// Run tests
func Test() error {
	env := map[string]string{"GOFLAGS": testGoFlags()}
	return runCmd(env, goexe, "test", "./...", "-tags", buildTags())
}

// Run tests with race detector
func TestRace() error {
	env := map[string]string{"GOFLAGS": testGoFlags()}
	return runCmd(env, goexe, "test", "-race", "./...", "-tags", buildTags())
}

// Run gofmt linter
func Fmt() error {
	if !isGoLatest() {
		return nil
	}
	pkgs, err := hugoPackages()
	if err != nil {
		return err
	}
	failed := false
	first := true
	for _, pkg := range pkgs {
		files, err := filepath.Glob(filepath.Join(pkg, "*.go"))
		if err != nil {
			return nil
		}
		for _, f := range files {
			// gofmt doesn't exit with non-zero when it finds unformatted code
			// so we have to explicitly look for output, and if we find any, we
			// should fail this target.
			s, err := sh.Output("gofmt", "-l", f)
			if err != nil {
				fmt.Printf("ERROR: running gofmt on %q: %v\n", f, err)
				failed = true
			}
			if s != "" {
				if first {
					fmt.Println("The following files are not gofmt'ed:")
					first = false
				}
				failed = true
				fmt.Println(s)
			}
		}
	}
	if failed {
		return errors.New("improperly formatted go files")
	}
	return nil
}

var (
	pkgPrefixLen = len("github.com/gohugoio/hugo")
	pkgs         []string
	pkgsInit     sync.Once
)

func hugoPackages() ([]string, error) {
	var err error
	pkgsInit.Do(func() {
		var s string
		s, err = sh.Output(goexe, "list", "./...")
		if err != nil {
			return
		}
		pkgs = strings.Split(s, "\n")
		for i := range pkgs {
			pkgs[i] = "." + pkgs[i][pkgPrefixLen:]
		}
	})
	return pkgs, err
}

// Run golint linter
func Lint() error {
	pkgs, err := hugoPackages()
	if err != nil {
		return err
	}
	failed := false
	for _, pkg := range pkgs {
		// We don't actually want to fail this target if we find golint errors,
		// so we don't pass -set_exit_status, but we still print out any failures.
		if _, err := sh.Exec(nil, os.Stderr, nil, "golint", pkg); err != nil {
			fmt.Printf("ERROR: running go lint on %q: %v\n", pkg, err)
			failed = true
		}
	}
	if failed {
		return errors.New("errors running golint")
	}
	return nil
}

//  Run go vet linter
func Vet() error {
	if err := sh.Run(goexe, "vet", "./..."); err != nil {
		return fmt.Errorf("error running go vet: %v", err)
	}
	return nil
}

// Generate test coverage report
func TestCoverHTML() error {
	const (
		coverAll = "coverage-all.out"
		cover    = "coverage.out"
	)
	f, err := os.Create(coverAll)
	if err != nil {
		return err
	}
	defer f.Close()
	if _, err := f.Write([]byte("mode: count")); err != nil {
		return err
	}
	pkgs, err := hugoPackages()
	if err != nil {
		return err
	}
	for _, pkg := range pkgs {
		if err := sh.Run(goexe, "test", "-coverprofile="+cover, "-covermode=count", pkg); err != nil {
			return err
		}
		b, err := ioutil.ReadFile(cover)
		if err != nil {
			if os.IsNotExist(err) {
				continue
			}
			return err
		}
		idx := bytes.Index(b, []byte{'\n'})
		b = b[idx+1:]
		if _, err := f.Write(b); err != nil {
			return err
		}
	}
	if err := f.Close(); err != nil {
		return err
	}
	return sh.Run(goexe, "tool", "cover", "-html="+coverAll)
}

func runCmd(env map[string]string, cmd string, args ...string) error {
	if mg.Verbose() {
		return sh.RunWith(env, cmd, args...)
	}
	output, err := sh.OutputWith(env, cmd, args...)
	if err != nil {
		fmt.Fprint(os.Stderr, output)
	}

	return err
}

func isGoLatest() bool {
	return strings.Contains(runtime.Version(), "1.14")
}

func isCI() bool {
	return os.Getenv("CI") != ""
}

func buildTags() string {
	// To build the extended Hugo SCSS/SASS enabled version, build with
	// HUGO_BUILD_TAGS=extended mage install etc.
	if envtags := os.Getenv("HUGO_BUILD_TAGS"); envtags != "" {
		return envtags
	}
	return "none"
}
<|endoftext|>


<commit_before>// Package database is a library help for interact with database by model
package gomodel

import (
	"database/sql"

	"github.com/cosiner/gohper/bitset"
)

type (
	// Model represent a database model
	Model interface {
		Table() string
		// Vals store values of fields to given slice
		Vals(fields uint, vals []interface{})
		Ptrs(fields uint, ptrs []interface{})
	}

	// DB holds database connection, all typeinfos, and sql cache
	DB struct {
		// driver string
		*sql.DB
		types map[string]*TypeInfo
		*Cacher

		ModelCount int
	}
)

var (
	FieldCount = bitset.BitCountUint
)

// Open create a database manager and connect to database server
func Open(driver, dsn string, maxIdle, maxOpen int) (*DB, error) {
	db := NewDB()
	err := db.Connect(driver, dsn, maxIdle, maxOpen)

	return db, err
}

// New create a new db
func NewDB() *DB {
	return &DB{
		types:      make(map[string]*TypeInfo),
		ModelCount: 10,
	}
}

// Connect connect to database server
func (db *DB) Connect(driver, dsn string, maxIdle, maxOpen int) error {
	db_, err := sql.Open(driver, dsn)
	if err != nil {
		return err
	}

	db_.SetMaxIdleConns(maxIdle)
	db_.SetMaxOpenConns(maxOpen)
	db.DB = db_
	db.Cacher = NewCacher(Types, db)

	return nil
}

// registerType save type info of model
func (db *DB) registerType(v Model, table string) *TypeInfo {
	ti := parseTypeInfo(v, db)
	db.types[table] = ti

	return ti
}

// TypeInfo return type information of given model
// if type info not exist, it will parseTypeInfo it and save type info
func (db *DB) TypeInfo(v Model) *TypeInfo {
	table := v.Table()
	if ti, has := db.types[table]; has {
		return ti
	}

	return db.registerType(v, table)
}

func FieldVals(fields uint, v Model) []interface{} {
	args := make([]interface{}, FieldCount(fields))
	v.Vals(fields, args)

	return args
}

func FieldPtrs(fields uint, v Model) []interface{} {
	ptrs := make([]interface{}, FieldCount(fields))
	v.Ptrs(fields, ptrs)

	return ptrs
}

func (db *DB) Insert(v Model, fields uint, needId bool) (int64, error) {
	return db.ArgsInsert(v, fields, needId, FieldVals(fields, v))
}

func (db *DB) ArgsInsert(v Model, fields uint, needId bool, args ...interface{}) (int64, error) {
	stmt, err := db.TypeInfo(v).InsertStmt(fields)

	return StmtExec(stmt, err, needId, args...)
}

func (db *DB) Update(v Model, fields, whereFields uint) (int64, error) {
	c1, c2 := FieldCount(fields), FieldCount(whereFields)
	args := make([]interface{}, c1+c2)
	v.Vals(fields, args)
	v.Vals(whereFields, args[c1:])

	return db.ArgsUpdate(v, fields, whereFields, args)
}

func (db *DB) ArgsUpdate(v Model, fields, whereFields uint, args ...interface{}) (int64, error) {
	stmt, err := db.TypeInfo(v).UpdateStmt(fields, whereFields)

	return StmtExec(stmt, err, false, args...)
}

func (db *DB) Delete(v Model, whereFields uint) (int64, error) {
	return db.ArgsDelete(v, whereFields, FieldVals(whereFields, v))
}

func (db *DB) ArgsDelete(v Model, whereFields uint, args ...interface{}) (int64, error) {
	stmt, err := db.TypeInfo(v).DeleteStmt(whereFields)

	return StmtExec(stmt, err, false, args...)
}

// One select one row from database
func (db *DB) One(v Model, fields, whereFields uint) error {
	stmt, err := db.TypeInfo(v).SelectOneStmt(fields, whereFields)
	scanner, rows := StmtQuery(stmt, err, FieldVals(whereFields, v)...)

	return scanner.One(rows, FieldPtrs(fields, v)...)
}

func (db *DB) Limit(s Store, v Model, fields, whereFields uint, start, count int) error {
	c := FieldCount(whereFields)
	args := make([]interface{}, c+2)
	v.Vals(whereFields, args)
	args[c], args[c+1] = start, count

	return db.ArgsLimit(s, v, fields, whereFields, args)
}

func (db *DB) ArgsLimit(s Store, v Model, fields, whereFields uint, args ...interface{}) error {
	stmt, err := db.TypeInfo(v).SelectLimitStmt(fields, whereFields)
	scanner, rows := StmtQuery(stmt, err, args...)

	return scanner.Limit(rows, s, args[len(args)-1].(int))
}

func (db *DB) All(s Store, v Model, fields, whereFields uint) error {
	return db.ArgsAll(s, v, fields, whereFields, FieldVals(whereFields, v))
}

// ArgsAll select all rows, the last two argument must be "start" and "count"
func (db *DB) ArgsAll(s Store, v Model, fields, whereFields uint, args ...interface{}) error {
	stmt, err := db.TypeInfo(v).SelectAllStmt(fields, whereFields)
	scanner, rows := StmtQuery(stmt, err, args...)

	return scanner.All(rows, s, db.ModelCount)
}

// Count return count of rows for model, arguments was extracted from Model
func (db *DB) Count(v Model, whereFields uint) (count int64, err error) {
	return db.ArgsCount(v, whereFields, FieldVals(whereFields, v))
}

//Args Count return count of rows for model use custome arguments
func (db *DB) ArgsCount(v Model, whereFields uint,
	args ...interface{}) (count int64, err error) {
	ti := db.TypeInfo(v)

	stmt, err := ti.CountStmt(whereFields)
	scanner, rows := StmtQuery(stmt, err, args...)

	err = scanner.One(rows, &count)

	return
}

// ExecUpdate execute a update operation, return resolved result
func (db *DB) ExecUpdate(s string, needId bool, args ...interface{}) (ret int64, err error) {
	res, err := db.Exec(s, args...)

	return ResolveResult(res, err, needId)
}

// StmtExec execute stmt with given arguments and resolve the result if error is nil
func StmtExec(stmt *sql.Stmt, err error, needId bool, args ...interface{}) (int64, error) {
	if err != nil {
		return 0, err
	}

	res, err := stmt.Exec(args...)

	return ResolveResult(res, err, needId)
}

// StmtQuery execute the query stmt, error stored in Scanner
func StmtQuery(stmt *sql.Stmt, err error, args ...interface{}) (Scanner, *sql.Rows) {
	if err != nil {
		return Scanner{err}, nil
	}

	rows, err := stmt.Query(args...)
	if err != nil {
		return Scanner{err}, nil
	}

	return normalScanner, rows
}

// ResolveResult resolve sql result, if need id, return last insert id
// else return affected rows count
func ResolveResult(res sql.Result, err error, needId bool) (int64, error) {
	if err != nil {
		return 0, err
	}

	if needId {
		return res.LastInsertId()
	} else {
		return res.RowsAffected()
	}
}
<commit_msg>add StmtUpdate for operation don't need lastInsertId<commit_after>// Package database is a library help for interact with database by model
package gomodel

import (
	"database/sql"

	"github.com/cosiner/gohper/bitset"
)

type (
	// Model represent a database model
	Model interface {
		Table() string
		// Vals store values of fields to given slice
		Vals(fields uint, vals []interface{})
		Ptrs(fields uint, ptrs []interface{})
	}

	// DB holds database connection, all typeinfos, and sql cache
	DB struct {
		// driver string
		*sql.DB
		types map[string]*TypeInfo
		*Cacher

		ModelCount int
	}
)

var (
	FieldCount = bitset.BitCountUint
)

// Open create a database manager and connect to database server
func Open(driver, dsn string, maxIdle, maxOpen int) (*DB, error) {
	db := NewDB()
	err := db.Connect(driver, dsn, maxIdle, maxOpen)

	return db, err
}

// New create a new db
func NewDB() *DB {
	return &DB{
		types:      make(map[string]*TypeInfo),
		ModelCount: 10,
	}
}

// Connect connect to database server
func (db *DB) Connect(driver, dsn string, maxIdle, maxOpen int) error {
	db_, err := sql.Open(driver, dsn)
	if err != nil {
		return err
	}

	db_.SetMaxIdleConns(maxIdle)
	db_.SetMaxOpenConns(maxOpen)
	db.DB = db_
	db.Cacher = NewCacher(Types, db)

	return nil
}

// registerType save type info of model
func (db *DB) registerType(v Model, table string) *TypeInfo {
	ti := parseTypeInfo(v, db)
	db.types[table] = ti

	return ti
}

// TypeInfo return type information of given model
// if type info not exist, it will parseTypeInfo it and save type info
func (db *DB) TypeInfo(v Model) *TypeInfo {
	table := v.Table()
	if ti, has := db.types[table]; has {
		return ti
	}

	return db.registerType(v, table)
}

func FieldVals(fields uint, v Model) []interface{} {
	args := make([]interface{}, FieldCount(fields))
	v.Vals(fields, args)

	return args
}

func FieldPtrs(fields uint, v Model) []interface{} {
	ptrs := make([]interface{}, FieldCount(fields))
	v.Ptrs(fields, ptrs)

	return ptrs
}

func (db *DB) Insert(v Model, fields uint, needId bool) (int64, error) {
	return db.ArgsInsert(v, fields, needId, FieldVals(fields, v))
}

func (db *DB) ArgsInsert(v Model, fields uint, needId bool, args ...interface{}) (int64, error) {
	stmt, err := db.TypeInfo(v).InsertStmt(fields)

	return StmtExec(stmt, err, needId, args...)
}

func (db *DB) Update(v Model, fields, whereFields uint) (int64, error) {
	c1, c2 := FieldCount(fields), FieldCount(whereFields)
	args := make([]interface{}, c1+c2)
	v.Vals(fields, args)
	v.Vals(whereFields, args[c1:])

	return db.ArgsUpdate(v, fields, whereFields, args)
}

func (db *DB) ArgsUpdate(v Model, fields, whereFields uint, args ...interface{}) (int64, error) {
	stmt, err := db.TypeInfo(v).UpdateStmt(fields, whereFields)

	return StmtUpdate(stmt, err, args...)
}

func (db *DB) Delete(v Model, whereFields uint) (int64, error) {
	return db.ArgsDelete(v, whereFields, FieldVals(whereFields, v))
}

func (db *DB) ArgsDelete(v Model, whereFields uint, args ...interface{}) (int64, error) {
	stmt, err := db.TypeInfo(v).DeleteStmt(whereFields)

	return StmtUpdate(stmt, err, args...)
}

// One select one row from database
func (db *DB) One(v Model, fields, whereFields uint) error {
	stmt, err := db.TypeInfo(v).SelectOneStmt(fields, whereFields)
	scanner, rows := StmtQuery(stmt, err, FieldVals(whereFields, v)...)

	return scanner.One(rows, FieldPtrs(fields, v)...)
}

func (db *DB) Limit(s Store, v Model, fields, whereFields uint, start, count int) error {
	c := FieldCount(whereFields)
	args := make([]interface{}, c+2)
	v.Vals(whereFields, args)
	args[c], args[c+1] = start, count

	return db.ArgsLimit(s, v, fields, whereFields, args)
}

func (db *DB) ArgsLimit(s Store, v Model, fields, whereFields uint, args ...interface{}) error {
	stmt, err := db.TypeInfo(v).SelectLimitStmt(fields, whereFields)
	scanner, rows := StmtQuery(stmt, err, args...)

	return scanner.Limit(rows, s, args[len(args)-1].(int))
}

func (db *DB) All(s Store, v Model, fields, whereFields uint) error {
	return db.ArgsAll(s, v, fields, whereFields, FieldVals(whereFields, v))
}

// ArgsAll select all rows, the last two argument must be "start" and "count"
func (db *DB) ArgsAll(s Store, v Model, fields, whereFields uint, args ...interface{}) error {
	stmt, err := db.TypeInfo(v).SelectAllStmt(fields, whereFields)
	scanner, rows := StmtQuery(stmt, err, args...)

	return scanner.All(rows, s, db.ModelCount)
}

// Count return count of rows for model, arguments was extracted from Model
func (db *DB) Count(v Model, whereFields uint) (count int64, err error) {
	return db.ArgsCount(v, whereFields, FieldVals(whereFields, v))
}

//Args Count return count of rows for model use custome arguments
func (db *DB) ArgsCount(v Model, whereFields uint,
	args ...interface{}) (count int64, err error) {
	ti := db.TypeInfo(v)

	stmt, err := ti.CountStmt(whereFields)
	scanner, rows := StmtQuery(stmt, err, args...)

	err = scanner.One(rows, &count)

	return
}

// ExecUpdate execute a update operation, return resolved result
func (db *DB) ExecUpdate(s string, needId bool, args ...interface{}) (ret int64, err error) {
	res, err := db.Exec(s, args...)

	return ResolveResult(res, err, needId)
}

func StmtUpdate(stmt *sql.Stmt, err error, args ...interface{}) (int64, error) {
	return StmtUpdate(stmt, err, args...)
}

// StmtExec execute stmt with given arguments and resolve the result if error is nil
func StmtExec(stmt *sql.Stmt, err error, needId bool, args ...interface{}) (int64, error) {
	if err != nil {
		return 0, err
	}

	res, err := stmt.Exec(args...)
	return ResolveResult(res, err, needId)
}

// StmtQuery execute the query stmt, error stored in Scanner
func StmtQuery(stmt *sql.Stmt, err error, args ...interface{}) (Scanner, *sql.Rows) {
	if err != nil {
		return Scanner{err}, nil
	}

	rows, err := stmt.Query(args...)
	if err != nil {
		return Scanner{err}, nil
	}

	return normalScanner, rows
}

// ResolveResult resolve sql result, if need id, return last insert id
// else return affected rows count
func ResolveResult(res sql.Result, err error, needId bool) (int64, error) {
	if err != nil {
		return 0, err
	}

	if needId {
		return res.LastInsertId()
	} else {
		return res.RowsAffected()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"log"
	"time"

	"github.com/google/uuid"
	_ "github.com/mattn/go-sqlite3"
)

type Event struct {
	EnvelopeId  uuid.UUID
	Id          uuid.UUID
	Date        string
	Name        string
	Balance     int
	Target      int
	MonthTarget int
	Deleted     bool
}

type Envelope struct {
	// Values in Euro-cents
	Id          uuid.UUID
	Balance     int
	Target      int
	Name        string
	MonthDelta  int
	MonthTarget int
}

type DB struct {
	db     *sql.DB
	Events chan Event
}

func OpenDB() (*DB, error) {
	db, err := sql.Open("sqlite3", "envelopes.sqlite")
	if err != nil {
		return nil, err
	}

	rv := &DB{db, make(chan Event)}

	if err := rv.setup(); err != nil {
		return nil, err
	}

	var count int64
	if err := db.QueryRow("SELECT count(*) FROM envelopes WHERE not deleted").Scan(&count); err != nil {
		return nil, err
	}
	log.Printf(`DB contains %d envelopes`, count)

	return rv, nil
}

func (d *DB) Close() error {
	return d.db.Close()
}

func (d *DB) setup() error {
	tx, err := d.db.Begin()
	if err != nil {
		return err
	}

	if _, err := tx.Exec(`
		CREATE TABLE IF NOT EXISTS envelopes
		(id UUID PRIMARY KEY, name STRING,
		 balance INTEGER,
		 target INTEGER, monthtarget INTEGER,
		 deleted BOOLEAN)`); err != nil {
		return err
	}

	if _, err := tx.Exec(`
		CREATE TABLE IF NOT EXISTS history
		(id UUID PRIMARY KEY,
		 envelope UUID, date DATETIME, name STRING,
		 balance INTEGER, target INTEGER, monthtarget INTEGER,
		 deleted BOOLEAN,
		 FOREIGN KEY(envelope) REFERENCES envelopes(id))`); err != nil {
		return err
	}

	return tx.Commit()
}

func (d *DB) AllEnvelopes() []*Envelope {
	rv := []*Envelope{}

	rows, err := d.db.Query(`
		SELECT e.id, e.name, e.balance, e.target, e.monthtarget, h.balance
		FROM envelopes AS e LEFT OUTER JOIN
			(SELECT envelope, sum(balance) AS balance, date
			 FROM history
			 WHERE date > DATE('now', 'start of month')
			 GROUP BY envelope) AS h
		ON e.id = h.envelope
		WHERE not e.deleted`)
	if err != nil {
		log.Printf(`error querying DB: %v`, err)
		return nil
	}
	defer rows.Close()

	for rows.Next() {
		var e Envelope
		var delta sql.NullInt64
		if err := rows.Scan(&e.Id, &e.Name, &e.Balance, &e.Target, &e.MonthTarget, &delta); err != nil {
			log.Printf(`error querying DB: %v`, err)
			return nil
		}
		if delta.Valid {
			e.MonthDelta = int(delta.Int64)
		}
		rv = append(rv, &e)
	}

	return rv
}

func (d *DB) DeleteEnvelope(id uuid.UUID) error {
	evt := Event{
		EnvelopeId: id,
		Id:         uuid.New(),
		Date:       time.Now().String(),
		Deleted:    true,
	}

	select {
	case d.Events <- evt:
		/* Nothing */
	default:
		/* Nothing */
	}

	return d.MergeEvent(evt)
}

func (d *DB) Envelope(id uuid.UUID) (*Envelope, error) {
	tx, err := d.db.Begin()
	if err != nil {
		return nil, err
	}
	defer tx.Rollback()

	return d.envelopeWithTx(tx, id)
}

func (d *DB) envelopeWithTx(tx *sql.Tx, id uuid.UUID) (*Envelope, error) {
	e := Envelope{Id: id}

	err := tx.QueryRow(`
		SELECT id, name, balance, target, monthtarget
		FROM envelopes
		WHERE id = $1 AND not deleted`, id).Scan(&e.Id, &e.Name, &e.Balance, &e.Target, &e.MonthTarget)
	if err == nil {
		return &e, nil
	}

	if _, err := tx.Exec(`
		INSERT INTO envelopes(id, name, balance, target, monthtarget, deleted)
		VALUES ($1, "", 0, 0, 0, 'false')`, e.Id); err != nil {
		return nil, err
	}
	return &e, nil
}

func (d *DB) EnvelopeWithHistory(id uuid.UUID) (*Envelope, []Event, error) {
	events := []Event{}

	tx, err := d.db.Begin()
	if err != nil {
		return nil, events, err
	}
	defer tx.Rollback()

	envelope, err := d.envelopeWithTx(tx, id)
	if err != nil {
		return nil, events, err
	}

	rows, err := tx.Query(`
		SELECT id, envelope, date, name, balance, target, monthtarget, deleted
		FROM history
		WHERE envelope = $1`, id)
	if err != nil {
		return nil, events, err
	}
	defer rows.Close()

	for rows.Next() {
		var e Event
		if err := rows.Scan(&e.Id, &e.EnvelopeId, &e.Date, &e.Name, &e.Balance, &e.Target, &e.MonthTarget, &e.Deleted); err != nil {
			log.Printf(`can't scan event %s: %s`, e.Id, err)
		}
		if e.Deleted {
			e.Name = envelope.Name
		}
		events = append(events, e)
	}

	return envelope, events, nil
}

func (d *DB) MergeEvent(e Event) error {
	log.Printf(`merging event %v`, e.Id)

	tx, err := d.db.Begin()
	if err != nil {
		return err
	}

	env, err := d.envelopeWithTx(tx, e.EnvelopeId)
	if err != nil {
		tx.Rollback()
		return err
	}

	_, err = tx.Exec(`
		INSERT INTO history (id, envelope, name, balance, target, monthtarget, deleted, date)
		VALUES ($1, $2, $3, $4, $5, $6, $7, datetime('now'))`,
		e.Id, e.EnvelopeId, e.Name, e.Balance, e.Target, e.MonthTarget, e.Deleted)
	if err != nil {
		tx.Rollback()
		return err
	}

	if e.Name == "" {
		e.Name = env.Name
	}
	_, err = tx.Exec(`
		UPDATE envelopes
		SET name = $1, balance = $2, target = $3, monthtarget = $4, deleted = $5
		WHERE id = $6`, e.Name, env.Balance+e.Balance, env.Target+e.Target, env.MonthTarget+e.MonthTarget, e.Deleted, env.Id)
	if err != nil {
		tx.Rollback()
		return err
	}

	return tx.Commit()
}

func (d *DB) UpdateEnvelope(id uuid.UUID, name string, dBalance, dTarget, dMonthTarget int, makeRelative bool) error {
	env, err := d.Envelope(id)
	if err != nil {
		return err
	}

	if makeRelative {
		/* Make parameters relative */
		dBalance -= env.Balance
		dTarget -= env.Target
		dMonthTarget -= env.MonthTarget
	}

	log.Printf(`dB: %d dT: %d dMT: %d`, dBalance, dTarget, dMonthTarget)

	evt := Event{
		EnvelopeId:  env.Id,
		Id:          uuid.New(),
		Date:        time.Now().String(),
		Name:        name,
		Balance:     dBalance,
		Target:      dTarget,
		MonthTarget: dMonthTarget,
		Deleted:     false,
	}

	select {
	case d.Events <- evt:
		/* nothing */
	default:
		/* nothing */
	}

	return d.MergeEvent(evt)
}

func (d *DB) PreviewSpread(id uuid.UUID) (string, error) {
	es := d.AllEnvelopes()
	toSpread, err := d.Envelope(id)

	if err != nil {
		return "", err
	}

	totalMonthTarget := int(0)
	for _, e := range es {
		if e.Id == id {
			continue
		}
		totalMonthTarget += e.MonthTarget
	}

	res := fmt.Sprintf("Total Delta: %.02f, Spread balance: %.02f\n\n", float64(totalMonthTarget) / 100, float64(toSpread.Balance) / 100)
	for _, e := range es {
		if e.Id == id || e.MonthTarget == 0 {
			continue
		}

		pct := float64(e.MonthTarget) / float64(totalMonthTarget)
		amount := float64(toSpread.Balance) * pct

		res += fmt.Sprintf("% 25s: spread pct: % 10.02f%%, spread delta: % 10.02f, ", e.Name, pct * 100, amount / 100)
		res += fmt.Sprintf("balance: % 10.02f, new: % 10.02f\n", float64(e.Balance) / 100, (float64(e.Balance) + amount) / 100)

		if err := d.UpdateEnvelope(e.Id, e.Name, int(amount), 0, 0, false); err != nil {
			return "", err
		}
		if err := d.UpdateEnvelope(id, toSpread.Name, int(-amount), 0, 0, false); err != nil {
			return "", err
		}
	}

	return res, nil
}
<commit_msg>gofmt<commit_after>package main

import (
	"database/sql"
	"fmt"
	"log"
	"time"

	"github.com/google/uuid"
	_ "github.com/mattn/go-sqlite3"
)

type Event struct {
	EnvelopeId  uuid.UUID
	Id          uuid.UUID
	Date        string
	Name        string
	Balance     int
	Target      int
	MonthTarget int
	Deleted     bool
}

type Envelope struct {
	// Values in Euro-cents
	Id          uuid.UUID
	Balance     int
	Target      int
	Name        string
	MonthDelta  int
	MonthTarget int
}

type DB struct {
	db     *sql.DB
	Events chan Event
}

func OpenDB() (*DB, error) {
	db, err := sql.Open("sqlite3", "envelopes.sqlite")
	if err != nil {
		return nil, err
	}

	rv := &DB{db, make(chan Event)}

	if err := rv.setup(); err != nil {
		return nil, err
	}

	var count int64
	if err := db.QueryRow("SELECT count(*) FROM envelopes WHERE not deleted").Scan(&count); err != nil {
		return nil, err
	}
	log.Printf(`DB contains %d envelopes`, count)

	return rv, nil
}

func (d *DB) Close() error {
	return d.db.Close()
}

func (d *DB) setup() error {
	tx, err := d.db.Begin()
	if err != nil {
		return err
	}

	if _, err := tx.Exec(`
		CREATE TABLE IF NOT EXISTS envelopes
		(id UUID PRIMARY KEY, name STRING,
		 balance INTEGER,
		 target INTEGER, monthtarget INTEGER,
		 deleted BOOLEAN)`); err != nil {
		return err
	}

	if _, err := tx.Exec(`
		CREATE TABLE IF NOT EXISTS history
		(id UUID PRIMARY KEY,
		 envelope UUID, date DATETIME, name STRING,
		 balance INTEGER, target INTEGER, monthtarget INTEGER,
		 deleted BOOLEAN,
		 FOREIGN KEY(envelope) REFERENCES envelopes(id))`); err != nil {
		return err
	}

	return tx.Commit()
}

func (d *DB) AllEnvelopes() []*Envelope {
	rv := []*Envelope{}

	rows, err := d.db.Query(`
		SELECT e.id, e.name, e.balance, e.target, e.monthtarget, h.balance
		FROM envelopes AS e LEFT OUTER JOIN
			(SELECT envelope, sum(balance) AS balance, date
			 FROM history
			 WHERE date > DATE('now', 'start of month')
			 GROUP BY envelope) AS h
		ON e.id = h.envelope
		WHERE not e.deleted`)
	if err != nil {
		log.Printf(`error querying DB: %v`, err)
		return nil
	}
	defer rows.Close()

	for rows.Next() {
		var e Envelope
		var delta sql.NullInt64
		if err := rows.Scan(&e.Id, &e.Name, &e.Balance, &e.Target, &e.MonthTarget, &delta); err != nil {
			log.Printf(`error querying DB: %v`, err)
			return nil
		}
		if delta.Valid {
			e.MonthDelta = int(delta.Int64)
		}
		rv = append(rv, &e)
	}

	return rv
}

func (d *DB) DeleteEnvelope(id uuid.UUID) error {
	evt := Event{
		EnvelopeId: id,
		Id:         uuid.New(),
		Date:       time.Now().String(),
		Deleted:    true,
	}

	select {
	case d.Events <- evt:
		/* Nothing */
	default:
		/* Nothing */
	}

	return d.MergeEvent(evt)
}

func (d *DB) Envelope(id uuid.UUID) (*Envelope, error) {
	tx, err := d.db.Begin()
	if err != nil {
		return nil, err
	}
	defer tx.Rollback()

	return d.envelopeWithTx(tx, id)
}

func (d *DB) envelopeWithTx(tx *sql.Tx, id uuid.UUID) (*Envelope, error) {
	e := Envelope{Id: id}

	err := tx.QueryRow(`
		SELECT id, name, balance, target, monthtarget
		FROM envelopes
		WHERE id = $1 AND not deleted`, id).Scan(&e.Id, &e.Name, &e.Balance, &e.Target, &e.MonthTarget)
	if err == nil {
		return &e, nil
	}

	if _, err := tx.Exec(`
		INSERT INTO envelopes(id, name, balance, target, monthtarget, deleted)
		VALUES ($1, "", 0, 0, 0, 'false')`, e.Id); err != nil {
		return nil, err
	}
	return &e, nil
}

func (d *DB) EnvelopeWithHistory(id uuid.UUID) (*Envelope, []Event, error) {
	events := []Event{}

	tx, err := d.db.Begin()
	if err != nil {
		return nil, events, err
	}
	defer tx.Rollback()

	envelope, err := d.envelopeWithTx(tx, id)
	if err != nil {
		return nil, events, err
	}

	rows, err := tx.Query(`
		SELECT id, envelope, date, name, balance, target, monthtarget, deleted
		FROM history
		WHERE envelope = $1`, id)
	if err != nil {
		return nil, events, err
	}
	defer rows.Close()

	for rows.Next() {
		var e Event
		if err := rows.Scan(&e.Id, &e.EnvelopeId, &e.Date, &e.Name, &e.Balance, &e.Target, &e.MonthTarget, &e.Deleted); err != nil {
			log.Printf(`can't scan event %s: %s`, e.Id, err)
		}
		if e.Deleted {
			e.Name = envelope.Name
		}
		events = append(events, e)
	}

	return envelope, events, nil
}

func (d *DB) MergeEvent(e Event) error {
	log.Printf(`merging event %v`, e.Id)

	tx, err := d.db.Begin()
	if err != nil {
		return err
	}

	env, err := d.envelopeWithTx(tx, e.EnvelopeId)
	if err != nil {
		tx.Rollback()
		return err
	}

	_, err = tx.Exec(`
		INSERT INTO history (id, envelope, name, balance, target, monthtarget, deleted, date)
		VALUES ($1, $2, $3, $4, $5, $6, $7, datetime('now'))`,
		e.Id, e.EnvelopeId, e.Name, e.Balance, e.Target, e.MonthTarget, e.Deleted)
	if err != nil {
		tx.Rollback()
		return err
	}

	if e.Name == "" {
		e.Name = env.Name
	}
	_, err = tx.Exec(`
		UPDATE envelopes
		SET name = $1, balance = $2, target = $3, monthtarget = $4, deleted = $5
		WHERE id = $6`, e.Name, env.Balance+e.Balance, env.Target+e.Target, env.MonthTarget+e.MonthTarget, e.Deleted, env.Id)
	if err != nil {
		tx.Rollback()
		return err
	}

	return tx.Commit()
}

func (d *DB) UpdateEnvelope(id uuid.UUID, name string, dBalance, dTarget, dMonthTarget int, makeRelative bool) error {
	env, err := d.Envelope(id)
	if err != nil {
		return err
	}

	if makeRelative {
		/* Make parameters relative */
		dBalance -= env.Balance
		dTarget -= env.Target
		dMonthTarget -= env.MonthTarget
	}

	log.Printf(`dB: %d dT: %d dMT: %d`, dBalance, dTarget, dMonthTarget)

	evt := Event{
		EnvelopeId:  env.Id,
		Id:          uuid.New(),
		Date:        time.Now().String(),
		Name:        name,
		Balance:     dBalance,
		Target:      dTarget,
		MonthTarget: dMonthTarget,
		Deleted:     false,
	}

	select {
	case d.Events <- evt:
		/* nothing */
	default:
		/* nothing */
	}

	return d.MergeEvent(evt)
}

func (d *DB) PreviewSpread(id uuid.UUID) (string, error) {
	es := d.AllEnvelopes()
	toSpread, err := d.Envelope(id)

	if err != nil {
		return "", err
	}

	totalMonthTarget := int(0)
	for _, e := range es {
		if e.Id == id {
			continue
		}
		totalMonthTarget += e.MonthTarget
	}

	res := fmt.Sprintf("Total Delta: %.02f, Spread balance: %.02f\n\n", float64(totalMonthTarget)/100, float64(toSpread.Balance)/100)
	for _, e := range es {
		if e.Id == id || e.MonthTarget == 0 {
			continue
		}

		pct := float64(e.MonthTarget) / float64(totalMonthTarget)
		amount := float64(toSpread.Balance) * pct

		res += fmt.Sprintf("% 25s: spread pct: % 10.02f%%, spread delta: % 10.02f, ", e.Name, pct*100, amount/100)
		res += fmt.Sprintf("balance: % 10.02f, new: % 10.02f\n", float64(e.Balance)/100, (float64(e.Balance)+amount)/100)

		if err := d.UpdateEnvelope(e.Id, e.Name, int(amount), 0, 0, false); err != nil {
			return "", err
		}
		if err := d.UpdateEnvelope(id, toSpread.Name, int(-amount), 0, 0, false); err != nil {
			return "", err
		}
	}

	return res, nil
}
<|endoftext|>


<commit_before>package db_test

import (
	"github.com/deiwin/luncher-api/db/model"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"golang.org/x/oauth2"
)

const (
	facebookUserID = "1387231118239727"
	facebookPageID = "1556442521239635"
)

var _ = Describe("User", func() {
	Describe("GetFbID", func() {
		It("should get by facebook user id", func(done Done) {
			defer close(done)
			user, err := usersCollection.GetFbID(facebookUserID)
			Expect(err).NotTo(HaveOccurred())
			Expect(user).NotTo(BeNil())
			Expect(user.FacebookPageID).To(Equal(facebookPageID))
		})

		It("should get nothing for wrong facebook id", func(done Done) {
			defer close(done)
			_, err := usersCollection.GetFbID(facebookPageID)
			Expect(err).To(HaveOccurred())
		})

		It("should link to the restaurant", func(done Done) {
			defer close(done)
			user, err := usersCollection.GetFbID(facebookUserID)
			Expect(err).NotTo(HaveOccurred())
			Expect(user).NotTo(BeNil())
			restaurant, err := restaurantsCollection.GetID(user.RestaurantID)
			Expect(err).NotTo(HaveOccurred())
			Expect(restaurant).NotTo(BeNil())
			Expect(restaurant.Name).To(Equal("Asian Chef"))
		})
	})

	Describe("GetAll", func() {
		It("should list all the users", func(done Done) {
			defer close(done)
			iter := usersCollection.GetAll()
			count := 0
			fbIDs := map[string]int{
				facebookUserID: 0,
				"another user": 0,
			}
			var user model.User
			for iter.Next(&user) {
				Expect(user).NotTo(BeNil())
				i, contains := fbIDs[user.FacebookUserID]
				Expect(contains).To(BeTrue())
				Expect(i).To(Equal(0))
				fbIDs[user.FacebookUserID]++
				count++
			}
			Expect(count).To(Equal(2))
			err := iter.Close()
			Expect(err).NotTo(HaveOccurred())
		})
	})

	Describe("updates", func() {
		RebuildDBAfterEach()
		Describe("SetAccessToken", func() {
			Context("with access token set", func() {
				var token oauth2.Token

				BeforeEach(func(done Done) {
					defer close(done)
					token = oauth2.Token{
						AccessToken: "asd",
					}
					err := usersCollection.SetAccessToken(facebookUserID, token)
					Expect(err).NotTo(HaveOccurred())
				})

				It("should be included in the Get", func(done Done) {
					defer close(done)
					user, err := usersCollection.GetFbID(facebookUserID)
					Expect(err).NotTo(HaveOccurred())
					Expect(user).NotTo(BeNil())
					Expect(user.Session.FacebookUserToken.AccessToken).To(Equal("asd"))
				})
			})
		})

		Describe("SetPageAccessToken", func() {
			Context("with access token set", func() {
				var token string

				BeforeEach(func(done Done) {
					defer close(done)
					token = "bsd"
					err := usersCollection.SetPageAccessToken(facebookUserID, token)
					Expect(err).NotTo(HaveOccurred())
				})

				It("should be included in the Get", func(done Done) {
					defer close(done)
					user, err := usersCollection.GetFbID(facebookUserID)
					Expect(err).NotTo(HaveOccurred())
					Expect(user).NotTo(BeNil())
					Expect(user.Session.FacebookPageToken).To(Equal("bsd"))
				})
			})
		})

		Describe("Update", func() {
			Context("with user updated with a page id change", func() {
				BeforeEach(func(done Done) {
					defer close(done)
					updatedUser := mocks.users[0]
					updatedUser.FacebookPageID = "bsd"
					err := usersCollection.Update(facebookUserID, updatedUser)
					Expect(err).NotTo(HaveOccurred())
				})

				It("should be reflected in the Get", func(done Done) {
					defer close(done)
					user, err := usersCollection.GetFbID(facebookUserID)
					Expect(err).NotTo(HaveOccurred())
					Expect(user).NotTo(BeNil())
					Expect(user.FacebookPageID).To(Equal("bsd"))
				})
			})
		})

		Describe("SessionID", func() {
			Context("with SessionID set", func() {
				var id string

				BeforeEach(func(done Done) {
					defer close(done)
					id = "someid"
					err := usersCollection.SetSessionID(mocks.userID, id)
					Expect(err).NotTo(HaveOccurred())
				})

				Describe("SetSessionID", func() {
					It("should be included in the Get", func(done Done) {
						defer close(done)
						user, err := usersCollection.GetFbID(facebookUserID)
						Expect(err).NotTo(HaveOccurred())
						Expect(user).NotTo(BeNil())
						Expect(user.Session.ID).To(Equal("someid"))
					})
				})

				Describe("UnsetSessionID", func() {
					It("should remove the session ID", func(done Done) {
						defer close(done)
						err := usersCollection.UnsetSessionID(mocks.userID)
						Expect(err).NotTo(HaveOccurred())

						user, err := usersCollection.GetFbID(facebookUserID)
						Expect(err).NotTo(HaveOccurred())
						Expect(user).NotTo(BeNil())
						Expect(user.Session.ID).To(BeEmpty())
					})
				})

				Describe("GetBySessionID", func() {
					It("should be included in the Get", func(done Done) {
						defer close(done)
						user, err := usersCollection.GetSessionID(id)
						Expect(err).NotTo(HaveOccurred())
						Expect(user).NotTo(BeNil())
						Expect(user.FacebookUserID).To(Equal(facebookUserID))
					})
				})
			})
		})
	})
})
<commit_msg>fix tests on older MongoDB versions<commit_after>package db_test

import (
	"github.com/deiwin/luncher-api/db/model"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"golang.org/x/oauth2"
)

const (
	facebookUserID = "1387231118239727"
	facebookPageID = "1556442521239635"
)

var _ = Describe("User", func() {
	Describe("GetFbID", func() {
		It("should get by facebook user id", func(done Done) {
			defer close(done)
			user, err := usersCollection.GetFbID(facebookUserID)
			Expect(err).NotTo(HaveOccurred())
			Expect(user).NotTo(BeNil())
			Expect(user.FacebookPageID).To(Equal(facebookPageID))
		})

		It("should get nothing for wrong facebook id", func(done Done) {
			defer close(done)
			_, err := usersCollection.GetFbID(facebookPageID)
			Expect(err).To(HaveOccurred())
		})

		It("should link to the restaurant", func(done Done) {
			defer close(done)
			user, err := usersCollection.GetFbID(facebookUserID)
			Expect(err).NotTo(HaveOccurred())
			Expect(user).NotTo(BeNil())
			restaurant, err := restaurantsCollection.GetID(user.RestaurantID)
			Expect(err).NotTo(HaveOccurred())
			Expect(restaurant).NotTo(BeNil())
			Expect(restaurant.Name).To(Equal("Asian Chef"))
		})
	})

	Describe("GetAll", func() {
		It("should list all the users", func(done Done) {
			defer close(done)
			iter := usersCollection.GetAll()
			count := 0
			fbIDs := map[string]int{
				facebookUserID: 0,
				"another user": 0,
			}
			var user model.User
			for iter.Next(&user) {
				Expect(user).NotTo(BeNil())
				i, contains := fbIDs[user.FacebookUserID]
				Expect(contains).To(BeTrue())
				Expect(i).To(Equal(0))
				fbIDs[user.FacebookUserID]++
				count++
			}
			Expect(count).To(Equal(2))
			err := iter.Close()
			Expect(err).NotTo(HaveOccurred())
		})
	})

	Describe("updates", func() {
		RebuildDBAfterEach()
		Describe("SetAccessToken", func() {
			Context("with access token set", func() {
				var token oauth2.Token

				BeforeEach(func(done Done) {
					defer close(done)
					token = oauth2.Token{
						AccessToken: "asd",
					}
					err := usersCollection.SetAccessToken(facebookUserID, token)
					Expect(err).NotTo(HaveOccurred())
				})

				It("should be included in the Get", func(done Done) {
					defer close(done)
					user, err := usersCollection.GetFbID(facebookUserID)
					Expect(err).NotTo(HaveOccurred())
					Expect(user).NotTo(BeNil())
					Expect(user.Session.FacebookUserToken.AccessToken).To(Equal("asd"))
				})
			})
		})

		Describe("SetPageAccessToken", func() {
			Context("with access token set", func() {
				var token string

				BeforeEach(func(done Done) {
					defer close(done)
					token = "bsd"
					err := usersCollection.SetPageAccessToken(facebookUserID, token)
					Expect(err).NotTo(HaveOccurred())
				})

				It("should be included in the Get", func(done Done) {
					defer close(done)
					user, err := usersCollection.GetFbID(facebookUserID)
					Expect(err).NotTo(HaveOccurred())
					Expect(user).NotTo(BeNil())
					Expect(user.Session.FacebookPageToken).To(Equal("bsd"))
				})
			})
		})

		Describe("Update", func() {
			Context("with user updated with a page id change", func() {
				BeforeEach(func(done Done) {
					defer close(done)
					updatedUser := *mocks.users[0]
					updatedUser.FacebookPageID = "bsd"
					// this isn't strictly necessary, but otherwise this test seems to fail
					// on older MongoDB versions
					updatedUser.ID = ""
					err := usersCollection.Update(facebookUserID, &updatedUser)
					Expect(err).NotTo(HaveOccurred())
				})

				It("should be reflected in the Get", func(done Done) {
					defer close(done)
					user, err := usersCollection.GetFbID(facebookUserID)
					Expect(err).NotTo(HaveOccurred())
					Expect(user).NotTo(BeNil())
					Expect(user.FacebookPageID).To(Equal("bsd"))
				})
			})
		})

		Describe("SessionID", func() {
			Context("with SessionID set", func() {
				var id string

				BeforeEach(func(done Done) {
					defer close(done)
					id = "someid"
					err := usersCollection.SetSessionID(mocks.userID, id)
					Expect(err).NotTo(HaveOccurred())
				})

				Describe("SetSessionID", func() {
					It("should be included in the Get", func(done Done) {
						defer close(done)
						user, err := usersCollection.GetFbID(facebookUserID)
						Expect(err).NotTo(HaveOccurred())
						Expect(user).NotTo(BeNil())
						Expect(user.Session.ID).To(Equal("someid"))
					})
				})

				Describe("UnsetSessionID", func() {
					It("should remove the session ID", func(done Done) {
						defer close(done)
						err := usersCollection.UnsetSessionID(mocks.userID)
						Expect(err).NotTo(HaveOccurred())

						user, err := usersCollection.GetFbID(facebookUserID)
						Expect(err).NotTo(HaveOccurred())
						Expect(user).NotTo(BeNil())
						Expect(user.Session.ID).To(BeEmpty())
					})
				})

				Describe("GetBySessionID", func() {
					It("should be included in the Get", func(done Done) {
						defer close(done)
						user, err := usersCollection.GetSessionID(id)
						Expect(err).NotTo(HaveOccurred())
						Expect(user).NotTo(BeNil())
						Expect(user.FacebookUserID).To(Equal(facebookUserID))
					})
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	glmenu "github.com/4ydx/glmenu"
	glfw "github.com/go-gl/glfw3"
	"github.com/go-gl/mathgl/mgl32"
	"os"
)

func mainMenuInit(window *glfw.Window) (err error) {
	fontScale := int32(25)
	width, height := window.GetSize()
	err = mainMenu.Load(float32(width), float32(height), fontScale)
	if err != nil {
		fmt.Println("error loading the font")
		os.Exit(1)
	}
	mainMenu.ResizeWindow(float32(width), float32(height))
	mainMenu.Background = mgl32.Vec4{0, 0, .20, 0}

	//2DO: sounds

	// start
	var textbox1 glmenu.TextBox
	mainMenu.AddTextBox(&textbox1, "127.0.0.1", 100, 50, 2)
	textbox1.Text.SetColor(1, 1, 1)

	// options
	var label2 glmenu.Label
	mainMenu.AddLabel(&label2, "Options")

	label2.Text.SetColor(0.5, 0.5, 0.5)
	label2.OnClick = func(label *glmenu.Label, xPos, yPos float64, button glmenu.MouseClick, inBox bool) {
		label.Text.SetColor(250.0/255.0, 0, 154.0/255.0)
	}
	label2.OnRelease = func(label *glmenu.Label, xPos, yPos float64, button glmenu.MouseClick, inBox bool) {
		label.Text.SetColor(0, 250.0/255.0, 154.0/255.0)
		if inBox {
			mainMenu.Toggle()
			optionMenu.Toggle()
		}
		if label.IsHover {
			label.Text.SetColor(0, 250.0/255.0, 154.0/255.0)
		} else {
			label.Text.SetColor(0.5, 0.5, 0.5)
		}
	}
	label2.OnHover = func(label *glmenu.Label, xPos, yPos float64, button glmenu.MouseClick, inBox bool) {
		if !label.IsClick {
			label.Text.SetColor(0, 250.0/255.0, 154.0/255.0)
			label.Text.AddScale(mainMenu.TextScaleRate)
		}
	}
	label2.OnNotHover = func(label *glmenu.Label) {
		if !label.IsClick {
			label.Text.SetColor(0.5, 0.5, 0.5)
			label.Text.AddScale(-mainMenu.TextScaleRate)
		}
	}

	// quit
	var label3 glmenu.Label
	mainMenu.AddLabel(&label3, "Quit")

	label3.Text.SetColor(0.5, 0.5, 0.5)
	label3.OnClick = func(label *glmenu.Label, xPos, yPos float64, button glmenu.MouseClick, inBox bool) {
		label.Text.SetColor(250.0/255.0, 0, 154.0/255.0)
	}
	label3.OnRelease = func(label *glmenu.Label, xPos, yPos float64, button glmenu.MouseClick, inBox bool) {
		label.Text.SetColor(0, 250.0/255.0, 154.0/255.0)
		if inBox {
			window.SetShouldClose(true)
		}
		if label.IsHover {
			label.Text.SetColor(0, 250.0/255.0, 154.0/255.0)
		} else {
			label.Text.SetColor(0.5, 0.5, 0.5)
		}
	}
	label3.OnHover = func(label *glmenu.Label, xPos, yPos float64, button glmenu.MouseClick, inBox bool) {
		if !label.IsClick {
			label.Text.SetColor(0, 250.0/255.0, 154.0/255.0)
			label.Text.AddScale(mainMenu.TextScaleRate)
		}
	}
	label3.OnNotHover = func(label *glmenu.Label) {
		if !label.IsClick {
			label.Text.SetColor(0.5, 0.5, 0.5)
			label.Text.AddScale(-mainMenu.TextScaleRate)
		}
	}

	// simple centering of values
	totalHeight := textbox1.Text.X2.Y - textbox1.Text.X1.Y +
		label2.Text.X2.Y - label2.Text.X1.Y +
		label2.Text.X2.Y - label2.Text.X1.Y
	textbox1.Text.SetPosition(0, totalHeight/2)

	label3.Text.SetPosition(0, -totalHeight/2)

	return
}
<commit_msg>Make textbox a bit wider.<commit_after>package main

import (
	"fmt"
	glmenu "github.com/4ydx/glmenu"
	glfw "github.com/go-gl/glfw3"
	"github.com/go-gl/mathgl/mgl32"
	"os"
)

func mainMenuInit(window *glfw.Window) (err error) {
	fontScale := int32(25)
	width, height := window.GetSize()
	err = mainMenu.Load(float32(width), float32(height), fontScale)
	if err != nil {
		fmt.Println("error loading the font")
		os.Exit(1)
	}
	mainMenu.ResizeWindow(float32(width), float32(height))
	mainMenu.Background = mgl32.Vec4{0, 0, .20, 0}

	//2DO: sounds

	// start
	var textbox1 glmenu.TextBox
	mainMenu.AddTextBox(&textbox1, "127.0.0.1", 200, 50, 2)
	textbox1.Text.SetColor(1, 1, 1)

	// options
	var label2 glmenu.Label
	mainMenu.AddLabel(&label2, "Options")

	label2.Text.SetColor(0.5, 0.5, 0.5)
	label2.OnClick = func(label *glmenu.Label, xPos, yPos float64, button glmenu.MouseClick, inBox bool) {
		label.Text.SetColor(250.0/255.0, 0, 154.0/255.0)
	}
	label2.OnRelease = func(label *glmenu.Label, xPos, yPos float64, button glmenu.MouseClick, inBox bool) {
		label.Text.SetColor(0, 250.0/255.0, 154.0/255.0)
		if inBox {
			mainMenu.Toggle()
			optionMenu.Toggle()
		}
		if label.IsHover {
			label.Text.SetColor(0, 250.0/255.0, 154.0/255.0)
		} else {
			label.Text.SetColor(0.5, 0.5, 0.5)
		}
	}
	label2.OnHover = func(label *glmenu.Label, xPos, yPos float64, button glmenu.MouseClick, inBox bool) {
		if !label.IsClick {
			label.Text.SetColor(0, 250.0/255.0, 154.0/255.0)
			label.Text.AddScale(mainMenu.TextScaleRate)
		}
	}
	label2.OnNotHover = func(label *glmenu.Label) {
		if !label.IsClick {
			label.Text.SetColor(0.5, 0.5, 0.5)
			label.Text.AddScale(-mainMenu.TextScaleRate)
		}
	}

	// quit
	var label3 glmenu.Label
	mainMenu.AddLabel(&label3, "Quit")

	label3.Text.SetColor(0.5, 0.5, 0.5)
	label3.OnClick = func(label *glmenu.Label, xPos, yPos float64, button glmenu.MouseClick, inBox bool) {
		label.Text.SetColor(250.0/255.0, 0, 154.0/255.0)
	}
	label3.OnRelease = func(label *glmenu.Label, xPos, yPos float64, button glmenu.MouseClick, inBox bool) {
		label.Text.SetColor(0, 250.0/255.0, 154.0/255.0)
		if inBox {
			window.SetShouldClose(true)
		}
		if label.IsHover {
			label.Text.SetColor(0, 250.0/255.0, 154.0/255.0)
		} else {
			label.Text.SetColor(0.5, 0.5, 0.5)
		}
	}
	label3.OnHover = func(label *glmenu.Label, xPos, yPos float64, button glmenu.MouseClick, inBox bool) {
		if !label.IsClick {
			label.Text.SetColor(0, 250.0/255.0, 154.0/255.0)
			label.Text.AddScale(mainMenu.TextScaleRate)
		}
	}
	label3.OnNotHover = func(label *glmenu.Label) {
		if !label.IsClick {
			label.Text.SetColor(0.5, 0.5, 0.5)
			label.Text.AddScale(-mainMenu.TextScaleRate)
		}
	}

	// simple centering of values
	totalHeight := textbox1.Text.X2.Y - textbox1.Text.X1.Y +
		label2.Text.X2.Y - label2.Text.X1.Y +
		label2.Text.X2.Y - label2.Text.X1.Y
	textbox1.Text.SetPosition(0, totalHeight/2)

	label3.Text.SetPosition(0, -totalHeight/2)

	return
}
<|endoftext|>