<commit_before>package gui

import (
	"errors"
	"github.com/mattn/go-gtk/gdk"
	"github.com/mattn/go-gtk/glib"
	"github.com/mattn/go-gtk/gtk"
	"regexp"
	"unsafe"
)

type SettingsFunc func()

type GUI struct {
	width    int
	height   int
	window   *gtk.Window
	notebook *gtk.Notebook
	pages    map[string]*Page
	settingsBox *gtk.VBox
	settingspopup *gtk.Window
	sf SettingsFunc
}

type Page struct {
	textView *gtk.TextView
	nickTV   *gtk.TextView
	entry    *gtk.Entry
}

func NewGUI(title string, width, height int) *GUI {
	glib.ThreadInit(nil)
	gdk.ThreadsInit()
	gdk.ThreadsEnter()
	gtk.Init(nil)

	window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
	window.SetPosition(gtk.WIN_POS_CENTER)
	window.SetTitle(title)
	window.SetIconName("gtk-dialog-info")
	window.Connect("destroy", func(ctx *glib.CallbackContext) {
		println("got destroy!", ctx.Data().(string))
		gtk.MainQuit()
	}, "foo")

	vbox := gtk.NewVBox(false, 0)

	notebook := gtk.NewNotebook()

	vbox.Add(notebook)
	window.Add(vbox)
	window.SetSizeRequest(width, height)

	gui := &GUI{window: window, notebook: notebook, pages: make(map[string]*Page),
		width: width, height: height}

	gui.createMenu(vbox)

	return gui
}

func (gui *GUI) StartMain() {
	gui.window.ShowAll()
	gtk.Main()
	gdk.ThreadsLeave()
}

func (gui *GUI) CreateChannelWindow(context string, sendFunc func()) {
	var page *gtk.Frame

	conversationRegex := "^\\w"
	regex := regexp.MustCompile(conversationRegex)
	if context == "" {
		page = gtk.NewFrame("Server")
		gui.notebook.AppendPage(page, gtk.NewLabel("Server"))
	} else {
		page = gtk.NewFrame(context)
		gui.notebook.AppendPage(page, gtk.NewLabel(context))
	}

	vbox := gtk.NewVBox(false, 1)
	hbox1 := gtk.NewHBox(false, 1)

	var nickTV *gtk.TextView
	var textView *gtk.TextView

	if context != "" && !regex.MatchString(context) {
		swin := gtk.NewScrolledWindow(nil, nil)
		swin.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		swin.SetShadowType(gtk.SHADOW_IN)
		textView = gtk.NewTextView()
		textView.SetEditable(false)
		textView.SetCursorVisible(false)
		textView.SetWrapMode(gtk.WRAP_WORD)
		textView.SetSizeRequest(600, 500)
		swin.Add(textView)
		hbox1.Add(swin)

		swin2 := gtk.NewScrolledWindow(nil, nil)
		swin2.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		swin2.SetShadowType(gtk.SHADOW_IN)
		nickTV = gtk.NewTextView()
		nickTV.SetEditable(false)
		nickTV.SetCursorVisible(false)
		nickTV.SetWrapMode(gtk.WRAP_WORD)
		nickTV.SetSizeRequest(200, 500)
		swin2.Add(nickTV)
		hbox1.Add(swin2)
	} else {
		swin := gtk.NewScrolledWindow(nil, nil)
		swin.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		swin.SetShadowType(gtk.SHADOW_IN)
		textView = gtk.NewTextView()
		textView.SetEditable(false)
		textView.SetCursorVisible(false)
		textView.SetWrapMode(gtk.WRAP_WORD)
		textView.SetSizeRequest(800, 500)
		swin.Add(textView)
		hbox1.Add(swin)
	}

	vbox.Add(hbox1)
	hbox2 := gtk.NewHBox(false, 1)

	// entry
	entry := gtk.NewEntry()
	entry.SetSizeRequest(700, 40)
	entry.Connect("key-press-event", func(ctx *glib.CallbackContext) {
		arg := ctx.Args(0)
		kev := *(**gdk.EventKey)(unsafe.Pointer(&arg))
		if kev.Keyval == gdk.KEY_Return {
			sendFunc()
		}
	})
	hbox2.Add(entry)

	button := gtk.NewButtonWithLabel("Send")
	button.Clicked(sendFunc)
	hbox2.Add(button)

	vbox.Add(hbox2)

	page.Add(vbox)

	newPage := &Page{textView: textView, nickTV: nickTV, entry: entry}
	gui.pages[context] = newPage
	gui.window.ShowAll()
}

func (gui *GUI) DeleteCurrentWindow() {
	gui.notebook.RemovePage(nil, gui.notebook.GetCurrentPage())
	gui.window.ShowAll()
}

func (gui *GUI) DeleteChannelWindow(context string) error {
	len := gui.notebook.GetNPages()
	for i := 0; i < len; i++ {
		frame := gui.notebook.GetNthPage(i)
		if gui.notebook.GetTabLabelText(frame) == context {
			gui.notebook.RemovePage(nil, i)
			gui.window.ShowAll()
			return nil
		}
	}
	return errors.New("Context doesn't exist")
}

func (gui *GUI) WriteToChannel(s, context string) error {
	var endIter gtk.TextIter
	page, ok := gui.pages[context]
	if !ok {
		return errors.New("WriteToChannel: No Such Window!")
	}
	textBuffer := page.textView.GetBuffer()
	textBuffer.GetEndIter(&endIter)
	textBuffer.Insert(&endIter, s+"\n")

	gui.AutoScroll(page.textView, &endIter)
	return nil
}

func (gui *GUI) WriteToCurrentWindow(s string) error {
	var endIter gtk.TextIter
	i := gui.notebook.GetCurrentPage()
	frame := gui.notebook.GetNthPage(i)
	labelText := gui.notebook.GetTabLabelText(frame)

	var context string
	if labelText == "Server" {
		context = ""
	} else {
		context = labelText
	}

	page, ok := gui.pages[context]
	if !ok {
		return errors.New("WriteToChannel: No Such Window!")
	}
	textBuffer := page.textView.GetBuffer()
	textBuffer.GetEndIter(&endIter)
	textBuffer.Insert(&endIter, s+"\n")

	gui.AutoScroll(page.textView, &endIter)
	return nil
}

func (gui *GUI) WriteToNicks(s, context string) error {
	page, ok := gui.pages[context]
	if !ok {
		return errors.New("WriteToChannel: No Such Window!")
	}
	var endIter gtk.TextIter
	textBuffer := page.nickTV.GetBuffer()
	textBuffer.GetEndIter(&endIter)
	textBuffer.Insert(&endIter, s+"\n")
	return nil
}

func (gui *GUI) EmptyNicks(context string) error {
	page, ok := gui.pages[context]
	if !ok {
		return errors.New("WriteToChannel: No Such Window!")
	}
	textBuffer := page.nickTV.GetBuffer()
	textBuffer.SetText("")
	return nil
}

func (gui *GUI) AutoScroll(textview *gtk.TextView, endIter *gtk.TextIter) {
	textview.ScrollToIter(endIter, 0.0, true, 0.0, 0.0)
}

func (gui *GUI) GetEntryText(context string) (string, error) {
	page, ok := gui.pages[context]
	if !ok {
		return "", errors.New("GetEntryBuffer: No such window!")
	}
	return page.entry.GetText(), nil
}

func (gui *GUI) EmptyEntryText(context string) error {
	page, ok := gui.pages[context]
	if !ok {
		return errors.New("EmptyEntryBuffer: No such window!")
	}
	page.entry.SetText("")
	return nil
}

func (gui *GUI) Notebook() *gtk.Notebook {
	return gui.notebook
}

func (gui *GUI) createMenu(vbox *gtk.VBox) {
	menubar := gtk.NewMenuBar()
	vbox.PackStart(menubar, false, false, 0)

	menuitem := gtk.NewMenuItem()
	vbox.PackStart(menuitem, false, false, 0)

	cascademenu := gtk.NewMenuItemWithMnemonic("_File")
	menubar.Append(cascademenu)
	submenu := gtk.NewMenu()
	cascademenu.SetSubmenu(submenu)

	menuitem = gtk.NewMenuItemWithMnemonic("E_xit")
	menuitem.Connect("activate", func() {
		gtk.MainQuit()
	})
	submenu.Append(menuitem)

	cascademenu = gtk.NewMenuItemWithMnemonic("_Tools")
	menubar.Append(cascademenu)
	submenu = gtk.NewMenu()
	cascademenu.SetSubmenu(submenu)

	settings := gtk.NewMenuItemWithMnemonic("_Settings")
	settings.Connect("activate", func() {

		gui.settingspopup = gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
		gui.settingspopup.SetPosition(gtk.WIN_POS_CENTER)
		gui.settingspopup.SetTitle("Settings")
		gui.settingspopup.SetKeepAbove(true)
		gui.settingspopup.Connect("destroy", func(ctx *glib.CallbackContext) {
			println("settings window got destroy!", ctx.Data().(string))
			gui.CloseSettingsWindow()
		}, "foo")
		gui.settingsBox = gtk.NewVBox(false, 0)

		gui.sf() // settings function

		gui.settingspopup.Add(gui.settingsBox)
		gui.settingspopup.ShowAll()
	})

	submenu.Append(settings)

	cascademenu = gtk.NewMenuItemWithMnemonic("_Help")
	menubar.Append(cascademenu)
	submenu = gtk.NewMenu()
	cascademenu.SetSubmenu(submenu)

	menuitem = gtk.NewMenuItemWithMnemonic("_Info")
	menuitem.Connect("activate", func() {
		dialog := gtk.NewMessageDialog(gui.window, gtk.DIALOG_DESTROY_WITH_PARENT,
			gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "%s",
			"Irken works like most IRC-clients. All commands start with forward-slash(/).\n\nFor a list of commands type /help")
		dialog.Run()
		dialog.Destroy()
	})

	submenu.Append(menuitem)

	menuitem = gtk.NewMenuItemWithMnemonic("_About")
	menuitem.Connect("activate", func() {
		dialog := gtk.NewAboutDialog()
		dialog.SetName("About")
		dialog.SetProgramName("Irken")
		dialog.SetAuthors([]string{"André Nyström - github.com/andren32", "Axel Riese - github.com/axelri"})
		dialog.Run()
		dialog.Destroy()
	})

	submenu.Append(menuitem)
}

// AddSetting adds a setting to the setting menu in the form of an entry.
// Takes the setting name and the initialtext in the entry and returns
// a pointer to the entry.
func (gui *GUI) AddSetting(settingsName, initialText string) *gtk.Entry {
	vbox := gtk.NewVBox(false, 0)

	hbox1 := gtk.NewHBox(false, 0)
	label := gtk.NewLabel(settingsName)
	hbox1.Add(label)
	vbox.Add(hbox1)

	hbox2 := gtk.NewHBox(false, 0)
	entry := gtk.NewEntry()
	entry.SetEditable(true)
	entry.SetText(initialText)
	hbox2.Add(entry)
	vbox.Add(hbox2)

	gui.settingsBox.Add(vbox)
	return entry
}

// AddSettingButton adds a button to the settings menu with the label
// of the first argument. Calls on buttonFunc on click
func (gui *GUI) AddSettingButton (text string, buttonFunc func()) {
	vbox := gtk.NewVBox(false, 0)
	button := gtk.NewButtonWithLabel(text)
	button.Clicked(buttonFunc)
	vbox.Add(button)
	gui.settingsBox.Add(vbox)
}

func (gui *GUI) SetSettingsFunc (settingsfunc func()) {
	gui.sf = settingsfunc
}

func (gui *GUI) CloseSettingsWindow() {
	gui.settingspopup.Hide()
}<commit_msg>implement icon<commit_after>package gui

