<commit_before>package materials

import (
	"errors"
	"github.com/howeyc/fsnotify"
	"log"
	"os"
	"path/filepath"
)

type RecursiveWatcher struct {
	*fsnotify.Watcher
	Files   chan string
	Folders chan string
}

func NewRecursiveWatcher(path string) (*RecursiveWatcher, error) {
	folders := Subfolders(path)
	if len(folders) == 0 {
		return nil, errors.New("No folders to watch")
	}

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}

	rWatcher := &RecursiveWatcher{Watcher: watcher}
	rWatcher.Files = make(chan string, 10)
	rWatcher.Folders = make(chan string, len(folder))

	for _, folder := range folders {
		rWatcher.AddFolder(folder)
	}

	return rWatcher, nil
}

func (watcher *RecursiveWatcher) AddFolder(folder string) {
	err := watcher.WatchFlags(folder, fsnotify.FSN_ALL)
	if err != nil {
		log.Println("Error watching folder: ", folder, err)
	}
	watcher.Folders <- folder
}

func (watcher *RecursiveWatcher) Run() {
	go func() {
		for {
			select {
			case event := <-watcher.Event:
				switch {
				case event.IsCreate():
					finfo, err := os.Stat(event.Name)
					if err != nil {
						// do something
					} else if finfo.IsDir() {
						watcher.AddFolder(event.Name)
					} else {
						watcher.Files <- event.Name
					}

				case event.IsModify():
					log.Println("IsModify")
				}
			case err := <-watcher.Error:
				log.Println("error:", err)
			}
		}
	}()
}

func Subfolders(path string) (paths []string) {
	filepath.Walk(path, func(subpath, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			name := info.Name()
			hidden := filepath.HasPrefix(name, ".") && name != "." && name != ".."
			if hidden {
				return filepath.SkipDir
			} else {
				paths = append(paths, subpath)
			}
		}
		return nil
	})
	return paths
}
<commit_msg>Fix compilation errors.<commit_after>package materials

import (
	"errors"
	"github.com/howeyc/fsnotify"
	"log"
	"os"
	"path/filepath"
)

type RecursiveWatcher struct {
	*fsnotify.Watcher
	Files   chan string
	Folders chan string
}

func NewRecursiveWatcher(path string) (*RecursiveWatcher, error) {
	folders := Subfolders(path)
	if len(folders) == 0 {
		return nil, errors.New("No folders to watch")
	}

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return nil, err
	}

	rWatcher := &RecursiveWatcher{Watcher: watcher}
	rWatcher.Files = make(chan string, 10)
	rWatcher.Folders = make(chan string, len(folders))

	for _, folder := range folders {
		rWatcher.AddFolder(folder)
	}

	return rWatcher, nil
}

func (watcher *RecursiveWatcher) AddFolder(folder string) {
	err := watcher.WatchFlags(folder, fsnotify.FSN_ALL)
	if err != nil {
		log.Println("Error watching folder: ", folder, err)
	}
	watcher.Folders <- folder
}

func (watcher *RecursiveWatcher) Run() {
	go func() {
		for {
			select {
			case event := <-watcher.Event:
				switch {
				case event.IsCreate():
					finfo, err := os.Stat(event.Name)
					if err != nil {
						// do something
					} else if finfo.IsDir() {
						watcher.AddFolder(event.Name)
					} else {
						watcher.Files <- event.Name
					}

				case event.IsModify():
					log.Println("IsModify")
				}
			case err := <-watcher.Error:
				log.Println("error:", err)
			}
		}
	}()
}

func Subfolders(path string) (paths []string) {
	filepath.Walk(path, func(subpath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.IsDir() {
			name := info.Name()
			hidden := filepath.HasPrefix(name, ".") && name != "." && name != ".."
			if hidden {
				return filepath.SkipDir
			} else {
				paths = append(paths, subpath)
			}
		}
		return nil
	})
	return paths
}
<|endoftext|>


<commit_before>package web

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"go/build"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"path"

	"code.google.com/p/go.net/websocket"
	"github.com/nogiushi/marvin/nog"
)

var pkg struct {
	Version string `json:"version"`
}
var Root string
var site *template.Template
var templates = make(map[string]*template.Template)

func init() {
	if p, err := build.Default.Import("github.com/nogiushi/marvin/web", "", build.FindOnly); err == nil {
		Root = p.Dir
	} else {
		log.Println("WARNING: could not import package:", err)
	}

	if j, err := os.OpenFile(path.Join(Root, "bower.json"), os.O_RDONLY, 0666); err == nil {
		dec := json.NewDecoder(j)
		if err = dec.Decode(&pkg); err != nil {
			log.Println("WARNING: could not decode bower.json", err)
		}
		j.Close()
	} else {
		log.Println("WARNING: could not open bower.json", err)
	}

}

type longExpireHandler struct {
	h http.Handler
}

func longExpire(h http.Handler) http.Handler {
	return &longExpireHandler{h}
}

func (le *longExpireHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ttl := int64(86400)
	w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", ttl))
	le.h.ServeHTTP(w, r)
}

func getTemplate(name string) *template.Template {
	if t, ok := templates[name]; ok {
		return t
	} else {
		if site == nil {
			site = template.Must(template.ParseFiles(path.Join(Root, "templates/site.html")))
		}
		t, err := site.Clone()
		if err != nil {
			log.Fatal("cloning site: ", err)
		}
		t = template.Must(t.ParseFiles(path.Join(Root, name)))
		templates[name] = t
		return t
	}
}

type templateData map[string]interface{}

