<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 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;
}

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(Hit{infinity, Vec3{0, 0, 0}, nil}, 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 int, t int, r int, 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) rayForPixel(x int, y int) Ray {
    dir := normalize(Vec3{float(x) - float(c.w) * 0.5, float(y) - float(c.h) * 0.5,
        float(c.w)});
    return Ray{c.eye, dir};
}

type Renderer struct {
    scene *Scene;
    t *Texture;
    cam *Camera;
    jobChan chan Rect;
    quitChan chan bool;
    joinChan chan bool;
}

func (renderer *Renderer) renderRect(tint Vec3, r *Rect) {
    for y := r.t; y < r.b; y++ {
        for x := r.l; x < r.r; x++ {
            ray := renderer.cam.rayForPixel(x, y);
            var g Vec3;
            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 = renderer.scene.rayTrace(ray);
        	    }
        	} else {
        	    g = renderer.scene.rayTrace(ray);
        	}
            renderer.t.SetV(x, renderer.cam.h - (y + 1), g);
        }
    }
}

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;
    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, 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>Added vector in-place functions<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 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;
}

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(Hit{infinity, Vec3{0, 0, 0}, nil}, 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 int, t int, r int, 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) rayForPixel(x int, y int) Ray {
    dir := normalize(Vec3{float(x) - float(c.w) * 0.5, float(y) - float(c.h) * 0.5,
        float(c.w)});
    return Ray{c.eye, dir};
}

type Renderer struct {
    scene *Scene;
    t *Texture;
    cam *Camera;
    jobChan chan Rect;
    quitChan chan bool;
    joinChan chan bool;
}

func (renderer *Renderer) renderRect(tint Vec3, r *Rect) {
    for y := r.t; y < r.b; y++ {
        for x := r.l; x < r.r; x++ {
            ray := renderer.cam.rayForPixel(x, y);
            var g Vec3;
            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 = renderer.scene.rayTrace(ray);
        	    }
        	} else {
        	    g = renderer.scene.rayTrace(ray);
        	}
            renderer.t.SetV(x, renderer.cam.h - (y + 1), g);
        }
    }
}

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;
    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, 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();
    }
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"reflect"

	"github.com/aerogo/aero"
	"github.com/animenotifier/arn"
	"github.com/animenotifier/notify.moe/utils/gql"
	"github.com/fatih/color"
	"github.com/graphql-go/graphql"
)

func init() {
	rootQueryFields := graphql.Fields{}

	for name, typ := range arn.DB.Types() {
		if typ.Kind() != reflect.Struct {
			continue
		}

		// Bind name for the closure
		typeName := name

		fmt.Println(typeName)
		rootQueryFields[typeName] = &graphql.Field{
			Args: graphql.FieldConfigArgument{
				"id": &graphql.ArgumentConfig{
					Type:         graphql.String,
					DefaultValue: "",
				},
			},
			Type: graphql.NewObject(graphql.ObjectConfig{
				Name:   typeName,
				Fields: gql.BindFields(typ),
			}),
			Resolve: func(p graphql.ResolveParams) (interface{}, error) {
				id := p.Args["id"].(string)
				return arn.DB.Get(typeName, id)
			},
		}
	}

	// Schema
	rootQuery := graphql.ObjectConfig{
		Name:   "RootQuery",
		Fields: rootQueryFields,
	}

	schemaConfig := graphql.SchemaConfig{
		Query: graphql.NewObject(rootQuery),
	}

	schema, err := graphql.NewSchema(schemaConfig)

	if err != nil {
		log.Fatalf("failed to create new schema, error: %v", err)
	}

	app.Post("/graphql", func(ctx *aero.Context) string {
		body, err := ctx.Request().Body().JSONObject()

		if err != nil {
			return ctx.Error(http.StatusBadRequest, "Expected JSON data containing a query and variables", err)
		}

		query := body["query"].(string)
		variables := body["variables"].(map[string]interface{})

		params := graphql.Params{
			Schema:         schema,
			RequestString:  query,
			VariableValues: variables,
		}

		result := graphql.Do(params)

		if len(result.Errors) > 0 {
			color.Red("failed to execute graphql operation, errors: %+v", result.Errors)
		}

		return ctx.JSON(result)
	})
}
<commit_msg>Added access control<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"reflect"

	"github.com/aerogo/aero"
	"github.com/animenotifier/arn"
	"github.com/animenotifier/notify.moe/utils/gql"
	"github.com/fatih/color"
	"github.com/graphql-go/graphql"
)

func init() {
	rootQueryFields := graphql.Fields{}

	for name, typ := range arn.DB.Types() {
		if typ.Kind() != reflect.Struct {
			continue
		}

		// Bind name for the closure
		typeName := name

		fmt.Println(typeName)
		rootQueryFields[typeName] = &graphql.Field{
			Args: graphql.FieldConfigArgument{
				"id": &graphql.ArgumentConfig{
					Type:         graphql.String,
					DefaultValue: "",
				},
			},
			Type: graphql.NewObject(graphql.ObjectConfig{
				Name:   typeName,
				Fields: gql.BindFields(typ),
			}),
			Resolve: func(p graphql.ResolveParams) (interface{}, error) {
				id := p.Args["id"].(string)
				return arn.DB.Get(typeName, id)
			},
		}
	}

	// Schema
	rootQuery := graphql.ObjectConfig{
		Name:   "RootQuery",
		Fields: rootQueryFields,
	}

	schemaConfig := graphql.SchemaConfig{
		Query: graphql.NewObject(rootQuery),
	}

	schema, err := graphql.NewSchema(schemaConfig)

	if err != nil {
		log.Fatalf("failed to create new schema, error: %v", err)
	}

	app.Post("/graphql", func(ctx *aero.Context) string {
		ctx.Response().Header().Set("Access-Control-Allow-Origin", "*")
		body, err := ctx.Request().Body().JSONObject()

		if err != nil {
			return ctx.Error(http.StatusBadRequest, "Expected JSON data containing a query and variables", err)
		}

		query := body["query"].(string)
		variables := body["variables"].(map[string]interface{})

		params := graphql.Params{
			Schema:         schema,
			RequestString:  query,
			VariableValues: variables,
		}

		result := graphql.Do(params)

		if len(result.Errors) > 0 {
			color.Red("failed to execute graphql operation, errors: %+v", result.Errors)
		}

		return ctx.JSON(result)
	})
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"html/template"
	"log"
	"net"
	"net/http"
	"strings"
	"time"
)

type Reminder struct {
	*Notification
	TemplateData *Template
	ThenDate     string
	ThenTime     string
}

func CreateReminder(app *App) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		reminder := newReminder(app)
		switch r.Method {
		case "GET":
			if matchesAndroidBrowserUserAgent(r) {
				reminder.TemplateData.fallbackToFormInputTypeText()
			}
			reminder.renderTemplate(w)
			return
		case "POST":
			reminder.submit(w, r)
			return
		}
	})
}

func newReminder(app *App) *Reminder {
	return &Reminder{
		Notification: app.Notification,
		TemplateData: &Template{
			SuccessMsg: "",
			InputType:  "time",
			Domain:     app.Domain,
			Path:       app.Path,
		},
	}
}

func (self *Reminder) renderTemplate(w http.ResponseWriter) {
	tmpl, err := template.New("createReminder").Parse(createReminderTemplate)
	die("error when rendering template: %v", err)
	w.WriteHeader(http.StatusOK)
	err = tmpl.ExecuteTemplate(w, "createReminder", self.TemplateData)
	die("error when executing template: %v", err)
	return
}

func (self *Reminder) submit(w http.ResponseWriter, r *http.Request) {
	if err := self.validateClientInput(r); err != nil {
		http.Error(w, "Sorry, we were unable to process your Input.",
			http.StatusInternalServerError)
		return
	}
	go self.sendReminder(r)
	if isAjaxRequest(r) {
		w.WriteHeader(http.StatusOK)
		return
	}
	self.TemplateData.SuccessMsg = "Thank you! Your reminder will be sent at " +
		self.ThenDate + " " + self.ThenTime
	self.renderTemplate(w)
	return
}

func (self *Reminder) validateClientInput(r *http.Request) error {
	self.ThenDate = r.FormValue("date")
	self.ThenTime = r.FormValue("time")
	self.Message = r.FormValue("message")
	_, err := time.Parse("2006-01-02 15:04", self.ThenDate+" "+self.ThenTime)
	if err != nil {
		return err
	}
	return nil
}

func (self *Reminder) sendReminder(r *http.Request) {
	loc := self.detectClientLocation(r)
	delay := self.calculateNotificationDelay(loc)
	select {
	case <-time.After(delay):
		err := self.Notification.Notify()
		die("error when using Notification API, got: %v", err)
	}
	return
}

func (self *Reminder) detectClientLocation(r *http.Request) (location string) {
	ip, _, _ := net.SplitHostPort(r.RemoteAddr)
	resp, err := http.Get("http://freegeoip.net/json/" + ip)
	if err != nil {
		self.notifyTheError("error when trying to geolocate client IP: " +
			err.Error())
	}
	defer resp.Body.Close()
	decoder := json.NewDecoder(resp.Body)
	type freegeoipJson struct {
		Time_zone string
	}
	jsonResp := new(freegeoipJson)
	if err = decoder.Decode(&jsonResp); err != nil {
		self.notifyTheError("error when trying to parse json response: " + err.Error())
	}
	if jsonResp.Time_zone == "" {
		self.notifyTheError("error: timezone from freegeoip was empty!" + err.Error())
	}
	return jsonResp.Time_zone
}

func (self *Reminder) calculateNotificationDelay(loc string) time.Duration {
	locationOfClient, err := time.LoadLocation(loc)
	if err != nil {
		self.notifyTheError("error when using autodetected location: " + err.Error())
	}
	then, err := time.ParseInLocation("2006-01-02 15:04",
		self.ThenDate+" "+self.ThenTime, locationOfClient)
	if err != nil {
		self.notifyTheError("error when parsing autodetected location: " + err.Error())
		return 0
	}
	now := time.Now().In(locationOfClient)
	delay := then.Sub(now)
	logReminder(now, delay, self.Message)
	return delay
}

func logReminder(now time.Time, delay time.Duration, msg string) {
	log.Printf("The reminder '%v' will be sent at %v", msg, now.Add(delay))
	return
}

func isAjaxRequest(r *http.Request) bool {
	if r.Header.Get("X-Requested-With") == "XMLHTTPRequest" {
		return true
	}
	return false
}

func matchesAndroidBrowserUserAgent(r *http.Request) bool {
	if strings.Contains(r.Header.Get("User-Agent"), "Android") {
		return true
	}
	return false
}

func (self *Reminder) notifyTheError(err string) {
	self.Notification.Message = err
	notifyErr := self.Notification.Notify()
	die("%v", notifyErr)
}
<commit_msg>Fix error notification<commit_after>package main

import (
	"encoding/json"
	"html/template"
	"log"
	"net"
	"net/http"
	"strings"
	"time"
)