import (
	"errors"
	"github.com/mattn/go-gtk/gdk"
	"github.com/mattn/go-gtk/glib"
	"github.com/mattn/go-gtk/gtk"
	"regexp"
	"unsafe"
)

type SettingsFunc func()

type GUI struct {
	width    int
	height   int
	window   *gtk.Window
	notebook *gtk.Notebook
	pages    map[string]*Page
	settingsBox *gtk.VBox
	settingspopup *gtk.Window
	sf SettingsFunc
}

type Page struct {
	textView *gtk.TextView
	nickTV   *gtk.TextView
	entry    *gtk.Entry
}

func NewGUI(title string, width, height int) *GUI {
	glib.ThreadInit(nil)
	gdk.ThreadsInit()
	gdk.ThreadsEnter()
	gtk.Init(nil)

	window := gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
	window.SetPosition(gtk.WIN_POS_CENTER)
	window.SetTitle(title)
	window.SetIconFromFile("icon/64.png")
	window.Connect("destroy", func(ctx *glib.CallbackContext) {
		println("got destroy!", ctx.Data().(string))
		gtk.MainQuit()
	}, "foo")

	vbox := gtk.NewVBox(false, 0)

	notebook := gtk.NewNotebook()

	vbox.Add(notebook)
	window.Add(vbox)
	window.SetSizeRequest(width, height)

	gui := &GUI{window: window, notebook: notebook, pages: make(map[string]*Page),
		width: width, height: height}

	gui.createMenu(vbox)

	return gui
}

func (gui *GUI) StartMain() {
	gui.window.ShowAll()
	gtk.Main()
	gdk.ThreadsLeave()
}

func (gui *GUI) CreateChannelWindow(context string, sendFunc func()) {
	var page *gtk.Frame

	conversationRegex := "^\\w"
	regex := regexp.MustCompile(conversationRegex)
	if context == "" {
		page = gtk.NewFrame("Server")
		gui.notebook.AppendPage(page, gtk.NewLabel("Server"))
	} else {
		page = gtk.NewFrame(context)
		gui.notebook.AppendPage(page, gtk.NewLabel(context))
	}

	vbox := gtk.NewVBox(false, 1)
	hbox1 := gtk.NewHBox(false, 1)

	var nickTV *gtk.TextView
	var textView *gtk.TextView

	if context != "" && !regex.MatchString(context) {
		swin := gtk.NewScrolledWindow(nil, nil)
		swin.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		swin.SetShadowType(gtk.SHADOW_IN)
		textView = gtk.NewTextView()
		textView.SetEditable(false)
		textView.SetCursorVisible(false)
		textView.SetWrapMode(gtk.WRAP_WORD)
		textView.SetSizeRequest(600, 500)
		swin.Add(textView)
		hbox1.Add(swin)

		swin2 := gtk.NewScrolledWindow(nil, nil)
		swin2.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		swin2.SetShadowType(gtk.SHADOW_IN)
		nickTV = gtk.NewTextView()
		nickTV.SetEditable(false)
		nickTV.SetCursorVisible(false)
		nickTV.SetWrapMode(gtk.WRAP_WORD)
		nickTV.SetSizeRequest(200, 500)
		swin2.Add(nickTV)
		hbox1.Add(swin2)
	} else {
		swin := gtk.NewScrolledWindow(nil, nil)
		swin.SetPolicy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
		swin.SetShadowType(gtk.SHADOW_IN)
		textView = gtk.NewTextView()
		textView.SetEditable(false)
		textView.SetCursorVisible(false)
		textView.SetWrapMode(gtk.WRAP_WORD)
		textView.SetSizeRequest(800, 500)
		swin.Add(textView)
		hbox1.Add(swin)
	}

	vbox.Add(hbox1)
	hbox2 := gtk.NewHBox(false, 1)

	// entry
	entry := gtk.NewEntry()
	entry.SetSizeRequest(700, 40)
	entry.Connect("key-press-event", func(ctx *glib.CallbackContext) {
		arg := ctx.Args(0)
		kev := *(**gdk.EventKey)(unsafe.Pointer(&arg))
		if kev.Keyval == gdk.KEY_Return {
			sendFunc()
		}
	})
	hbox2.Add(entry)

	button := gtk.NewButtonWithLabel("Send")
	button.Clicked(sendFunc)
	hbox2.Add(button)

	vbox.Add(hbox2)

	page.Add(vbox)

	newPage := &Page{textView: textView, nickTV: nickTV, entry: entry}
	gui.pages[context] = newPage
	gui.window.ShowAll()
}

func (gui *GUI) DeleteCurrentWindow() {
	gui.notebook.RemovePage(nil, gui.notebook.GetCurrentPage())
	gui.window.ShowAll()
}

func (gui *GUI) DeleteChannelWindow(context string) error {
	len := gui.notebook.GetNPages()
	for i := 0; i < len; i++ {
		frame := gui.notebook.GetNthPage(i)
		if gui.notebook.GetTabLabelText(frame) == context {
			gui.notebook.RemovePage(nil, i)
			gui.window.ShowAll()
			return nil
		}
	}
	return errors.New("Context doesn't exist")
}

func (gui *GUI) WriteToChannel(s, context string) error {
	var endIter gtk.TextIter
	page, ok := gui.pages[context]
	if !ok {
		return errors.New("WriteToChannel: No Such Window!")
	}
	textBuffer := page.textView.GetBuffer()
	textBuffer.GetEndIter(&endIter)
	textBuffer.Insert(&endIter, s+"\n")

	gui.AutoScroll(page.textView, &endIter)
	return nil
}

func (gui *GUI) WriteToCurrentWindow(s string) error {
	var endIter gtk.TextIter
	i := gui.notebook.GetCurrentPage()
	frame := gui.notebook.GetNthPage(i)
	labelText := gui.notebook.GetTabLabelText(frame)

	var context string
	if labelText == "Server" {
		context = ""
	} else {
		context = labelText
	}

	page, ok := gui.pages[context]
	if !ok {
		return errors.New("WriteToChannel: No Such Window!")
	}
	textBuffer := page.textView.GetBuffer()
	textBuffer.GetEndIter(&endIter)
	textBuffer.Insert(&endIter, s+"\n")

	gui.AutoScroll(page.textView, &endIter)
	return nil
}

func (gui *GUI) WriteToNicks(s, context string) error {
	page, ok := gui.pages[context]
	if !ok {
		return errors.New("WriteToChannel: No Such Window!")
	}
	var endIter gtk.TextIter
	textBuffer := page.nickTV.GetBuffer()
	textBuffer.GetEndIter(&endIter)
	textBuffer.Insert(&endIter, s+"\n")
	return nil
}

func (gui *GUI) EmptyNicks(context string) error {
	page, ok := gui.pages[context]
	if !ok {
		return errors.New("WriteToChannel: No Such Window!")
	}
	textBuffer := page.nickTV.GetBuffer()
	textBuffer.SetText("")
	return nil
}

func (gui *GUI) AutoScroll(textview *gtk.TextView, endIter *gtk.TextIter) {
	textview.ScrollToIter(endIter, 0.0, true, 0.0, 0.0)
}

func (gui *GUI) GetEntryText(context string) (string, error) {
	page, ok := gui.pages[context]
	if !ok {
		return "", errors.New("GetEntryBuffer: No such window!")
	}
	return page.entry.GetText(), nil
}

func (gui *GUI) EmptyEntryText(context string) error {
	page, ok := gui.pages[context]
	if !ok {
		return errors.New("EmptyEntryBuffer: No such window!")
	}
	page.entry.SetText("")
	return nil
}

func (gui *GUI) Notebook() *gtk.Notebook {
	return gui.notebook
}

func (gui *GUI) createMenu(vbox *gtk.VBox) {
	menubar := gtk.NewMenuBar()
	vbox.PackStart(menubar, false, false, 0)

	menuitem := gtk.NewMenuItem()
	vbox.PackStart(menuitem, false, false, 0)

	cascademenu := gtk.NewMenuItemWithMnemonic("_File")
	menubar.Append(cascademenu)
	submenu := gtk.NewMenu()
	cascademenu.SetSubmenu(submenu)

	menuitem = gtk.NewMenuItemWithMnemonic("E_xit")
	menuitem.Connect("activate", func() {
		gtk.MainQuit()
	})
	submenu.Append(menuitem)

	cascademenu = gtk.NewMenuItemWithMnemonic("_Tools")
	menubar.Append(cascademenu)
	submenu = gtk.NewMenu()
	cascademenu.SetSubmenu(submenu)

	settings := gtk.NewMenuItemWithMnemonic("_Settings")
	settings.Connect("activate", func() {

		gui.settingspopup = gtk.NewWindow(gtk.WINDOW_TOPLEVEL)
		gui.settingspopup.SetPosition(gtk.WIN_POS_CENTER)
		gui.settingspopup.SetTitle("Settings")
		gui.settingspopup.SetKeepAbove(true)
		gui.settingspopup.Connect("destroy", func(ctx *glib.CallbackContext) {
			println("settings window got destroy!", ctx.Data().(string))
			gui.CloseSettingsWindow()
		}, "foo")
		gui.settingsBox = gtk.NewVBox(false, 0)

		gui.sf() // settings function

		gui.settingspopup.Add(gui.settingsBox)
		gui.settingspopup.ShowAll()
	})

	submenu.Append(settings)

	cascademenu = gtk.NewMenuItemWithMnemonic("_Help")
	menubar.Append(cascademenu)
	submenu = gtk.NewMenu()
	cascademenu.SetSubmenu(submenu)

	menuitem = gtk.NewMenuItemWithMnemonic("_Info")
	menuitem.Connect("activate", func() {
		dialog := gtk.NewMessageDialog(gui.window, gtk.DIALOG_DESTROY_WITH_PARENT,
			gtk.MESSAGE_INFO, gtk.BUTTONS_OK, "%s",
			"Irken works like most IRC-clients. All commands start with forward-slash(/).\n\nFor a list of commands type /help")
		dialog.Run()
		dialog.Destroy()
	})

	submenu.Append(menuitem)

	menuitem = gtk.NewMenuItemWithMnemonic("_About")
	menuitem.Connect("activate", func() {
		dialog := gtk.NewAboutDialog()
		dialog.SetName("About")
		dialog.SetProgramName("Irken")
		dialog.SetAuthors([]string{"André Nyström - github.com/andren32", "Axel Riese - github.com/axelri"})
		dialog.Run()
		dialog.Destroy()
	})

	submenu.Append(menuitem)
}

// AddSetting adds a setting to the setting menu in the form of an entry.
// Takes the setting name and the initialtext in the entry and returns
// a pointer to the entry.
func (gui *GUI) AddSetting(settingsName, initialText string) *gtk.Entry {
	vbox := gtk.NewVBox(false, 0)

	hbox1 := gtk.NewHBox(false, 0)
	label := gtk.NewLabel(settingsName)
	hbox1.Add(label)
	vbox.Add(hbox1)

	hbox2 := gtk.NewHBox(false, 0)
	entry := gtk.NewEntry()
	entry.SetEditable(true)
	entry.SetText(initialText)
	hbox2.Add(entry)
	vbox.Add(hbox2)

	gui.settingsBox.Add(vbox)
	return entry
}

// AddSettingButton adds a button to the settings menu with the label
// of the first argument. Calls on buttonFunc on click
func (gui *GUI) AddSettingButton (text string, buttonFunc func()) {
	vbox := gtk.NewVBox(false, 0)
	button := gtk.NewButtonWithLabel(text)
	button.Clicked(buttonFunc)
	vbox.Add(button)
	gui.settingsBox.Add(vbox)
}

func (gui *GUI) SetSettingsFunc (settingsfunc func()) {
	gui.sf = settingsfunc
}

func (gui *GUI) CloseSettingsWindow() {
	gui.settingspopup.Hide()
}<|endoftext|>