func writeTemplate(t *template.Template, d templateData, w http.ResponseWriter) {
	var bw bytes.Buffer
	h := md5.New()
	mw := io.MultiWriter(&bw, h)
	err := t.ExecuteTemplate(mw, "html", d)
	if err == nil {
		w.Header().Set("ETag", fmt.Sprintf(`"%x"`, h.Sum(nil)))
		w.Header().Set("Content-Length", fmt.Sprintf("%d", bw.Len()))
		w.Write(bw.Bytes())
	} else {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func handleTemplate(prefix, name string, data templateData) {
	t := getTemplate("templates/" + name + ".html")
	http.HandleFunc(prefix, func(w http.ResponseWriter, req *http.Request) {
		d := templateData{}
		d["Title"] = name
		d["Version"] = pkg.Version
		if data != nil {
			for k, v := range data {
				d[k] = v
			}
		}
		if req.URL.Path == prefix {
			d["Found"] = true
		} else {
			w.Header().Set("Cache-Control", "max-age=10, must-revalidate")
			w.WriteHeader(http.StatusNotFound)
		}
		writeTemplate(t, d, w)
	})
}

func AddHandlers(m *nog.Nog) {
	handleTemplate("/", "home", templateData{"Marvin": m})

	fs := longExpire(http.FileServer(http.Dir(path.Join(Root, "static/"))))
	http.Handle("/"+pkg.Version+"/", fs)

	http.HandleFunc("/messages", func(w http.ResponseWriter, req *http.Request) {
		if req.Method == "GET" {
			w.Header().Set("Content-Type", "application/json; charset=utf-8")
			if err := req.ParseForm(); err == nil {
				_, ok := req.Form["since"]
				if true || ok {
					log := m.Log()
					ec := json.NewEncoder(w)
					if err := ec.Encode(log); err != nil {
						return
					}

				}
			} else {
				log.Println("Error parsing form:", err)
			}
		} else {
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})

	http.Handle("/message", websocket.Handler(func(ws *websocket.Conn) {
		c := nog.InOut{}
		in := c.ReceiveIn()
		out := c.SendOut()

		go m.Add(c.ReceiveOut(), c.SendIn())

		go func() {
			for {
				var m nog.Message
				if err := websocket.JSON.Receive(ws, &m); err == nil {
					req := ws.Request()
					who := req.RemoteAddr
					if req.TLS != nil {
						for _, c := range req.TLS.PeerCertificates {
							who = c.Subject.CommonName
						}
					}
					m.Who = who
					out <- m
				} else {
					log.Println("Message Websocket receive err:", err)
					return
				}
			}
		}()

		for m := range in {
			if err := websocket.JSON.Send(ws, &m); err != nil {
				log.Println("Message Websocket send err:", err)
				break
			}
		}

		ws.Close()

	}))
}
<commit_msg>Fixed to shutdown bit.<commit_after>package web

import (
	"bytes"
	"crypto/md5"
	"encoding/json"
	"fmt"
	"go/build"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"path"

	"code.google.com/p/go.net/websocket"
	"github.com/nogiushi/marvin/nog"
)

var pkg struct {
	Version string `json:"version"`
}
var Root string
var site *template.Template
var templates = make(map[string]*template.Template)

func init() {
	if p, err := build.Default.Import("github.com/nogiushi/marvin/web", "", build.FindOnly); err == nil {
		Root = p.Dir
	} else {
		log.Println("WARNING: could not import package:", err)
	}

	if j, err := os.OpenFile(path.Join(Root, "bower.json"), os.O_RDONLY, 0666); err == nil {
		dec := json.NewDecoder(j)
		if err = dec.Decode(&pkg); err != nil {
			log.Println("WARNING: could not decode bower.json", err)
		}
		j.Close()
	} else {
		log.Println("WARNING: could not open bower.json", err)
	}

}

type longExpireHandler struct {
	h http.Handler
}

func longExpire(h http.Handler) http.Handler {
	return &longExpireHandler{h}
}

func (le *longExpireHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ttl := int64(86400)
	w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", ttl))
	le.h.ServeHTTP(w, r)
}

func getTemplate(name string) *template.Template {
	if t, ok := templates[name]; ok {
		return t
	} else {
		if site == nil {
			site = template.Must(template.ParseFiles(path.Join(Root, "templates/site.html")))
		}
		t, err := site.Clone()
		if err != nil {
			log.Fatal("cloning site: ", err)
		}
		t = template.Must(t.ParseFiles(path.Join(Root, name)))
		templates[name] = t
		return t
	}
}

type templateData map[string]interface{}

func writeTemplate(t *template.Template, d templateData, w http.ResponseWriter) {
	var bw bytes.Buffer
	h := md5.New()
	mw := io.MultiWriter(&bw, h)
	err := t.ExecuteTemplate(mw, "html", d)
	if err == nil {
		w.Header().Set("ETag", fmt.Sprintf(`"%x"`, h.Sum(nil)))
		w.Header().Set("Content-Length", fmt.Sprintf("%d", bw.Len()))
		w.Write(bw.Bytes())
	} else {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

func handleTemplate(prefix, name string, data templateData) {
	t := getTemplate("templates/" + name + ".html")
	http.HandleFunc(prefix, func(w http.ResponseWriter, req *http.Request) {
		d := templateData{}
		d["Title"] = name
		d["Version"] = pkg.Version
		if data != nil {
			for k, v := range data {
				d[k] = v
			}
		}
		if req.URL.Path == prefix {
			d["Found"] = true
		} else {
			w.Header().Set("Cache-Control", "max-age=10, must-revalidate")
			w.WriteHeader(http.StatusNotFound)
		}
		writeTemplate(t, d, w)
	})
}

func AddHandlers(m *nog.Nog) {
	handleTemplate("/", "home", templateData{"Marvin": m})

	fs := longExpire(http.FileServer(http.Dir(path.Join(Root, "static/"))))
	http.Handle("/"+pkg.Version+"/", fs)

	http.HandleFunc("/messages", func(w http.ResponseWriter, req *http.Request) {
		if req.Method == "GET" {
			w.Header().Set("Content-Type", "application/json; charset=utf-8")
			if err := req.ParseForm(); err == nil {
				_, ok := req.Form["since"]
				if true || ok {
					log := m.Log()
					ec := json.NewEncoder(w)
					if err := ec.Encode(log); err != nil {
						return
					}

				}
			} else {
				log.Println("Error parsing form:", err)
			}
		} else {
			w.WriteHeader(http.StatusMethodNotAllowed)
		}
	})

	http.Handle("/message", websocket.Handler(func(ws *websocket.Conn) {
		c := nog.InOut{}
		in := c.ReceiveIn()
		out := c.SendOut()

		go m.Add(c.ReceiveOut(), c.SendIn())

		go func() {
			for {
				var m nog.Message
				if err := websocket.JSON.Receive(ws, &m); err == nil {
					req := ws.Request()
					who := req.RemoteAddr
					if req.TLS != nil {
						for _, c := range req.TLS.PeerCertificates {
							who = c.Subject.CommonName
						}
					}
					m.Who = who
					out <- m
				} else {
					log.Println("Message Websocket receive err:", err)
					close(out)
					return
				}
			}
		}()

		for m := range in {
			if err := websocket.JSON.Send(ws, &m); err != nil {
				log.Println("Message Websocket send err:", err)
				break
			}
		}

		ws.Close()

	}))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"strings"

	"github.com/adnanh/webhook/helpers"
	"github.com/adnanh/webhook/hook"

	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"

	fsnotify "gopkg.in/fsnotify.v1"
)

const (
	version = "2.0.0"
)

var (
	ip            = flag.String("ip", "", "ip the webhook should serve hooks on")
	port          = flag.Int("port", 9000, "port the webhook should serve hooks on")
	verbose       = flag.Bool("verbose", false, "show verbose output")
	hotReload     = flag.Bool("hotreload", false, "watch hooks file for changes and reload them automatically")
	hooksFilePath = flag.String("hooks", "hooks.json", "path to the json file containing defined hooks the webhook should serve")

	watcher *fsnotify.Watcher

	hooks hook.Hooks
)

func init() {
	hooks = hook.Hooks{}

	flag.Parse()

	log.SetPrefix("[webhook] ")
	log.SetFlags(log.Ldate | log.Ltime)

	if !*verbose {
		log.SetOutput(ioutil.Discard)
	}

	log.Println("version " + version + " starting")

	// load and parse hooks
	log.Printf("attempting to load hooks from %s\n", *hooksFilePath)

	err := hooks.LoadFromFile(*hooksFilePath)

	if err != nil {
		log.Printf("couldn't load hooks from file! %+v\n", err)
	} else {
		log.Printf("loaded %d hook(s) from file\n", len(hooks))

		for _, hook := range hooks {
			log.Printf("\t> %s\n", hook.ID)
		}
	}
}

func main() {
	if *hotReload {
		// set up file watcher
		log.Printf("setting up file watcher for %s\n", *hooksFilePath)

		var err error

		watcher, err = fsnotify.NewWatcher()
		if err != nil {
			log.Fatal("error creating file watcher instance", err)
		}

		defer watcher.Close()

		go watchForFileChange()

		err = watcher.Add(*hooksFilePath)
		if err != nil {
			log.Fatal("error adding hooks file to the watcher", err)
		}
	}

	l := log.New(os.Stdout, "[webhook] ", log.Ldate|log.Ltime)

	negroniLogger := &negroni.Logger{l}

	negroniRecovery := &negroni.Recovery{
		Logger:     l,
		PrintStack: true,
		StackAll:   false,
		StackSize:  1024 * 8,
	}

	n := negroni.New(negroniRecovery, negroniLogger)

	router := mux.NewRouter()
	router.HandleFunc("/hooks/{id}", hookHandler)

	n.UseHandler(router)

	log.Printf("listening on %s:%d", *ip, *port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", *ip, *port), n))
}

func hookHandler(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]

	hook := hooks.Match(id)

	if hook != nil {
		log.Printf("%s got matched\n", id)

		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.Printf("error reading the request body. %+v\n", err)
		}

		// parse headers
		headers := helpers.ValuesToMap(r.Header)

		// parse query variables
		query := helpers.ValuesToMap(r.URL.Query())

		// parse body
		var payload map[string]interface{}

		contentType := r.Header.Get("Content-Type")

		if contentType == "application/json" {
			decoder := json.NewDecoder(strings.NewReader(string(body)))
			decoder.UseNumber()

			err := decoder.Decode(&payload)

			if err != nil {
				log.Printf("error parsing JSON payload %+v\n", err)
			}
		} else if contentType == "application/x-www-form-urlencoded" {
			fd, err := url.ParseQuery(string(body))
			if err != nil {
				log.Printf("error parsing form payload %+v\n", err)
			} else {
				payload = helpers.ValuesToMap(fd)
			}
		}

		// handle hook
		go handleHook(hook, &headers, &query, &payload, &body)

		// say thanks
		fmt.Fprintf(w, "Thanks.")
	} else {
		fmt.Fprintf(w, "Hook not found.")
	}
}

func handleHook(hook *hook.Hook, headers, query, payload *map[string]interface{}, body *[]byte) {
	if hook.TriggerRule == nil || hook.TriggerRule != nil && hook.TriggerRule.Evaluate(headers, query, payload, body) {
		log.Printf("%s hook triggered successfully\n", hook.ID)

		cmd := exec.Command(hook.ExecuteCommand)
		cmd.Args = hook.ExtractCommandArguments(headers, query, payload)
		cmd.Dir = hook.CommandWorkingDirectory

		log.Printf("executing %s (%s) with arguments %s using %s as cwd\n", hook.ExecuteCommand, cmd.Path, cmd.Args, cmd.Dir)

		out, err := cmd.Output()

		log.Printf("stdout: %s\n", out)

		if err != nil {
			log.Printf("stderr: %+v\n", err)
		}
		log.Printf("finished handling %s\n", hook.ID)
	} else {
		log.Printf("%s hook did not get triggered\n", hook.ID)
	}
}

func watchForFileChange() {
	for {
		select {
		case event := <-(*watcher).Events:
			if event.Op&fsnotify.Write == fsnotify.Write {
				log.Println("hooks file modified")

				newHooks := hook.Hooks{}

				// parse and swap
				log.Printf("attempting to reload hooks from %s\n", *hooksFilePath)

				err := newHooks.LoadFromFile(*hooksFilePath)

				if err != nil {
					log.Printf("couldn't load hooks from file! %+v\n", err)
				} else {
					log.Printf("loaded %d hook(s) from file\n", len(hooks))

					for _, hook := range hooks {
						log.Printf("\t> %s\n", hook.ID)
					}

					hooks = newHooks
				}
			}
		case err := <-(*watcher).Errors:
			log.Println("watcher error:", err)
		}
	}
}
<commit_msg>bumped up the minor version to 2.1.0<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"strings"

	"github.com/adnanh/webhook/helpers"
	"github.com/adnanh/webhook/hook"

	"github.com/codegangsta/negroni"
	"github.com/gorilla/mux"

	fsnotify "gopkg.in/fsnotify.v1"
)

const (
	version = "2.1.0"
)

var (
	ip            = flag.String("ip", "", "ip the webhook should serve hooks on")
	port          = flag.Int("port", 9000, "port the webhook should serve hooks on")
	verbose       = flag.Bool("verbose", false, "show verbose output")
	hotReload     = flag.Bool("hotreload", false, "watch hooks file for changes and reload them automatically")
	hooksFilePath = flag.String("hooks", "hooks.json", "path to the json file containing defined hooks the webhook should serve")

	watcher *fsnotify.Watcher

	hooks hook.Hooks
)

func init() {
	hooks = hook.Hooks{}

	flag.Parse()

	log.SetPrefix("[webhook] ")
	log.SetFlags(log.Ldate | log.Ltime)

	if !*verbose {
		log.SetOutput(ioutil.Discard)
	}

	log.Println("version " + version + " starting")

	// load and parse hooks
	log.Printf("attempting to load hooks from %s\n", *hooksFilePath)

	err := hooks.LoadFromFile(*hooksFilePath)

	if err != nil {
		log.Printf("couldn't load hooks from file! %+v\n", err)
	} else {
		log.Printf("loaded %d hook(s) from file\n", len(hooks))

		for _, hook := range hooks {
			log.Printf("\t> %s\n", hook.ID)
		}
	}
}

func main() {
	if *hotReload {
		// set up file watcher
		log.Printf("setting up file watcher for %s\n", *hooksFilePath)

		var err error

		watcher, err = fsnotify.NewWatcher()
		if err != nil {
			log.Fatal("error creating file watcher instance", err)
		}

		defer watcher.Close()

		go watchForFileChange()

		err = watcher.Add(*hooksFilePath)
		if err != nil {
			log.Fatal("error adding hooks file to the watcher", err)
		}
	}

	l := log.New(os.Stdout, "[webhook] ", log.Ldate|log.Ltime)

	negroniLogger := &negroni.Logger{l}

	negroniRecovery := &negroni.Recovery{
		Logger:     l,
		PrintStack: true,
		StackAll:   false,
		StackSize:  1024 * 8,
	}

	n := negroni.New(negroniRecovery, negroniLogger)

	router := mux.NewRouter()
	router.HandleFunc("/hooks/{id}", hookHandler)

	n.UseHandler(router)

	log.Printf("listening on %s:%d", *ip, *port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf("%s:%d", *ip, *port), n))
}

func hookHandler(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)["id"]

	hook := hooks.Match(id)

	if hook != nil {
		log.Printf("%s got matched\n", id)

		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			log.Printf("error reading the request body. %+v\n", err)
		}

		// parse headers
		headers := helpers.ValuesToMap(r.Header)

		// parse query variables
		query := helpers.ValuesToMap(r.URL.Query())

		// parse body
		var payload map[string]interface{}

		contentType := r.Header.Get("Content-Type")

		if contentType == "application/json" {
			decoder := json.NewDecoder(strings.NewReader(string(body)))
			decoder.UseNumber()

			err := decoder.Decode(&payload)

			if err != nil {
				log.Printf("error parsing JSON payload %+v\n", err)
			}
		} else if contentType == "application/x-www-form-urlencoded" {
			fd, err := url.ParseQuery(string(body))
			if err != nil {
				log.Printf("error parsing form payload %+v\n", err)
			} else {
				payload = helpers.ValuesToMap(fd)
			}
		}

		// handle hook
		go handleHook(hook, &headers, &query, &payload, &body)

		// say thanks
		fmt.Fprintf(w, "Thanks.")
	} else {
		fmt.Fprintf(w, "Hook not found.")
	}
}

func handleHook(hook *hook.Hook, headers, query, payload *map[string]interface{}, body *[]byte) {
	if hook.TriggerRule == nil || hook.TriggerRule != nil && hook.TriggerRule.Evaluate(headers, query, payload, body) {
		log.Printf("%s hook triggered successfully\n", hook.ID)

		cmd := exec.Command(hook.ExecuteCommand)
		cmd.Args = hook.ExtractCommandArguments(headers, query, payload)
		cmd.Dir = hook.CommandWorkingDirectory

		log.Printf("executing %s (%s) with arguments %s using %s as cwd\n", hook.ExecuteCommand, cmd.Path, cmd.Args, cmd.Dir)

		out, err := cmd.Output()

		log.Printf("stdout: %s\n", out)

		if err != nil {
			log.Printf("stderr: %+v\n", err)
		}
		log.Printf("finished handling %s\n", hook.ID)
	} else {
		log.Printf("%s hook did not get triggered\n", hook.ID)
	}
}