type Reminder struct {
	*Notification
	TemplateData *Template
	ThenDate     string
	ThenTime     string
}

func CreateReminder(app *App) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		reminder := newReminder(app)
		switch r.Method {
		case "GET":
			if matchesAndroidBrowserUserAgent(r) {
				reminder.TemplateData.fallbackToFormInputTypeText()
			}
			reminder.renderTemplate(w)
			return
		case "POST":
			reminder.submit(w, r)
			return
		}
	})
}

func newReminder(app *App) *Reminder {
	return &Reminder{
		Notification: app.Notification,
		TemplateData: &Template{
			SuccessMsg: "",
			InputType:  "time",
			Domain:     app.Domain,
			Path:       app.Path,
		},
	}
}

func (self *Reminder) renderTemplate(w http.ResponseWriter) {
	tmpl, err := template.New("createReminder").Parse(createReminderTemplate)
	die("error when rendering template: %v", err)
	w.WriteHeader(http.StatusOK)
	err = tmpl.ExecuteTemplate(w, "createReminder", self.TemplateData)
	die("error when executing template: %v", err)
	return
}

func (self *Reminder) submit(w http.ResponseWriter, r *http.Request) {
	if err := self.validateClientInput(r); err != nil {
		http.Error(w, "Sorry, we were unable to process your Input.",
			http.StatusInternalServerError)
		return
	}
	go self.sendReminder(r)
	if isAjaxRequest(r) {
		w.WriteHeader(http.StatusOK)
		return
	}
	self.TemplateData.SuccessMsg = "Thank you! Your reminder will be sent at " +
		self.ThenDate + " " + self.ThenTime
	self.renderTemplate(w)
	return
}

func (self *Reminder) validateClientInput(r *http.Request) error {
	self.ThenDate = r.FormValue("date")
	self.ThenTime = r.FormValue("time")
	self.Message = r.FormValue("message")
	_, err := time.Parse("2006-01-02 15:04", self.ThenDate+" "+self.ThenTime)
	if err != nil {
		return err
	}
	return nil
}

func (self *Reminder) sendReminder(r *http.Request) {
	loc := self.detectClientLocation(r)
	delay := self.calculateNotificationDelay(loc)
	select {
	case <-time.After(delay):
		err := self.Notification.Notify()
		die("error when using Notification API, got: %v", err)
	}
	return
}

func (self *Reminder) detectClientLocation(r *http.Request) (location string) {
	ip, _, _ := net.SplitHostPort(r.RemoteAddr)
	resp, err := http.Get("http://freegeoip.net/json/" + ip)
	if err != nil {
		self.notifyTheError("error when trying to geolocate client IP: " +
			err.Error())
	}
	defer resp.Body.Close()
	decoder := json.NewDecoder(resp.Body)
	type freegeoipJson struct {
		Time_zone string
	}
	jsonResp := new(freegeoipJson)
	if err = decoder.Decode(&jsonResp); err != nil {
		self.notifyTheError("error when trying to parse json response: " + err.Error())
	}
	if jsonResp.Time_zone == "" {
		self.notifyTheError("error: timezone from freegeoip was empty!")
	}
	return jsonResp.Time_zone
}

func (self *Reminder) calculateNotificationDelay(loc string) time.Duration {
	locationOfClient, err := time.LoadLocation(loc)
	if err != nil {
		self.notifyTheError("error when using autodetected location: " + err.Error())
	}
	then, err := time.ParseInLocation("2006-01-02 15:04",
		self.ThenDate+" "+self.ThenTime, locationOfClient)
	if err != nil {
		self.notifyTheError("error when parsing autodetected location: " + err.Error())
		return 0
	}
	now := time.Now().In(locationOfClient)
	delay := then.Sub(now)
	logReminder(now, delay, self.Message)
	return delay
}

func logReminder(now time.Time, delay time.Duration, msg string) {
	log.Printf("The reminder '%v' will be sent at %v", msg, now.Add(delay))
	return
}

func isAjaxRequest(r *http.Request) bool {
	if r.Header.Get("X-Requested-With") == "XMLHTTPRequest" {
		return true
	}
	return false
}

func matchesAndroidBrowserUserAgent(r *http.Request) bool {
	if strings.Contains(r.Header.Get("User-Agent"), "Android") {
		return true
	}
	return false
}

func (self *Reminder) notifyTheError(err string) {
	self.Notification.Message = err
	notifyErr := self.Notification.Notify()
	die("%v", notifyErr)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/ajg/form"
	"net/http"
	"strings"
	"path"
)

type fieldListHandler struct {
	config ServerSection
	index  *Index
}

func (h *fieldListHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fields := strings.SplitN(r.URL.Path, "/", 2)

	if fields[0] == "" {
		// to do if a field was not given
		response, err := h.index.ListField(h.config.Default)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		err = allTemplates.ExecuteTemplate(w, h.config.Template,
			struct{ AllFields []string }{AllFields: response})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	} else {
		// to be done if a field was given
		response, err := h.index.ListFieldValues(h.config.Default, fields[0], 100, 1)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		modifiedSearchResponse := struct {
			SearchResponse
			AllFields []string
		}{
			SearchResponse: response,
			AllFields:      []string{},
		}

		err = allTemplates.ExecuteTemplate(w, h.config.Template, modifiedSearchResponse)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}
}

type fuzzySearchHandler struct {
	config ServerSection
	index  *Index
}