<commit_before>// A quick and easy way to setup a RESTful JSON API
//
// Go-Json-Rest is a thin layer on top of net/http that helps building RESTful JSON APIs easily.
// It provides fast URL routing using a Trie based implementation, and helpers to deal
// with JSON requests and responses. It is not a high-level REST framework that transparently maps
// HTTP requests to procedure calls, on the opposite, you constantly have access to the underlying
// net/http objects.
//
// Example:
//
//      package main
//
//      import (
//              "github.com/ant0ine/go-json-rest"
//              "net/http"
//      )
//
//      type User struct {
//              Id   string
//              Name string
//      }
//
//      func GetUser(w rest.ResponseWriter, req *rest.Request) {
//              user := User{
//                      Id:   req.PathParam("id"),
//                      Name: "Antoine",
//              }
//              w.WriteJson(&user)
//      }
//
//      func main() {
//              handler := rest.ResourceHandler{}
//              handler.SetRoutes(
//                      rest.Route{"GET", "/users/:id", GetUser},
//              )
//              http.ListenAndServe(":8080", &handler)
//      }
//
//
// Note about the URL routing: Instead of using the usual
// "evaluate all the routes and return the first regexp that matches" strategy,
// it uses a Trie data structure to perform the routing. This is more efficient,
// and scales better for a large number of routes.
// It supports the :param and *splat placeholders in the route strings.
//
package rest

import (
	"fmt"
	"log"
	"mime"
	"net/http"
	"reflect"
	"runtime/debug"
	"strings"
)

// Signature of a handler method in the context of go-json-rest.
type HandlerFunc func(ResponseWriter, *Request)

// Implement the http.Handler interface and act as a router for the defined Routes.
// The defaults are intended to be developemnt friendly, for production you may want
// to turn on gzip and disable the JSON indentation.
type ResourceHandler struct {
	internalRouter *router
	statusService  *statusService

	// If true, and if the client accepts the Gzip encoding, the response payloads
	// will be compressed using gzip, and the corresponding response header will set.
	EnableGzip bool

	// If true, the JSON payload will be written in one line with no space.
	DisableJsonIndent bool

	// If true, the status service will be enabled. Various stats and status will
	// then be available at GET /.status in a JSON format.
	EnableStatusService bool

	// If true, when a "panic" happens, the error string and the stack trace will be
	// printed in the 500 response body.
	EnableResponseStackTrace bool

	// If true, the record that is logged for each response will be printed as JSON
	// in the log. Convenient for log parsing.
	EnableLogAsJson bool

	// If true, the handler does NOT check the request Content-Type. Otherwise, it
	// must be set to 'application/json' if the content is non-null.
	// Note: If a charset parameter exists, it MUST be UTF-8
	EnableRelaxedContentType bool

	// Optional middleware that can be used to wrap the REST endpoints.
	// It can be used for instance to manage CORS or authentication.
	// (see the CORS example in go-json-rest-example)
	// This is run pre REST routing, request.PathParams is not set yet.
	PreRoutingMiddleware func(handler HandlerFunc) HandlerFunc

	// Custom logger, defaults to log.New(os.Stderr, "", log.LstdFlags)
	Logger *log.Logger
}

// Used with SetRoutes.
type Route struct {

	// Any http method. It will be used as uppercase to avoid common mistakes.
	HttpMethod string

	// A string like "/resource/:id.json".
	// Placeholders supported are:
	// :param that matches any char to the first '/' or '.'
	// *splat that matches everything to the end of the string
	// (placeholder names should be unique per PathExp)
	PathExp string

	// Code that will be executed when this route is taken.
	Func HandlerFunc
}

// Create a Route that points to an object method. It can be convenient to point to an object method instead
// of a function, this helper makes it easy by passing the object instance and the method name as parameters.
func RouteObjectMethod(httpMethod string, pathExp string, objectInstance interface{}, objectMethod string) Route {

	value := reflect.ValueOf(objectInstance)
	funcValue := value.MethodByName(objectMethod)
	if funcValue.IsValid() == false {
		panic(fmt.Sprintf(
			"Cannot find the object method %s on %s",
			objectMethod,
			value,
		))
	}
	routeFunc := func(w ResponseWriter, r *Request) {
		funcValue.Call([]reflect.Value{
			reflect.ValueOf(w),
			reflect.ValueOf(r),
		})
	}

	return Route{
		HttpMethod: httpMethod,
		PathExp:    pathExp,
		Func:       routeFunc,
	}
}

// Define the Routes. The order the Routes matters,
// if a request matches multiple Routes, the first one will be used.
func (rh *ResourceHandler) SetRoutes(routes ...Route) error {

	rh.internalRouter = &router{
		routes: routes,
	}

	// add the status route as the last route.
	if rh.EnableStatusService == true {
		rh.statusService = newStatusService()
		rh.internalRouter.routes = append(rh.internalRouter.routes, rh.statusService.getRoute())
	}

	// start the router
	err := rh.internalRouter.start()
	if err != nil {
		return err
	}

	return nil
}

// Middleware that handles the transition between http and rest objects.
func (rh *ResourceHandler) adapter(handler HandlerFunc) http.HandlerFunc {
	return func(origWriter http.ResponseWriter, origRequest *http.Request) {

		// catch user code's panic, and convert to http response
		// (this does not use the JSON error response on purpose)
		defer func() {
			if reco := recover(); reco != nil {
				trace := debug.Stack()

				// log the trace
				rh.Logger.Printf("%s\n%s", reco, trace)

				// write error response
				message := "Internal Server Error"
				if rh.EnableResponseStackTrace {
					message = fmt.Sprintf("%s\n\n%s", reco, trace)
				}
				http.Error(origWriter, message, http.StatusInternalServerError)
			}
		}()

		request := Request{
			origRequest,
			nil,
			map[string]interface{}{},
		}

		isIndented := !rh.DisableJsonIndent

		writer := responseWriter{
			origWriter,
			isIndented,
			false,
		}

		handler(&writer, &request)
	}
}

// Handle the REST routing and run the user code.
func (rh *ResourceHandler) app() HandlerFunc {
	return func(writer ResponseWriter, request *Request) {

		// check the Content-Type
		mediatype, params, _ := mime.ParseMediaType(request.Header.Get("Content-Type"))
		charset, ok := params["charset"]
		if !ok {
			charset = "UTF-8"
		}

		if rh.EnableRelaxedContentType == false &&
			request.ContentLength > 0 && // per net/http doc, means that the length is known and non-null
			!(mediatype == "application/json" && strings.ToUpper(charset) == "UTF-8") {

			Error(writer,
				"Bad Content-Type or charset, expected 'application/json'",
				http.StatusUnsupportedMediaType,
			)
			return
		}

		// find the route
		route, params, pathMatched := rh.internalRouter.findRouteFromURL(request.Method, request.URL)
		if route == nil {

			if pathMatched {
				// no route found, but path was matched: 405 Method Not Allowed
				Error(writer, "Method not allowed", http.StatusMethodNotAllowed)
				return
			}

			// no route found, the path was not matched: 404 Not Found
			NotFound(writer, request)
			return
		}

		// a route was found, set the PathParams
		request.PathParams = params

		// run the user code
		handler := route.Func
		handler(writer, request)
	}
}

// This makes ResourceHandler implement the http.Handler interface.
// You probably don't want to use it directly.
func (rh *ResourceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

	if rh.PreRoutingMiddleware == nil {
		rh.PreRoutingMiddleware = func(handler HandlerFunc) HandlerFunc {
			return func(writer ResponseWriter, request *Request) {
				handler(writer, request)
			}
		}
	}

	handlerFunc := rh.adapter(
		rh.logWrapper(
			rh.gzipWrapper(
				rh.statusWrapper(
					rh.timerWrapper(
						rh.recorderWrapper(
							rh.PreRoutingMiddleware(
								rh.app(),
							),
						),
					),
				),
			),
		),
	)

	handlerFunc(w, r)
}
<commit_msg>Assemble the middleware at init time.<commit_after>// A quick and easy way to setup a RESTful JSON API
//
// Go-Json-Rest is a thin layer on top of net/http that helps building RESTful JSON APIs easily.
// It provides fast URL routing using a Trie based implementation, and helpers to deal
// with JSON requests and responses. It is not a high-level REST framework that transparently maps
// HTTP requests to procedure calls, on the opposite, you constantly have access to the underlying
// net/http objects.
//
// Example:
//
//      package main
//
//      import (
//              "github.com/ant0ine/go-json-rest"
//              "net/http"
//      )
//
//      type User struct {
//              Id   string
//              Name string
//      }
//
//      func GetUser(w rest.ResponseWriter, req *rest.Request) {
//              user := User{
//                      Id:   req.PathParam("id"),
//                      Name: "Antoine",
//              }
//              w.WriteJson(&user)
//      }
//
//      func main() {
//              handler := rest.ResourceHandler{}
//              handler.SetRoutes(
//                      rest.Route{"GET", "/users/:id", GetUser},
//              )
//              http.ListenAndServe(":8080", &handler)
//      }
//
//
// Note about the URL routing: Instead of using the usual
// "evaluate all the routes and return the first regexp that matches" strategy,
// it uses a Trie data structure to perform the routing. This is more efficient,
// and scales better for a large number of routes.
// It supports the :param and *splat placeholders in the route strings.
//
package rest

import (
	"fmt"
	"log"
	"mime"
	"net/http"
	"reflect"
	"runtime/debug"
	"strings"
)

// Signature of a handler method in the context of go-json-rest.
type HandlerFunc func(ResponseWriter, *Request)

// Implement the http.Handler interface and act as a router for the defined Routes.
// The defaults are intended to be developemnt friendly, for production you may want
// to turn on gzip and disable the JSON indentation.
type ResourceHandler struct {
	internalRouter *router
	statusService  *statusService
	handlerFunc    http.HandlerFunc

	// If true, and if the client accepts the Gzip encoding, the response payloads
	// will be compressed using gzip, and the corresponding response header will set.
	EnableGzip bool

	// If true, the JSON payload will be written in one line with no space.
	DisableJsonIndent bool

	// If true, the status service will be enabled. Various stats and status will
	// then be available at GET /.status in a JSON format.
	EnableStatusService bool

	// If true, when a "panic" happens, the error string and the stack trace will be
	// printed in the 500 response body.
	EnableResponseStackTrace bool

	// If true, the record that is logged for each response will be printed as JSON
	// in the log. Convenient for log parsing.
	EnableLogAsJson bool

	// If true, the handler does NOT check the request Content-Type. Otherwise, it
	// must be set to 'application/json' if the content is non-null.
	// Note: If a charset parameter exists, it MUST be UTF-8
	EnableRelaxedContentType bool

	// Optional middleware that can be used to wrap the REST endpoints.
	// It can be used for instance to manage CORS or authentication.
	// (see the CORS example in go-json-rest-example)
	// This is run pre REST routing, request.PathParams is not set yet.
	PreRoutingMiddleware func(handler HandlerFunc) HandlerFunc

	// Custom logger, defaults to log.New(os.Stderr, "", log.LstdFlags)
	Logger *log.Logger
}

// Used with SetRoutes.
type Route struct {

	// Any http method. It will be used as uppercase to avoid common mistakes.
	HttpMethod string

	// A string like "/resource/:id.json".
	// Placeholders supported are:
	// :param that matches any char to the first '/' or '.'
	// *splat that matches everything to the end of the string
	// (placeholder names should be unique per PathExp)
	PathExp string

	// Code that will be executed when this route is taken.
	Func HandlerFunc
}