func watchForFileChange() {
	for {
		select {
		case event := <-(*watcher).Events:
			if event.Op&fsnotify.Write == fsnotify.Write {
				log.Println("hooks file modified")

				newHooks := hook.Hooks{}

				// parse and swap
				log.Printf("attempting to reload hooks from %s\n", *hooksFilePath)

				err := newHooks.LoadFromFile(*hooksFilePath)

				if err != nil {
					log.Printf("couldn't load hooks from file! %+v\n", err)
				} else {
					log.Printf("loaded %d hook(s) from file\n", len(hooks))

					for _, hook := range hooks {
						log.Printf("\t> %s\n", hook.ID)
					}

					hooks = newHooks
				}
			}
		case err := <-(*watcher).Errors:
			log.Println("watcher error:", err)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.

package trello

import (
	"fmt"
)

type Webhook struct {
	client      *Client
	ID          string `json:"id,omitempty"`
	IDModel     string `json:"idModel"`
	Description string `json:"description"`
	CallbackURL string `json:"callbackURL"`
	Active      bool   `json:"active"`
}

func (c *Client) CreateWebhook(webhook *Webhook) error {
	path := "webhooks"
	args := Arguments{"idModel": webhook.IDModel, "description": webhook.Description, "callbackURL": webhook.CallbackURL}
	err := c.Post(path, args, webhook)
	if err == nil {
		webhook.client = c
	}
	return err
}

func (c *Client) GetWebhook(webhookID string, args Arguments) (webhook *Webhook, err error) {
	path := fmt.Sprintf("webhooks/%s", webhookID)
	err = c.Get(path, args, &webhook)
	if webhook != nil {
		webhook.client = c
	}
	return
}

func (t *Token) GetWebhooks(args Arguments) (webhooks []*Webhook, err error) {
	path := fmt.Sprintf("tokens/%s/webhooks", t.ID)
	err = t.client.Get(path, args, &webhooks)
	return
}
<commit_msg>Adds helper functions to decode http.Requests from webhook submissions.<commit_after>// Copyright © 2016 Aaron Longwell
//
// Use of this source code is governed by an MIT licese.
// Details in the LICENSE file.

package trello

import (
	"encoding/json"
	"fmt"
	"net/http"

	"github.com/pkg/errors"
)

// Webhook is the Go representation of a webhook registered in Trello's systems.
// Used when creating, modifying or deleting webhooks.
//
type Webhook struct {
	client      *Client
	ID          string `json:"id,omitempty"`
	IDModel     string `json:"idModel"`
	Description string `json:"description"`
	CallbackURL string `json:"callbackURL"`
	Active      bool   `json:"active"`
}

// BoardWebhookRequest is the object sent by Trello to a Webhook for Board-triggered
// webhooks.
//
type BoardWebhookRequest struct {
	Model  *Board
	Action *Action
}

// ListWebhookRequest is the object sent by Trello to a Webhook for List-triggered
// webhooks.
//
type ListWebhookRequest struct {
	Model  *List
	Action *Action
}

// CardWebhookRequest is the object sent by Trello to a Webhook for Card-triggered
// webhooks.
//
type CardWebhookRequest struct {
	Model  *Card
	Action *Action
}

func (c *Client) CreateWebhook(webhook *Webhook) error {
	path := "webhooks"
	args := Arguments{"idModel": webhook.IDModel, "description": webhook.Description, "callbackURL": webhook.CallbackURL}
	err := c.Post(path, args, webhook)
	if err == nil {
		webhook.client = c
	}
	return err
}

func (c *Client) GetWebhook(webhookID string, args Arguments) (webhook *Webhook, err error) {
	path := fmt.Sprintf("webhooks/%s", webhookID)
	err = c.Get(path, args, &webhook)
	if webhook != nil {
		webhook.client = c
	}
	return
}

func (t *Token) GetWebhooks(args Arguments) (webhooks []*Webhook, err error) {
	path := fmt.Sprintf("tokens/%s/webhooks", t.ID)
	err = t.client.Get(path, args, &webhooks)
	return
}

func GetBoardWebhookRequest(r *http.Request) (whr *BoardWebhookRequest, err error) {
	decoder := json.NewDecoder(r.Body)
	err = decoder.Decode(&whr)
	if err != nil {
		err = errors.Wrapf(err, "GetBoardWebhookRequest() failed to decode '%s'.", r.URL)
	}
	return
}

func GetListWebhookRequest(r *http.Request) (whr *ListWebhookRequest, err error) {
	decoder := json.NewDecoder(r.Body)
	err = decoder.Decode(&whr)
	if err != nil {
		err = errors.Wrapf(err, "GetListWebhookRequest() failed to decode '%s'.", r.URL)
	}
	return
}

func GetCardWebhookRequest(r *http.Request) (whr *CardWebhookRequest, err error) {
	decoder := json.NewDecoder(r.Body)
	err = decoder.Decode(&whr)
	if err != nil {
		err = errors.Wrapf(err, "GetCardWebhookRequest() failed to decode '%s'.", r.URL)
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2013 Travis Keep. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or
// at http://opensource.org/licenses/BSD-3-Clause.

// Package weblogs provides access logs for webservers written in go.
package weblogs

import (
  "bytes"
  "fmt"
  "github.com/gorilla/context"
  "io"
  "net/http"
  "net/url"
  "os"
  "runtime/debug"
  "strings"
  "time"
)

type contextKeyType int

const (
  kBufferKey contextKeyType = iota
)

// Snapshot represents a snapshot of an HTTP request.
type Snapshot interface{}

// Capture captures a server response. Implementations delegate to an
// underlying ResponseWriter.
type Capture interface {
  http.ResponseWriter
  // HasStatus returns true if server has sent a status. False means that
  // server failed to send a response.
  HasStatus() bool
}

// LogRecord represents a single entry in the access logs.
type LogRecord struct {
  // The time request was received.
  T time.Time
  // The request snapshot
  R Snapshot
  // The capture of the response
  W Capture
  // Time spent processing the request
  Duration time.Duration
  // Additional information added with the Writer method.
  Extra string
}

// Logger represents an access log format. Clients are free to provide their
// own implementations.
type Logger interface {
  // NewSnapshot creates a new snapshot of a request.
  NewSnapshot(r *http.Request) Snapshot
  // NewCapture creates a new capture for capturing a response. w is the
  // original ResponseWriter.
  NewCapture(w http.ResponseWriter) Capture
  // Log writes the log record.
  Log(w io.Writer, record *LogRecord)
}

// Options specifies options for writing to access logs.
type Options struct {
  // Where to write the web logs. nil means write to stderr,
  Writer io.Writer
  // How to write the web logs. nil means use SimpleLogger.
  Logger Logger
  // How to get current time. nil means use time.Now(). This field is used
  // for testing purposes.
  Now func() time.Time
}

func (o *Options) writer() io.Writer {
  if o.Writer == nil {
    return os.Stderr
  }
  return o.Writer
}

func (o *Options) logger() Logger {
  if o.Logger == nil {
    return SimpleLogger{}
  }
  return o.Logger
}

func (o *Options) now() func() time.Time {
  if o.Now == nil {
    return time.Now
  }
  return o.Now
}

// Handler wraps a handler creating access logs. Access logs are written to
// stderr using SimpleLogger. Returned handler must be wrapped by
// context.ClearHandler.
func Handler(handler http.Handler) http.Handler {
  return HandlerWithOptions(handler, nil)
}

// HandlerWithOptions wraps a handler creating access logs and allows caller to
// configure how access logs are written. Returned handler must be
// wrapped by context.ClearHandler.
func HandlerWithOptions(
    handler http.Handler, options *Options) http.Handler {
  if options == nil {
    options = &Options{}
  }
  return &logHandler{
      handler: handler,
      w: options.writer(),
      logger: options.logger(),
      now: options.now()}
}

// Writer returns a writer whereby the caller can add additional information
// to the current log entry. If the handler calling this is not wrapped by
// the Handler() method, then writing to the returned io.Writer does
// nothing.
func Writer(r *http.Request) io.Writer {
  value := context.Get(r, kBufferKey)
  if value == nil {
    return nilWriter{}
  }
  return value.(*bytes.Buffer)
}

type logHandler struct {
  handler http.Handler
  w io.Writer
  logger Logger
  now func() time.Time
}

func (h *logHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  snapshot :=  h.logger.NewSnapshot(r)
  capture := h.logger.NewCapture(w)
  additional := &bytes.Buffer{}
  context.Set(r, kBufferKey, additional)
  startTime := h.now()
  defer func() {
    endTime := h.now()
    err := recover()
    maybeSend500(capture)
    h.logger.Log(
        h.w,
        &LogRecord{
            T: startTime,
            R: snapshot,
            W: capture,
            Duration: endTime.Sub(startTime),
            Extra: additional.String()})
    if err != nil {
      fmt.Fprintf(h.w, "Panic: %v\n", err)
      h.w.Write(debug.Stack())
    }
  }()
  h.handler.ServeHTTP(capture, r)
}

// ApacheCommonSnapshot provides a request snapshot for apache common
// access logs.
type ApacheCommonSnapshot struct {
  // Copied from Request.RemoteAddr
  RemoteAddr string
  // Copied from Request.Method
  Method string
  // Copied from Request.Proto
  Proto string
  // Copied from Request.URL
  URL *url.URL
}

func NewApacheCommonSnapshot(r *http.Request) ApacheCommonSnapshot {
  urlSnapshot := *r.URL
  return ApacheCommonSnapshot{
      RemoteAddr: r.RemoteAddr,
      Method: r.Method,
      Proto: r.Proto,
      URL: &urlSnapshot}
}

// ApacheCombinedSnapshot provides a request snapshot for apache combined
// access logs.
type ApacheCombinedSnapshot struct {
  ApacheCommonSnapshot
  Referer string
  UserAgent string
}

func NewApacheCombinedSnapshot(r *http.Request) ApacheCombinedSnapshot {
  return ApacheCombinedSnapshot{
      ApacheCommonSnapshot: NewApacheCommonSnapshot(r),
      Referer: r.Referer(),
      UserAgent: r.UserAgent()}
}

// SimpleCapture provides a capture of a response that includes the http
// status code and the size of the response.
type SimpleCapture struct {
  // The underlying ResponseWriter
  http.ResponseWriter
  // The HTTP status code shows up here.
  Status int
  // The size of the response in bytes shows up here.
  Size int
  statusSet bool
}

func (c *SimpleCapture) Write(b []byte) (int, error) {
  result, err := c.ResponseWriter.Write(b)
  c.Size += result
  c.maybeSetStatus(http.StatusOK)
  return result, err
}

func (c *SimpleCapture) WriteHeader(status int) {
  c.ResponseWriter.WriteHeader(status)
  c.maybeSetStatus(status)
}

func (c *SimpleCapture) HasStatus() bool {
  return c.statusSet
}

func (c *SimpleCapture) maybeSetStatus(status int) {
  if !c.statusSet {
    c.Status = status
    c.statusSet = true
  }
}

// SimpleLogger provides access logs with the following columns:
// date, remote address, method, URI, status, time elapsed milliseconds,
// followed by any additional information provided via the Writer method.
type SimpleLogger struct {
}

func (l SimpleLogger) NewSnapshot(r *http.Request) Snapshot {
  snapshot := NewApacheCommonSnapshot(r)
  return &snapshot
}

func (l SimpleLogger) NewCapture(w http.ResponseWriter) Capture {
  return &SimpleCapture{ResponseWriter: w}
}

func (l SimpleLogger) Log(w io.Writer, log *LogRecord) {
  s := log.R.(*ApacheCommonSnapshot)
  c := log.W.(*SimpleCapture)
  fmt.Fprintf(w, "%s %s %s %s %d %d%s\n",
      log.T.Format("01/02/2006 15:04:05"),
      s.RemoteAddr,
      s.Method,
      s.URL,
      c.Status,
      log.Duration / time.Millisecond,
      log.Extra)
}

// ApacheCommonLogger provides access logs in apache common log format.
type ApacheCommonLogger struct {
}

func (l ApacheCommonLogger) NewSnapshot(r *http.Request) Snapshot {
  snapshot := NewApacheCommonSnapshot(r)
  return &snapshot
}

func (l ApacheCommonLogger) NewCapture(w http.ResponseWriter) Capture {
  return &SimpleCapture{ResponseWriter: w}
}

func (l ApacheCommonLogger) Log(w io.Writer, log *LogRecord) {
  s := log.R.(*ApacheCommonSnapshot)
  c := log.W.(*SimpleCapture)
  fmt.Fprintf(w, "%s - %s [%s] \"%s %s %s\" %d %d\n",
        strings.Split(s.RemoteAddr, ":")[0],
        ApacheUser(s.URL.User),
        log.T.Format("02/Jan/2006:15:04:05 -0700"),
        s.Method,
        s.URL.RequestURI(),
        s.Proto,
        c.Status,
        c.Size)
}

// ApacheCombinedLogger provides access logs in apache combined log format.
type ApacheCombinedLogger struct {
}

func (l ApacheCombinedLogger) NewSnapshot(r *http.Request) Snapshot {
  snapshot := NewApacheCombinedSnapshot(r)
  return &snapshot
}

func (l ApacheCombinedLogger) NewCapture(w http.ResponseWriter) Capture {
  return &SimpleCapture{ResponseWriter: w}
}

func (l ApacheCombinedLogger) Log(w io.Writer, log *LogRecord) {
  s := log.R.(*ApacheCombinedSnapshot)
  c := log.W.(*SimpleCapture)
  fmt.Fprintf(w, "%s - %s [%s] \"%s %s %s\" %d %d \"%s\" \"%s\"\n",
        strings.Split(s.RemoteAddr, ":")[0],
        ApacheUser(s.URL.User),
        log.T.Format("02/Jan/2006:15:04:05 -0700"),
        s.Method,
        s.URL.RequestURI(),
        s.Proto,
        c.Status,
        c.Size,
        s.Referer,
        s.UserAgent)
}

// ApacheUser is a utility method for Logger implementations that formats
// user info in a request for apache logging.
func ApacheUser(user *url.Userinfo) string {
  result := "-"
  if user != nil {
    if name := user.Username(); name != "" {
      result = name
    }
  }
  return result
}

func maybeSend500(c Capture) {
  if !c.HasStatus() {
    sendError(c, http.StatusInternalServerError)
  }
}

func sendError(w http.ResponseWriter, status int) {
  http.Error(w, fmt.Sprintf("%d %s", status, http.StatusText(status)), status)
}

type nilWriter struct {
}

func (w nilWriter) Write(p []byte) (n int, err error) {
  return len(p), nil
}
<commit_msg>Remove :port from default logs.<commit_after>// Copyright 2013 Travis Keep. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or
// at http://opensource.org/licenses/BSD-3-Clause.

// Package weblogs provides access logs for webservers written in go.
package weblogs

import (
  "bytes"
  "fmt"
  "github.com/gorilla/context"
  "io"
  "net/http"
  "net/url"
  "os"
  "runtime/debug"
  "strings"
  "time"
)

type contextKeyType int

const (
  kBufferKey contextKeyType = iota
)

// Snapshot represents a snapshot of an HTTP request.
type Snapshot interface{}

// Capture captures a server response. Implementations delegate to an
// underlying ResponseWriter.
type Capture interface {
  http.ResponseWriter
  // HasStatus returns true if server has sent a status. False means that
  // server failed to send a response.
  HasStatus() bool
}

// LogRecord represents a single entry in the access logs.
type LogRecord struct {
  // The time request was received.
  T time.Time
  // The request snapshot
  R Snapshot
  // The capture of the response
  W Capture
  // Time spent processing the request
  Duration time.Duration
  // Additional information added with the Writer method.
  Extra string
}

// Logger represents an access log format. Clients are free to provide their
// own implementations.
type Logger interface {
  // NewSnapshot creates a new snapshot of a request.
  NewSnapshot(r *http.Request) Snapshot
  // NewCapture creates a new capture for capturing a response. w is the
  // original ResponseWriter.
  NewCapture(w http.ResponseWriter) Capture
  // Log writes the log record.
  Log(w io.Writer, record *LogRecord)
}

// Options specifies options for writing to access logs.
type Options struct {
  // Where to write the web logs. nil means write to stderr,
  Writer io.Writer
  // How to write the web logs. nil means use SimpleLogger.
  Logger Logger
  // How to get current time. nil means use time.Now(). This field is used
  // for testing purposes.
  Now func() time.Time
}

func (o *Options) writer() io.Writer {
  if o.Writer == nil {
    return os.Stderr
  }
  return o.Writer
}

func (o *Options) logger() Logger {
  if o.Logger == nil {
    return SimpleLogger{}
  }
  return o.Logger
}

func (o *Options) now() func() time.Time {
  if o.Now == nil {
    return time.Now
  }
  return o.Now
}

// Handler wraps a handler creating access logs. Access logs are written to
// stderr using SimpleLogger. Returned handler must be wrapped by
// context.ClearHandler.
func Handler(handler http.Handler) http.Handler {
  return HandlerWithOptions(handler, nil)
}

// HandlerWithOptions wraps a handler creating access logs and allows caller to
// configure how access logs are written. Returned handler must be
// wrapped by context.ClearHandler.
func HandlerWithOptions(
    handler http.Handler, options *Options) http.Handler {
  if options == nil {
    options = &Options{}
  }
  return &logHandler{
      handler: handler,
      w: options.writer(),
      logger: options.logger(),
      now: options.now()}
}

// Writer returns a writer whereby the caller can add additional information
// to the current log entry. If the handler calling this is not wrapped by
// the Handler() method, then writing to the returned io.Writer does
// nothing.
func Writer(r *http.Request) io.Writer {
  value := context.Get(r, kBufferKey)
  if value == nil {
    return nilWriter{}
  }
  return value.(*bytes.Buffer)
}

type logHandler struct {
  handler http.Handler
  w io.Writer
  logger Logger
  now func() time.Time
}

func (h *logHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  snapshot :=  h.logger.NewSnapshot(r)
  capture := h.logger.NewCapture(w)
  additional := &bytes.Buffer{}
  context.Set(r, kBufferKey, additional)
  startTime := h.now()
  defer func() {
    endTime := h.now()
    err := recover()
    maybeSend500(capture)
    h.logger.Log(
        h.w,
        &LogRecord{
            T: startTime,
            R: snapshot,
            W: capture,
            Duration: endTime.Sub(startTime),
            Extra: additional.String()})
    if err != nil {
      fmt.Fprintf(h.w, "Panic: %v\n", err)
      h.w.Write(debug.Stack())
    }
  }()
  h.handler.ServeHTTP(capture, r)
}

// ApacheCommonSnapshot provides a request snapshot for apache common
// access logs.
type ApacheCommonSnapshot struct {
  // Copied from Request.RemoteAddr
  RemoteAddr string
  // Copied from Request.Method
  Method string
  // Copied from Request.Proto
  Proto string
  // Copied from Request.URL
  URL *url.URL
}

func NewApacheCommonSnapshot(r *http.Request) ApacheCommonSnapshot {
  urlSnapshot := *r.URL
  return ApacheCommonSnapshot{
      RemoteAddr: r.RemoteAddr,
      Method: r.Method,
      Proto: r.Proto,
      URL: &urlSnapshot}
}

// ApacheCombinedSnapshot provides a request snapshot for apache combined
// access logs.
type ApacheCombinedSnapshot struct {
  ApacheCommonSnapshot
  Referer string
  UserAgent string
}

func NewApacheCombinedSnapshot(r *http.Request) ApacheCombinedSnapshot {
  return ApacheCombinedSnapshot{
      ApacheCommonSnapshot: NewApacheCommonSnapshot(r),
      Referer: r.Referer(),
      UserAgent: r.UserAgent()}
}

// SimpleCapture provides a capture of a response that includes the http
// status code and the size of the response.
type SimpleCapture struct {
  // The underlying ResponseWriter
  http.ResponseWriter
  // The HTTP status code shows up here.
  Status int
  // The size of the response in bytes shows up here.
  Size int
  statusSet bool
}

func (c *SimpleCapture) Write(b []byte) (int, error) {
  result, err := c.ResponseWriter.Write(b)
  c.Size += result
  c.maybeSetStatus(http.StatusOK)
  return result, err
}

func (c *SimpleCapture) WriteHeader(status int) {
  c.ResponseWriter.WriteHeader(status)
  c.maybeSetStatus(status)
}

func (c *SimpleCapture) HasStatus() bool {
  return c.statusSet
}

func (c *SimpleCapture) maybeSetStatus(status int) {
  if !c.statusSet {
    c.Status = status
    c.statusSet = true
  }
}

// SimpleLogger provides access logs with the following columns:
// date, remote address, method, URI, status, time elapsed milliseconds,
// followed by any additional information provided via the Writer method.
type SimpleLogger struct {
}

func (l SimpleLogger) NewSnapshot(r *http.Request) Snapshot {
  snapshot := NewApacheCommonSnapshot(r)
  return &snapshot
}

func (l SimpleLogger) NewCapture(w http.ResponseWriter) Capture {
  return &SimpleCapture{ResponseWriter: w}
}

func (l SimpleLogger) Log(w io.Writer, log *LogRecord) {
  s := log.R.(*ApacheCommonSnapshot)
  c := log.W.(*SimpleCapture)
  fmt.Fprintf(w, "%s %s %s %s %d %d%s\n",
      log.T.Format("01/02/2006 15:04:05"),
      strings.Split(s.RemoteAddr, ":")[0],
      s.Method,
      s.URL,
      c.Status,
      log.Duration / time.Millisecond,
      log.Extra)
}

// ApacheCommonLogger provides access logs in apache common log format.
type ApacheCommonLogger struct {
}

func (l ApacheCommonLogger) NewSnapshot(r *http.Request) Snapshot {
  snapshot := NewApacheCommonSnapshot(r)
  return &snapshot
}

func (l ApacheCommonLogger) NewCapture(w http.ResponseWriter) Capture {
  return &SimpleCapture{ResponseWriter: w}
}

func (l ApacheCommonLogger) Log(w io.Writer, log *LogRecord) {
  s := log.R.(*ApacheCommonSnapshot)
  c := log.W.(*SimpleCapture)
  fmt.Fprintf(w, "%s - %s [%s] \"%s %s %s\" %d %d\n",
        strings.Split(s.RemoteAddr, ":")[0],
        ApacheUser(s.URL.User),
        log.T.Format("02/Jan/2006:15:04:05 -0700"),
        s.Method,
        s.URL.RequestURI(),
        s.Proto,
        c.Status,
        c.Size)
}

// ApacheCombinedLogger provides access logs in apache combined log format.
type ApacheCombinedLogger struct {
}

func (l ApacheCombinedLogger) NewSnapshot(r *http.Request) Snapshot {
  snapshot := NewApacheCombinedSnapshot(r)
  return &snapshot
}

func (l ApacheCombinedLogger) NewCapture(w http.ResponseWriter) Capture {
  return &SimpleCapture{ResponseWriter: w}
}

func (l ApacheCombinedLogger) Log(w io.Writer, log *LogRecord) {
  s := log.R.(*ApacheCombinedSnapshot)
  c := log.W.(*SimpleCapture)
  fmt.Fprintf(w, "%s - %s [%s] \"%s %s %s\" %d %d \"%s\" \"%s\"\n",
        strings.Split(s.RemoteAddr, ":")[0],
        ApacheUser(s.URL.User),
        log.T.Format("02/Jan/2006:15:04:05 -0700"),
        s.Method,
        s.URL.RequestURI(),
        s.Proto,
        c.Status,
        c.Size,
        s.Referer,
        s.UserAgent)
}

// ApacheUser is a utility method for Logger implementations that formats
// user info in a request for apache logging.
func ApacheUser(user *url.Userinfo) string {
  result := "-"
  if user != nil {
    if name := user.Username(); name != "" {
      result = name
    }
  }
  return result
}

func maybeSend500(c Capture) {
  if !c.HasStatus() {
    sendError(c, http.StatusInternalServerError)
  }
}

func sendError(w http.ResponseWriter, status int) {
  http.Error(w, fmt.Sprintf("%d %s", status, http.StatusText(status)), status)
}

type nilWriter struct {
}

func (w nilWriter) Write(p []byte) (n int, err error) {
  return len(p), nil
}
<|endoftext|>


<commit_before>// Copyright 2018 The CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cue

import (
	"cuelang.org/go/cue/ast"
	"cuelang.org/go/cue/build"
	"cuelang.org/go/cue/errors"
	"cuelang.org/go/internal"
	"cuelang.org/go/internal/core/adt"
	"cuelang.org/go/internal/core/compile"
	"cuelang.org/go/internal/core/convert"
	"cuelang.org/go/internal/core/eval"
)

// An Instance defines a single configuration based on a collection of
// underlying CUE files.
type Instance struct {
	*index

	root *adt.Vertex

	ImportPath  string
	Dir         string
	PkgName     string
	DisplayName string

	Incomplete bool         // true if Pkg and all its dependencies are free of errors
	Err        errors.Error // non-nil if the package had errors

	inst *build.Instance

	// complete bool // for cycle detection
}

func (x *index) addInst(p *Instance) *Instance {
	if p.inst == nil {
		p.inst = &build.Instance{
			ImportPath: p.ImportPath,
			PkgName:    p.PkgName,
		}
	}
	// fmt.Println(p.ImportPath, "XXX")
	x.AddInst(p.ImportPath, p.root, p.inst)
	x.loaded[p.inst] = p
	p.index = x
	return p
}

func (x *index) getImportFromBuild(p *build.Instance) *Instance {
	inst := x.loaded[p]

	if inst != nil {
		return inst
	}

	v := x.GetNodeFromInstance(p)
	if v == nil {
		return nil
	}

	inst = &Instance{
		ImportPath:  p.ImportPath,
		Dir:         p.Dir,
		PkgName:     p.PkgName,
		DisplayName: p.ImportPath,
		root:        v,
		inst:        p,
		index:       x,
	}
	if p.Err != nil {
		inst.setListOrError(p.Err)
	}

	x.loaded[p] = inst

	return inst
}

func (x *index) getImportFromNode(v *adt.Vertex) *Instance {
	p := x.GetInstanceFromNode(v)
	if p == nil {
		return nil
	}

	return x.getImportFromBuild(p)
}

func (x *index) getImportFromPath(id string) *Instance {
	node, _ := x.LoadImport(id)
	if node == nil {
		return nil
	}
	b := x.GetInstanceFromNode(node)
	inst := x.loaded[b]
	if inst == nil {
		inst = &Instance{
			ImportPath: b.ImportPath,
			PkgName:    b.PkgName,
			root:       node,
			inst:       b,
			index:      x,
		}
	}
	return inst
}

func init() {
	internal.MakeInstance = func(value interface{}) interface{} {
		v := value.(Value)
		x := v.eval(v.ctx())
		st, ok := x.(*adt.Vertex)
		if !ok {
			st = &adt.Vertex{}
			st.AddConjunct(adt.MakeRootConjunct(nil, x))
		}
		return v.ctx().index.addInst(&Instance{
			root: st,
		})
	}
}

// newInstance creates a new instance. Use Insert to populate the instance.
func newInstance(x *index, p *build.Instance, v *adt.Vertex) *Instance {
	// TODO: associate root source with structLit.
	inst := &Instance{
		root: v,
		inst: p,
	}
	if p != nil {
		inst.ImportPath = p.ImportPath
		inst.Dir = p.Dir
		inst.PkgName = p.PkgName
		inst.DisplayName = p.ImportPath
		if p.Err != nil {
			inst.setListOrError(p.Err)
		}
	}

	x.AddInst(p.ImportPath, v, p)
	x.loaded[p] = inst
	inst.index = x
	return inst
}

func (inst *Instance) setListOrError(err errors.Error) {
	inst.Incomplete = true
	inst.Err = errors.Append(inst.Err, err)
}

func (inst *Instance) setError(err errors.Error) {
	inst.Incomplete = true
	inst.Err = errors.Append(inst.Err, err)
}

func (inst *Instance) eval(ctx *context) adt.Value {
	// TODO: remove manifest here?
	v := ctx.manifest(inst.root)
	return v
}

func init() {
	internal.EvalExpr = func(value, expr interface{}) interface{} {
		v := value.(Value)
		e := expr.(ast.Expr)
		ctx := v.idx.newContext()
		return newValueRoot(ctx, evalExpr(ctx, v.vertex(ctx), e))
	}
}

// pkgID reports a package path that can never resolve to a valid package.
func pkgID() string {
	return "_"
}

// evalExpr evaluates expr within scope.
func evalExpr(ctx *context, scope *adt.Vertex, expr ast.Expr) adt.Value {
	cfg := &compile.Config{
		Scope: scope,
		Imports: func(x *ast.Ident) (pkgPath string) {
			if !isBuiltin(x.Name) {
				return ""
			}
			return x.Name
		},
	}

	c, err := compile.Expr(cfg, ctx.opCtx, pkgID(), expr)
	if err != nil {
		return &adt.Bottom{Err: err}
	}
	return adt.Resolve(ctx.opCtx, c)

	// scope.Finalize(ctx.opCtx) // TODO: not appropriate here.
	// switch s := scope.Value.(type) {
	// case *bottom:
	// 	return s
	// case *adt.StructMarker:
	// default:
	// 	return ctx.mkErr(scope, "instance is not a struct, found %s", scope.Kind())
	// }

	// c := ctx.opCtx

	// x, err := compile.Expr(&compile.Config{Scope: scope}, c.Runtime, expr)
	// if err != nil {
	// 	return c.NewErrf("could not evaluate %s: %v", c.Str(x), err)
	// }

	// env := &adt.Environment{Vertex: scope}

	// switch v := x.(type) {
	// case adt.Value:
	// 	return v
	// case adt.Resolver:
	// 	r, err := c.Resolve(env, v)
	// 	if err != nil {
	// 		return err
	// 	}
	// 	return r

	// case adt.Evaluator:
	// 	e, _ := c.Evaluate(env, x)
	// 	return e

	// }

	// return c.NewErrf("could not evaluate %s", c.Str(x))
}

// ID returns the package identifier that uniquely qualifies module and
// package name.
func (inst *Instance) ID() string {
	if inst == nil || inst.inst == nil {
		return ""
	}
	return inst.inst.ID()
}

// Doc returns the package comments for this instance.
func (inst *Instance) Doc() []*ast.CommentGroup {
	var docs []*ast.CommentGroup
	if inst.inst == nil {
		return nil
	}
	for _, f := range inst.inst.Files {
		if c := internal.FileComment(f); c != nil {
			docs = append(docs, c)
		}
	}
	return docs
}

// Value returns the root value of the configuration. If the configuration
// defines in emit value, it will be that value. Otherwise it will be all
// top-level values.
func (inst *Instance) Value() Value {
	ctx := inst.newContext()
	inst.root.Finalize(ctx.opCtx)
	return newVertexRoot(ctx, inst.root)
}

// Eval evaluates an expression within an existing instance.
//
// Expressions may refer to builtin packages if they can be uniquely identified.
func (inst *Instance) Eval(expr ast.Expr) Value {
	ctx := inst.newContext()
	v := inst.root
	v.Finalize(ctx.opCtx)
	result := evalExpr(ctx, v, expr)
	return newValueRoot(ctx, result)
}

// Merge unifies the given instances into a single one.
//
// Errors regarding conflicts are included in the result, but not reported, so
// that these will only surface during manifestation. This allows
// non-conflicting parts to be used.
func Merge(inst ...*Instance) *Instance {
	v := &adt.Vertex{}

	i := inst[0]
	ctx := i.index.newContext().opCtx

	// TODO: interesting test: use actual unification and then on K8s corpus.

	for _, i := range inst {
		w := i.Value()
		v.AddConjunct(adt.MakeRootConjunct(nil, w.v.ToDataAll()))
	}
	v.Finalize(ctx)

	p := i.index.addInst(&Instance{
		root: v,
		// complete: true,
	})
	return p
}

// Build creates a new instance from the build instances, allowing unbound
// identifier to bind to the top-level field in inst. The top-level fields in
// inst take precedence over predeclared identifier and builtin functions.
func (inst *Instance) Build(p *build.Instance) *Instance {
	p.Complete()

	idx := inst.index
	r := inst.index.Runtime

	rErr := r.ResolveFiles(p)

	cfg := &compile.Config{Scope: inst.root}
	v, err := compile.Files(cfg, r, p.ID(), p.Files...)

	v.AddConjunct(adt.MakeRootConjunct(nil, inst.root))

	i := newInstance(idx, p, v)
	if rErr != nil {
		i.setListOrError(rErr)
	}
	if i.Err != nil {
		i.setListOrError(i.Err)
	}

	if err != nil {
		i.setListOrError(err)
	}

	// i.complete = true

	return i
}

func (inst *Instance) value() Value {
	return newVertexRoot(inst.newContext(), inst.root)
}

// Lookup reports the value at a path starting from the top level struct. The
// Exists method of the returned value will report false if the path did not
// exist. The Err method reports if any error occurred during evaluation. The
// empty path returns the top-level configuration struct. Use LookupDef for definitions or LookupField for
// any kind of field.
func (inst *Instance) Lookup(path ...string) Value {
	return inst.value().Lookup(path...)
}

// LookupDef reports the definition with the given name within struct v. The
// Exists method of the returned value will report false if the definition did
// not exist. The Err method reports if any error occurred during evaluation.
func (inst *Instance) LookupDef(path string) Value {
	return inst.value().LookupDef(path)
}

// LookupField reports a Field at a path starting from v, or an error if the
// path is not. The empty path returns v itself.
//
// It cannot look up hidden or unexported fields.
//
// Deprecated: this API does not work with new-style definitions. Use
// FieldByName defined on inst.Value().
func (inst *Instance) LookupField(path ...string) (f FieldInfo, err error) {
	v := inst.value()
	for _, k := range path {
		s, err := v.Struct()
		if err != nil {
			return f, err
		}

		f, err = s.FieldByName(k, true)
		if err != nil {
			return f, err
		}
		if f.IsHidden {
			return f, errNotFound
		}
		v = f.Value
	}
	return f, err
}

// Fill creates a new instance with the values of the old instance unified with
// the given value. It is not possible to update the emit value.
//
// Values may be any Go value that can be converted to CUE, an ast.Expr or
// a Value. In the latter case, it will panic if the Value is not from the same
// Runtime.
func (inst *Instance) Fill(x interface{}, path ...string) (*Instance, error) {
	for i := len(path) - 1; i >= 0; i-- {
		x = map[string]interface{}{path[i]: x}
	}
	a := make([]adt.Conjunct, len(inst.root.Conjuncts))
	copy(a, inst.root.Conjuncts)
	u := &adt.Vertex{Conjuncts: a}

	if v, ok := x.(Value); ok {
		if inst.index != v.idx {
			panic("value of type Value is not created with same Runtime as Instance")
		}
		for _, c := range v.v.Conjuncts {
			u.AddConjunct(c)
		}
	} else {
		ctx := eval.NewContext(inst.index.Runtime, nil)
		expr := convert.GoValueToExpr(ctx, true, x)
		u.AddConjunct(adt.MakeRootConjunct(nil, expr))
		u.Finalize(ctx)
	}
	inst = inst.index.addInst(&Instance{
		root: u,
		inst: nil,

		// Omit ImportPath to indicate this is not an importable package.
		Dir:        inst.Dir,
		PkgName:    inst.PkgName,
		Incomplete: inst.Incomplete,

		// complete: true,
	})
	return inst, nil
}
<commit_msg>cue: deprecate Merge<commit_after>// Copyright 2018 The CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cue

import (
	"cuelang.org/go/cue/ast"
	"cuelang.org/go/cue/build"
	"cuelang.org/go/cue/errors"
	"cuelang.org/go/internal"
	"cuelang.org/go/internal/core/adt"
	"cuelang.org/go/internal/core/compile"
	"cuelang.org/go/internal/core/convert"
	"cuelang.org/go/internal/core/eval"
)

// An Instance defines a single configuration based on a collection of
// underlying CUE files.
type Instance struct {
	*index

	root *adt.Vertex

	ImportPath  string
	Dir         string
	PkgName     string
	DisplayName string

	Incomplete bool         // true if Pkg and all its dependencies are free of errors
	Err        errors.Error // non-nil if the package had errors

	inst *build.Instance

	// complete bool // for cycle detection
}

func (x *index) addInst(p *Instance) *Instance {
	if p.inst == nil {
		p.inst = &build.Instance{
			ImportPath: p.ImportPath,
			PkgName:    p.PkgName,
		}
	}
	// fmt.Println(p.ImportPath, "XXX")
	x.AddInst(p.ImportPath, p.root, p.inst)
	x.loaded[p.inst] = p
	p.index = x
	return p
}

func (x *index) getImportFromBuild(p *build.Instance) *Instance {
	inst := x.loaded[p]

	if inst != nil {
		return inst
	}

	v := x.GetNodeFromInstance(p)
	if v == nil {
		return nil
	}

	inst = &Instance{
		ImportPath:  p.ImportPath,
		Dir:         p.Dir,
		PkgName:     p.PkgName,
		DisplayName: p.ImportPath,
		root:        v,
		inst:        p,
		index:       x,
	}
	if p.Err != nil {
		inst.setListOrError(p.Err)
	}

	x.loaded[p] = inst

	return inst
}

func (x *index) getImportFromNode(v *adt.Vertex) *Instance {
	p := x.GetInstanceFromNode(v)
	if p == nil {
		return nil
	}

	return x.getImportFromBuild(p)
}

func (x *index) getImportFromPath(id string) *Instance {
	node, _ := x.LoadImport(id)
	if node == nil {
		return nil
	}
	b := x.GetInstanceFromNode(node)
	inst := x.loaded[b]
	if inst == nil {
		inst = &Instance{
			ImportPath: b.ImportPath,
			PkgName:    b.PkgName,
			root:       node,
			inst:       b,
			index:      x,
		}
	}
	return inst
}

func init() {
	internal.MakeInstance = func(value interface{}) interface{} {
		v := value.(Value)
		x := v.eval(v.ctx())
		st, ok := x.(*adt.Vertex)
		if !ok {
			st = &adt.Vertex{}
			st.AddConjunct(adt.MakeRootConjunct(nil, x))
		}
		return v.ctx().index.addInst(&Instance{
			root: st,
		})
	}
}

// newInstance creates a new instance. Use Insert to populate the instance.
func newInstance(x *index, p *build.Instance, v *adt.Vertex) *Instance {
	// TODO: associate root source with structLit.
	inst := &Instance{
		root: v,
		inst: p,
	}
	if p != nil {
		inst.ImportPath = p.ImportPath
		inst.Dir = p.Dir
		inst.PkgName = p.PkgName
		inst.DisplayName = p.ImportPath
		if p.Err != nil {
			inst.setListOrError(p.Err)
		}
	}

	x.AddInst(p.ImportPath, v, p)
	x.loaded[p] = inst
	inst.index = x
	return inst
}

func (inst *Instance) setListOrError(err errors.Error) {
	inst.Incomplete = true
	inst.Err = errors.Append(inst.Err, err)
}

func (inst *Instance) setError(err errors.Error) {
	inst.Incomplete = true
	inst.Err = errors.Append(inst.Err, err)
}

func (inst *Instance) eval(ctx *context) adt.Value {
	// TODO: remove manifest here?
	v := ctx.manifest(inst.root)
	return v
}

func init() {
	internal.EvalExpr = func(value, expr interface{}) interface{} {
		v := value.(Value)
		e := expr.(ast.Expr)
		ctx := v.idx.newContext()
		return newValueRoot(ctx, evalExpr(ctx, v.vertex(ctx), e))
	}
}

// pkgID reports a package path that can never resolve to a valid package.
func pkgID() string {
	return "_"
}

// evalExpr evaluates expr within scope.
func evalExpr(ctx *context, scope *adt.Vertex, expr ast.Expr) adt.Value {
	cfg := &compile.Config{
		Scope: scope,
		Imports: func(x *ast.Ident) (pkgPath string) {
			if !isBuiltin(x.Name) {
				return ""
			}
			return x.Name
		},
	}

	c, err := compile.Expr(cfg, ctx.opCtx, pkgID(), expr)
	if err != nil {
		return &adt.Bottom{Err: err}
	}
	return adt.Resolve(ctx.opCtx, c)

	// scope.Finalize(ctx.opCtx) // TODO: not appropriate here.
	// switch s := scope.Value.(type) {
	// case *bottom:
	// 	return s
	// case *adt.StructMarker:
	// default:
	// 	return ctx.mkErr(scope, "instance is not a struct, found %s", scope.Kind())
	// }

	// c := ctx.opCtx

	// x, err := compile.Expr(&compile.Config{Scope: scope}, c.Runtime, expr)
	// if err != nil {
	// 	return c.NewErrf("could not evaluate %s: %v", c.Str(x), err)
	// }

	// env := &adt.Environment{Vertex: scope}

	// switch v := x.(type) {
	// case adt.Value:
	// 	return v
	// case adt.Resolver:
	// 	r, err := c.Resolve(env, v)
	// 	if err != nil {
	// 		return err
	// 	}
	// 	return r

	// case adt.Evaluator:
	// 	e, _ := c.Evaluate(env, x)
	// 	return e

	// }

	// return c.NewErrf("could not evaluate %s", c.Str(x))
}

// ID returns the package identifier that uniquely qualifies module and
// package name.
func (inst *Instance) ID() string {
	if inst == nil || inst.inst == nil {
		return ""
	}
	return inst.inst.ID()
}

// Doc returns the package comments for this instance.
func (inst *Instance) Doc() []*ast.CommentGroup {
	var docs []*ast.CommentGroup
	if inst.inst == nil {
		return nil
	}
	for _, f := range inst.inst.Files {
		if c := internal.FileComment(f); c != nil {
			docs = append(docs, c)
		}
	}
	return docs
}

// Value returns the root value of the configuration. If the configuration
// defines in emit value, it will be that value. Otherwise it will be all
// top-level values.
func (inst *Instance) Value() Value {
	ctx := inst.newContext()
	inst.root.Finalize(ctx.opCtx)
	return newVertexRoot(ctx, inst.root)
}

// Eval evaluates an expression within an existing instance.
//
// Expressions may refer to builtin packages if they can be uniquely identified.
func (inst *Instance) Eval(expr ast.Expr) Value {
	ctx := inst.newContext()
	v := inst.root
	v.Finalize(ctx.opCtx)
	result := evalExpr(ctx, v, expr)
	return newValueRoot(ctx, result)
}

// Merge unifies the given instances into a single one.
//
// Deprecated: do not use.
func Merge(inst ...*Instance) *Instance {
	v := &adt.Vertex{}

	i := inst[0]
	ctx := i.index.newContext().opCtx

	// TODO: interesting test: use actual unification and then on K8s corpus.

	for _, i := range inst {
		w := i.Value()
		v.AddConjunct(adt.MakeRootConjunct(nil, w.v.ToDataAll()))
	}
	v.Finalize(ctx)

	p := i.index.addInst(&Instance{
		root: v,
		// complete: true,
	})
	return p
}

// Build creates a new instance from the build instances, allowing unbound
// identifier to bind to the top-level field in inst. The top-level fields in
// inst take precedence over predeclared identifier and builtin functions.
func (inst *Instance) Build(p *build.Instance) *Instance {
	p.Complete()

	idx := inst.index
	r := inst.index.Runtime

	rErr := r.ResolveFiles(p)

	cfg := &compile.Config{Scope: inst.root}
	v, err := compile.Files(cfg, r, p.ID(), p.Files...)

	v.AddConjunct(adt.MakeRootConjunct(nil, inst.root))

	i := newInstance(idx, p, v)
	if rErr != nil {
		i.setListOrError(rErr)
	}
	if i.Err != nil {
		i.setListOrError(i.Err)
	}

	if err != nil {
		i.setListOrError(err)
	}

	// i.complete = true

	return i
}

func (inst *Instance) value() Value {
	return newVertexRoot(inst.newContext(), inst.root)
}

// Lookup reports the value at a path starting from the top level struct. The
// Exists method of the returned value will report false if the path did not
// exist. The Err method reports if any error occurred during evaluation. The
// empty path returns the top-level configuration struct. Use LookupDef for definitions or LookupField for
// any kind of field.
func (inst *Instance) Lookup(path ...string) Value {
	return inst.value().Lookup(path...)
}

// LookupDef reports the definition with the given name within struct v. The
// Exists method of the returned value will report false if the definition did
// not exist. The Err method reports if any error occurred during evaluation.
func (inst *Instance) LookupDef(path string) Value {
	return inst.value().LookupDef(path)
}

// LookupField reports a Field at a path starting from v, or an error if the
// path is not. The empty path returns v itself.
//
// It cannot look up hidden or unexported fields.
//
// Deprecated: this API does not work with new-style definitions. Use
// FieldByName defined on inst.Value().
func (inst *Instance) LookupField(path ...string) (f FieldInfo, err error) {
	v := inst.value()
	for _, k := range path {
		s, err := v.Struct()
		if err != nil {
			return f, err
		}

		f, err = s.FieldByName(k, true)
		if err != nil {
			return f, err
		}
		if f.IsHidden {
			return f, errNotFound
		}
		v = f.Value
	}
	return f, err
}

// Fill creates a new instance with the values of the old instance unified with
// the given value. It is not possible to update the emit value.
//
// Values may be any Go value that can be converted to CUE, an ast.Expr or
// a Value. In the latter case, it will panic if the Value is not from the same
// Runtime.
func (inst *Instance) Fill(x interface{}, path ...string) (*Instance, error) {
	for i := len(path) - 1; i >= 0; i-- {
		x = map[string]interface{}{path[i]: x}
	}
	a := make([]adt.Conjunct, len(inst.root.Conjuncts))
	copy(a, inst.root.Conjuncts)
	u := &adt.Vertex{Conjuncts: a}

	if v, ok := x.(Value); ok {
		if inst.index != v.idx {
			panic("value of type Value is not created with same Runtime as Instance")
		}
		for _, c := range v.v.Conjuncts {
			u.AddConjunct(c)
		}
	} else {
		ctx := eval.NewContext(inst.index.Runtime, nil)
		expr := convert.GoValueToExpr(ctx, true, x)
		u.AddConjunct(adt.MakeRootConjunct(nil, expr))
		u.Finalize(ctx)
	}
	inst = inst.index.addInst(&Instance{
		root: u,
		inst: nil,

		// Omit ImportPath to indicate this is not an importable package.
		Dir:        inst.Dir,
		PkgName:    inst.PkgName,
		Incomplete: inst.Incomplete,

		// complete: true,
	})
	return inst, nil
}
<|endoftext|>


<commit_before>// Main package wraps sprite_sass tool for use with the command line
// See -h for list of available options
package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"runtime/pprof"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	libsass "github.com/wellington/go-libsass"
	"github.com/wellington/wellington/version"

	wt "github.com/wellington/wellington"
	_ "github.com/wellington/wellington/handlers"
)

var (
	font, dir, gen, includes      string
	mainFile, style               string
	comments, watch               bool
	cpuprofile, buildDir          string
	jsDir                         string
	ishttp, showHelp, showVersion bool
	httpPath                      string
	timeB                         bool
	config                        string
	debug                         bool
	multi                         bool

	// unused
	noLineComments bool
	relativeAssets bool
	cssDir         string
)

/*
   --app APP                    Tell compass what kind of application it is integrating with. E.g. rails
   --fonts-dir FONTS_DIR        The directory where you keep your fonts.
*/
func init() {

	// Interoperability args
}

func flags(set *pflag.FlagSet) {
	// Unused cli args
	set.BoolVar(&noLineComments, "no-line-comments", false, "UNSUPPORTED: Disable line comments")
	set.BoolVar(&relativeAssets, "relative-assets", false, "UNSUPPORTED: Make compass asset helpers generate relative urls to assets.")

	set.BoolVarP(&showVersion, "version", "v", false, "Show the app version")
	//wtCmd.PersistentFlags().BoolVarP(&showHelp, "help", "h", false, "this help")
	set.BoolVar(&debug, "debug", false, "Show detailed debug information")
	set.StringVar(&dir, "images-dir", "", "Compass Image Directory")
	set.StringVarP(&dir, "dir", "d", "", "Compass Image Directory")
	set.StringVar(&jsDir, "javascripts-dir", "", "Compass JS Directory")
	set.BoolVar(&timeB, "time", false, "Retrieve timing information")

	set.StringVarP(&buildDir, "build", "b", "", "Target directory for generated CSS, relative paths from sass-dir are preserved")
	set.StringVar(&buildDir, "css-dir", "", "Location of CSS files")

	set.StringVar(&gen, "gen", ".", "Generated images directory")

	set.StringVar(&includes, "sass-dir", "", "Compass Sass Directory")
	set.StringVarP(&includes, "proj", "p", "", "Project directory")

	set.StringVar(&font, "font", ".", "Font Directory")
	set.StringVarP(&style, "style", "s", "nested", "CSS nested style")

	set.StringVarP(&config, "config", "c", "", "Location of the config file")

	set.BoolVarP(&comments, "comment", "", true, "Turn on source comments")

	set.BoolVarP(&watch, "watch", "w", false, "File watcher that will rebuild css on file changes")

	set.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
	set.BoolVarP(&multi, "multi", "", false, "Enable multi-threaded operation")
}

var compileCmd = &cobra.Command{
	Use:   "compile",
	Short: "Compile Sass stylesheets to CSS",
	Long: `Fast compilation of Sass stylesheets to CSS. For usage consult
the documentation at https://github.com/wellington/wellington#wellington`,
	Run: Run,
}

var watchCmd = &cobra.Command{
	Use:   "watch",
	Short: "Watch Sass files for changes and rebuild CSS",
	Long:  ``,
	Run: func(cmd *cobra.Command, args []string) {
		watch = true
		Run(cmd, args)
	},
}

var httpCmd = &cobra.Command{
	Use:   "serve",
	Short: "Starts a http server that will convert Sass to CSS",
	Long:  ``,
	Run: func(cmd *cobra.Command, args []string) {
		ishttp = true
		Run(cmd, args)
	},
}

func init() {
	hostname := os.Getenv("HOSTNAME")
	if len(hostname) > 0 {
		if !strings.HasPrefix(hostname, "http") {
			hostname = "http://" + hostname
		}
	} else if host, err := os.Hostname(); err == nil {
		hostname = "http://" + host
	}
	httpCmd.Flags().StringVar(&httpPath, "httppath", hostname,
		"Only for HTTP, overrides generated sprite paths to support http")

}

func root() {
	flags(wtCmd.PersistentFlags())
}

// AddCommands attaches the cli subcommands ie. http, compile to the
// main cli entrypoint.
func AddCommands() {
	wtCmd.AddCommand(httpCmd)
	wtCmd.AddCommand(compileCmd)
	wtCmd.AddCommand(watchCmd)
}

var wtCmd = &cobra.Command{
	Use:   "wt",
	Short: "wt builds Sass",
	Run:   Run,
}

func main() {
	AddCommands()
	root()

	wtCmd.Execute()
}

func argExit() bool {

	if showVersion {
		fmt.Printf("   libsass: %s\n", libsass.Version())
		fmt.Printf("Wellington: %s\n", version.Version)
		return true
	}

	if showHelp {
		fmt.Println("Please specify input filepath.")
		fmt.Println("\nAvailable options:")
		//flag.PrintDefaults()
		return true
	}
	return false

}

func parseBuildArgs() *wt.BuildArgs {
	style, ok := libsass.Style[style]

	if !ok {
		style = libsass.NESTED_STYLE
	}

	gba := &wt.BuildArgs{
		ImageDir: dir,
		BuildDir: buildDir,
		Includes: includes,
		Font:     font,
		Style:    style,
		Gen:      gen,
		Comments: comments,
	}
	gba.Init()

	return gba
}

// Run is the main entrypoint for the cli.
func Run(cmd *cobra.Command, paths []string) {

	start := time.Now()

	defer func() {
		diff := float64(time.Since(start).Nanoseconds()) / float64(time.Millisecond)
		log.Printf("Compilation took: %sms\n",
			strconv.FormatFloat(diff, 'f', 3, 32))
	}()

	if argExit() {
		return
	}

	// Profiling code
	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		log.Println("Starting profiler")
		pprof.StartCPUProfile(f)
		defer func() {
			pprof.StopCPUProfile()
			err := f.Close()
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Stopping Profiller")
		}()
	}

	for _, v := range paths {
		if strings.HasPrefix(v, "-") {
			log.Fatalf("Please specify flags before other arguments: %s", v)
		}
	}

	if gen != "" {
		err := os.MkdirAll(gen, 0755)
		if err != nil {
			log.Fatal(err)
		}
	}

	pMap := wt.NewPartialMap()

	gba := parseBuildArgs()
	if debug {
		fmt.Printf("      Font  Dir: %s\n", gba.Font)
		fmt.Printf("      Image Dir: %s\n", gba.ImageDir)
		fmt.Printf("      Build Dir: %s\n", gba.BuildDir)
		fmt.Printf("Build Image Dir: %s\n", gba.Gen)
		fmt.Printf(" Include Dir(s): %s\n", gba.Includes)
		fmt.Println("===================================")
	}

	if ishttp {
		if len(gba.Gen) == 0 {
			log.Fatal("Must pass an image build directory to use HTTP")
		}
		http.Handle("/build/", wt.FileHandler(gba.Gen))
		log.Println("Web server started on :12345")
		http.HandleFunc("/", wt.HTTPHandler(wt.NewContext(gba)))
		err := http.ListenAndServe(":12345", nil)
		if err != nil {
			log.Fatal("ListenAndServe: ", err)
		}

		return
	}

	if !watch && len(paths) == 0 && len(config) == 0 {

		// Read from stdin
		fmt.Println("Reading from stdin, -h for help")
		out := os.Stdout
		in := os.Stdin

		var pout bytes.Buffer
		ctx := wt.NewContext(gba)
		_, err := wt.StartParser(ctx, in, &pout, wt.NewPartialMap())
		if err != nil {
			log.Println(err)
		}
		err = ctx.Compile(&pout, out)

		if err != nil {
			color.Red(err.Error())
		}
		return
	}
	sassPaths := paths

	bOpts := wt.NewBuild(paths, gba, pMap, multi)

	err := bOpts.Run()
	if err != nil {
		log.Fatal(err)
	}

	if watch {
		w := wt.NewWatcher(&wt.WatchOptions{
			Paths:      sassPaths,
			BArgs:      gba,
			PartialMap: pMap,
		})
		w.Watch()

		fmt.Println("File watcher started use `ctrl+d` to exit")
		in := bufio.NewReader(os.Stdin)
		for {
			_, err := in.ReadString(' ')
			if err != nil {
				if err == io.EOF {
					os.Exit(0)
				}
				fmt.Println("error", err)
			}
		}
	} else {

		// Before shutting down, check that every sprite has been
		// flushed to disk.
		flush := time.Now()
		img := sync.WaitGroup{}
		pMap.RLock()
		// It's not currently possible to wait on Image. This is often
		// to inline images, so it shouldn't be a factor...
		/*for _, s := range gba.Payload.Image().M {
			img.Add(1)
			err := s.Wait()
			img.Done()
			if err != nil {
				log.Printf("error writing image: %s\n", err)
			}
		}*/
		for _, s := range gba.Payload.Sprite().M {
			img.Add(1)
			err := s.Wait()
			img.Done()
			if err != nil {
				log.Printf("error writing sprite: %s\n", err)
			}
		}
		img.Wait()
		_ = flush
		// log.Println("Extra time spent flushing images to disk: ", time.Since(flush))
		pMap.RUnlock()
	}
}
<commit_msg>move serve out of run<commit_after>// Main package wraps sprite_sass tool for use with the command line
// See -h for list of available options
package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"runtime/pprof"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/fatih/color"
	"github.com/spf13/cobra"
	"github.com/spf13/pflag"
	libsass "github.com/wellington/go-libsass"
	"github.com/wellington/wellington/version"

	wt "github.com/wellington/wellington"
	_ "github.com/wellington/wellington/handlers"
)

var (
	font, dir, gen, includes      string
	mainFile, style               string
	comments, watch               bool
	cpuprofile, buildDir          string
	jsDir                         string
	ishttp, showHelp, showVersion bool
	httpPath                      string
	timeB                         bool
	config                        string
	debug                         bool
	multi                         bool

	// unused
	noLineComments bool
	relativeAssets bool
	cssDir         string
)

/*
   --app APP                    Tell compass what kind of application it is integrating with. E.g. rails
   --fonts-dir FONTS_DIR        The directory where you keep your fonts.
*/
func init() {

	// Interoperability args
}

func flags(set *pflag.FlagSet) {
	// Unused cli args
	set.BoolVar(&noLineComments, "no-line-comments", false, "UNSUPPORTED: Disable line comments")
	set.BoolVar(&relativeAssets, "relative-assets", false, "UNSUPPORTED: Make compass asset helpers generate relative urls to assets.")

	set.BoolVarP(&showVersion, "version", "v", false, "Show the app version")
	//wtCmd.PersistentFlags().BoolVarP(&showHelp, "help", "h", false, "this help")
	set.BoolVar(&debug, "debug", false, "Show detailed debug information")
	set.StringVar(&dir, "images-dir", "", "Compass Image Directory")
	set.StringVarP(&dir, "dir", "d", "", "Compass Image Directory")
	set.StringVar(&jsDir, "javascripts-dir", "", "Compass JS Directory")
	set.BoolVar(&timeB, "time", false, "Retrieve timing information")

	set.StringVarP(&buildDir, "build", "b", "", "Target directory for generated CSS, relative paths from sass-dir are preserved")
	set.StringVar(&buildDir, "css-dir", "", "Location of CSS files")

	set.StringVar(&gen, "gen", ".", "Generated images directory")

	set.StringVar(&includes, "sass-dir", "", "Compass Sass Directory")
	set.StringVarP(&includes, "proj", "p", "", "Project directory")

	set.StringVar(&font, "font", ".", "Font Directory")
	set.StringVarP(&style, "style", "s", "nested", "CSS nested style")

	set.StringVarP(&config, "config", "c", "", "Location of the config file")

	set.BoolVarP(&comments, "comment", "", true, "Turn on source comments")

	set.StringVar(&cpuprofile, "cpuprofile", "", "write cpu profile to file")
	set.BoolVarP(&multi, "multi", "", true, "Enable multi-threaded operation")
}

var compileCmd = &cobra.Command{
	Use:   "compile",
	Short: "Compile Sass stylesheets to CSS",
	Long: `Fast compilation of Sass stylesheets to CSS. For usage consult
the documentation at https://github.com/wellington/wellington#wellington`,
	Run: Run,
}

var watchCmd = &cobra.Command{
	Use:   "watch",
	Short: "Watch Sass files for changes and rebuild CSS",
	Long:  ``,
	Run: func(cmd *cobra.Command, args []string) {
		watch = true
		Run(cmd, args)
	},
}

var httpCmd = &cobra.Command{
	Use:   "serve",
	Short: "Starts a http server that will convert Sass to CSS",
	Long:  ``,
	Run:   Serve,
}

func init() {
	hostname := os.Getenv("HOSTNAME")
	if len(hostname) > 0 {
		if !strings.HasPrefix(hostname, "http") {
			hostname = "http://" + hostname
		}
	} else if host, err := os.Hostname(); err == nil {
		hostname = "http://" + host
	}
	httpCmd.Flags().StringVar(&httpPath, "httppath", hostname,
		"Only for HTTP, overrides generated sprite paths to support http")

}

func root() {
	flags(wtCmd.PersistentFlags())
}

// AddCommands attaches the cli subcommands ie. http, compile to the
// main cli entrypoint.
func AddCommands() {
	wtCmd.AddCommand(httpCmd)
	wtCmd.AddCommand(compileCmd)
	wtCmd.AddCommand(watchCmd)
}

var wtCmd = &cobra.Command{
	Use:   "wt",
	Short: "wt is a Sass project tool",
	Run:   Run,
}

func main() {
	AddCommands()
	root()

	wtCmd.Execute()
}

func argExit() bool {

	if showVersion {
		fmt.Printf("   libsass: %s\n", libsass.Version())
		fmt.Printf("Wellington: %s\n", version.Version)
		return true
	}

	if showHelp {
		fmt.Println("Please specify input filepath.")
		fmt.Println("\nAvailable options:")
		//flag.PrintDefaults()
		return true
	}
	return false

}

func parseBuildArgs() *wt.BuildArgs {
	style, ok := libsass.Style[style]

	if !ok {
		style = libsass.NESTED_STYLE
	}

	gba := &wt.BuildArgs{
		ImageDir: dir,
		BuildDir: buildDir,
		Includes: includes,
		Font:     font,
		Style:    style,
		Gen:      gen,
		Comments: comments,
	}
	gba.Init()

	return gba
}

func globalRun(paths []string) (*wt.SafePartialMap, *wt.BuildArgs) {

	if argExit() {
		return nil, nil
	}

	// Profiling code
	if cpuprofile != "" {
		f, err := os.Create(cpuprofile)
		if err != nil {
			log.Fatal(err)
		}
		log.Println("Starting profiler")
		pprof.StartCPUProfile(f)
		defer func() {
			pprof.StopCPUProfile()
			err := f.Close()
			if err != nil {
				log.Fatal(err)
			}
			log.Println("Stopping Profiller")
		}()
	}

	for _, v := range paths {
		if strings.HasPrefix(v, "-") {
			log.Fatalf("Please specify flags before other arguments: %s", v)
		}
	}

	if gen != "" {
		err := os.MkdirAll(gen, 0755)
		if err != nil {
			log.Fatal(err)
		}
	}

	pMap := wt.NewPartialMap()
	gba := parseBuildArgs()

	if debug {
		fmt.Printf("      Font  Dir: %s\n", gba.Font)
		fmt.Printf("      Image Dir: %s\n", gba.ImageDir)
		fmt.Printf("      Build Dir: %s\n", gba.BuildDir)
		fmt.Printf("Build Image Dir: %s\n", gba.Gen)
		fmt.Printf(" Include Dir(s): %s\n", gba.Includes)
		fmt.Println("===================================")
	}

	return pMap, gba

}

func Compile(cmd *cobra.Command, paths []string) {

	start := time.Now()

	defer func() {
		diff := float64(time.Since(start).Nanoseconds()) / float64(time.Millisecond)
		log.Printf("Compilation took: %sms\n",
			strconv.FormatFloat(diff, 'f', 3, 32))
	}()

}

func Serve(cmd *cobra.Command, paths []string) {
	start := time.Now()
	defer func() {
		diff := float64(time.Since(start).Nanoseconds()) / float64(time.Millisecond)
		log.Printf("Compilation took: %sms\n",
			strconv.FormatFloat(diff, 'f', 3, 32))
	}()

	pMap, gba := globalRun(paths)
	_ = pMap
	if len(gba.Gen) == 0 {
		log.Fatal("Must pass an image build directory to use HTTP")
	}
	http.Handle("/build/", wt.FileHandler(gba.Gen))
	log.Println("Web server started on :12345")
	http.HandleFunc("/", wt.HTTPHandler(wt.NewContext(gba)))
	err := http.ListenAndServe(":12345", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}

}

// Run is the main entrypoint for the cli.
func Run(cmd *cobra.Command, paths []string) {

	start := time.Now()
	defer func() {
		diff := float64(time.Since(start).Nanoseconds()) / float64(time.Millisecond)
		log.Printf("Compilation took: %sms\n",
			strconv.FormatFloat(diff, 'f', 3, 32))
	}()

	pMap, gba := globalRun(paths)

	if !watch && len(paths) == 0 && len(config) == 0 {

		// Read from stdin
		fmt.Println("Reading from stdin, -h for help")
		out := os.Stdout
		in := os.Stdin

		var pout bytes.Buffer
		ctx := wt.NewContext(gba)
		_, err := wt.StartParser(ctx, in, &pout, wt.NewPartialMap())
		if err != nil {
			log.Println(err)
		}
		err = ctx.Compile(&pout, out)

		if err != nil {
			color.Red(err.Error())
		}
		return
	}
	sassPaths := paths

	bOpts := wt.NewBuild(paths, gba, pMap, multi)

	err := bOpts.Run()
	if err != nil {
		log.Fatal(err)
	}

	if watch {
		w := wt.NewWatcher(&wt.WatchOptions{
			Paths:      sassPaths,
			BArgs:      gba,
			PartialMap: pMap,
		})
		w.Watch()

		fmt.Println("File watcher started use `ctrl+d` to exit")
		in := bufio.NewReader(os.Stdin)
		for {
			_, err := in.ReadString(' ')
			if err != nil {
				if err == io.EOF {
					os.Exit(0)
				}
				fmt.Println("error", err)
			}
		}
	} else {

		// Before shutting down, check that every sprite has been
		// flushed to disk.
		flush := time.Now()
		img := sync.WaitGroup{}
		pMap.RLock()
		// It's not currently possible to wait on Image. This is often
		// to inline images, so it shouldn't be a factor...
		/*for _, s := range gba.Payload.Image().M {
			img.Add(1)
			err := s.Wait()
			img.Done()
			if err != nil {
				log.Printf("error writing image: %s\n", err)
			}
		}*/
		for _, s := range gba.Payload.Sprite().M {
			img.Add(1)
			err := s.Wait()
			img.Done()
			if err != nil {
				log.Printf("error writing sprite: %s\n", err)
			}
		}
		img.Wait()
		_ = flush
		// log.Println("Extra time spent flushing images to disk: ", time.Since(flush))
		pMap.RUnlock()
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gcsproxy_test

import (
	"errors"
	"testing"
	"time"

	"github.com/googlecloudplatform/gcsfuse/gcsproxy"
	"github.com/googlecloudplatform/gcsfuse/lease/mock_lease"
	"github.com/googlecloudplatform/gcsfuse/timeutil"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
)

func TestMutableContent(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Invariant-checking mutable content
////////////////////////////////////////////////////////////////////////

// A wrapper around MutableContent that calls CheckInvariants whenever
// invariants should hold. For catching logic errors early in the test.
type checkingMutableContent struct {
	ctx     context.Context
	wrapped *gcsproxy.MutableContent
}

func (mc *checkingMutableContent) Stat() (gcsproxy.StatResult, error) {
	mc.wrapped.CheckInvariants()
	defer mc.wrapped.CheckInvariants()
	return mc.wrapped.Stat(mc.ctx)
}

func (mc *checkingMutableContent) ReadAt(b []byte, o int64) (int, error) {
	mc.wrapped.CheckInvariants()
	defer mc.wrapped.CheckInvariants()
	return mc.wrapped.ReadAt(mc.ctx, b, o)
}

func (mc *checkingMutableContent) WriteAt(b []byte, o int64) (int, error) {
	mc.wrapped.CheckInvariants()
	defer mc.wrapped.CheckInvariants()
	return mc.wrapped.WriteAt(mc.ctx, b, o)
}

func (mc *checkingMutableContent) Truncate(n int64) error {
	mc.wrapped.CheckInvariants()
	defer mc.wrapped.CheckInvariants()
	return mc.wrapped.Truncate(mc.ctx, n)
}

func (mc *checkingMutableContent) Destroy() {
	mc.wrapped.CheckInvariants()
	defer mc.wrapped.CheckInvariants()
	mc.wrapped.Destroy()
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type mutableContentTest struct {
	ctx context.Context

	initialContent mock_lease.MockReadProxy
	clock          timeutil.SimulatedClock
	mc             checkingMutableContent
}

var _ SetUpInterface = &mutableContentTest{}

func (t *mutableContentTest) SetUp(ti *TestInfo) {
	t.ctx = ti.Ctx

	// Set up the mock initial contents, including a default size.
	t.initialContent = mock_lease.NewMockReadProxy(
		ti.MockController,
		"initialContent")

	const initialContentSize = 11
	ExpectCall(t.initialContent, "Size")().
		WillRepeatedly(Return(initialContentSize))

	// Ignore uninteresting calls.
	ExpectCall(t.initialContent, "CheckInvariants")().
		WillRepeatedly(Return())

	// Set up the clock.
	t.clock.SetTime(time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local))

	// And the mutable content.
	t.mc.ctx = ti.Ctx
	t.mc.wrapped = gcsproxy.NewMutableContent(
		t.initialContent,
		&t.clock)
}

////////////////////////////////////////////////////////////////////////
// Clean state
////////////////////////////////////////////////////////////////////////

type CleanTest struct {
	mutableContentTest
}

func init() { RegisterTestSuite(&CleanTest{}) }

func (t *CleanTest) ReadAt_CallsProxy() {
	AssertTrue(false, "TODO")
}

func (t *CleanTest) ReadAt_ProxyFails() {
	AssertTrue(false, "TODO")
}

func (t *CleanTest) ReadAt_ProxySuceeds() {
	AssertTrue(false, "TODO")
}

func (t *CleanTest) Stat() {
	AssertTrue(false, "TODO")
}

func (t *CleanTest) WriteAt_UpgradeFails() {
	AssertTrue(false, "TODO")
}

func (t *CleanTest) WriteAt_UpgradeSucceeds() {
	AssertTrue(false, "TODO")
}

func (t *CleanTest) Truncate_UpgradeFails() {
	// Lease
	ExpectCall(t.initialContent, "Upgrade")(Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	err := t.mc.Truncate(0)

	ExpectThat(err, Error(HasSubstr("Upgrade")))
	ExpectThat(err, Error(HasSubstr("taco")))
}

func (t *CleanTest) Truncate_UpgradeSucceeds() {
	AssertTrue(false, "TODO")
}

////////////////////////////////////////////////////////////////////////
// Dirty state
////////////////////////////////////////////////////////////////////////

type DirtyTest struct {
	mutableContentTest
}

func init() { RegisterTestSuite(&DirtyTest{}) }

func (t *DirtyTest) ReadAt_CallsLease() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) ReadAt_LeaseFails() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) ReadAt_LeaseSuceeds() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) WriteAt_CallsLease() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) WriteAt_LeaseFails() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) WriteAt_LeaseSucceeds() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) WriteAt_DirtyThreshold() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) Truncate_CallsLease() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) Truncate_LeaseFails() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) Truncate_LeaseSucceeds() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) Truncate_DirtyThreshold() {
	AssertTrue(false, "TODO")
}
<commit_msg>CleanTest.Truncate_UpgradeSucceeds<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gcsproxy_test