func (h *fuzzySearchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	values := struct {
		s string `form:"s"`
		topics []string `form:"topic"`
		authors []string `form:"author"`
		page int `form:"page"`
		pageSize int `form:"pageSize"`
	}

	err := form.Decode(&values, request.URL.Values)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	query := bleve.NewQueryStringQuery(values.s)
	searchRequest := bleve.NewSearchRequest(query)
	searchRequest.Fields = []string{"path", "title", "topic", "author", "modified"}
	searchRequest.Size = values.pageSize
	searchRequest.From = values.pageSize + values.page

	results, err  := h.index.Query(searchRequest)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = allTemplates.ExecuteTemplate(w, h.config.Template, results)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

type querySearchHandler struct {
	config ServerSection
	index  *Index
}

func (h *querySearchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	values := struct {
		s string `form:"s"`
		page int `form:"page"`
		pageSize int `form:"pageSize"`
	}

	err := form.Decode(&values, request.URL.Values)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	query := bleve.NewQueryStringQuery(values.s)
	searchRequest := bleve.NewSearchRequest(query)
	searchRequest.Fields = []string{"path", "title", "topic", "author", "modified"}
	searchRequest.Size = values.pageSize
	searchRequest.From = values.pageSize + values.page

	results, err  := h.index.Query(searchRequest)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = allTemplates.ExecuteTemplate(w, h.config.Template, results)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

type parsedHandler struct {
	config ServerSection
}

func (h *parsedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// If the request is empty, set it to the default.
	if requestPath == "/" {
		requestPath = path.Clean(serverConfig.Default)
	}

	// If the request doesn't end in .md, add that
	if path.Ext(requestPath) != "md" {
		requestPath = requestPath + ".md"
	}

	pdata := new(PageMetadata)
	err := pdata.LoadPage(serverConfig.Path + requestPath)
	if err != nil {
		log.Printf("request [ %s ] points to an bad file target [ %s ] sent to server %s",
			request.URL.Path, requestPath, serverConfig.Prefix)
		http.Error(responsePipe, "Page not Found", http.StatusNotFound)
		return
	}

	if pdata.MatchedTopic(serverConfig.Restricted) {
		log.Printf("request [ %s ] was against a page [ %s ] with a restricted tag",
			request.URL.Path, requestPath)
		http.Error(responsePipe, "Restricted Page", http.StatusNotFound)
		//http.Error(responsePipe, err.Error(), http.StatusForbidden)
		return
	}

	// parse any markdown in the input
	body := template.HTML(bodyParseMarkdown(pdata.Page))
	toc := template.HTML(tocParseMarkdown(pdata.Page))
	topics, keywords, authors := pdata.ListMeta()

	// ##TODO## put this template right in the function call
	// Then remove the Page Struct above
	response := Page{Title: pdata.Title, ToC: toc, Body: body, Keywords: keywords, Topics: topics, Authors: authors}
	err = allTemplates.ExecuteTemplate(responsePipe, serverConfig.Template, response)
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
}
<commit_msg>added a raw handler that should cover the rest?<commit_after>package main

import (
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"strings"

	"github.com/ajg/form"
	"github.com/blevesearch/bleve"
)

type fieldListHandler struct {
	config ServerSection
	index  *Index
}

func (h *fieldListHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fields := strings.SplitN(r.URL.Path, "/", 2)

	if fields[0] == "" {
		// to do if a field was not given
		response, err := h.index.ListField(h.config.Default)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		err = allTemplates.ExecuteTemplate(w, h.config.Template,
			struct{ AllFields []string }{AllFields: response})
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
	} else {
		// to be done if a field was given
		response, err := h.index.ListFieldValues(h.config.Default, fields[0], 100, 1)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		modifiedSearchResponse := struct {
			SearchResponse
			AllFields []string
		}{
			SearchResponse: response,
			AllFields:      []string{},
		}

		err = allTemplates.ExecuteTemplate(w, h.config.Template, modifiedSearchResponse)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
		}
	}
}

type fuzzySearchHandler struct {
	config ServerSection
	index  *Index
}

func (h *fuzzySearchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	values := struct {
		s        string   `form:"s"`
		topics   []string `form:"topic"`
		authors  []string `form:"author"`
		page     int      `form:"page"`
		pageSize int      `form:"pageSize"`
	}{}

	err := form.Decode(&values, request.URL.Values)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	query := bleve.NewQueryStringQuery(values.s)
	searchRequest := bleve.NewSearchRequest(query)
	searchRequest.Fields = []string{"path", "title", "topic", "author", "modified"}
	searchRequest.Size = values.pageSize
	searchRequest.From = values.pageSize + values.page

	results, err := h.index.Query(searchRequest)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = allTemplates.ExecuteTemplate(w, h.config.Template, results)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

type querySearchHandler struct {
	config ServerSection
	index  *Index
}

func (h *querySearchHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	values := struct {
		s        string `form:"s"`
		page     int    `form:"page"`
		pageSize int    `form:"pageSize"`
	}{}

	err := form.Decode(&values, request.URL.Values)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	query := bleve.NewQueryStringQuery(values.s)
	searchRequest := bleve.NewSearchRequest(query)
	searchRequest.Fields = []string{"path", "title", "topic", "author", "modified"}
	searchRequest.Size = values.pageSize
	searchRequest.From = values.pageSize + values.page

	results, err := h.index.Query(searchRequest)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = allTemplates.ExecuteTemplate(w, h.config.Template, results)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

type parsedHandler struct {
	config ServerSection
}

func (h *parsedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// If the request is empty, set it to the default.
	if requestPath == "/" {
		requestPath = path.Clean(h.serverConfig.Default)
	}

	// If the request doesn't end in .md, add that
	if path.Ext(requestPath) != "md" {
		requestPath = requestPath + ".md"
	}

	pdata := new(PageMetadata)
	err := pdata.LoadPage(serverConfig.Path + requestPath)
	if err != nil {
		log.Printf("request [ %s ] points to an bad file target [ %s ] sent to server %s",
			request.URL.Path, requestPath, serverConfig.Prefix)
		http.Error(responsePipe, "Page not Found", http.StatusNotFound)
		return
	}

	if pdata.MatchedTopic(serverConfig.Restricted) {
		log.Printf("request [ %s ] was against a page [ %s ] with a restricted tag",
			request.URL.Path, requestPath)
		http.Error(responsePipe, "Restricted Page", http.StatusNotFound)
		//http.Error(responsePipe, err.Error(), http.StatusForbidden)
		return
	}

	// parse any markdown in the input
	body := template.HTML(bodyParseMarkdown(pdata.Page))
	toc := template.HTML(tocParseMarkdown(pdata.Page))
	topics, keywords, authors := pdata.ListMeta()

	// ##TODO## put this template right in the function call
	// Then remove the Page Struct above
	response := Page{
		Title:    pdata.Title,
		ToC:      toc,
		Body:     body,
		Keywords: keywords,
		Topics:   topics,
		Authors:  authors,
	}
	err = allTemplates.ExecuteTemplate(responsePipe, serverConfig.Template, response)
	if err != nil {
		http.Error(responsePipe, err.Error(), 500)
	}
}

type rawHandler struct {
	config ServerSection
}

func (h *parsedHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// If the request is empty, set it to the default.
	if requestPath == "/" {
		requestPath = path.Clean(h.serverConfig.Default)
	}

	for _, restricted := range h.config.Restricted {
		if path.Ext(requestPath) == restricted {
			log.Printf("request %s was improperly routed to the file handler with an disallowed extension %s", request.URL.Path, restricted)
			http.Error(responsePipe, "Request not allowed", 403)
			return
		}
	}

	f, err := os.Open(filePath.Join(h.config.Path, requestPath))
	if err != nil {
		http.Error(w, err.Error, http.StatusForbidden)
		log.Print(err)
		return
	}

	_, err = io.Copy(w, f)
	if err != nil {
		http.Error(w, err.Error, http.StatusInternalServerError)
		log.Print(err)
	}
}
<|endoftext|>


<commit_before>package xlate

import (
	"io"

	"github.com/polydawn/go-xlate/json"
	"github.com/polydawn/go-xlate/obj"
)

func NewJsonEncoder(wr io.Writer) *JsonEncoder {
	return &JsonEncoder{wr}
}

type JsonEncoder struct {
	wr io.Writer
}

func (d *JsonEncoder) Marshal(v interface{}) {
	TokenPump{
		nil, // todo get the rest of obj.NewMarshaller(v) in place
		json.NewSerializer(d.wr),
	}.Run()
}

func NewJsonDecoder(r io.Reader) *JsonDecoder {
	return &JsonDecoder{r}
}

type JsonDecoder struct {
	r io.Reader
}

func (d *JsonDecoder) Unmarshal(v interface{}) {
	TokenPump{
		nil, // todo get the whole json package in place
		obj.NewUnmarshaler(v),
	}.Run()
}
<commit_msg>Rig obj marshaller into the top level encoder setup functions, and add helpers for basic buffered use.<commit_after>package xlate

import (
	"bytes"
	"io"

	"github.com/polydawn/go-xlate/json"
	"github.com/polydawn/go-xlate/obj"
)

func NewJsonEncoder(wr io.Writer) *JsonEncoder {
	return &JsonEncoder{wr}
}

type JsonEncoder struct {
	wr io.Writer
}

func (d *JsonEncoder) Marshal(v interface{}) {
	TokenPump{
		obj.NewMarshaler(&obj.Suite{}, v),
		json.NewSerializer(d.wr),
	}.Run()
}

func JsonEncode(v interface{}) []byte {
	var buf bytes.Buffer
	NewJsonEncoder(&buf).Marshal(v)
	return buf.Bytes()
}

func NewJsonDecoder(r io.Reader) *JsonDecoder {
	return &JsonDecoder{r}
}

type JsonDecoder struct {
	r io.Reader
}

func (d *JsonDecoder) Unmarshal(v interface{}) {
	TokenPump{
		nil, // todo get the whole json package in place
		obj.NewUnmarshaler(v),
	}.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"github.com/micanzhang/gohosts"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"
)

const VERSION = "1.0@dev"
const PLATFORM = runtime.GOOS

func usage() {
	fmt.Fprintf(os.Stderr, "usage: %s [options]\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(2)
}

func help() {
	usage()
}

func version() {
	fmt.Printf("%s version %s\n", os.Args[0], VERSION)
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	parseCMD()
}

func edit() {
	hostFile := "/etc/hosts"
	if PLATFORM == "win" {
		hostFile = "C:/Windows/System32/driver/etc/hosts"
	}
	//cmd := exec.Command("open", "-a", "/Applications/Emacs.app/Contents/MacOS/Emacs", hostFile)
	cmd := exec.Command("/usr/bin/vim", hostFile)
	//cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		fmt.Println(err)
	}
}

func listHosts(params map[string]string) {
	groups := getHost()
	if group, ok := params["group"]; ok {
		groups = groups.FindByName(group)
	}

	if ip, ok := params["ip"]; ok {
		groups = groups.FilterByIp(ip)
	}

	if domain, ok := params["domain"]; ok {
		groups = groups.FilterByDomain(domain)
	}

	fmt.Println(groups)
}

func switchHosts(params map[string]string, enable bool) {
	groups := getHost()
	if name, ok := params["group"]; ok {
		if ip, ok := params["ip"]; ok {
			groups.SwitchByIp(ip, enable)
		} else if domain, ok := params["domain"]; ok {
			groups.SwitchByDomain(domain, enable)
		} else {
			groups.SwitchByName(name, enable)
		}
	} else {

	}

	if ip, ok := params["ip"]; ok {
		groups.SwitchByIp(ip, enable)
	}

	if domain, ok := params["domain"]; ok {
		groups.SwitchByDomain(domain, enable)
	}

	if groups != nil {
		content := groups.String()
		if err := updateHostString(content); err != nil {
			fmt.Printf("Error: %v\n", err)
		} else {
			fmt.Println(content)
		}
	} else {

	}
}

func parseCMD() {
	args := os.Args
	params := make(map[string]string)
	if len(args) == 1 {
		usage()
	}

	key := ""
	action := ""
	//support list, edit, disable, enable, help, version
	for _, arg := range args[1:] {
		if len(key) > 0 {
			params[key] = arg
			key = ""
		} else {
			switch arg {
			case "-h", "--help":
				action = "help"
				help()
				break
			case "-v", "--version":
				action = "version"
				version()
				break
			case "-l", "--list", "list":
				if len(action) == 0 {
					action = "list"
				} else if action != "list" {
					usage()
				}
				break
			case "-e", "--edit":
				action = "edit"
				edit()
				os.Exit(0)
			case "-r", "--remove", "remove":
				if len(action) == 0 {
					action = "disable"
				} else if action != "disable" {
					usage()
				}
				break
			case "-s", "--switch", "switch":
				if len(action) == 0 {
					action = "enable"
				} else if action != "enable" {
					usage()
				}
				break
			case "-g", "--group":
				key = "group"
				break
			case "-d", "--domain":
				key = "domain"
				break
			case "-i", "--ip":
				key = "ip"
				break
			default:
				if action != "" {
					params["group"] = arg
				} else {
					usage()
				}
				break
			}
		}
	}

	if action == "disable" {
		switchHosts(params, false)
	} else if action == "enable" {
		switchHosts(params, true)
	} else {
		listHosts(params)
	}
}

func listHost(params map[string]string) {
	groups := getHost()
	fmt.Println(groups)
	//list all
	//list by group
	// list by domain
	// list by ip
}

func getHost() *hosts.Groups {
	hostStr, err := loadHostString()
	if err != nil {
		panic(err)
	}
	lines := strings.Split(hostStr, "\n")
	groups := new(hosts.Groups)
	group := new(hosts.Group)
	inGroup := false
	for _, line := range lines {
		line = strings.TrimSpace(line)
		length := len(line)
		if length == 5 && inGroup {
			*groups = append(*groups, *group)
			group = new(hosts.Group)
			inGroup = false
		} else if length > 5 {
			if !inGroup && string(line[:5]) == "#====" {
				inGroup = true
				group.Name = strings.TrimSpace(string(line[5:]))
			} else if inGroup {
				host := new(hosts.Host)
				if line[0] == '#' {
					line = string(line[1:])
					host.Enable = false
				} else {
					host.Enable = true
				}
				items := strings.Fields(line)
				if len(items) > 0 {
					host.Ip = items[0]
					host.Domain = items[1:]
				}

				group.Items = append(group.Items, *host)
			}
		}
	}
	return groups
}

func loadHostString() (string, error) {
	hostPath := ""
	if PLATFORM == "darwin" || PLATFORM == "linux" {
		hostPath = "/etc/hosts"
	} else {
		return "", errors.New("unsupported PLATFORM!")
	}

	bytes, err := ioutil.ReadFile(hostPath)
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}

func updateHostString(hosts string) error {
	hostFile := "/etc/hosts"
	return ioutil.WriteFile(hostFile, []byte(hosts), 0777)
}
<commit_msg>implement edit command.<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"github.com/micanzhang/gohosts"
	"io/ioutil"
	"os"
	"os/exec"
	"runtime"
	"strings"
)

const VERSION = "1.0@dev"
const PLATFORM = runtime.GOOS

func usage() {
	fmt.Fprintf(os.Stderr, "usage: %s [options]\n", os.Args[0])
	flag.PrintDefaults()
	os.Exit(2)
}

func help() {
	usage()
}

func version() {
	fmt.Printf("%s version %s\n", os.Args[0], VERSION)
	flag.PrintDefaults()
	os.Exit(2)
}

func main() {
	parseCMD()
}

func edit() {
	hostFile := "/etc/hosts"
	if PLATFORM == "win" {
		hostFile = "C:/Windows/System32/driver/etc/hosts"
	}
	cmd := exec.Command("emacs", hostFile)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		fmt.Println(err)
	}
}

func listHosts(params map[string]string) {
	groups := getHost()
	if group, ok := params["group"]; ok {
		groups = groups.FindByName(group)
	}

	if ip, ok := params["ip"]; ok {
		groups = groups.FilterByIp(ip)
	}

	if domain, ok := params["domain"]; ok {
		groups = groups.FilterByDomain(domain)
	}

	fmt.Println(groups)
}

func switchHosts(params map[string]string, enable bool) {
	groups := getHost()
	if name, ok := params["group"]; ok {
		if ip, ok := params["ip"]; ok {
			groups.SwitchByIp(ip, enable)
		} else if domain, ok := params["domain"]; ok {
			groups.SwitchByDomain(domain, enable)
		} else {
			groups.SwitchByName(name, enable)
		}
	} else {

	}

	if ip, ok := params["ip"]; ok {
		groups.SwitchByIp(ip, enable)
	}

	if domain, ok := params["domain"]; ok {
		groups.SwitchByDomain(domain, enable)
	}

	if groups != nil {
		content := groups.String()
		if err := updateHostString(content); err != nil {
			fmt.Printf("Error: %v\n", err)
		} else {
			fmt.Println(content)
		}
	} else {

	}
}

func parseCMD() {
	args := os.Args
	params := make(map[string]string)
	if len(args) == 1 {
		usage()
	}

	key := ""
	action := ""
	//support list, edit, disable, enable, help, version
	for _, arg := range args[1:] {
		if len(key) > 0 {
			params[key] = arg
			key = ""
		} else {
			switch arg {
			case "-h", "--help":
				action = "help"
				help()
				break
			case "-v", "--version":
				action = "version"
				version()
				break
			case "-l", "--list", "list":
				if len(action) == 0 {
					action = "list"
				} else if action != "list" {
					usage()
				}
				break
			case "-e", "--edit":
				action = "edit"
				edit()
				os.Exit(0)
			case "-r", "--remove", "remove":
				if len(action) == 0 {
					action = "disable"
				} else if action != "disable" {
					usage()
				}
				break
			case "-s", "--switch", "switch":
				if len(action) == 0 {
					action = "enable"
				} else if action != "enable" {
					usage()
				}
				break
			case "-g", "--group":
				key = "group"
				break
			case "-d", "--domain":
				key = "domain"
				break
			case "-i", "--ip":
				key = "ip"
				break
			default:
				if action != "" {
					params["group"] = arg
				} else {
					usage()
				}
				break
			}
		}
	}

	if action == "disable" {
		switchHosts(params, false)
	} else if action == "enable" {
		switchHosts(params, true)
	} else {
		listHosts(params)
	}
}

func listHost(params map[string]string) {
	groups := getHost()
	fmt.Println(groups)
	//list all
	//list by group
	// list by domain
	// list by ip
}

func getHost() *hosts.Groups {
	hostStr, err := loadHostString()
	if err != nil {
		panic(err)
	}
	lines := strings.Split(hostStr, "\n")
	groups := new(hosts.Groups)
	group := new(hosts.Group)
	inGroup := false
	for _, line := range lines {
		line = strings.TrimSpace(line)
		length := len(line)
		if length == 5 && inGroup {
			*groups = append(*groups, *group)
			group = new(hosts.Group)
			inGroup = false
		} else if length > 5 {
			if !inGroup && string(line[:5]) == "#====" {
				inGroup = true
				group.Name = strings.TrimSpace(string(line[5:]))
			} else if inGroup {
				host := new(hosts.Host)
				if line[0] == '#' {
					line = string(line[1:])
					host.Enable = false
				} else {
					host.Enable = true
				}
				items := strings.Fields(line)
				if len(items) > 0 {
					host.Ip = items[0]
					host.Domain = items[1:]
				}

				group.Items = append(group.Items, *host)
			}
		}
	}
	return groups
}

func loadHostString() (string, error) {
	hostPath := ""
	if PLATFORM == "darwin" || PLATFORM == "linux" {
		hostPath = "/etc/hosts"
	} else {
		return "", errors.New("unsupported PLATFORM!")
	}

	bytes, err := ioutil.ReadFile(hostPath)
	if err != nil {
		return "", err
	}
	return string(bytes), nil
}

func updateHostString(hosts string) error {
	hostFile := "/etc/hosts"
	return ioutil.WriteFile(hostFile, []byte(hosts), 0777)
}
<|endoftext|>


<commit_before>package hub

import (
	"code.google.com/p/go.net/websocket"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"sync"
	"time"
)

const (
	defaultBufferSize     = 1024
	defaultHeartbeat      = time.Second * 5
	defaultSessionTimeout = time.Second * 30
	idLength              = 16
	bufLength             = 4096
	defaultLoglevel       = 1
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

type Authorizer func(uri, token string, write bool) (authorized bool, err error)

func prettify(obj interface{}) string {
	b, err := json.MarshalIndent(obj, "", "  ")
	if err != nil {
		panic(err)
	}
	return string(b)
}

type Server struct {
	heartbeat      time.Duration
	sessionTimeout time.Duration
	sessions       map[string]*Session
	subscriptions  map[string]map[string]*Session
	subscribers    map[string]map[string]bool
	lock           *sync.RWMutex
	loglevel       int
	bufferSize     int
	logger         *log.Logger
	authorizer     Authorizer
}

func NewServer() *Server {
	return &Server{
		heartbeat:      defaultHeartbeat,
		bufferSize:     defaultBufferSize,
		sessionTimeout: defaultSessionTimeout,
		loglevel:       defaultLoglevel,
		sessions:       map[string]*Session{},
		subscriptions:  map[string]map[string]*Session{},
		subscribers:    map[string]map[string]bool{},
		lock:           &sync.RWMutex{},
		logger:         log.New(os.Stdout, "pusher: ", 0),
	}
}

func (self *Server) Loglevel(i int) *Server {
	self.loglevel = i
	return self
}

func (self *Server) Logger(l *log.Logger) *Server {
	self.logger = l
	return self
}

func (self *Server) Fatalf(fmt string, i ...interface{}) {
	self.logger.Printf(fmt, i...)
}

func (self *Server) Errorf(fmt string, i ...interface{}) {
	if self.loglevel > 0 {
		self.logger.Printf(fmt, i...)
	}
}

func (self *Server) Infof(fmt string, i ...interface{}) {
	if self.loglevel > 1 {
		self.logger.Printf(fmt, i...)
	}
}

func (self *Server) Debugf(fmt string, i ...interface{}) {
	if self.loglevel > 2 {
		self.logger.Printf(fmt, i...)
	}
}

func (self *Server) Authorizer(f Authorizer) *Server {
	self.authorizer = f
	return self
}

func (self *Server) addSubscription(sess *Session, uri string) {
	self.lock.Lock()
	defer self.lock.Unlock()

	if _, found := self.subscriptions[uri]; !found {
		self.subscriptions[uri] = map[string]*Session{}
	}
	self.subscriptions[uri][sess.id] = sess

	if _, found := self.subscribers[sess.id]; !found {
		self.subscribers[sess.id] = map[string]bool{}
	}
	self.subscribers[sess.id][uri] = true

	self.Infof("%v\t-\t%v\t%v\t%v\t[subscribe]", time.Now(), uri, sess.RemoteAddr, sess.id)
}

func (self *Server) Emit(message Message) {
	self.lock.RLock()
	defer self.lock.RUnlock()

	sent := 0
	for _, sess := range self.subscriptions[message.URI] {
		sess.send(message)
		sent++
	}
	self.Debugf("%v\t%v\t[emitted] x %v", time.Now(), message.URI, sent)
}

func (self *Server) removeSubscription(id, uri string, withLocking bool) {
	if withLocking {
		self.lock.Lock()
		defer self.lock.Unlock()
	}

	delete(self.subscriptions[uri], id)
	if len(self.subscriptions[uri]) == 0 {
		delete(self.subscriptions, uri)
	}

	delete(self.subscribers[id], uri)
	if len(self.subscribers[id]) == 0 {
		delete(self.subscribers, id)
	}
	self.Infof("%v\t-\t%v\t%v\t-\t[unsubscribe]", time.Now(), uri, id)
}

func (self *Server) randomId() string {
	buf := make([]byte, idLength)
	for index, _ := range buf {
		buf[index] = byte(rand.Int31())
	}
	return (base64.URLEncoding.EncodeToString(buf))
}

func (self *Server) removeSession(id string) {
	self.lock.Lock()
	defer self.lock.Unlock()

	delete(self.sessions, id)

	for uri, _ := range self.subscribers[id] {
		self.removeSubscription(id, uri, false)
	}
	self.Infof("%v\t-\t[cleanup]\t%v", time.Now(), id)
}

func (self *Server) GetSession(id string) (result *Session) {
	self.lock.Lock()
	defer self.lock.Unlock()

	// if the id is not found (because the server restarted, or the client gave the initial empty id) we just create a new id and insert a new session
	if result = self.sessions[id]; result == nil {
		result = &Session{
			output:         make(chan Message, self.bufferSize),
			id:             self.randomId(),
			server:         self,
			authorizations: map[string]bool{},
			lock:           &sync.RWMutex{},
		}
		result.cleanupTimer = &time.Timer{}
		self.sessions[result.id] = result
	}
	return
}

func (self *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	self.Infof("%v\t%v\t%v\t%v", time.Now(), r.Method, r.URL, r.RemoteAddr)
	websocket.Handler(self.GetSession(r.URL.Query().Get("session_id")).handleWS).ServeHTTP(w, r)
}

type MessageType string
type ErrorType string

const (
	TypeError       = "Error"
	TypeHeartbeat   = "Heartbeat"
	TypeWelcome     = "Welcome"
	TypeSubscribe   = "Subscribe"
	TypeUnsubscribe = "Unsubscribe"
	TypeMessage     = "Message"
	TypeAuthorize   = "Authorize"
	TypeAck         = "Ack"
)

const (
	TypeJSONError          = "JSONError"
	TypeAuthorizationError = "AuthorizationError"
	TypeSyntaxError        = "SyntaxError"
)

type Welcome struct {
	Heartbeat      time.Duration
	SessionTimeout time.Duration
	Id             string
}

type Error struct {
	Message string
	Type    ErrorType
}

type Message struct {
	Type    MessageType
	Id      string      `json:",omitempty"`
	Welcome *Welcome    `json:",omitempty"`
	Error   *Error      `json:",omitempty"`
	Data    interface{} `json:",omitempty"`
	URI     string      `json:",omitempty"`
	Token   string      `json:",omitempty"`
	Write   bool        `json:",omitempty"`
}

type Session struct {
	ws             io.ReadWriteCloser
	id             string
	RemoteAddr     string
	input          chan Message
	output         chan Message
	closing        chan struct{}
	server         *Server
	cleanupTimer   *time.Timer
	authorizations map[string]bool
	lock           *sync.RWMutex
}

func (self *Session) parseMessage(b []byte) (result Message, err error) {
	err = json.Unmarshal(b, &result)
	return
}

func (self *Session) readLoop() {
	defer self.terminate()
	buf := make([]byte, bufLength)
	n, err := self.ws.Read(buf)
	for err == nil {
		if message, err := self.parseMessage(buf[:n]); err == nil {
			self.input <- message
			self.server.Debugf("%v\t%v\t%v\t%v\t[received from socket]", time.Now(), message.URI, self.RemoteAddr, self.id)
		} else {
			buf_copy := make([]byte, n)
			copy(buf_copy, buf[:n])
			self.send(Message{
				Type: TypeError,
				Error: &Error{
					Message: err.Error(),
					Type:    TypeJSONError,
				},
				Data: string(buf_copy)})
		}
		n, err = self.ws.Read(buf)
	}
}

func (self *Session) writeLoop() {
	defer self.terminate()
	var message Message
	var err error
	var n int
	var encoded []byte
	for {
		select {
		case message = <-self.output:
			if encoded, err = json.Marshal(message); err == nil {
				if n, err = self.ws.Write(encoded); err != nil {
					self.server.Fatalf("Error sending %s on %+v: %v", encoded, self.ws, err)
					return
				} else if n != len(encoded) {
					self.server.Fatalf("Unable to send all of %s on %+v: only sent %v bytes", encoded, self.ws, n)
					return
				}
			} else {
				self.server.Fatalf("Unable to JSON marshal %+v: %v", message, err)
				return
			}
			self.server.Debugf("%v\t%v\t%v\t%v\t[sent to socket]", time.Now(), message.URI, self.RemoteAddr, self.id)
		case <-self.closing:
			return
		}
	}
}

func (self *Session) heartbeatLoop() {
	for {
		select {
		case <-self.closing:
			return
		case <-time.After(self.server.heartbeat / 2):
			self.send(Message{Type: TypeHeartbeat})
		}
	}
}

func (self *Session) authorized(uri string, wantWrite bool) bool {
	if self.server.authorizer == nil {
		return true
	}
	self.lock.RLock()
	defer self.lock.RUnlock()
	hasWrite, found := self.authorizations[uri]
	if !found {
		return false
	}
	return !wantWrite || hasWrite
}

func (self *Session) send(message Message) {
	select {
	case self.output <- message:
	default:
		self.server.Errorf("Unable to send %+v to %+v, output buffer full", message, self)
	}
}

func (self *Session) handleMessage(message Message) {
	switch message.Type {
	case TypeHeartbeat:
	case TypeMessage:
		if !self.authorized(message.URI, true) {
			self.send(Message{
				Type: TypeError,
				Id:   message.Id,
				Error: &Error{
					Message: fmt.Sprintf("%v not authorized for writing to %v", self.id, message.URI),
					Type:    TypeAuthorizationError,
				},
				Data: message,
			})
			return
		}
		self.server.Emit(message)
	case TypeUnsubscribe:
		self.server.removeSubscription(self.id, message.URI, true)
	case TypeSubscribe:
		if !self.authorized(message.URI, false) {
			self.send(Message{
				Type: TypeError,
				Id:   message.Id,
				Error: &Error{
					Message: fmt.Sprintf("%v not authorized for subscribing to %v", self.id, message.URI),
					Type:    TypeAuthorizationError,
				},
				Data: message,
			})
			return
		}
		self.server.addSubscription(self, message.URI)
	case TypeAuthorize:
		if self.server.authorizer != nil {
			ok, err := self.server.authorizer(message.URI, message.Token, message.Write)
			if err != nil {
				self.send(Message{
					Type: TypeError,
					Id:   message.Id,
					Error: &Error{
						Message: err.Error(),
						Type:    TypeAuthorizationError,
					}, Data: message,
				})
				return
			}
			if !ok {
				self.send(Message{
					Type: TypeError,
					Id:   message.Id,
					Error: &Error{
						Message: fmt.Sprintf("%v does not provide authorization for %v", message.Token, message.URI),
						Type:    TypeAuthorizationError,
					}, Data: message,
				})
				return
			}
		}
		self.lock.Lock()
		defer self.lock.Unlock()
		self.authorizations[message.URI] = (self.authorizations[message.URI] || message.Write)
		self.server.Infof("%v\t-\t[authorize]\t%v\t%v\t%v", time.Now(), self.RemoteAddr, self.id, message.URI)
	default:
		self.send(Message{
			Type: TypeError,
			Id:   message.Id,
			Error: &Error{
				Message: fmt.Sprintf("Unknown message type %#v", message.Type),
				Type:    TypeSyntaxError,
			},
			Data: message,
		})
		return
	}
	if message.Id != "" {
		self.send(Message{
			Type: TypeAck,
			Id:   message.Id,
		})
	}

}

func (self *Session) remove() {
	self.server.removeSession(self.id)
}

func (self *Session) terminate() {
	self.ws.Close()
	select {
	case _ = <-self.closing:
	default:
		close(self.closing)
	}
	self.server.Infof("%v\t-\t[disconnect]\t%v\t%v", time.Now(), self.RemoteAddr, self.id)
	self.cleanupTimer.Stop()
	self.cleanupTimer = time.AfterFunc(self.server.sessionTimeout, self.remove)
}

func (self *Session) handleWS(ws *websocket.Conn) {
	self.RemoteAddr = ws.Request().RemoteAddr
	self.Handle(ws)
}

/*
 * This function will work as the session main-loop listening on events
 * on a websocket or other source that interfaces io.ReadWriteCloser.
 */
func (self *Session) Handle(ws io.ReadWriteCloser) {
	self.server.Infof("%v\t-\t[connect]\t%v\t%v", time.Now(), self.RemoteAddr, self.id)

	defer self.terminate()

	self.ws = ws
	self.cleanupTimer.Stop()
	self.input = make(chan Message)
	self.closing = make(chan struct{})

	go self.readLoop()
	go self.writeLoop()
	go self.heartbeatLoop()

	self.send(Message{
		Type: TypeWelcome,
		Welcome: &Welcome{
			Heartbeat:      self.server.heartbeat / time.Millisecond,
			SessionTimeout: self.server.sessionTimeout / time.Millisecond,
			Id:             self.id,
		},
	})

	var message Message
	for {
		select {
		case _ = <-self.closing:
			return
		case message = <-self.input:
			self.handleMessage(message)
		case <-time.After(self.server.heartbeat):
			return
		}
	}
}
<commit_msg>Durr, I just remembered why I was still fine with the buffer reuse: string(...) copies the buffer - a string is NOT backed by a []byte, a string contains its own (non-mutable!) data.<commit_after>package hub

import (
	"code.google.com/p/go.net/websocket"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"math/rand"
	"net/http"
	"os"
	"sync"
	"time"
)

const (
	defaultBufferSize     = 1024
	defaultHeartbeat      = time.Second * 5
	defaultSessionTimeout = time.Second * 30
	idLength              = 16
	bufLength             = 4096
	defaultLoglevel       = 1
)

func init() {
	rand.Seed(time.Now().UnixNano())
}

type Authorizer func(uri, token string, write bool) (authorized bool, err error)

func prettify(obj interface{}) string {
	b, err := json.MarshalIndent(obj, "", "  ")
	if err != nil {
		panic(err)
	}
	return string(b)
}

type Server struct {
	heartbeat      time.Duration
	sessionTimeout time.Duration
	sessions       map[string]*Session
	subscriptions  map[string]map[string]*Session
	subscribers    map[string]map[string]bool
	lock           *sync.RWMutex
	loglevel       int
	bufferSize     int
	logger         *log.Logger
	authorizer     Authorizer
}

func NewServer() *Server {
	return &Server{
		heartbeat:      defaultHeartbeat,
		bufferSize:     defaultBufferSize,
		sessionTimeout: defaultSessionTimeout,
		loglevel:       defaultLoglevel,
		sessions:       map[string]*Session{},
		subscriptions:  map[string]map[string]*Session{},
		subscribers:    map[string]map[string]bool{},
		lock:           &sync.RWMutex{},
		logger:         log.New(os.Stdout, "pusher: ", 0),
	}
}

func (self *Server) Loglevel(i int) *Server {
	self.loglevel = i
	return self
}

func (self *Server) Logger(l *log.Logger) *Server {
	self.logger = l
	return self
}

func (self *Server) Fatalf(fmt string, i ...interface{}) {
	self.logger.Printf(fmt, i...)
}

func (self *Server) Errorf(fmt string, i ...interface{}) {
	if self.loglevel > 0 {
		self.logger.Printf(fmt, i...)
	}
}

func (self *Server) Infof(fmt string, i ...interface{}) {
	if self.loglevel > 1 {
		self.logger.Printf(fmt, i...)
	}
}

func (self *Server) Debugf(fmt string, i ...interface{}) {
	if self.loglevel > 2 {
		self.logger.Printf(fmt, i...)
	}
}

func (self *Server) Authorizer(f Authorizer) *Server {
	self.authorizer = f
	return self
}

func (self *Server) addSubscription(sess *Session, uri string) {
	self.lock.Lock()
	defer self.lock.Unlock()

	if _, found := self.subscriptions[uri]; !found {
		self.subscriptions[uri] = map[string]*Session{}
	}
	self.subscriptions[uri][sess.id] = sess

	if _, found := self.subscribers[sess.id]; !found {
		self.subscribers[sess.id] = map[string]bool{}
	}
	self.subscribers[sess.id][uri] = true

	self.Infof("%v\t-\t%v\t%v\t%v\t[subscribe]", time.Now(), uri, sess.RemoteAddr, sess.id)
}

func (self *Server) Emit(message Message) {
	self.lock.RLock()
	defer self.lock.RUnlock()

	sent := 0
	for _, sess := range self.subscriptions[message.URI] {
		sess.send(message)
		sent++
	}
	self.Debugf("%v\t%v\t[emitted] x %v", time.Now(), message.URI, sent)
}

func (self *Server) removeSubscription(id, uri string, withLocking bool) {
	if withLocking {
		self.lock.Lock()
		defer self.lock.Unlock()
	}

	delete(self.subscriptions[uri], id)
	if len(self.subscriptions[uri]) == 0 {
		delete(self.subscriptions, uri)
	}

	delete(self.subscribers[id], uri)
	if len(self.subscribers[id]) == 0 {
		delete(self.subscribers, id)
	}
	self.Infof("%v\t-\t%v\t%v\t-\t[unsubscribe]", time.Now(), uri, id)
}

func (self *Server) randomId() string {
	buf := make([]byte, idLength)
	for index, _ := range buf {
		buf[index] = byte(rand.Int31())
	}
	return (base64.URLEncoding.EncodeToString(buf))
}

func (self *Server) removeSession(id string) {
	self.lock.Lock()
	defer self.lock.Unlock()

	delete(self.sessions, id)

	for uri, _ := range self.subscribers[id] {
		self.removeSubscription(id, uri, false)
	}
	self.Infof("%v\t-\t[cleanup]\t%v", time.Now(), id)
}

func (self *Server) GetSession(id string) (result *Session) {
	self.lock.Lock()
	defer self.lock.Unlock()

	// if the id is not found (because the server restarted, or the client gave the initial empty id) we just create a new id and insert a new session
	if result = self.sessions[id]; result == nil {
		result = &Session{
			output:         make(chan Message, self.bufferSize),
			id:             self.randomId(),
			server:         self,
			authorizations: map[string]bool{},
			lock:           &sync.RWMutex{},
		}
		result.cleanupTimer = &time.Timer{}
		self.sessions[result.id] = result
	}
	return
}

func (self *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	self.Infof("%v\t%v\t%v\t%v", time.Now(), r.Method, r.URL, r.RemoteAddr)
	websocket.Handler(self.GetSession(r.URL.Query().Get("session_id")).handleWS).ServeHTTP(w, r)
}

type MessageType string
type ErrorType string

const (
	TypeError       = "Error"
	TypeHeartbeat   = "Heartbeat"
	TypeWelcome     = "Welcome"
	TypeSubscribe   = "Subscribe"
	TypeUnsubscribe = "Unsubscribe"
	TypeMessage     = "Message"
	TypeAuthorize   = "Authorize"
	TypeAck         = "Ack"
)

const (
	TypeJSONError          = "JSONError"
	TypeAuthorizationError = "AuthorizationError"
	TypeSyntaxError        = "SyntaxError"
)

type Welcome struct {
	Heartbeat      time.Duration
	SessionTimeout time.Duration
	Id             string
}

type Error struct {
	Message string
	Type    ErrorType
}

type Message struct {
	Type    MessageType
	Id      string      `json:",omitempty"`
	Welcome *Welcome    `json:",omitempty"`
	Error   *Error      `json:",omitempty"`
	Data    interface{} `json:",omitempty"`
	URI     string      `json:",omitempty"`
	Token   string      `json:",omitempty"`
	Write   bool        `json:",omitempty"`
}

type Session struct {
	ws             io.ReadWriteCloser
	id             string
	RemoteAddr     string
	input          chan Message
	output         chan Message
	closing        chan struct{}
	server         *Server
	cleanupTimer   *time.Timer
	authorizations map[string]bool
	lock           *sync.RWMutex
}

func (self *Session) parseMessage(b []byte) (result Message, err error) {
	err = json.Unmarshal(b, &result)
	return
}

func (self *Session) readLoop() {
	defer self.terminate()
	buf := make([]byte, bufLength)
	n, err := self.ws.Read(buf)
	for err == nil {
		if message, err := self.parseMessage(buf[:n]); err == nil {
			self.input <- message
			self.server.Debugf("%v\t%v\t%v\t%v\t[received from socket]", time.Now(), message.URI, self.RemoteAddr, self.id)
		} else {
			self.send(Message{
				Type: TypeError,
				Error: &Error{
					Message: err.Error(),
					Type:    TypeJSONError,
				},
				Data: string(buf[:n]),
			})
		}
		n, err = self.ws.Read(buf)
	}
}

func (self *Session) writeLoop() {
	defer self.terminate()
	var message Message
	var err error
	var n int
	var encoded []byte
	for {
		select {
		case message = <-self.output:
			if encoded, err = json.Marshal(message); err == nil {
				if n, err = self.ws.Write(encoded); err != nil {
					self.server.Fatalf("Error sending %s on %+v: %v", encoded, self.ws, err)
					return
				} else if n != len(encoded) {
					self.server.Fatalf("Unable to send all of %s on %+v: only sent %v bytes", encoded, self.ws, n)
					return
				}
			} else {
				self.server.Fatalf("Unable to JSON marshal %+v: %v", message, err)
				return
			}
			self.server.Debugf("%v\t%v\t%v\t%v\t[sent to socket]", time.Now(), message.URI, self.RemoteAddr, self.id)
		case <-self.closing:
			return
		}
	}
}

func (self *Session) heartbeatLoop() {
	for {
		select {
		case <-self.closing:
			return
		case <-time.After(self.server.heartbeat / 2):
			self.send(Message{Type: TypeHeartbeat})
		}
	}
}

func (self *Session) authorized(uri string, wantWrite bool) bool {
	if self.server.authorizer == nil {
		return true
	}
	self.lock.RLock()
	defer self.lock.RUnlock()
	hasWrite, found := self.authorizations[uri]
	if !found {
		return false
	}
	return !wantWrite || hasWrite
}

func (self *Session) send(message Message) {
	select {
	case self.output <- message:
	default:
		self.server.Errorf("Unable to send %+v to %+v, output buffer full", message, self)
	}
}

func (self *Session) handleMessage(message Message) {
	switch message.Type {
	case TypeHeartbeat:
	case TypeMessage:
		if !self.authorized(message.URI, true) {
			self.send(Message{
				Type: TypeError,
				Id:   message.Id,
				Error: &Error{
					Message: fmt.Sprintf("%v not authorized for writing to %v", self.id, message.URI),
					Type:    TypeAuthorizationError,
				},
				Data: message,
			})
			return
		}
		self.server.Emit(message)
	case TypeUnsubscribe:
		self.server.removeSubscription(self.id, message.URI, true)
	case TypeSubscribe:
		if !self.authorized(message.URI, false) {
			self.send(Message{
				Type: TypeError,
				Id:   message.Id,
				Error: &Error{
					Message: fmt.Sprintf("%v not authorized for subscribing to %v", self.id, message.URI),
					Type:    TypeAuthorizationError,
				},
				Data: message,
			})
			return
		}
		self.server.addSubscription(self, message.URI)
	case TypeAuthorize:
		if self.server.authorizer != nil {
			ok, err := self.server.authorizer(message.URI, message.Token, message.Write)
			if err != nil {
				self.send(Message{
					Type: TypeError,
					Id:   message.Id,
					Error: &Error{
						Message: err.Error(),
						Type:    TypeAuthorizationError,
					}, Data: message,
				})
				return
			}
			if !ok {
				self.send(Message{
					Type: TypeError,
					Id:   message.Id,
					Error: &Error{
						Message: fmt.Sprintf("%v does not provide authorization for %v", message.Token, message.URI),
						Type:    TypeAuthorizationError,
					}, Data: message,
				})
				return
			}
		}
		self.lock.Lock()
		defer self.lock.Unlock()
		self.authorizations[message.URI] = (self.authorizations[message.URI] || message.Write)
		self.server.Infof("%v\t-\t[authorize]\t%v\t%v\t%v", time.Now(), self.RemoteAddr, self.id, message.URI)
	default:
		self.send(Message{
			Type: TypeError,
			Id:   message.Id,
			Error: &Error{
				Message: fmt.Sprintf("Unknown message type %#v", message.Type),
				Type:    TypeSyntaxError,
			},
			Data: message,
		})
		return
	}
	if message.Id != "" {
		self.send(Message{
			Type: TypeAck,
			Id:   message.Id,
		})
	}

}

func (self *Session) remove() {
	self.server.removeSession(self.id)
}

func (self *Session) terminate() {
	self.ws.Close()
	select {
	case _ = <-self.closing:
	default:
		close(self.closing)
	}
	self.server.Infof("%v\t-\t[disconnect]\t%v\t%v", time.Now(), self.RemoteAddr, self.id)
	self.cleanupTimer.Stop()
	self.cleanupTimer = time.AfterFunc(self.server.sessionTimeout, self.remove)
}

func (self *Session) handleWS(ws *websocket.Conn) {
	self.RemoteAddr = ws.Request().RemoteAddr
	self.Handle(ws)
}

/*
 * This function will work as the session main-loop listening on events
 * on a websocket or other source that interfaces io.ReadWriteCloser.
 */
func (self *Session) Handle(ws io.ReadWriteCloser) {
	self.server.Infof("%v\t-\t[connect]\t%v\t%v", time.Now(), self.RemoteAddr, self.id)

	defer self.terminate()

	self.ws = ws
	self.cleanupTimer.Stop()
	self.input = make(chan Message)
	self.closing = make(chan struct{})

	go self.readLoop()
	go self.writeLoop()
	go self.heartbeatLoop()

	self.send(Message{
		Type: TypeWelcome,
		Welcome: &Welcome{
			Heartbeat:      self.server.heartbeat / time.Millisecond,
			SessionTimeout: self.server.sessionTimeout / time.Millisecond,
			Id:             self.id,
		},
	})

	var message Message
	for {
		select {
		case _ = <-self.closing:
			return
		case message = <-self.input:
			self.handleMessage(message)
		case <-time.After(self.server.heartbeat):
			return
		}
	}
}
<|endoftext|>


<commit_before>package toolbox

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"go/types"
	"io/ioutil"
	"path"
	"reflect"
	"strings"
)

//FieldInfo represents a filed info
type FieldInfo struct {
	Name          string
	TypeName      string
	KeyTypeName   string
	ValueTypeName string
	TypePackage   string
	IsMap         bool
	IsChannel     bool
	IsSlice       bool
	IsStruct      bool
	IsPointer     bool
	Tag           string
	Comment       string
}

//NewFieldInfo creates a new field info.
func NewFieldInfo(field *ast.Field) *FieldInfo {
	result := &FieldInfo{
		Name:     "",
		TypeName: types.ExprString(field.Type),
	}

	if len(field.Names) > 0 {
		result.Name = field.Names[0].Name
	}
	_, result.IsMap = field.Type.(*ast.MapType)
	_, result.IsSlice = field.Type.(*ast.ArrayType)
	_, result.IsPointer = field.Type.(*ast.StarExpr)
	_, result.IsChannel = field.Type.(*ast.ChanType)
	if selector, ok := field.Type.(*ast.SelectorExpr); ok {
		result.TypePackage = types.ExprString(selector.X)
		result.IsStruct = true
	}

	if result.IsPointer {
		if pointerExpr, casted := field.Type.(*ast.StarExpr); casted {
			if identExpr, ok := pointerExpr.X.(*ast.Ident); ok {
				result.TypeName = identExpr.Name
				if reflect.TypeOf(identExpr.Obj).Elem().Kind() == reflect.Struct {
					result.IsStruct = true
				}

			}
		}
	}

	if field.Tag != nil {
		result.Tag = field.Tag.Value
	}
	if ident, ok := field.Type.(*ast.Ident); ok {
		kind := ""
		if ident.Obj != nil {
			result.IsStruct = kind == "type"
		}

	}
	if mapType, ok := field.Type.(*ast.MapType); ok {
		result.KeyTypeName = types.ExprString(mapType.Key)
		result.ValueTypeName = types.ExprString(mapType.Value)
	}
	return result
}

//FunctionInfo represents a function info
type FunctionInfo struct {
	Name             string
	ReceiverTypeName string
	ParameterFields  []*FieldInfo
	ResultsFields    []*FieldInfo
}

//NewFunctionInfo create a new function
func NewFunctionInfo(funcDeclaration *ast.FuncDecl) *FunctionInfo {
	result := &FunctionInfo{
		Name:            "",
		ParameterFields: make([]*FieldInfo, 0),
		ResultsFields:   make([]*FieldInfo, 0),
	}

	if funcDeclaration.Name != nil {
		result.Name = funcDeclaration.Name.Name
	}
	if funcDeclaration.Recv != nil {
		receiverType := funcDeclaration.Recv.List[0].Type
		if ident, ok := receiverType.(*ast.Ident); ok {
			result.ReceiverTypeName = ident.Name
		} else if startExpr, ok := receiverType.(*ast.StarExpr); ok {
			if ident, ok := startExpr.X.(*ast.Ident); ok {
				result.ReceiverTypeName = ident.Name
			}
		}
	}
	return result
}

//StructInfo represents a struct info
type StructInfo struct {
	Name            string
	Package         string
	FileName        string
	Comment         string
	Settings        map[string]string
	fields          []*FieldInfo
	indexedField    map[string]*FieldInfo
	receivers       []*FunctionInfo
	indexedReceiver map[string]*FunctionInfo
}

//AddFields appends fileds to structinfo
func (s *StructInfo) AddFields(fields ...*FieldInfo) {
	s.fields = append(s.fields, fields...)
	for _, field := range fields {
		s.indexedField[field.Name] = field
	}
}

//Field returns filedinfo for supplied file name
func (s *StructInfo) Field(name string) *FieldInfo {
	return s.indexedField[name]
}

//Fields returns all fields
func (s *StructInfo) Fields() []*FieldInfo {
	return s.fields
}

//HasField returns true if struct has passed in field.
func (s *StructInfo) HasField(name string) bool {
	_, found := s.indexedField[name]
	return found
}

//Receivers returns struct functions
func (s *StructInfo) Receivers() []*FunctionInfo {
	return s.receivers
}

//Receiver returns receiver for passed in name
func (s *StructInfo) Receiver(name string) *FunctionInfo {
	return s.indexedReceiver[name]
}

//HasReceiver returns true if receiver is defined for struct
func (s *StructInfo) HasReceiver(name string) bool {
	_, found := s.indexedReceiver[name]
	return found
}

//AddReceivers adds receiver for the struct
func (s *StructInfo) AddReceivers(receivers ...*FunctionInfo) {
	s.receivers = append(s.receivers, receivers...)
	for _, receiver := range receivers {
		s.indexedReceiver[receiver.Name] = receiver
	}
}

//NewStructInfo creates a new struct info
func NewStructInfo(name string) *StructInfo {
	return &StructInfo{Name: name,
		fields:          make([]*FieldInfo, 0),
		receivers:       make([]*FunctionInfo, 0),
		indexedReceiver: make(map[string]*FunctionInfo),
		indexedField:    make(map[string]*FieldInfo),
		Settings:        make(map[string]string)}
}

//FileInfo represent hold definition about all defined structs and its receivers in a file
type FileInfo struct {
	basePath            string
	filename            string
	structs             map[string]*StructInfo
	functions           map[string][]*FunctionInfo
	packageName         string
	currentStructInfo   *StructInfo
	fileSet             *token.FileSet
	currentFunctionInfo *FunctionInfo
}

//Struct returns a struct info for passed in name
func (f *FileInfo) Struct(name string) *StructInfo {
	return f.structs[name]
}

//Struct returns a struct info for passed in name
func (f *FileInfo) addFunction(funcion *FunctionInfo) {
	functions, found := f.functions[funcion.ReceiverTypeName]
	if !found {
		functions = make([]*FunctionInfo, 0)
		f.functions[funcion.ReceiverTypeName] = functions
	}
	f.functions[funcion.ReceiverTypeName] = append(f.functions[funcion.ReceiverTypeName], funcion)
}

//Struct returns all struct info
func (f *FileInfo) Structs() []*StructInfo {
	var result = make([]*StructInfo, 0)
	for _, v := range f.structs {
		result = append(result, v)
	}
	return result
}

//HasStructInfo returns truc if struct info is defined in a file
func (f *FileInfo) HasStructInfo(name string) bool {
	_, found := f.structs[name]
	return found
}

//readComment reads comment from the position
func (v *FileInfo) readComment(pos token.Pos) string {
	position := v.fileSet.Position(pos)
	fileName := path.Join(v.basePath, v.filename)
	content, err := ioutil.ReadFile(fileName)
	if err != nil {
		panic("Unable to open file " + fileName)
	}
	line := strings.Split(string(content), "\n")[position.Line-1]
	commentPosition := strings.LastIndex(line, "//")
	if commentPosition != -1 {
		return line[commentPosition+2:]
	}
	return ""
}

//toFieldInfoSlice convers filedList to FiledInfo slice.
func toFieldInfoSlice(source *ast.FieldList) []*FieldInfo {
	var result = make([]*FieldInfo, 0)
	if source == nil || len(source.List) == 0 {
		return result
	}
	for _, fields := range source.List {
		result = append(result, NewFieldInfo(fields))
	}
	return result
}

//Visit visits ast node to extract struct details from the passed file
func (v *FileInfo) Visit(node ast.Node) ast.Visitor {
	if node != nil {
		//	fmt.Printf("visit %v %T\n", node, node)
		switch value := node.(type) {
		case *ast.TypeSpec:
			typeName := value.Name.Name
			structInfo := NewStructInfo(typeName)
			structInfo.Package = v.packageName
			structInfo.FileName = v.filename
			v.currentStructInfo = structInfo
			v.structs[typeName] = structInfo
		case *ast.StructType:
			v.currentStructInfo.Comment = v.readComment(value.Pos())
			v.currentStructInfo.AddFields(toFieldInfoSlice(value.Fields)...)

		case *ast.FuncDecl:
			functionInfo := NewFunctionInfo(value)
			v.currentFunctionInfo = functionInfo
			if len(functionInfo.ReceiverTypeName) > 0 {
				v.addFunction(functionInfo)
			}
		case *ast.FuncType:

			if v.currentFunctionInfo != nil {
				if value.Params != nil {
					v.currentFunctionInfo.ParameterFields = toFieldInfoSlice(value.Params)
				}
				if value.Results != nil {
					v.currentFunctionInfo.ResultsFields = toFieldInfoSlice(value.Results)
				}
				v.currentFunctionInfo = nil
			}
		}
	}
	return v
}

//Visit creates a new file info.
func NewFileInfo(basePath, packageName, filename string, fileSet *token.FileSet) *FileInfo {
	result := &FileInfo{
		basePath:    basePath,
		filename:    filename,
		packageName: packageName,
		structs:     make(map[string]*StructInfo),
		functions:   make(map[string][]*FunctionInfo),
		fileSet:     fileSet}
	return result
}

//FileSetInfo represents a fileset info storing information about go file with their struct definition
type FileSetInfo struct {
	files map[string]*FileInfo
}

//FileInfo returns fileinfo for supplied file name
func (f *FileSetInfo) FileInfo(name string) *FileInfo {
	return f.files[name]
}

//FilesInfo returns all files info.
func (f *FileSetInfo) FilesInfo() map[string]*FileInfo {
	return f.files
}

//StructInfo returns struct info for passed in struct name.
func (f *FileSetInfo) Struct(name string) *StructInfo {
	for _, v := range f.files {
		if v.HasStructInfo(name) {
			return v.Struct(name)
		}
	}
	return nil
}

//NewFileSetInfo creates a new fileset info
func NewFileSetInfo(baseDir string) (*FileSetInfo, error) {
	fileSet := token.NewFileSet()
	pkgs, err := parser.ParseDir(fileSet, baseDir, nil, parser.ParseComments)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse path %v: %v", baseDir, err)
	}

	var result = &FileSetInfo{
		files: make(map[string]*FileInfo),
	}
	for packageName, pkg := range pkgs {
		for filename, file := range pkg.Files {
			filename := path.Base(filename)
			fileInfo := NewFileInfo(baseDir, packageName, filename, fileSet)
			ast.Walk(fileInfo, file)
			result.files[filename] = fileInfo
		}
	}

	for _, fileInfo := range result.files {

		for k, functionsInfo := range fileInfo.functions {
			structInfo := result.Struct(k)
			if structInfo != nil {
				structInfo.AddReceivers(functionsInfo...)
			}
		}

	}
	return result, nil
}
<commit_msg>patched struct pointer detection<commit_after>package toolbox