// Create a Route that points to an object method. It can be convenient to point to an object method instead
// of a function, this helper makes it easy by passing the object instance and the method name as parameters.
func RouteObjectMethod(httpMethod string, pathExp string, objectInstance interface{}, objectMethod string) Route {

	value := reflect.ValueOf(objectInstance)
	funcValue := value.MethodByName(objectMethod)
	if funcValue.IsValid() == false {
		panic(fmt.Sprintf(
			"Cannot find the object method %s on %s",
			objectMethod,
			value,
		))
	}
	routeFunc := func(w ResponseWriter, r *Request) {
		funcValue.Call([]reflect.Value{
			reflect.ValueOf(w),
			reflect.ValueOf(r),
		})
	}

	return Route{
		HttpMethod: httpMethod,
		PathExp:    pathExp,
		Func:       routeFunc,
	}
}

// Define the Routes. The order the Routes matters,
// if a request matches multiple Routes, the first one will be used.
func (rh *ResourceHandler) SetRoutes(routes ...Route) error {

	rh.internalRouter = &router{
		routes: routes,
	}

	// add the status route as the last route.
	if rh.EnableStatusService == true {
		rh.statusService = newStatusService()
		rh.internalRouter.routes = append(rh.internalRouter.routes, rh.statusService.getRoute())
	}

	// start the router
	err := rh.internalRouter.start()
	if err != nil {
		return err
	}

	// assemble all the middlewares
	if rh.PreRoutingMiddleware == nil {
		rh.PreRoutingMiddleware = func(handler HandlerFunc) HandlerFunc {
			return func(writer ResponseWriter, request *Request) {
				handler(writer, request)
			}
		}
	}
	rh.handlerFunc = rh.adapter(
		rh.logWrapper(
			rh.gzipWrapper(
				rh.statusWrapper(
					rh.timerWrapper(
						rh.recorderWrapper(
							rh.PreRoutingMiddleware(
								rh.app(),
							),
						),
					),
				),
			),
		),
	)

	return nil
}

// Middleware that handles the transition between http and rest objects.
func (rh *ResourceHandler) adapter(handler HandlerFunc) http.HandlerFunc {
	return func(origWriter http.ResponseWriter, origRequest *http.Request) {

		// catch user code's panic, and convert to http response
		// (this does not use the JSON error response on purpose)
		defer func() {
			if reco := recover(); reco != nil {
				trace := debug.Stack()

				// log the trace
				rh.Logger.Printf("%s\n%s", reco, trace)

				// write error response
				message := "Internal Server Error"
				if rh.EnableResponseStackTrace {
					message = fmt.Sprintf("%s\n\n%s", reco, trace)
				}
				http.Error(origWriter, message, http.StatusInternalServerError)
			}
		}()

		// instantiate the rest objects
		request := Request{
			origRequest,
			nil,
			map[string]interface{}{},
		}

		isIndented := !rh.DisableJsonIndent

		writer := responseWriter{
			origWriter,
			isIndented,
			false,
		}

		// call the wrapped handler
		handler(&writer, &request)
	}
}

// Handle the REST routing and run the user code.
func (rh *ResourceHandler) app() HandlerFunc {
	return func(writer ResponseWriter, request *Request) {

		// check the Content-Type
		mediatype, params, _ := mime.ParseMediaType(request.Header.Get("Content-Type"))
		charset, ok := params["charset"]
		if !ok {
			charset = "UTF-8"
		}

		if rh.EnableRelaxedContentType == false &&
			request.ContentLength > 0 && // per net/http doc, means that the length is known and non-null
			!(mediatype == "application/json" && strings.ToUpper(charset) == "UTF-8") {

			Error(writer,
				"Bad Content-Type or charset, expected 'application/json'",
				http.StatusUnsupportedMediaType,
			)
			return
		}

		// find the route
		route, params, pathMatched := rh.internalRouter.findRouteFromURL(request.Method, request.URL)
		if route == nil {

			if pathMatched {
				// no route found, but path was matched: 405 Method Not Allowed
				Error(writer, "Method not allowed", http.StatusMethodNotAllowed)
				return
			}

			// no route found, the path was not matched: 404 Not Found
			NotFound(writer, request)
			return
		}

		// a route was found, set the PathParams
		request.PathParams = params

		// run the user code
		handler := route.Func
		handler(writer, request)
	}
}

// This makes ResourceHandler implement the http.Handler interface.
// You probably don't want to use it directly.
func (rh *ResourceHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	rh.handlerFunc(w, r)
}
<|endoftext|>


<commit_before>package db

import (
	"fmt"
)

var (
	// ErrAlreadyDefined hapens when the given entry already exists,
	// for example a container.
	ErrAlreadyDefined = fmt.Errorf("The instance/snapshot already exists")

	// ErrNoSuchObject is in the case of joins (and probably other) queries,
	// we don't get back sql.ErrNoRows when no rows are returned, even though we do
	// on selects without joins. Instead, you can use this error to
	// propagate up and generate proper 404s to the client when something
	// isn't found so we don't abuse sql.ErrNoRows any more than we
	// already do.
	ErrNoSuchObject = fmt.Errorf("No such object")
)
<commit_msg>lxd/db/errors: Updates ErrAlreadyDefined text to be generic<commit_after>package db

import (
	"fmt"
)

var (
	// ErrAlreadyDefined hapens when the given entry already exists,
	// for example a container.
	ErrAlreadyDefined = fmt.Errorf("The record already exists")

	// ErrNoSuchObject is in the case of joins (and probably other) queries,
	// we don't get back sql.ErrNoRows when no rows are returned, even though we do
	// on selects without joins. Instead, you can use this error to
	// propagate up and generate proper 404s to the client when something
	// isn't found so we don't abuse sql.ErrNoRows any more than we
	// already do.
	ErrNoSuchObject = fmt.Errorf("No such object")
)
<|endoftext|>


<commit_before>package main

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

	"github.com/ajg/form"
)

// FieldsHandler is a standard handler that pulls the first folder of the
//  response, and lists that topic or author. If there is none, it falls back to
//  listing all topics or authors with the fallback template.
type FieldsHandler struct {
	c ServerSection
	i Index
}

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

	if len(fields) < 2 || fields[1] == "" {
		// to do if a field was not given
		FallbackSearchResponse(h.i, w, h.c.FallbackTemplate)
		return
	}
	// to be done if a field was given - might actually have to be 1 idk
	results, err := ListAllField(h.i, h.c.Default, fields[0], 100, 1)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

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

// FuzzySearchHandler is a normal search format - it should provide a point and
//  click interface to allow searching.
type FuzzyHandler struct {
	c ServerSection
	i Index
}

func (h FuzzyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		// to do if a field was not given
		FallbackSearchResponse(h.i, w, h.c.FallbackTemplate)
		return
	}

	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var values FuzzySearchValues
	err = form.DecodeValues(&values, r.Form)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	results, err := FuzzySearch(h.i, values)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

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

// QuerySearchHAndler is a handler that uses a custom search format to do
// custom queries.
type QueryHandler struct {
	c ServerSection
	i Index
}

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

	if r.Method != http.MethodPost {
		// to do if a field was not given
		FallbackSearchResponse(h.i, w, h.c.FallbackTemplate)
		return
	}

	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	err = form.DecodeValues(&values, r.Form)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if values.s == "" {
		// to do if a field was not given
		FallbackSearchResponse(h.i, w, h.c.FallbackTemplate)
		return
	}

	results, err := QuerySearch(h.i, values.s, values.page, values.pageSize)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

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

// Page is a standard data structure used to render markdown pages.
type Page struct {
	Title    string
	ToC      template.HTML
	Body     template.HTML
	Topics   []string
	Keywords []string
	Authors  []string
}

// Markdown is an http.Handler that renders a markdown file and serves it back.
//  Author and Topic tags before the first major title are parsed and displayed.
//  It is possible to restrict access to a page based on topic tag.
type Markdown struct {
	c ServerSection
}

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

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

	pdata := new(PageMetadata)
	filePath := filepath.Join(h.c.Path, r.URL.Path)
	err := pdata.LoadPage(filePath)
	if err != nil {
		log.Printf("request [ %s ] points bad file target [ %s ] sent to server",
			r.URL.Path, filePath)
		http.Error(w, "Page not Found", http.StatusNotFound)
		return
	}

	if pdata.MatchedTopic(h.c.Restricted) {
		log.Printf("request [ %s ] was a page [ %s ] with a restricted tag",
			r.URL.Path, filePath)
		http.Error(w, "Page not Found", http.StatusNotFound)
		//http.Error(w, err.Error(), http.StatusForbidden)
		return
	}

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

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

// RawFile is a http.Handler that serves a raw file back, restricting by file
//  extension if necessary and adding approipate mime-types.
type RawFile struct {
	c ServerSection
}

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

	for _, restricted := range h.c.Restricted {
		if path.Ext(r.URL.Path) == restricted {
			log.Printf("request %s was has a disallowed extension %s",
				r.URL.Path, restricted)
			http.Error(w, "Request not allowed", 403)
			return
		}
	}

	f, err := os.Open(filepath.Join(h.c.Path, r.URL.Path))
	if err != nil {
		http.Error(w, err.Error(), http.StatusForbidden)
		log.Print(err)
		return
	}

	switch path.Ext(r.URL.Path) {
	case "js":
		w.Header().Set("Content-Type", "text/javascript")
	case "css":
		w.Header().Set("Content-Type", "text/css")
	case "gif":
		w.Header().Set("Content-Type", "image/gif")
	case "png":
		w.Header().Set("Content-Type", "image/png")
	case "jpg":
		w.Header().Set("Content-Type", "image/jpeg")
	case "jpeg":
		w.Header().Set("Content-Type", "image/jpeg")
	}

	_, err = io.Copy(w, f)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		log.Print(err)
	}
}
<commit_msg>fixed the field handler, i was overthinking it<commit_after>package main

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

	"github.com/ajg/form"
)

// FieldsHandler is a standard handler that pulls the first folder of the
//  response, and lists that topic or author. If there is none, it falls back to
//  listing all topics or authors with the fallback template.
type FieldsHandler struct {
	c ServerSection
	i Index
}

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

	// if len(fields) < 2 || fields[1] == "" {
	if r.URL.Path == "" {
		// to do if a field was not given
		switch h.c.FallbackTemplate {
		case "":
			FallbackSearchResponse(h.i, w, h.c.Template)
		default:
			FallbackSearchResponse(h.i, w, h.c.FallbackTemplate)
		}
		return
	}

	// to be done if a field was given - might actually have to be 1 idk
	// results, err := ListAllField(h.i, h.c.Default, fields[0], 100, 1)
	results, err := ListAllField(h.i, h.c.Default, r.URL.Path, 100, 1)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	log.Printf("got back %#v", results)

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

// FuzzySearch is a normal search format - it should provide a point and click interface to allow searching.
type FuzzyHandler struct {
	c ServerSection
	i Index
}