import (
	"errors"
	"testing"
	"time"

	"github.com/googlecloudplatform/gcsfuse/gcsproxy"
	"github.com/googlecloudplatform/gcsfuse/lease/mock_lease"
	"github.com/googlecloudplatform/gcsfuse/timeutil"
	. "github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/oglemock"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
)

func TestMutableContent(t *testing.T) { RunTests(t) }

////////////////////////////////////////////////////////////////////////
// Invariant-checking mutable content
////////////////////////////////////////////////////////////////////////

// A wrapper around MutableContent that calls CheckInvariants whenever
// invariants should hold. For catching logic errors early in the test.
type checkingMutableContent struct {
	ctx     context.Context
	wrapped *gcsproxy.MutableContent
}

func (mc *checkingMutableContent) Stat() (gcsproxy.StatResult, error) {
	mc.wrapped.CheckInvariants()
	defer mc.wrapped.CheckInvariants()
	return mc.wrapped.Stat(mc.ctx)
}

func (mc *checkingMutableContent) ReadAt(b []byte, o int64) (int, error) {
	mc.wrapped.CheckInvariants()
	defer mc.wrapped.CheckInvariants()
	return mc.wrapped.ReadAt(mc.ctx, b, o)
}

func (mc *checkingMutableContent) WriteAt(b []byte, o int64) (int, error) {
	mc.wrapped.CheckInvariants()
	defer mc.wrapped.CheckInvariants()
	return mc.wrapped.WriteAt(mc.ctx, b, o)
}