import (
	"fmt"
	"go/ast"
	"go/parser"
	"go/token"
	"go/types"
	"io/ioutil"
	"path"
	"reflect"
	"strings"
)

//FieldInfo represents a filed info
type FieldInfo struct {
	Name          string
	TypeName      string
	KeyTypeName   string
	ValueTypeName string
	TypePackage   string
	IsMap         bool
	IsChannel     bool
	IsSlice       bool
	IsStruct      bool
	IsPointer     bool
	Tag           string
	Comment       string
}

//NewFieldInfo creates a new field info.
func NewFieldInfo(field *ast.Field) *FieldInfo {
	result := &FieldInfo{
		Name:     "",
		TypeName: types.ExprString(field.Type),
	}

	if len(field.Names) > 0 {
		result.Name = field.Names[0].Name
	}
	_, result.IsMap = field.Type.(*ast.MapType)
	_, result.IsSlice = field.Type.(*ast.ArrayType)
	_, result.IsPointer = field.Type.(*ast.StarExpr)
	_, result.IsChannel = field.Type.(*ast.ChanType)
	if selector, ok := field.Type.(*ast.SelectorExpr); ok {
		result.TypePackage = types.ExprString(selector.X)
		result.IsStruct = true
	}

	if result.IsPointer {
		if pointerExpr, casted := field.Type.(*ast.StarExpr); casted {
			if identExpr, ok := pointerExpr.X.(*ast.Ident); ok {
				result.TypeName = identExpr.Name
				if reflect.TypeOf(identExpr.Obj).Elem().Kind() == reflect.Struct {
					result.IsStruct = true
				}

			}
		}
	}

	if field.Tag != nil {
		result.Tag = field.Tag.Value
	}

	if mapType, ok := field.Type.(*ast.MapType); ok {
		result.KeyTypeName = types.ExprString(mapType.Key)
		result.ValueTypeName = types.ExprString(mapType.Value)
	}
	return result
}