func (h FuzzyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodPost {
		// to do if a field was not given
		FallbackSearchResponse(h.i, w, h.c.FallbackTemplate)
		return
	}

	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	var values FuzzySearchValues
	err = form.DecodeValues(&values, r.Form)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	results, err := FuzzySearch(h.i, values)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

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

// QuerySearchHAndler is a handler that uses a custom search format to do
// custom queries.
type QueryHandler struct {
	c ServerSection
	i Index
}

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

	if r.Method != http.MethodPost {
		// to do if a field was not given
		FallbackSearchResponse(h.i, w, h.c.FallbackTemplate)
		return
	}

	err := r.ParseForm()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	err = form.DecodeValues(&values, r.Form)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if values.s == "" {
		// to do if a field was not given
		FallbackSearchResponse(h.i, w, h.c.FallbackTemplate)
		return
	}

	results, err := QuerySearch(h.i, values.s, values.page, values.pageSize)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

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

// Page is a standard data structure used to render markdown pages.
type Page struct {
	Title    string
	ToC      template.HTML
	Body     template.HTML
	Topics   []string
	Keywords []string
	Authors  []string
}

// Markdown is an http.Handler that renders a markdown file and serves it back.
//  Author and Topic tags before the first major title are parsed and displayed.
//  It is possible to restrict access to a page based on topic tag.
type Markdown struct {
	c ServerSection
}

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

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

	pdata := new(PageMetadata)
	filePath := filepath.Join(h.c.Path, r.URL.Path)
	err := pdata.LoadPage(filePath)
	if err != nil {
		log.Printf("request [ %s ] points bad file target [ %s ] sent to server",
			r.URL.Path, filePath)
		http.Error(w, "Page not Found", http.StatusNotFound)
		return
	}

	if pdata.MatchedTopic(h.c.Restricted) {
		log.Printf("request [ %s ] was a page [ %s ] with a restricted tag",
			r.URL.Path, filePath)
		http.Error(w, "Page not Found", http.StatusNotFound)
		//http.Error(w, err.Error(), http.StatusForbidden)
		return
	}

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

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

// RawFile is a http.Handler that serves a raw file back, restricting by file
//  extension if necessary and adding approipate mime-types.
type RawFile struct {
	c ServerSection
}

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

	for _, restricted := range h.c.Restricted {
		if path.Ext(r.URL.Path) == restricted {
			log.Printf("request %s was has a disallowed extension %s",
				r.URL.Path, restricted)
			http.Error(w, "Request not allowed", 403)
			return
		}
	}

	f, err := os.Open(filepath.Join(h.c.Path, r.URL.Path))
	if err != nil {
		http.Error(w, err.Error(), http.StatusForbidden)
		log.Print(err)
		return
	}

	switch path.Ext(r.URL.Path) {
	case "js":
		w.Header().Set("Content-Type", "text/javascript")
	case "css":
		w.Header().Set("Content-Type", "text/css")
	case "gif":
		w.Header().Set("Content-Type", "image/gif")
	case "png":
		w.Header().Set("Content-Type", "image/png")
	case "jpg":
		w.Header().Set("Content-Type", "image/jpeg")
	case "jpeg":
		w.Header().Set("Content-Type", "image/jpeg")
	}

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


<commit_before>/*
Package log provides a handler that logs each request/response (time, duration, status, method, path).
The log formatting can either be couloured or not.

Make sure to include this handler above any other handler to get accurate logs.
*/
package log

import (
	"log"
	"net/http"
	"time"
)

// The list of Unix terminal color codes used for coloured formatting.
const (
	cReset    = "\033[0m"
	cDim      = "\033[2m"
	cRed      = "\033[31m"
	cGreen    = "\033[32m"
	cBlue     = "\033[34m"
	cCyan     = "\033[36m"
	cWhite    = "\033[97m"
	cBgRed    = "\033[41m"
	cBgGreen  = "\033[42m"
	cBgYellow = "\033[43m"
	cBgCyan   = "\033[46m"
)

// A Handler provides a clever gzip compressing handler.
type Handler struct {
	Options *Options
	Next    http.Handler
}

// Options provides the handler options.
type Options struct {
	Color bool // Colors triggers a coloured formatting compatible with Unix-based terminals.
}

// Handle returns a Handler wrapping another http.Handler.
func Handle(h http.Handler, o *Options) *Handler {
	return &Handler{o, h}
}

// HandleFunc returns a Handler wrapping an http.HandlerFunc.
func HandleFunc(f http.HandlerFunc, o *Options) *Handler {
	return Handle(f, o)
}

func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	lw := &logWriter{
		ResponseWriter: w,
		status:         http.StatusOK,
	}
	// Keep originals in case the response will be altered.
	method := r.Method
	path := r.URL.Path

	defer func() {
		if h.Options == nil || !h.Options.Color {
			log.Printf("%s %s ▶ %d @ %s", method, path, lw.status, time.Since(start))
			return
		}

		var cBgStatus string
		switch {
		case lw.status >= 200 && lw.status <= 299:
			cBgStatus += cBgGreen
		case lw.status >= 300 && lw.status <= 399:
			cBgStatus += cBgCyan
		case lw.status >= 400 && lw.status <= 499:
			cBgStatus += cBgYellow
		default:
			cBgStatus += cBgRed
		}

		var cMethod string
		switch method {
		case "GET":
			cMethod += cGreen
		case "POST":
			cMethod += cCyan
		case "PUT", "PATCH":
			cMethod += cBlue
		case "DELETE":
			cMethod += cRed
		}

		log.Printf("%s  %s%13s%s   %s%s %3d %s   %s%s%s  %s%s%s", cReset, cDim, time.Since(start), cReset, cWhite, cBgStatus, lw.status, cReset, cMethod, method, cReset, cDim, path, cReset)
	}()

	h.Next.ServeHTTP(lw, r)
}

// logWriter catches the status code from the downstream repsonse writing.
type logWriter struct {
	http.ResponseWriter
	used   bool
	status int
}

// WriteHeader catches and seals the status code from the downstream WriteHeader call.
func (lw *logWriter) WriteHeader(status int) {
	if lw.used {
		return
	}
	lw.used = true
	lw.status = status
	lw.ResponseWriter.WriteHeader(status)
}

// Write catches the downstream Write call to seal the status code.
func (lw *logWriter) Write(b []byte) (int, error) {
	lw.used = true
	return lw.ResponseWriter.Write(b)
}
<commit_msg>Hide handler structure<commit_after>/*
Package log provides a handler that logs each request/response (time, duration, status, method, path).
The log formatting can either be couloured or not.

Make sure to include this handler above any other handler to get accurate logs.
*/
package log

import (
	"log"
	"net/http"
	"time"
)

// The list of Unix terminal color codes used for coloured formatting.
const (
	cReset    = "\033[0m"
	cDim      = "\033[2m"
	cRed      = "\033[31m"
	cGreen    = "\033[32m"
	cBlue     = "\033[34m"
	cCyan     = "\033[36m"
	cWhite    = "\033[97m"
	cBgRed    = "\033[41m"
	cBgGreen  = "\033[42m"
	cBgYellow = "\033[43m"
	cBgCyan   = "\033[46m"
)

// A handler provides a clever gzip compressing handler.
type handler struct {
	options *Options
	next    http.Handler
}

// Options provides the handler options.
type Options struct {
	Color bool // Colors triggers a coloured formatting compatible with Unix-based terminals.
}

// Handle returns a Handler wrapping another http.Handler.
func Handle(h http.Handler, o *Options) http.Handler {
	return &handler{o, h}
}

// HandleFunc returns a Handler wrapping an http.HandlerFunc.
func HandleFunc(f http.HandlerFunc, o *Options) http.Handler {
	return Handle(f, o)
}

func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	lw := &logWriter{
		ResponseWriter: w,
		status:         http.StatusOK,
	}
	// Keep originals in case the response will be altered.
	method := r.Method
	path := r.URL.Path

	defer func() {
		if h.options == nil || !h.options.Color {
			log.Printf("%s %s ▶ %d @ %s", method, path, lw.status, time.Since(start))
			return
		}

		var cBgStatus string
		switch {
		case lw.status >= 200 && lw.status <= 299:
			cBgStatus += cBgGreen
		case lw.status >= 300 && lw.status <= 399:
			cBgStatus += cBgCyan
		case lw.status >= 400 && lw.status <= 499:
			cBgStatus += cBgYellow
		default:
			cBgStatus += cBgRed
		}

		var cMethod string
		switch method {
		case "GET":
			cMethod += cGreen
		case "POST":
			cMethod += cCyan
		case "PUT", "PATCH":
			cMethod += cBlue
		case "DELETE":
			cMethod += cRed
		}

		log.Printf("%s  %s%13s%s   %s%s %3d %s   %s%s%s  %s%s%s", cReset, cDim, time.Since(start), cReset, cWhite, cBgStatus, lw.status, cReset, cMethod, method, cReset, cDim, path, cReset)
	}()

	h.next.ServeHTTP(lw, r)
}

// logWriter catches the status code from the downstream repsonse writing.
type logWriter struct {
	http.ResponseWriter
	used   bool
	status int
}

// WriteHeader catches and seals the status code from the downstream WriteHeader call.
func (lw *logWriter) WriteHeader(status int) {
	if lw.used {
		return
	}
	lw.used = true
	lw.status = status
	lw.ResponseWriter.WriteHeader(status)
}

// Write catches the downstream Write call to seal the status code.
func (lw *logWriter) Write(b []byte) (int, error) {
	lw.used = true
	return lw.ResponseWriter.Write(b)
}
<|endoftext|>


<commit_before>package main

import (
	"sync"
	"time"

	"github.com/miekg/dns"
)

type Question struct {
	qname  string
	qtype  string
	qclass string
}

const (
	notIPQuery = 0
	_IP4Query  = 4
	_IP6Query  = 6
)

func (q *Question) String() string {
	return q.qname + " " + q.qclass + " " + q.qtype
}

type GODNSHandler struct {
	resolver *Resolver
	cache    Cache
	hosts    Hosts
	mu       *sync.Mutex
}

func NewHandler() *GODNSHandler {

	var (
		cacheConfig CacheSettings
		resolver    *Resolver
		cache       Cache
	)

	resolver = NewResolver(settings.ResolvConfig)

	cacheConfig = settings.Cache
	switch cacheConfig.Backend {
	case "memory":
		cache = &MemoryCache{
			Backend:  make(map[string]Mesg),
			Expire:   time.Duration(cacheConfig.Expire) * time.Second,
			Maxcount: cacheConfig.Maxcount,
			mu:       new(sync.RWMutex),
		}
	case "redis":
		// cache = &MemoryCache{
		// 	Backend:    make(map[string]*dns.Msg),
		//  Expire:   time.Duration(cacheConfig.Expire) * time.Second,
		// 	Serializer: new(JsonSerializer),
		// 	Maxcount:   cacheConfig.Maxcount,
		// }
		panic("Redis cache backend not implement yet")
	default:
		logger.Printf("Invalid cache backend %s", cacheConfig.Backend)
		panic("Invalid cache backend")
	}

	hosts := NewHosts(settings.Hosts, settings.Redis)

	return &GODNSHandler{resolver, cache, hosts, new(sync.Mutex)}
}

func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
	q := req.Question[0]
	Q := Question{UnFqdn(q.Name), dns.TypeToString[q.Qtype], dns.ClassToString[q.Qclass]}

	Debug("Question:　%s", Q.String())

	IPQuery := h.isIPQuery(q)

	// Query hosts
	if settings.Hosts.Enable && IPQuery > 0 {
		if ip, ok := h.hosts.Get(Q.qname, IPQuery); ok {
			m := new(dns.Msg)
			m.SetReply(req)

			switch IPQuery {
			case _IP4Query:
				rr_header := dns.RR_Header{
					Name:   q.Name,
					Rrtype: dns.TypeA,
					Class:  dns.ClassINET,
					Ttl:    settings.Hosts.TTL,
				}
				a := &dns.A{rr_header, ip}
				m.Answer = append(m.Answer, a)
			case _IP6Query:
				rr_header := dns.RR_Header{
					Name:   q.Name,
					Rrtype: dns.TypeAAAA,
					Class:  dns.ClassINET,
					Ttl:    settings.Hosts.TTL,
				}
				aaaa := &dns.AAAA{rr_header, ip}
				m.Answer = append(m.Answer, aaaa)
			}

			w.WriteMsg(m)
			Debug("%s found in hosts", Q.qname)
			return
		}

	}

	// Only query cache when qtype == 'A' , qclass == 'IN'
	key := KeyGen(Q)
	if IPQuery > 0 {
		mesg, err := h.cache.Get(key)
		if err != nil {
			Debug("%s didn't hit cache: %s", Q.String(), err)
		} else {
			Debug("%s hit cache", Q.String())
			h.mu.Lock()
			mesg.Id = req.Id
			w.WriteMsg(mesg)
			h.mu.Unlock()
			return
		}

	}

	mesg, err := h.resolver.Lookup(Net, req)

	if err != nil {
		Debug("%s", err)
		dns.HandleFailed(w, req)
		return
	}

	w.WriteMsg(mesg)

	if IPQuery > 0 {
		err = h.cache.Set(key, mesg)

		if err != nil {
			Debug("Set %s cache failed: %s", Q.String(), err.Error())
		}

		Debug("Insert %s into cache", Q.String())
	}

}