func (mc *checkingMutableContent) Truncate(n int64) error {
	mc.wrapped.CheckInvariants()
	defer mc.wrapped.CheckInvariants()
	return mc.wrapped.Truncate(mc.ctx, n)
}

func (mc *checkingMutableContent) Destroy() {
	mc.wrapped.CheckInvariants()
	defer mc.wrapped.CheckInvariants()
	mc.wrapped.Destroy()
}

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

type mutableContentTest struct {
	ctx context.Context

	initialContent mock_lease.MockReadProxy
	rwl            mock_lease.MockReadWriteLease
	clock          timeutil.SimulatedClock

	mc checkingMutableContent
}

var _ SetUpInterface = &mutableContentTest{}

func (t *mutableContentTest) SetUp(ti *TestInfo) {
	t.ctx = ti.Ctx

	// Set up the mock initial contents, including a default size.
	t.initialContent = mock_lease.NewMockReadProxy(
		ti.MockController,
		"initialContent")

	const initialContentSize = 11
	ExpectCall(t.initialContent, "Size")().
		WillRepeatedly(Return(initialContentSize))

	// Set up a mock read/write lease.
	t.rwl = mock_lease.NewMockReadWriteLease(
		ti.MockController,
		"rwl")

	// Ignore uninteresting calls.
	ExpectCall(t.initialContent, "CheckInvariants")().
		WillRepeatedly(Return())

	// Set up the clock.
	t.clock.SetTime(time.Date(2012, 8, 15, 22, 56, 0, 0, time.Local))

	// And the mutable content.
	t.mc.ctx = ti.Ctx
	t.mc.wrapped = gcsproxy.NewMutableContent(
		t.initialContent,
		&t.clock)
}