//FunctionInfo represents a function info
type FunctionInfo struct {
	Name             string
	ReceiverTypeName string
	ParameterFields  []*FieldInfo
	ResultsFields    []*FieldInfo
}

//NewFunctionInfo create a new function
func NewFunctionInfo(funcDeclaration *ast.FuncDecl) *FunctionInfo {
	result := &FunctionInfo{
		Name:            "",
		ParameterFields: make([]*FieldInfo, 0),
		ResultsFields:   make([]*FieldInfo, 0),
	}

	if funcDeclaration.Name != nil {
		result.Name = funcDeclaration.Name.Name
	}
	if funcDeclaration.Recv != nil {
		receiverType := funcDeclaration.Recv.List[0].Type
		if ident, ok := receiverType.(*ast.Ident); ok {
			result.ReceiverTypeName = ident.Name
		} else if startExpr, ok := receiverType.(*ast.StarExpr); ok {
			if ident, ok := startExpr.X.(*ast.Ident); ok {
				result.ReceiverTypeName = ident.Name
			}
		}
	}
	return result
}

//StructInfo represents a struct info
type StructInfo struct {
	Name            string
	Package         string
	FileName        string
	Comment         string
	Settings        map[string]string
	fields          []*FieldInfo
	indexedField    map[string]*FieldInfo
	receivers       []*FunctionInfo
	indexedReceiver map[string]*FunctionInfo
}