func (h *GODNSHandler) DoTCP(w dns.ResponseWriter, req *dns.Msg) {
	h.do("tcp", w, req)
}

func (h *GODNSHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) {
	h.do("udp", w, req)
}

func (h *GODNSHandler) isIPQuery(q dns.Question) int {
	if q.Qclass != dns.ClassINET {
		return notIPQuery
	}

	switch q.Qtype {
	case dns.TypeA:
		return _IP4Query
	case dns.TypeAAAA:
		return _IP6Query
	default:
		return notIPQuery
	}
}

func UnFqdn(s string) string {
	if dns.IsFqdn(s) {
		return s[:len(s)-1]
	}
	return s
}
<commit_msg>only cache successful lookups<commit_after>package main

import (
	"sync"
	"time"

	"github.com/miekg/dns"
)

type Question struct {
	qname  string
	qtype  string
	qclass string
}

const (
	notIPQuery = 0
	_IP4Query  = 4
	_IP6Query  = 6
)

func (q *Question) String() string {
	return q.qname + " " + q.qclass + " " + q.qtype
}

type GODNSHandler struct {
	resolver *Resolver
	cache    Cache
	hosts    Hosts
	mu       *sync.Mutex
}

func NewHandler() *GODNSHandler {

	var (
		cacheConfig CacheSettings
		resolver    *Resolver
		cache       Cache
	)

	resolver = NewResolver(settings.ResolvConfig)

	cacheConfig = settings.Cache
	switch cacheConfig.Backend {
	case "memory":
		cache = &MemoryCache{
			Backend:  make(map[string]Mesg),
			Expire:   time.Duration(cacheConfig.Expire) * time.Second,
			Maxcount: cacheConfig.Maxcount,
			mu:       new(sync.RWMutex),
		}
	case "redis":
		// cache = &MemoryCache{
		// 	Backend:    make(map[string]*dns.Msg),
		//  Expire:   time.Duration(cacheConfig.Expire) * time.Second,
		// 	Serializer: new(JsonSerializer),
		// 	Maxcount:   cacheConfig.Maxcount,
		// }
		panic("Redis cache backend not implement yet")
	default:
		logger.Printf("Invalid cache backend %s", cacheConfig.Backend)
		panic("Invalid cache backend")
	}

	hosts := NewHosts(settings.Hosts, settings.Redis)

	return &GODNSHandler{resolver, cache, hosts, new(sync.Mutex)}
}

func (h *GODNSHandler) do(Net string, w dns.ResponseWriter, req *dns.Msg) {
	q := req.Question[0]
	Q := Question{UnFqdn(q.Name), dns.TypeToString[q.Qtype], dns.ClassToString[q.Qclass]}

	Debug("Question:　%s", Q.String())

	IPQuery := h.isIPQuery(q)

	// Query hosts
	if settings.Hosts.Enable && IPQuery > 0 {
		if ip, ok := h.hosts.Get(Q.qname, IPQuery); ok {
			m := new(dns.Msg)
			m.SetReply(req)

			switch IPQuery {
			case _IP4Query:
				rr_header := dns.RR_Header{
					Name:   q.Name,
					Rrtype: dns.TypeA,
					Class:  dns.ClassINET,
					Ttl:    settings.Hosts.TTL,
				}
				a := &dns.A{rr_header, ip}
				m.Answer = append(m.Answer, a)
			case _IP6Query:
				rr_header := dns.RR_Header{
					Name:   q.Name,
					Rrtype: dns.TypeAAAA,
					Class:  dns.ClassINET,
					Ttl:    settings.Hosts.TTL,
				}
				aaaa := &dns.AAAA{rr_header, ip}
				m.Answer = append(m.Answer, aaaa)
			}

			w.WriteMsg(m)
			Debug("%s found in hosts", Q.qname)
			return
		}

	}

	// Only query cache when qtype == 'A' , qclass == 'IN'
	key := KeyGen(Q)
	if IPQuery > 0 {
		mesg, err := h.cache.Get(key)
		if err != nil {
			Debug("%s didn't hit cache: %s", Q.String(), err)
		} else {
			Debug("%s hit cache", Q.String())
			h.mu.Lock()
			mesg.Id = req.Id
			w.WriteMsg(mesg)
			h.mu.Unlock()
			return
		}

	}

	mesg, err := h.resolver.Lookup(Net, req)

	if err != nil {
		Debug("%s", err)
		dns.HandleFailed(w, req)
		return
	}

	w.WriteMsg(mesg)

	if IPQuery > 0 && len(mesg.Answer) > 0 {
		err = h.cache.Set(key, mesg)

		if err != nil {
			Debug("Set %s cache failed: %s", Q.String(), err.Error())
		}

		Debug("Insert %s into cache", Q.String())
	}

}

func (h *GODNSHandler) DoTCP(w dns.ResponseWriter, req *dns.Msg) {
	h.do("tcp", w, req)
}

func (h *GODNSHandler) DoUDP(w dns.ResponseWriter, req *dns.Msg) {
	h.do("udp", w, req)
}

func (h *GODNSHandler) isIPQuery(q dns.Question) int {
	if q.Qclass != dns.ClassINET {
		return notIPQuery
	}

	switch q.Qtype {
	case dns.TypeA:
		return _IP4Query
	case dns.TypeAAAA:
		return _IP6Query
	default:
		return notIPQuery
	}
}

func UnFqdn(s string) string {
	if dns.IsFqdn(s) {
		return s[:len(s)-1]
	}
	return s
}
<|endoftext|>


<commit_before>package gash

// a collection of various hash function implementations.

// a simple djb2 implementation
func Djb2(s string) int {
    hash := 5381
    for _, c := range s {
        hash += (hash * 33) + int(c)
    }

    return hash
}

// sdbm
func Sdbm(s string) int {
    hash := 0
    for _, c := range s {
        hash += (hash * 65599) + int(c)
    }

    return hash
}

// hash by sum of character values
func SumHash(s string) int {
    hash := 0
    for _, c := range s {
        hash += int(c)
    }

    return hash
}

// hash by product of character values
func ProductHash(s string) int {
    hash := 1
    for _, c := range s {
        hash *= int(c)
    }

    return hash
}<commit_msg>Define HashFn type<commit_after>package gash

// a collection of various hash function implementations.

type HashFn func(string) int

// a simple djb2 implementation
func Djb2(s string) int {
    hash := 5381
    for _, c := range s {
        hash += (hash * 33) + int(c)
    }

    return hash
}

// sdbm
func Sdbm(s string) int {
    hash := 0
    for _, c := range s {
        hash += (hash * 65599) + int(c)
    }

    return hash
}

// hash by sum of character values
func SumHash(s string) int {
    hash := 0
    for _, c := range s {
        hash += int(c)
    }

    return hash
}

// hash by product of character values
func ProductHash(s string) int {
    hash := 1
    for _, c := range s {
        hash *= int(c)
    }

    return hash
}<|endoftext|>


<commit_before>package api2go

import (
	"database/sql"
	"errors"
	"reflect"
	"strconv"
	"strings"
	"unicode"

	"github.com/gedex/inflector"
)

// commonInitialisms, taken from
// https://github.com/golang/lint/blob/3d26dc39376c307203d3a221bada26816b3073cf/lint.go#L482
var commonInitialisms = map[string]bool{
	"API":   true,
	"ASCII": true,
	"CPU":   true,
	"CSS":   true,
	"DNS":   true,
	"EOF":   true,
	"GUID":  true,
	"HTML":  true,
	"HTTP":  true,
	"HTTPS": true,
	"ID":    true,
	"IP":    true,
	"JSON":  true,
	"LHS":   true,
	"QPS":   true,
	"RAM":   true,
	"RHS":   true,
	"RPC":   true,
	"SLA":   true,
	"SMTP":  true,
	"SSH":   true,
	"TLS":   true,
	"TTL":   true,
	"UI":    true,
	"UID":   true,
	"UUID":  true,
	"URI":   true,
	"URL":   true,
	"UTF8":  true,
	"VM":    true,
	"XML":   true,
}

// dejsonify returns a go struct key name from a JSON key name
func dejsonify(s string) string {
	if s == "" {
		return ""
	}
	if upper := strings.ToUpper(s); commonInitialisms[upper] {
		return upper
	}
	rs := []rune(s)
	rs[0] = unicode.ToUpper(rs[0])
	return string(rs)
}

// jsonify returns a JSON formatted key name from a go struct field name
func jsonify(s string) string {
	if s == "" {
		return ""
	}
	if commonInitialisms[s] {
		return strings.ToLower(s)
	}
	rs := []rune(s)
	rs[0] = unicode.ToLower(rs[0])
	return string(rs)
}

// pluralize a noun
func pluralize(word string) string {
	return inflector.Pluralize(word)
}

// singularize a noun
func singularize(word string) string {
	return inflector.Singularize(word)
}

func idFromObject(obj reflect.Value) (string, error) {
	if obj.Kind() == reflect.Ptr {
		obj = obj.Elem()
	}
	idField := obj.FieldByName("ID")
	if !idField.IsValid() {
		return "", errors.New("expected 'ID' field in struct")
	}
	return idFromValue(idField)
}

func idFromValue(v reflect.Value) (string, error) {
	kind := v.Kind()
	if kind == reflect.Struct {
		if sv, err := extractIDFromSqlStruct(v); err == nil {
			v = sv
			kind = v.Kind()
		} else {
			return "", err
		}
	}

	switch kind {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return strconv.FormatInt(v.Int(), 10), nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return strconv.FormatUint(v.Uint(), 10), nil
	case reflect.String:
		return v.String(), nil
	default:
		return "", errors.New("need int or string as type of ID")
	}
}

func extractIDFromSqlStruct(v reflect.Value) (reflect.Value, error) {
	i := v.Interface()
	switch value := i.(type) {
	case sql.NullInt64:
		if value.Valid {
			return reflect.ValueOf(value.Int64), nil
		}
	case sql.NullFloat64:
		if value.Valid {
			return reflect.ValueOf(value.Float64), nil
		}
	case sql.NullString:
		if value.Valid {
			return reflect.ValueOf(value.String), nil
		}
	default:
		return reflect.ValueOf(""), errors.New("invalid type, allowed sql/database types are sql.NullInt64, sql.NullFloat64, sql.NullString")
	}

	return reflect.ValueOf(""), nil
}

func setObjectID(obj reflect.Value, idInterface interface{}) error {
	field := obj.FieldByName("ID")
	if !field.IsValid() {
		return errors.New("expected struct to have field 'ID'")
	}
	return setIDValue(field, idInterface)
}

func setIDValue(val reflect.Value, idInterface interface{}) error {
	id, ok := idInterface.(string)
	if !ok {
		return errors.New("expected ID to be string in json")
	}
	switch val.Kind() {
	case reflect.String:
		val.Set(reflect.ValueOf(id))

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		intID, err := strconv.ParseInt(id, 10, 64)
		if err != nil {
			return err
		}
		val.SetInt(intID)

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		intID, err := strconv.ParseUint(id, 10, 64)
		if err != nil {
			return err
		}
		val.SetUint(intID)

	default:
		return errors.New("expected ID to be of type int or string in struct")
	}

	return nil
}
<commit_msg>Add overflow checks in int conversion<commit_after>package api2go

import (
	"database/sql"
	"errors"
	"reflect"
	"strconv"
	"strings"
	"unicode"

	"github.com/gedex/inflector"
)