////////////////////////////////////////////////////////////////////////
// Clean state
////////////////////////////////////////////////////////////////////////

type CleanTest struct {
	mutableContentTest
}

func init() { RegisterTestSuite(&CleanTest{}) }

func (t *CleanTest) ReadAt_CallsProxy() {
	AssertTrue(false, "TODO")
}

func (t *CleanTest) ReadAt_ProxyFails() {
	AssertTrue(false, "TODO")
}

func (t *CleanTest) ReadAt_ProxySuceeds() {
	AssertTrue(false, "TODO")
}

func (t *CleanTest) Stat() {
	AssertTrue(false, "TODO")
}

func (t *CleanTest) WriteAt_UpgradeFails() {
	AssertTrue(false, "TODO")
}

func (t *CleanTest) WriteAt_UpgradeSucceeds() {
	AssertTrue(false, "TODO")
}

func (t *CleanTest) Truncate_UpgradeFails() {
	// Upgrade
	ExpectCall(t.initialContent, "Upgrade")(Any()).
		WillOnce(Return(nil, errors.New("taco")))

	// Call
	err := t.mc.Truncate(0)

	ExpectThat(err, Error(HasSubstr("Upgrade")))
	ExpectThat(err, Error(HasSubstr("taco")))
}

func (t *CleanTest) Truncate_UpgradeSucceeds() {
	// Upgrade -- succeed.
	ExpectCall(t.initialContent, "Upgrade")(Any()).
		WillOnce(Return(t.rwl, nil))

	// The read/write lease should be called.
	ExpectCall(t.rwl, "Truncate")(17).
		WillOnce(Return(errors.New("")))

	// Call.
	t.mc.Truncate(17)

	// A further call should go right through to the read/write lease again.
	ExpectCall(t.rwl, "Truncate")(19).
		WillOnce(Return(errors.New("")))

	t.mc.Truncate(19)
}

////////////////////////////////////////////////////////////////////////
// Dirty state
////////////////////////////////////////////////////////////////////////

type DirtyTest struct {
	mutableContentTest
}

func init() { RegisterTestSuite(&DirtyTest{}) }

func (t *DirtyTest) ReadAt_CallsLease() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) ReadAt_LeaseFails() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) ReadAt_LeaseSuceeds() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) WriteAt_CallsLease() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) WriteAt_LeaseFails() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) WriteAt_LeaseSucceeds() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) WriteAt_DirtyThreshold() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) Truncate_CallsLease() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) Truncate_LeaseFails() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) Truncate_LeaseSucceeds() {
	AssertTrue(false, "TODO")
}

func (t *DirtyTest) Truncate_DirtyThreshold() {
	AssertTrue(false, "TODO")
}
<|endoftext|>