//AddFields appends fileds to structinfo
func (s *StructInfo) AddFields(fields ...*FieldInfo) {
	s.fields = append(s.fields, fields...)
	for _, field := range fields {
		s.indexedField[field.Name] = field
	}
}

//Field returns filedinfo for supplied file name
func (s *StructInfo) Field(name string) *FieldInfo {
	return s.indexedField[name]
}

//Fields returns all fields
func (s *StructInfo) Fields() []*FieldInfo {
	return s.fields
}

//HasField returns true if struct has passed in field.
func (s *StructInfo) HasField(name string) bool {
	_, found := s.indexedField[name]
	return found
}

//Receivers returns struct functions
func (s *StructInfo) Receivers() []*FunctionInfo {
	return s.receivers
}

//Receiver returns receiver for passed in name
func (s *StructInfo) Receiver(name string) *FunctionInfo {
	return s.indexedReceiver[name]
}

//HasReceiver returns true if receiver is defined for struct
func (s *StructInfo) HasReceiver(name string) bool {
	_, found := s.indexedReceiver[name]
	return found
}

//AddReceivers adds receiver for the struct
func (s *StructInfo) AddReceivers(receivers ...*FunctionInfo) {
	s.receivers = append(s.receivers, receivers...)
	for _, receiver := range receivers {
		s.indexedReceiver[receiver.Name] = receiver
	}
}