// commonInitialisms, taken from
// https://github.com/golang/lint/blob/3d26dc39376c307203d3a221bada26816b3073cf/lint.go#L482
var commonInitialisms = map[string]bool{
	"API":   true,
	"ASCII": true,
	"CPU":   true,
	"CSS":   true,
	"DNS":   true,
	"EOF":   true,
	"GUID":  true,
	"HTML":  true,
	"HTTP":  true,
	"HTTPS": true,
	"ID":    true,
	"IP":    true,
	"JSON":  true,
	"LHS":   true,
	"QPS":   true,
	"RAM":   true,
	"RHS":   true,
	"RPC":   true,
	"SLA":   true,
	"SMTP":  true,
	"SSH":   true,
	"TLS":   true,
	"TTL":   true,
	"UI":    true,
	"UID":   true,
	"UUID":  true,
	"URI":   true,
	"URL":   true,
	"UTF8":  true,
	"VM":    true,
	"XML":   true,
}

// dejsonify returns a go struct key name from a JSON key name
func dejsonify(s string) string {
	if s == "" {
		return ""
	}
	if upper := strings.ToUpper(s); commonInitialisms[upper] {
		return upper
	}
	rs := []rune(s)
	rs[0] = unicode.ToUpper(rs[0])
	return string(rs)
}

// jsonify returns a JSON formatted key name from a go struct field name
func jsonify(s string) string {
	if s == "" {
		return ""
	}
	if commonInitialisms[s] {
		return strings.ToLower(s)
	}
	rs := []rune(s)
	rs[0] = unicode.ToLower(rs[0])
	return string(rs)
}

// pluralize a noun
func pluralize(word string) string {
	return inflector.Pluralize(word)
}

// singularize a noun
func singularize(word string) string {
	return inflector.Singularize(word)
}

func idFromObject(obj reflect.Value) (string, error) {
	if obj.Kind() == reflect.Ptr {
		obj = obj.Elem()
	}
	idField := obj.FieldByName("ID")
	if !idField.IsValid() {
		return "", errors.New("expected 'ID' field in struct")
	}
	return idFromValue(idField)
}

func idFromValue(v reflect.Value) (string, error) {
	kind := v.Kind()
	if kind == reflect.Struct {
		if sv, err := extractIDFromSqlStruct(v); err == nil {
			v = sv
			kind = v.Kind()
		} else {
			return "", err
		}
	}

	switch kind {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return strconv.FormatInt(v.Int(), 10), nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return strconv.FormatUint(v.Uint(), 10), nil
	case reflect.String:
		return v.String(), nil
	default:
		return "", errors.New("need int or string as type of ID")
	}
}

func extractIDFromSqlStruct(v reflect.Value) (reflect.Value, error) {
	i := v.Interface()
	switch value := i.(type) {
	case sql.NullInt64:
		if value.Valid {
			return reflect.ValueOf(value.Int64), nil
		}
	case sql.NullFloat64:
		if value.Valid {
			return reflect.ValueOf(value.Float64), nil
		}
	case sql.NullString:
		if value.Valid {
			return reflect.ValueOf(value.String), nil
		}
	default:
		return reflect.ValueOf(""), errors.New("invalid type, allowed sql/database types are sql.NullInt64, sql.NullFloat64, sql.NullString")
	}

	return reflect.ValueOf(""), nil
}

func setObjectID(obj reflect.Value, idInterface interface{}) error {
	field := obj.FieldByName("ID")
	if !field.IsValid() {
		return errors.New("expected struct to have field 'ID'")
	}
	return setIDValue(field, idInterface)
}

func setIDValue(val reflect.Value, idInterface interface{}) error {
	id, ok := idInterface.(string)
	if !ok {
		return errors.New("expected ID to be string in json")
	}
	switch val.Kind() {
	case reflect.String:
		val.Set(reflect.ValueOf(id))

	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		intID, err := strconv.ParseInt(id, 10, 64)
		if err != nil {
			return err
		}

		if val.OverflowInt(intID) {
			return errors.New("Value to high for given type")
		}

		val.SetInt(intID)

	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		intID, err := strconv.ParseUint(id, 10, 64)
		if err != nil {
			return err
		}

		if val.OverflowUint(intID) {
			return errors.New("Value to high for given type")
		}

		val.SetUint(intID)

	default:
		return errors.New("expected ID to be of type int or string in struct")
	}

	return nil
}
<|endoftext|>


<commit_before>package gorma

import (
	"bytes"
	"fmt"
	"path/filepath"
	"sort"
	"strings"
	"unicode"

	"github.com/qor/inflection"
	"github.com/raphael/goa/design"
	"github.com/raphael/goa/goagen/codegen"
)

// TitleCase converts a string to Title case
func TitleCase(s string) string {
	return strings.Title(s)
}

// CamelToSnake converts a given string to snake case
func CamelToSnake(s string) string {
	var result string
	var words []string
	var lastPos int
	rs := []rune(s)

	for i := 0; i < len(rs); i++ {
		if i > 0 && unicode.IsUpper(rs[i]) {
			if initialism := startsWithInitialism(s[lastPos:]); initialism != "" {
				words = append(words, initialism)

				i += len(initialism) - 1
				lastPos = i
				continue
			}

			words = append(words, s[lastPos:i])
			lastPos = i
		}
	}

	// append the last word
	if s[lastPos:] != "" {
		words = append(words, s[lastPos:])
	}

	for k, word := range words {
		if k > 0 {
			result += "_"
		}

		result += strings.ToLower(word)
	}

	return result
}

// ModelDir is the path to the directory where the schema controller is generated.
func ModelDir() string {
	return filepath.Join(codegen.OutputDir, "models")
}

// DeModel remove the word "Model" from the string
func DeModel(s string) string {
	return strings.Replace(s, "Model", "", -1)
}

// Lower returns the string in lowercase
func Lower(s string) string {
	return strings.ToLower(s)
}

// Upper returns the string in upper case
func Upper(s string) string {
	return strings.ToUpper(s)
}

// StorageDefinition creates the storage interface that will be used
// in place of a concrete type for testability
func StorageDefinition(res *design.UserTypeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#many2many"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			pieces := strings.Split(child, ":")
			associations = associations + "List" + pieces[0] + "(context.Context, int) []" + pieces[1] + "\n"
			associations = associations + "Add" + pieces[1] + "(context.Context, int, int) (error)\n"
			associations = associations + "Delete" + pieces[1] + "(context.Context, int, int) error \n"
		}
	}
	return associations
}

// IncludeForeignKey adds foreign key relations to the struct being
// generated
func IncludeForeignKey(res *design.AttributeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#belongsto"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			associations = associations + child + "ID int\n"

		}
	}
	return associations
}

// Plural returns the plural version of a word
func Plural(s string) string {
	return inflection.Plural(s)
}

// IncludeChildren adds the fields to a struct represented
// in a has-many relationship
func IncludeChildren(res *design.AttributeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#hasmany"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			associations = associations + inflection.Plural(child) + " []" + child + "\n"
		}
	}
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#hasone"]; ok {
		children := strings.Split(assoc, ",")
		for _, child := range children {
			associations = associations + child + " " + child + "\n"
			associations = associations + child + "ID " + "*sql.NullInt64\n"
		}
	}
	return associations
}

// IncludeMany2Many returns the appropriate struct tags
// for a m2m relationship in gorm
func IncludeMany2Many(res *design.AttributeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#many2many"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			pieces := strings.Split(child, ":")
			associations = associations + pieces[0] + "\t []" + pieces[1] + "\t" + "`gorm:\"many2many:" + pieces[2] + ";\"`\n"
		}
	}
	return associations
}

// Authboss returns the tags required to implement authboss storage
// currently experimental and quite unfinished
func Authboss(res *design.AttributeDefinition) string {
	if _, ok := res.Metadata["github.com/bketelsen/gorma#authboss"]; ok {
		fields := `	// Auth
	Password string

	// OAuth2
	Oauth2Uid      string
	Oauth2Provider string
	Oauth2Token    string
	Oauth2Refresh  string
	Oauth2Expiry   time.Time

	// Confirm
	ConfirmToken string
	Confirmed    bool

	// Lock
	AttemptNumber int64
	AttemptTime   time.Time
	Locked        time.Time

	// Recover
	RecoverToken       string
	RecoverTokenExpiry time.Time
	`
		return fields
	}
	return ""
}

// Split splits a string by separater `sep`
func Split(s string, sep string) []string {
	return strings.Split(s, sep)
}

// Timestamps returns the timestamp fields if "skipts" isn't set
func TimeStamps(res *design.AttributeDefinition) string {
	var ts string
	if _, ok := res.Metadata["github.com/bketelsen/gorma#skipts"]; ok {
		ts = ""
	} else {
		ts = "CreatedAt time.Time\nUpdatedAt time.Time\nDeletedAt *time.Time\n"
	}
	return ts
}

// MakeModelDef is the main function to create a struct definition
func MakeModelDef(res *design.UserTypeDefinition) string {
	var buffer bytes.Buffer
	def := res.Definition()
	t := def.Type
	switch actual := t.(type) {
	case design.Object:
		actual = setupIDAttribute(actual, res)

		buffer.WriteString("struct {\n")
		keys := make([]string, len(actual))
		i := 0
		for n := range actual {
			keys[i] = n
			i++
		}
		sort.Strings(keys)
		for _, name := range keys {
			codegen.WriteTabs(&buffer, 1)
			typedef := codegen.GoTypeDef(actual[name], 1, true, true)
			fname := codegen.Goify(name, true)
			var tags string
			var omit string
			var gorm, sql string
			if !def.IsRequired(name) {
				omit = ",omitempty"
			}
			if val, ok := actual[name].Metadata["github.com/bketelsen/gorma#gormtag"]; ok {
				gorm = fmt.Sprintf(" gorm:\"%s\"", val)
			}
			if val, ok := actual[name].Metadata["github.com/bketelsen/gorma#sqltag"]; ok {
				sql = fmt.Sprintf(" sql:\"%s\"", val)
			}
			tags = fmt.Sprintf(" `json:\"%s%s\"%s%s`", name, omit, gorm, sql)
			desc := actual[name].Description
			if desc != "" {
				desc = fmt.Sprintf("// %s\n", desc)
			}
			buffer.WriteString(fmt.Sprintf("%s%s %s%s\n", desc, fname, typedef, tags))
		}

		for k, v := range genfuncs {
			s := v(def)
			if s != "" {
				buffer.WriteString(fmt.Sprintf("%s%s", k, s))
			}
		}

		codegen.WriteTabs(&buffer, 0)
		buffer.WriteString("}")
		return buffer.String()
	default:
		panic("gorma bug: unexpected data structure type")
	}
}

// setupIDAttribute adds or updates the ID field of a user type definition.
func setupIDAttribute(obj design.Object, res *design.UserTypeDefinition) design.Object {
	idName := ""
	foundID := false
	for n := range obj {
		if n == "ID" || n == "Id" || n == "id" {
			idName = n
			foundID = true
		}
	}

	var gorm string
	if val, ok := res.Metadata["github.com/bketelsen/gorma#gormpktag"]; ok {
		gorm = val
	} else {
		gorm = "primary_key"
	}

	if foundID {
		// If the user already defined gormtag, leave it alone.
		if _, ok := obj[idName].Metadata["github.com/bketelsen/gorma#gormtag"]; !ok {
			obj[idName].Metadata["github.com/bketelsen/gorma#gormtag"] = gorm
		}
	} else {
		obj["ID"] = &design.AttributeDefinition{
			Type:     design.Integer,
			Metadata: design.MetadataDefinition{"github.com/bketelsen/gorma#gormtag": gorm},
		}
	}

	return obj
}

// Is c an ASCII lower-case letter?
func isASCIILower(c byte) bool {
	return 'a' <= c && c <= 'z'
}