//NewStructInfo creates a new struct info
func NewStructInfo(name string) *StructInfo {
	return &StructInfo{Name: name,
		fields:          make([]*FieldInfo, 0),
		receivers:       make([]*FunctionInfo, 0),
		indexedReceiver: make(map[string]*FunctionInfo),
		indexedField:    make(map[string]*FieldInfo),
		Settings:        make(map[string]string)}
}

//FileInfo represent hold definition about all defined structs and its receivers in a file
type FileInfo struct {
	basePath            string
	filename            string
	structs             map[string]*StructInfo
	functions           map[string][]*FunctionInfo
	packageName         string
	currentStructInfo   *StructInfo
	fileSet             *token.FileSet
	currentFunctionInfo *FunctionInfo
}

//Struct returns a struct info for passed in name
func (f *FileInfo) Struct(name string) *StructInfo {
	return f.structs[name]
}

//Struct returns a struct info for passed in name
func (f *FileInfo) addFunction(funcion *FunctionInfo) {
	functions, found := f.functions[funcion.ReceiverTypeName]
	if !found {
		functions = make([]*FunctionInfo, 0)
		f.functions[funcion.ReceiverTypeName] = functions
	}
	f.functions[funcion.ReceiverTypeName] = append(f.functions[funcion.ReceiverTypeName], funcion)
}

//Struct returns all struct info
func (f *FileInfo) Structs() []*StructInfo {
	var result = make([]*StructInfo, 0)
	for _, v := range f.structs {
		result = append(result, v)
	}
	return result
}

//HasStructInfo returns truc if struct info is defined in a file
func (f *FileInfo) HasStructInfo(name string) bool {
	_, found := f.structs[name]
	return found
}

//readComment reads comment from the position
func (v *FileInfo) readComment(pos token.Pos) string {
	position := v.fileSet.Position(pos)
	fileName := path.Join(v.basePath, v.filename)
	content, err := ioutil.ReadFile(fileName)
	if err != nil {
		panic("Unable to open file " + fileName)
	}
	line := strings.Split(string(content), "\n")[position.Line-1]
	commentPosition := strings.LastIndex(line, "//")
	if commentPosition != -1 {
		return line[commentPosition+2:]
	}
	return ""
}

//toFieldInfoSlice convers filedList to FiledInfo slice.
func toFieldInfoSlice(source *ast.FieldList) []*FieldInfo {
	var result = make([]*FieldInfo, 0)
	if source == nil || len(source.List) == 0 {
		return result
	}
	for _, fields := range source.List {
		result = append(result, NewFieldInfo(fields))
	}
	return result
}

//Visit visits ast node to extract struct details from the passed file
func (v *FileInfo) Visit(node ast.Node) ast.Visitor {
	if node != nil {
		//	fmt.Printf("visit %v %T\n", node, node)
		switch value := node.(type) {
		case *ast.TypeSpec:
			typeName := value.Name.Name
			structInfo := NewStructInfo(typeName)
			structInfo.Package = v.packageName
			structInfo.FileName = v.filename
			v.currentStructInfo = structInfo
			v.structs[typeName] = structInfo
		case *ast.StructType:
			v.currentStructInfo.Comment = v.readComment(value.Pos())
			v.currentStructInfo.AddFields(toFieldInfoSlice(value.Fields)...)

		case *ast.FuncDecl:
			functionInfo := NewFunctionInfo(value)
			v.currentFunctionInfo = functionInfo
			if len(functionInfo.ReceiverTypeName) > 0 {
				v.addFunction(functionInfo)
			}
		case *ast.FuncType:

			if v.currentFunctionInfo != nil {
				if value.Params != nil {
					v.currentFunctionInfo.ParameterFields = toFieldInfoSlice(value.Params)
				}
				if value.Results != nil {
					v.currentFunctionInfo.ResultsFields = toFieldInfoSlice(value.Results)
				}
				v.currentFunctionInfo = nil
			}
		}
	}
	return v
}

//Visit creates a new file info.
func NewFileInfo(basePath, packageName, filename string, fileSet *token.FileSet) *FileInfo {
	result := &FileInfo{
		basePath:    basePath,
		filename:    filename,
		packageName: packageName,
		structs:     make(map[string]*StructInfo),
		functions:   make(map[string][]*FunctionInfo),
		fileSet:     fileSet}
	return result
}

//FileSetInfo represents a fileset info storing information about go file with their struct definition
type FileSetInfo struct {
	files map[string]*FileInfo
}

//FileInfo returns fileinfo for supplied file name
func (f *FileSetInfo) FileInfo(name string) *FileInfo {
	return f.files[name]
}

//FilesInfo returns all files info.
func (f *FileSetInfo) FilesInfo() map[string]*FileInfo {
	return f.files
}

//StructInfo returns struct info for passed in struct name.
func (f *FileSetInfo) Struct(name string) *StructInfo {
	for _, v := range f.files {
		if v.HasStructInfo(name) {
			return v.Struct(name)
		}
	}
	return nil
}

//NewFileSetInfo creates a new fileset info
func NewFileSetInfo(baseDir string) (*FileSetInfo, error) {
	fileSet := token.NewFileSet()
	pkgs, err := parser.ParseDir(fileSet, baseDir, nil, parser.ParseComments)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse path %v: %v", baseDir, err)
	}

	var result = &FileSetInfo{
		files: make(map[string]*FileInfo),
	}
	for packageName, pkg := range pkgs {
		for filename, file := range pkg.Files {
			filename := path.Base(filename)
			fileInfo := NewFileInfo(baseDir, packageName, filename, fileSet)
			ast.Walk(fileInfo, file)
			result.files[filename] = fileInfo
		}
	}

	for _, fileInfo := range result.files {

		for k, functionsInfo := range fileInfo.functions {
			structInfo := result.Struct(k)
			if structInfo != nil {
				structInfo.AddReceivers(functionsInfo...)
			}
		}

	}
	return result, nil
}
<|endoftext|>


<commit_before>package main

import "testing"

func TestFilterInit(t *testing.T) {
	filterInit(&FiltOpts{ScoreF: true, FavesF: true}, false)
	if len(filters) != 2 {
		t.Error("Filter initialization doesn't work as expected")
	}
}

func TestFilterNone(t *testing.T) {
	in := make(ImageCh, 1)
	out := FilterChannel(in)
	in <- Image{}
	_, ok := <-out
	if !ok {
		t.Fatal("No-op filter closed unexpectedly")
	}
}

func TestFilterAlwaysTrue(t *testing.T) {
	in := make(ImageCh, 1)
	filter := filterGenerator(func(Image) bool { return true }, false)
	out := filter(in)
	in <- Image{}
	_, ok := <-out

	if !ok {
		t.Fatal("Filter closed unexpectedly")
	}
	close(in)
	_, ok = <-out
	if ok {
		t.Error("Filter remains unexpectedly open")
	}
}

func TestFilterAlwaysFalse(t *testing.T) {
	in := make(ImageCh, 1)
	filter := filterGenerator(func(Image) bool { return false }, false)
	out := filter(in)
	in <- Image{}

	close(in)
	_, ok := <-out

	if ok {
		t.Error("Filter remains unexpectedly open")
	}
}

func TestFilterComplex(t *testing.T) {
	filterInit(&FiltOpts{ScoreF: true, FavesF: true}, false)

	in := make(ImageCh, 3)
	in <- Image{Score: -1}
	in <- Image{Faves: -1}
	in <- Image{Imgid: 1}

	out := FilterChannel(in)
	close(in)
	pass, _ := <-out

	if (pass != Image{Imgid: 1}) {
		t.Error("Incorrect work of the filter, passed ", pass, "instead of ", Image{Imgid: 1})
	}

	pass, ok := <-out

	if ok && (pass != Image{}) {
		t.Error("Wat is going on with FilterChannel?")
	}
}
<commit_msg>Fixed tests after switch from int to string<commit_after>package main

import "testing"

func TestFilterInit(t *testing.T) {
	filterInit(&FiltOpts{ScoreF: true, FavesF: true}, false)
	if len(filters) != 2 {
		t.Error("Filter initialization doesn't work as expected")
	}
}

func TestFilterNone(t *testing.T) {
	in := make(ImageCh, 1)
	out := FilterChannel(in)
	in <- Image{}
	_, ok := <-out
	if !ok {
		t.Fatal("No-op filter closed unexpectedly")
	}
}

func TestFilterAlwaysTrue(t *testing.T) {
	in := make(ImageCh, 1)
	filter := filterGenerator(func(Image) bool { return true }, false)
	out := filter(in)
	in <- Image{}
	_, ok := <-out

	if !ok {
		t.Fatal("Filter closed unexpectedly")
	}
	close(in)
	_, ok = <-out
	if ok {
		t.Error("Filter remains unexpectedly open")
	}
}

func TestFilterAlwaysFalse(t *testing.T) {
	in := make(ImageCh, 1)
	filter := filterGenerator(func(Image) bool { return false }, false)
	out := filter(in)
	in <- Image{}

	close(in)
	_, ok := <-out

	if ok {
		t.Error("Filter remains unexpectedly open")
	}
}

func TestFilterComplex(t *testing.T) {
	filterInit(&FiltOpts{ScoreF: true, FavesF: true}, false)

	in := make(ImageCh, 3)
	in <- Image{Score: -1}
	in <- Image{Faves: -1}
	in <- Image{Imgid: "1"}

	out := FilterChannel(in)
	close(in)
	pass, _ := <-out

	if (pass != Image{Imgid: "1"}) {
		t.Error("Incorrect work of the filter, passed ", pass, "instead of ", Image{Imgid: "1"})
	}

	pass, ok := <-out

	if ok && (pass != Image{}) {
		t.Error("Wat is going on with FilterChannel?")
	}
}
<|endoftext|>