// Is c an ASCII digit?
func isASCIIDigit(c byte) bool {
	return '0' <= c && c <= '9'
}

func unexport(s string) string {
	return strings.ToLower(s[0:1]) + s[1:]
}

// startsWithInitialism returns the initialism if the given string begins with it
func startsWithInitialism(s string) string {
	var initialism string
	// the longest initialism is 5 char, the shortest 2
	for i := 1; i <= 5; i++ {
		if len(s) > i-1 && commonInitialisms[s[:i]] {
			initialism = s[:i]
		}
	}
	return initialism
}

var genfuncs = map[string]func(*design.AttributeDefinition) string{
	"\n// Timestamps\n":   TimeStamps,
	"\n// Many2Many\n":    IncludeMany2Many,
	"\n// Foreign Keys\n": IncludeForeignKey,
	"\n// Children\n":     IncludeChildren,
	"\n// Authboss\n\n":   Authboss,
}

// commonInitialisms, taken from
// https://github.com/golang/lint/blob/3d26dc39376c307203d3a221bada26816b3073cf/lint.go#L482
var commonInitialisms = map[string]bool{
	"API":   true,
	"ASCII": true,
	"CPU":   true,
	"CSS":   true,
	"DNS":   true,
	"EOF":   true,
	"GUID":  true,
	"HTML":  true,
	"HTTP":  true,
	"HTTPS": true,
	"ID":    true,
	"IP":    true,
	"JSON":  true,
	"LHS":   true,
	"QPS":   true,
	"RAM":   true,
	"RHS":   true,
	"RPC":   true,
	"SLA":   true,
	"SMTP":  true,
	"SSH":   true,
	"TLS":   true,
	"TTL":   true,
	"UI":    true,
	"UID":   true,
	"UUID":  true,
	"URI":   true,
	"URL":   true,
	"UTF8":  true,
	"VM":    true,
	"XML":   true,
}
<commit_msg>Enforce lowercase key in setupIDAttribute<commit_after>package gorma

import (
	"bytes"
	"fmt"
	"path/filepath"
	"sort"
	"strings"
	"unicode"

	"github.com/qor/inflection"
	"github.com/raphael/goa/design"
	"github.com/raphael/goa/goagen/codegen"
)

// TitleCase converts a string to Title case.
func TitleCase(s string) string {
	return strings.Title(s)
}

// CamelToSnake converts a given string to snake case.
func CamelToSnake(s string) string {
	var result string
	var words []string
	var lastPos int
	rs := []rune(s)

	for i := 0; i < len(rs); i++ {
		if i > 0 && unicode.IsUpper(rs[i]) {
			if initialism := startsWithInitialism(s[lastPos:]); initialism != "" {
				words = append(words, initialism)

				i += len(initialism) - 1
				lastPos = i
				continue
			}

			words = append(words, s[lastPos:i])
			lastPos = i
		}
	}

	// append the last word
	if s[lastPos:] != "" {
		words = append(words, s[lastPos:])
	}

	for k, word := range words {
		if k > 0 {
			result += "_"
		}

		result += strings.ToLower(word)
	}

	return result
}

// ModelDir is the path to the directory where the schema controller is generated.
func ModelDir() string {
	return filepath.Join(codegen.OutputDir, "models")
}

// DeModel remove the word "Model" from the string.
func DeModel(s string) string {
	return strings.Replace(s, "Model", "", -1)
}

// Lower returns the string in lowercase.
func Lower(s string) string {
	return strings.ToLower(s)
}

// Upper returns the string in upper case.
func Upper(s string) string {
	return strings.ToUpper(s)
}

// StorageDefinition creates the storage interface that will be used
// in place of a concrete type for testability.
func StorageDefinition(res *design.UserTypeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#many2many"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			pieces := strings.Split(child, ":")
			associations = associations + "List" + pieces[0] + "(context.Context, int) []" + pieces[1] + "\n"
			associations = associations + "Add" + pieces[1] + "(context.Context, int, int) (error)\n"
			associations = associations + "Delete" + pieces[1] + "(context.Context, int, int) error \n"
		}
	}
	return associations
}

// IncludeForeignKey adds foreign key relations to the struct being
// generated.
func IncludeForeignKey(res *design.AttributeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#belongsto"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			associations = associations + child + "ID int\n"

		}
	}
	return associations
}

// Plural returns the plural version of a word.
func Plural(s string) string {
	return inflection.Plural(s)
}

// IncludeChildren adds the fields to a struct represented
// in a has-many relationship.
func IncludeChildren(res *design.AttributeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#hasmany"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			associations = associations + inflection.Plural(child) + " []" + child + "\n"
		}
	}
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#hasone"]; ok {
		children := strings.Split(assoc, ",")
		for _, child := range children {
			associations = associations + child + " " + child + "\n"
			associations = associations + child + "ID " + "*sql.NullInt64\n"
		}
	}
	return associations
}

// IncludeMany2Many returns the appropriate struct tags
// for a m2m relationship in gorm.
func IncludeMany2Many(res *design.AttributeDefinition) string {
	var associations string
	if assoc, ok := res.Metadata["github.com/bketelsen/gorma#many2many"]; ok {
		children := strings.Split(assoc, ",")

		for _, child := range children {
			pieces := strings.Split(child, ":")
			associations = associations + pieces[0] + "\t []" + pieces[1] + "\t" + "`gorm:\"many2many:" + pieces[2] + ";\"`\n"
		}
	}
	return associations
}

// Authboss returns the tags required to implement authboss storage.
// Currently experimental and quite unfinished.
func Authboss(res *design.AttributeDefinition) string {
	if _, ok := res.Metadata["github.com/bketelsen/gorma#authboss"]; ok {
		fields := `	// Auth
	Password string

	// OAuth2
	Oauth2Uid      string
	Oauth2Provider string
	Oauth2Token    string
	Oauth2Refresh  string
	Oauth2Expiry   time.Time

	// Confirm
	ConfirmToken string
	Confirmed    bool

	// Lock
	AttemptNumber int64
	AttemptTime   time.Time
	Locked        time.Time

	// Recover
	RecoverToken       string
	RecoverTokenExpiry time.Time
	`
		return fields
	}
	return ""
}

// Split splits a string by separater `sep`.
func Split(s string, sep string) []string {
	return strings.Split(s, sep)
}

// TimeStamps returns the timestamp fields if "skipts" isn't set.
func TimeStamps(res *design.AttributeDefinition) string {
	var ts string
	if _, ok := res.Metadata["github.com/bketelsen/gorma#skipts"]; ok {
		ts = ""
	} else {
		ts = "CreatedAt time.Time\nUpdatedAt time.Time\nDeletedAt *time.Time\n"
	}
	return ts
}

// MakeModelDef is the main function to create a struct definition.
func MakeModelDef(res *design.UserTypeDefinition) string {
	var buffer bytes.Buffer
	def := res.Definition()
	t := def.Type
	switch actual := t.(type) {
	case design.Object:
		actual = setupIDAttribute(actual, res)

		buffer.WriteString("struct {\n")
		keys := make([]string, len(actual))
		i := 0
		for n := range actual {
			keys[i] = n
			i++
		}
		sort.Strings(keys)
		for _, name := range keys {
			codegen.WriteTabs(&buffer, 1)
			typedef := codegen.GoTypeDef(actual[name], 1, true, true)
			fname := codegen.Goify(name, true)
			var tags string
			var omit string
			var gorm, sql string
			if !def.IsRequired(name) {
				omit = ",omitempty"
			}
			if val, ok := actual[name].Metadata["github.com/bketelsen/gorma#gormtag"]; ok {
				gorm = fmt.Sprintf(" gorm:\"%s\"", val)
			}
			if val, ok := actual[name].Metadata["github.com/bketelsen/gorma#sqltag"]; ok {
				sql = fmt.Sprintf(" sql:\"%s\"", val)
			}
			tags = fmt.Sprintf(" `json:\"%s%s\"%s%s`", name, omit, gorm, sql)
			desc := actual[name].Description
			if desc != "" {
				desc = fmt.Sprintf("// %s\n", desc)
			}
			buffer.WriteString(fmt.Sprintf("%s%s %s%s\n", desc, fname, typedef, tags))
		}

		for k, v := range genfuncs {
			s := v(def)
			if s != "" {
				buffer.WriteString(fmt.Sprintf("%s%s", k, s))
			}
		}

		codegen.WriteTabs(&buffer, 0)
		buffer.WriteString("}")
		return buffer.String()
	default:
		panic("gorma bug: unexpected data structure type")
	}
}

// setupIDAttribute adds or updates the ID field of a user type definition.
func setupIDAttribute(obj design.Object, res *design.UserTypeDefinition) design.Object {
	idName := ""
	foundID := false
	for n := range obj {
		if n == "ID" || n == "Id" || n == "id" {
			idName = n
			foundID = true
		}
	}

	if foundID {
		// enforce lowercase key
		if idName != "id" {
			obj["id"] = obj[idName]
			delete(obj, idName)
		}
	} else {
		obj["id"] = &design.AttributeDefinition{
			Type:     design.Integer,
			Metadata: design.MetadataDefinition{},
		}
	}

	var gorm string
	if val, ok := res.Metadata["github.com/bketelsen/gorma#gormpktag"]; ok {
		gorm = val
	} else {
		gorm = "primary_key"
	}

	// If the user already defined gormtag, leave it alone.
	if _, ok := obj["id"].Metadata["github.com/bketelsen/gorma#gormtag"]; !ok {
		obj["id"].Metadata["github.com/bketelsen/gorma#gormtag"] = gorm
	}

	return obj
}

// isASCIILower returns whether c is an ASCII lower-case letter.
func isASCIILower(c byte) bool {
	return 'a' <= c && c <= 'z'
}

// isASCIIDigit returns whether c is an ASCII digit.
func isASCIIDigit(c byte) bool {
	return '0' <= c && c <= '9'
}

// unexport lowercases the first character of a string.
func unexport(s string) string {
	return strings.ToLower(s[0:1]) + s[1:]
}

// startsWithInitialism returns the initialism if the given string begins with it
func startsWithInitialism(s string) string {
	var initialism string
	// the longest initialism is 5 char, the shortest 2
	for i := 1; i <= 5; i++ {
		if len(s) > i-1 && commonInitialisms[s[:i]] {
			initialism = s[:i]
		}
	}
	return initialism
}

// genfuncs is a map of comments and functions that will be used by MakeModelDef
// to conditionally add fields to the model struct.  If the function returns
// content, the content will be preceded by the the map key, which should be a
// comment.
var genfuncs = map[string]func(*design.AttributeDefinition) string{
	"\n// Timestamps\n":   TimeStamps,
	"\n// Many2Many\n":    IncludeMany2Many,
	"\n// Foreign Keys\n": IncludeForeignKey,
	"\n// Children\n":     IncludeChildren,
	"\n// Authboss\n\n":   Authboss,
}

// commonInitialisms, taken from
// https://github.com/golang/lint/blob/3d26dc39376c307203d3a221bada26816b3073cf/lint.go#L482
var commonInitialisms = map[string]bool{
	"API":   true,
	"ASCII": true,
	"CPU":   true,
	"CSS":   true,
	"DNS":   true,
	"EOF":   true,
	"GUID":  true,
	"HTML":  true,
	"HTTP":  true,
	"HTTPS": true,
	"ID":    true,
	"IP":    true,
	"JSON":  true,
	"LHS":   true,
	"QPS":   true,
	"RAM":   true,
	"RHS":   true,
	"RPC":   true,
	"SLA":   true,
	"SMTP":  true,
	"SSH":   true,
	"TLS":   true,
	"TTL":   true,
	"UI":    true,
	"UID":   true,
	"UUID":  true,
	"URI":   true,
	"URL":   true,
	"UTF8":  true,
	"VM":    true,
	"XML":   true,
}
<|endoftext|>