<commit_before>package mocks

import (
	"fmt"
	"strings"

	"code.google.com/p/go-uuid/uuid"
	"github.com/Juniper/contrail-go-api"
	"github.com/Juniper/contrail-go-api/types"
	"github.com/stretchr/testify/mock"
)

type TypeInterceptor interface {
	Get(contrail.IObject)
}

type ApiClient struct {
	mock.Mock
	IdAssignMap    map[string]string
	InterceptorMap map[string]TypeInterceptor
	objByNameMap   map[string]contrail.IObject
	objByIdMap     map[string]contrail.IObject
}

func (m *ApiClient) Init() {
	m.objByNameMap = make(map[string]contrail.IObject)
	m.objByIdMap = make(map[string]contrail.IObject)

	domain := new(types.Domain)
	domain.SetName("default-domain")
	m.Create(domain)

	project := new(types.Project)
	project.SetFQName("domain", []string{"default-domain", "default-project"})
	m.Create(project)

	ipam := new(types.NetworkIpam)
	ipam.SetFQName("project", []string{"default-domain", "default-project", "default-network-ipam"})
	m.Create(ipam)
}

func objName(ptr contrail.IObject) string {
	return ptr.GetType() + ":" + strings.Join(ptr.GetFQName(), ":")
}

func (m *ApiClient) AddInterceptor(typename string, interceptor TypeInterceptor) {
	if m.InterceptorMap == nil {
		m.InterceptorMap = make(map[string]TypeInterceptor)
	}
	m.InterceptorMap[typename] = interceptor
}
func (m *ApiClient) interceptGet(ptr contrail.IObject) {
	if m.InterceptorMap == nil {
		return
	}
	if interceptor, ok := m.InterceptorMap[ptr.GetType()]; ok {
		interceptor.Get(ptr)
	}
}

func (m *ApiClient) Create(ptr contrail.IObject) error {
	if ptr.GetUuid() == "" {
		isSet := false
		if m.IdAssignMap != nil {
			if id, ok := m.IdAssignMap[objName(ptr)]; ok {
				ptr.SetUuid(id)
				isSet = true
			}
		}
		if !isSet {
			ptr.SetUuid(uuid.New())
		}
	}
	m.objByNameMap[objName(ptr)] = ptr
	m.objByIdMap[ptr.GetUuid()] = ptr
	return nil
}
func (m *ApiClient) Update(ptr contrail.IObject) error {
	return nil
}
func (m *ApiClient) DeleteByUuid(typename string, uuid string) error {
	obj, ok := m.objByIdMap[uuid]
	if !ok {
		return fmt.Errorf("%s %s: Not found", typename, uuid)
	}
	delete(m.objByIdMap, uuid)
	delete(m.objByNameMap, objName(obj))
	return nil
}
func (m *ApiClient) Delete(ptr contrail.IObject) error {
	delete(m.objByIdMap, ptr.GetUuid())
	delete(m.objByNameMap, objName(ptr))
	return nil
}
func (m *ApiClient) FindByUuid(typename string, uuid string) (contrail.IObject, error) {
	obj, ok := m.objByIdMap[uuid]
	if !ok {
		return nil, fmt.Errorf("%s %s: Not found", typename, uuid)
	}
	m.interceptGet(obj)
	return obj, nil
}
func (m *ApiClient) UuidByName(typename string, fqn string) (string, error) {
	obj, ok := m.objByNameMap[typename+":"+fqn]
	if !ok {
		return "", fmt.Errorf("%s %s: Not found", typename, fqn)
	}
	return obj.GetUuid(), nil
}
func (m *ApiClient) FQNameByUuid(uuid string) ([]string, error) {
	obj, ok := m.objByIdMap[uuid]
	if !ok {
		return []string{}, fmt.Errorf("%s: Not found", uuid)
	}
	return obj.GetFQName(), nil
}
func (m *ApiClient) FindByName(typename string, fqn string) (contrail.IObject, error) {
	obj, ok := m.objByNameMap[typename+":"+fqn]
	if !ok {
		return nil, fmt.Errorf("%s %s: Not found", typename, fqn)
	}
	m.interceptGet(obj)
	return obj, nil
}
func (m *ApiClient) List(typename string, count int) ([]contrail.ListResult, error) {
	ret := m.Called(typename, count)

	var r0 []contrail.ListResult
	if ret.Get(0) != nil {
		r0 = ret.Get(0).([]contrail.ListResult)
	}
	r1 := ret.Error(1)

	return r0, r1
}
func (m *ApiClient) ListByParent(typename string, parent_id string, count int) ([]contrail.ListResult, error) {
	ret := m.Called(typename, parent_id, count)

	var r0 []contrail.ListResult
	if ret.Get(0) != nil {
		r0 = ret.Get(0).([]contrail.ListResult)
	}
	r1 := ret.Error(1)

	return r0, r1
}
func (m *ApiClient) ListDetail(typename string, fields []string, count int) ([]contrail.IObject, error) {
	ret := m.Called(typename, fields, count)

	var r0 []contrail.IObject
	if ret.Get(0) != nil {
		r0 = ret.Get(0).([]contrail.IObject)
	}
	r1 := ret.Error(1)

	return r0, r1
}
func (m *ApiClient) ListDetailByParent(typename string, parent_id string, fields []string, count int) ([]contrail.IObject, error) {
	ret := m.Called(typename, parent_id, fields, count)

	var r0 []contrail.IObject
	if ret.Get(0) != nil {
		r0 = ret.Get(0).([]contrail.IObject)
	}
	r1 := ret.Error(1)

	return r0, r1
}
<commit_msg>Add PUT interceptor<commit_after>package mocks

import (
	"fmt"
	"strings"

	"code.google.com/p/go-uuid/uuid"
	"github.com/Juniper/contrail-go-api"
	"github.com/Juniper/contrail-go-api/types"
	"github.com/stretchr/testify/mock"
)

type TypeInterceptor interface {
	Get(contrail.IObject)
	Put(contrail.IObject)
}

type ApiClient struct {
	mock.Mock
	IdAssignMap    map[string]string
	InterceptorMap map[string]TypeInterceptor
	objByNameMap   map[string]contrail.IObject
	objByIdMap     map[string]contrail.IObject
}

func (m *ApiClient) Init() {
	m.objByNameMap = make(map[string]contrail.IObject)
	m.objByIdMap = make(map[string]contrail.IObject)

	domain := new(types.Domain)
	domain.SetName("default-domain")
	m.Create(domain)

	project := new(types.Project)
	project.SetFQName("domain", []string{"default-domain", "default-project"})
	m.Create(project)

	ipam := new(types.NetworkIpam)
	ipam.SetFQName("project", []string{"default-domain", "default-project", "default-network-ipam"})
	m.Create(ipam)
}

func objName(ptr contrail.IObject) string {
	return ptr.GetType() + ":" + strings.Join(ptr.GetFQName(), ":")
}

func (m *ApiClient) AddInterceptor(typename string, interceptor TypeInterceptor) {
	if m.InterceptorMap == nil {
		m.InterceptorMap = make(map[string]TypeInterceptor)
	}
	m.InterceptorMap[typename] = interceptor
}

func (m *ApiClient) interceptPut(ptr contrail.IObject) {
	if m.InterceptorMap == nil {
		return
	}
	if interceptor, ok := m.InterceptorMap[ptr.GetType()]; ok {
		interceptor.Put(ptr)
	}
}

func (m *ApiClient) interceptGet(ptr contrail.IObject) {
	if m.InterceptorMap == nil {
		return
	}
	if interceptor, ok := m.InterceptorMap[ptr.GetType()]; ok {
		interceptor.Get(ptr)
	}
}

func (m *ApiClient) Create(ptr contrail.IObject) error {
	if ptr.GetUuid() == "" {
		isSet := false
		if m.IdAssignMap != nil {
			if id, ok := m.IdAssignMap[objName(ptr)]; ok {
				ptr.SetUuid(id)
				isSet = true
			}
		}
		if !isSet {
			ptr.SetUuid(uuid.New())
		}
	}
	m.interceptPut(ptr)
	m.objByNameMap[objName(ptr)] = ptr
	m.objByIdMap[ptr.GetUuid()] = ptr
	return nil
}
func (m *ApiClient) Update(ptr contrail.IObject) error {
	return nil
}
func (m *ApiClient) DeleteByUuid(typename string, uuid string) error {
	obj, ok := m.objByIdMap[uuid]
	if !ok {
		return fmt.Errorf("%s %s: Not found", typename, uuid)
	}
	delete(m.objByIdMap, uuid)
	delete(m.objByNameMap, objName(obj))
	return nil
}
func (m *ApiClient) Delete(ptr contrail.IObject) error {
	delete(m.objByIdMap, ptr.GetUuid())
	delete(m.objByNameMap, objName(ptr))
	return nil
}
func (m *ApiClient) FindByUuid(typename string, uuid string) (contrail.IObject, error) {
	obj, ok := m.objByIdMap[uuid]
	if !ok {
		return nil, fmt.Errorf("%s %s: Not found", typename, uuid)
	}
	m.interceptGet(obj)
	return obj, nil
}
func (m *ApiClient) UuidByName(typename string, fqn string) (string, error) {
	obj, ok := m.objByNameMap[typename+":"+fqn]
	if !ok {
		return "", fmt.Errorf("%s %s: Not found", typename, fqn)
	}
	return obj.GetUuid(), nil
}
func (m *ApiClient) FQNameByUuid(uuid string) ([]string, error) {
	obj, ok := m.objByIdMap[uuid]
	if !ok {
		return []string{}, fmt.Errorf("%s: Not found", uuid)
	}
	return obj.GetFQName(), nil
}
func (m *ApiClient) FindByName(typename string, fqn string) (contrail.IObject, error) {
	obj, ok := m.objByNameMap[typename+":"+fqn]
	if !ok {
		return nil, fmt.Errorf("%s %s: Not found", typename, fqn)
	}
	m.interceptGet(obj)
	return obj, nil
}
func (m *ApiClient) List(typename string, count int) ([]contrail.ListResult, error) {
	ret := m.Called(typename, count)

	var r0 []contrail.ListResult
	if ret.Get(0) != nil {
		r0 = ret.Get(0).([]contrail.ListResult)
	}
	r1 := ret.Error(1)

	return r0, r1
}
func (m *ApiClient) ListByParent(typename string, parent_id string, count int) ([]contrail.ListResult, error) {
	ret := m.Called(typename, parent_id, count)

	var r0 []contrail.ListResult
	if ret.Get(0) != nil {
		r0 = ret.Get(0).([]contrail.ListResult)
	}
	r1 := ret.Error(1)

	return r0, r1
}
func (m *ApiClient) ListDetail(typename string, fields []string, count int) ([]contrail.IObject, error) {
	ret := m.Called(typename, fields, count)

	var r0 []contrail.IObject
	if ret.Get(0) != nil {
		r0 = ret.Get(0).([]contrail.IObject)
	}
	r1 := ret.Error(1)

	return r0, r1
}
func (m *ApiClient) ListDetailByParent(typename string, parent_id string, fields []string, count int) ([]contrail.IObject, error) {
	ret := m.Called(typename, parent_id, fields, count)

	var r0 []contrail.IObject
	if ret.Get(0) != nil {
		r0 = ret.Get(0).([]contrail.IObject)
	}
	r1 := ret.Error(1)

	return r0, r1
}
<|endoftext|>


<commit_before>package model

import (
	"testing"
	"time"

	"github.com/davidlu1997/fast-shortener/config"
)

const configPath = "../config/config.json"

func TestIsValid(t *testing.T) {
	config, err := config.GetConfig(configPath)
	if config == nil {
		t.Fatal(err)
	}

	link1 := Link{
		URL:      "https://google.com",
		Key:      "a",
		Duration: 5 * time.Minute,
	}

	if link1.IsValid(config) {
		t.Fatal("should be invalid, key too short")
	}

	link2 := Link{
		URL:      "https://google.com",
		Key:      "derp-herp",
		Duration: 1 * time.Second,
	}

	if link2.IsValid(config) {
		t.Fatal("should be invalid, duration too short")
	}

	link3 := Link{
		URL:      "https://google.com",
		Key:      "derp-herp",
		Duration: 5 * time.Minute,
	}

	if !link3.IsValid(config) {
		t.Fatal("should be valid")
	}

	link4 := Link{
		URL:      "https://google.com",
		Key:      "abcdefghijklmnopqrstuvwxyz",
		Duration: 5 * time.Minute,
	}

	if link4.IsValid(config) {
		t.Fatal("should be invalid, key too long")
	}

	link5 := Link{
		URL:      "https://google.com",
		Key:      "derp-herp",
		Duration: 100 * time.Hour,
	}

	if link5.IsValid(config) {
		t.Fatal("should be invalid, duration too long")
	}
}
<commit_msg>Use pointers<commit_after>package model

import (
	"testing"
	"time"

	"github.com/davidlu1997/fast-shortener/config"
)

const configPath = "../config/config.json"

func TestIsValid(t *testing.T) {
	config, err := config.GetConfig(configPath)
	if config == nil {
		t.Fatal(err)
	}

	link1 := &Link{
		URL:      "https://google.com",
		Key:      "a",
		Duration: 5 * time.Minute,
	}

	if link1.IsValid(config) {
		t.Fatal("should be invalid, key too short")
	}

	link2 := &Link{
		URL:      "https://google.com",
		Key:      "derp-herp",
		Duration: 1 * time.Second,
	}

	if link2.IsValid(config) {
		t.Fatal("should be invalid, duration too short")
	}

	link3 := &Link{
		URL:      "https://google.com",
		Key:      "derp-herp",
		Duration: 5 * time.Minute,
	}

	if !link3.IsValid(config) {
		t.Fatal("should be valid")
	}

	link4 := &Link{
		URL:      "https://google.com",
		Key:      "abcdefghijklmnopqrstuvwxyz",
		Duration: 5 * time.Minute,
	}

	if link4.IsValid(config) {
		t.Fatal("should be invalid, key too long")
	}

	link5 := &Link{
		URL:      "https://google.com",
		Key:      "derp-herp",
		Duration: 100 * time.Hour,
	}

	if link5.IsValid(config) {
		t.Fatal("should be invalid, duration too long")
	}
}
<|endoftext|>


<commit_before>package model

import (
	"bytes"
	"go/format"
	"io"
	"os"
	"path"
	"text/template"
)

// Generate writes a Go file with a client for using the parsed service.
func Generate(prefix string, forceService bool) error {
	pkg := service.PackageName()
	pkgDir := path.Join(prefix, pkg)
	os.MkdirAll(pkgDir, 0775)

	apiFile := path.Join(pkgDir, "api.go")
	apiWriter, err := os.Create(apiFile)
	if err != nil {
		panic(err)
	}
	defer apiWriter.Close()

	var svcWriter io.WriteCloser
	svcFile := path.Join(pkgDir, "service.go")
	if _, err := os.Stat(svcFile); forceService || (err != nil && os.IsNotExist(err)) {
		svcWriter, err = os.Create(svcFile)
		if err != nil {
			panic(err)
		}
		defer svcWriter.Close()
	}

	return generateSource(apiWriter, svcWriter)
}

func generateSource(api io.Writer, svc io.Writer) error {
	fmap := template.FuncMap{
		"godoc":            godoc,
		"exportable":       exportable,
		"structName":       structName,
		"enumList":         enumList,
		"shapeList":        shapeList,
		"shapeAlias":       shapeAlias,
		"protocolPackage":  protocolPackage,
		"requiredTraits":   requiredTraits,
		"locationTraits":   locationTraits,
		"hasRequiredTrait": hasRequiredTrait,
	}

	apiTemplate := template.New("api-root").Funcs(fmap)
	template.Must(tplAPI(apiTemplate))

	svcTemplate := template.New("service-root").Funcs(fmap)
	template.Must(tplService(svcTemplate))

	if api != nil {
		apiOut := new(bytes.Buffer)
		if err := apiTemplate.Execute(apiOut, service); err != nil {
			return err
		}
		apiFormatted, err := format.Source(apiOut.Bytes())
		if err != nil {
			return err
		}
		_, err = api.Write(apiFormatted)
		if err != nil {
			_, _ = api.Write(apiOut.Bytes())
			return err
		}
	}

	if svc != nil {
		svcOut := new(bytes.Buffer)
		if err := svcTemplate.Execute(svcOut, service); err != nil {
			return err
		}

		svcFormatted, err := format.Source(svcOut.Bytes())
		if err != nil {
			return err
		}

		_, err = svc.Write(svcFormatted)
		if err != nil {
			_, _ = svc.Write(svcOut.Bytes())
			return err
		}
	}

	return nil
}

func tplService(t *template.Template) (*template.Template, error) {
	return t.Parse(`
package {{ .PackageName }}

import (
    "github.com/awslabs/aws-sdk-go/aws"
    "github.com/awslabs/aws-sdk-go/aws/signer/v4"
    "github.com/awslabs/aws-sdk-go/aws/protocol/{{ protocolPackage .Metadata.Protocol }}"
)

// {{ .Name }} is a client for {{ .FullName }}.
type {{ .Name }} struct {
    *aws.Service
}

type {{ .Name }}Config struct {
    *aws.Config
}

// New returns a new {{ .Name }} client.
func New(config *{{ .Name }}Config) *{{ .Name }} {
  if config == nil {
    config = &{{ .Name }}Config{}
  }

  service := &aws.Service{
    Config:       aws.DefaultConfig.Merge(config.Config),
    ServiceName:  "{{ .Metadata.EndpointPrefix }}",
    APIVersion:   "{{ .Metadata.APIVersion }}",
{{ if eq .Metadata.Protocol "json" }}JSONVersion:  "{{ .Metadata.JSONVersion }}",
    TargetPrefix: "{{ .Metadata.TargetPrefix }}",
{{ end }}
  }
  service.Initialize()

  // Handlers
  service.Handlers.Sign.PushBack(v4.Sign)
  service.Handlers.Build.PushBack({{ protocolPackage .Metadata.Protocol }}.Build)
  service.Handlers.Unmarshal.PushBack({{ protocolPackage .Metadata.Protocol }}.Unmarshal)

  return &{{ .Name }}{service}
}
`)
}

func tplAPI(t *template.Template) (*template.Template, error) {
	template.Must(tplAPIHeader(t))
	template.Must(tplAPIOperations(t))
	template.Must(tplAPIShapes(t))
	template.Must(tplAPIFooter(t))
	return t.Parse(`
{{ template "api-header" $ }}
{{ template "api-operations" $ }}
{{ template "api-shapes" $ }}
{{ template "api-footer" }}
`)
}

func tplAPIHeader(t *template.Template) (*template.Template, error) {
	return t.Parse(`
{{ define "api-header" }}
////////////////////////////////////////////////////////
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. //
////////////////////////////////////////////////////////

{{ godoc .Name .Documentation }} package {{ .PackageName }}

import (
  "time"

  "github.com/awslabs/aws-sdk-go/aws"
)

{{ end }}
`)
}

func tplAPIFooter(t *template.Template) (*template.Template, error) {
	return t.Parse(`
{{ define "api-footer" }}
// avoid errors if the packages aren't referenced
var _ time.Time
var _ xml.Decoder
var _ = io.EOF
{{ end }}
`)
}

func tplAPIOperations(t *template.Template) (*template.Template, error) {
	return t.Parse(`
{{ define "api-operations" }}

{{ range $name, $op := .Operations }}

// {{ exportable $name }}Request generates a request for the {{ exportable $name }} operation.
func (c *{{ $.Name }}) {{ exportable $name }}Request({{ if $op.Input }}input {{ $op.Input.Type }}{{ end }}) (req *aws.Request{{ if $op.Output }}, output {{ structName $op.Output.Type }}{{ end }}) {
    if op{{ exportable $name }} == nil {
      op{{ exportable $name }} = &aws.Operation{
        Name:       "{{ $name }}",
        HTTPMethod: "{{ $op.HTTP.Method }}",
        HTTPPath:   "{{ $op.HTTP.RequestURI }}",
        {{ if eq $.Metadata.Protocol "rest-xml" "rest-json" }}
        OperationBindings: &aws.OperationBindings{
          {{ if $op.Input }}  InPayload:   "{{ $op.Input.Payload }}", {{ end }}
          {{ if $op.Output }} OutPayload:  "{{ $op.Output.Payload }}", {{ end }}
          Required:    []string{ {{ requiredTraits $op.Input }} },
          QueryParams: []string{ {{ locationTraits "query" $op.Input }} },
          URIParams:   []string{ {{ locationTraits "uri" $op.Input }} },
          InHeaders:   []string{ {{ locationTraits "header" $op.Input }} },
          OutHeaders:  []string{ {{ locationTraits "header" $op.Output }} },
        }, {{ else if hasRequiredTrait $op.Input }}
        OperationBindings: &aws.OperationBindings{
          Required:    []string{ {{ requiredTraits $op.Input }} },
        }, {{ end }}
      }
    }

    req = aws.NewRequest(c.Service, op{{ exportable $name }}, {{ if $op.Input }}input{{ else }}nil{{ end }}, {{ if $op.Output }}output{{ else }}nil{{ end }})
    {{ if $op.Output }}output = {{ structName $op.Output.Literal }}
    req.Data = output{{ end }}
    return
}

{{ godoc $name $op.Documentation }} func (c *{{ $.Name }}) {{ exportable $name }}({{ if $op.Input }}input {{ $op.Input.Type }}{{ end }}) ({{ if $op.Output }}output {{ structName $op.Output.Type }},{{ end }} err error) {
  req{{ if $op.Output }}, out{{ end }} := c.{{ exportable $name }}Request({{ if $op.Input }}input{{ end }})
  {{ if $op.Output }}output = out
  {{ end }}err = req.Send()
  return
}

{{ end }}

var (
{{ range $name, $op := .Operations }}op{{ exportable $name }} *aws.Operation
{{ end }}
)

{{ end }}
`)
}

func tplAPIShapes(t *template.Template) (*template.Template, error) {
	return t.Parse(`
{{ define "api-shapes" }}

{{ range $_, $s := enumList }}
// Possible values for {{ exportable $s.Alias }}.
const (
    {{ range $name, $value := $s.Shape.Enums }}
    {{ $name }} = {{ $value }}{{ end }}
)
{{ end }}

{{ range $_, $s := shapeList }}
{{ if eq $s.Shape.ShapeType "structure" }}

// {{ $s.Alias }} is undocumented.
type {{ $s.Alias }} struct {
{{ range $name, $m := $s.Shape.Members }}
{{ exportable $name }} {{ $m.Shape.Type }} {{ $m.Tag $.Metadata.Protocol $name }}  {{ end }}
}

{{ end }}
{{ end }}

{{ end }}
`)
}
<commit_msg>Use filepath.Join() instead of path.Join()<commit_after>package model

import (
	"bytes"
	"go/format"
	"io"
	"os"
	"path/filepath"
	"text/template"
)

// Generate writes a Go file with a client for using the parsed service.
func Generate(prefix string, forceService bool) error {
	pkg := service.PackageName()
	pkgDir := filepath.Join(prefix, pkg)
	os.MkdirAll(pkgDir, 0775)

	apiFile := filepath.Join(pkgDir, "api.go")
	apiWriter, err := os.Create(apiFile)
	if err != nil {
		panic(err)
	}
	defer apiWriter.Close()

	var svcWriter io.WriteCloser
	svcFile := filepath.Join(pkgDir, "service.go")
	if _, err := os.Stat(svcFile); forceService || (err != nil && os.IsNotExist(err)) {
		svcWriter, err = os.Create(svcFile)
		if err != nil {
			panic(err)
		}
		defer svcWriter.Close()
	}

	return generateSource(apiWriter, svcWriter)
}

func generateSource(api io.Writer, svc io.Writer) error {
	fmap := template.FuncMap{
		"godoc":            godoc,
		"exportable":       exportable,
		"structName":       structName,
		"enumList":         enumList,
		"shapeList":        shapeList,
		"shapeAlias":       shapeAlias,
		"protocolPackage":  protocolPackage,
		"requiredTraits":   requiredTraits,
		"locationTraits":   locationTraits,
		"hasRequiredTrait": hasRequiredTrait,
	}

	apiTemplate := template.New("api-root").Funcs(fmap)
	template.Must(tplAPI(apiTemplate))

	svcTemplate := template.New("service-root").Funcs(fmap)
	template.Must(tplService(svcTemplate))

	if api != nil {
		apiOut := new(bytes.Buffer)
		if err := apiTemplate.Execute(apiOut, service); err != nil {
			return err
		}
		apiFormatted, err := format.Source(apiOut.Bytes())
		if err != nil {
			return err
		}
		_, err = api.Write(apiFormatted)
		if err != nil {
			_, _ = api.Write(apiOut.Bytes())
			return err
		}
	}

	if svc != nil {
		svcOut := new(bytes.Buffer)
		if err := svcTemplate.Execute(svcOut, service); err != nil {
			return err
		}

		svcFormatted, err := format.Source(svcOut.Bytes())
		if err != nil {
			return err
		}

		_, err = svc.Write(svcFormatted)
		if err != nil {
			_, _ = svc.Write(svcOut.Bytes())
			return err
		}
	}

	return nil
}

func tplService(t *template.Template) (*template.Template, error) {
	return t.Parse(`
package {{ .PackageName }}

import (
    "github.com/awslabs/aws-sdk-go/aws"
    "github.com/awslabs/aws-sdk-go/aws/signer/v4"
    "github.com/awslabs/aws-sdk-go/aws/protocol/{{ protocolPackage .Metadata.Protocol }}"
)

// {{ .Name }} is a client for {{ .FullName }}.
type {{ .Name }} struct {
    *aws.Service
}

type {{ .Name }}Config struct {
    *aws.Config
}

// New returns a new {{ .Name }} client.
func New(config *{{ .Name }}Config) *{{ .Name }} {
  if config == nil {
    config = &{{ .Name }}Config{}
  }

  service := &aws.Service{
    Config:       aws.DefaultConfig.Merge(config.Config),
    ServiceName:  "{{ .Metadata.EndpointPrefix }}",
    APIVersion:   "{{ .Metadata.APIVersion }}",
{{ if eq .Metadata.Protocol "json" }}JSONVersion:  "{{ .Metadata.JSONVersion }}",
    TargetPrefix: "{{ .Metadata.TargetPrefix }}",
{{ end }}
  }
  service.Initialize()

  // Handlers
  service.Handlers.Sign.PushBack(v4.Sign)
  service.Handlers.Build.PushBack({{ protocolPackage .Metadata.Protocol }}.Build)
  service.Handlers.Unmarshal.PushBack({{ protocolPackage .Metadata.Protocol }}.Unmarshal)

  return &{{ .Name }}{service}
}
`)
}

func tplAPI(t *template.Template) (*template.Template, error) {
	template.Must(tplAPIHeader(t))
	template.Must(tplAPIOperations(t))
	template.Must(tplAPIShapes(t))
	template.Must(tplAPIFooter(t))
	return t.Parse(`
{{ template "api-header" $ }}
{{ template "api-operations" $ }}
{{ template "api-shapes" $ }}
{{ template "api-footer" }}
`)
}

func tplAPIHeader(t *template.Template) (*template.Template, error) {
	return t.Parse(`
{{ define "api-header" }}
////////////////////////////////////////////////////////
// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT EDIT. //
////////////////////////////////////////////////////////

{{ godoc .Name .Documentation }} package {{ .PackageName }}

import (
  "time"

  "github.com/awslabs/aws-sdk-go/aws"
)

{{ end }}
`)
}

func tplAPIFooter(t *template.Template) (*template.Template, error) {
	return t.Parse(`
{{ define "api-footer" }}
// avoid errors if the packages aren't referenced
var _ time.Time
var _ xml.Decoder
var _ = io.EOF
{{ end }}
`)
}

func tplAPIOperations(t *template.Template) (*template.Template, error) {
	return t.Parse(`
{{ define "api-operations" }}

{{ range $name, $op := .Operations }}

// {{ exportable $name }}Request generates a request for the {{ exportable $name }} operation.
func (c *{{ $.Name }}) {{ exportable $name }}Request({{ if $op.Input }}input {{ $op.Input.Type }}{{ end }}) (req *aws.Request{{ if $op.Output }}, output {{ structName $op.Output.Type }}{{ end }}) {
    if op{{ exportable $name }} == nil {
      op{{ exportable $name }} = &aws.Operation{
        Name:       "{{ $name }}",
        HTTPMethod: "{{ $op.HTTP.Method }}",
        HTTPPath:   "{{ $op.HTTP.RequestURI }}",
        {{ if eq $.Metadata.Protocol "rest-xml" "rest-json" }}
        OperationBindings: &aws.OperationBindings{
          {{ if $op.Input }}  InPayload:   "{{ $op.Input.Payload }}", {{ end }}
          {{ if $op.Output }} OutPayload:  "{{ $op.Output.Payload }}", {{ end }}
          Required:    []string{ {{ requiredTraits $op.Input }} },
          QueryParams: []string{ {{ locationTraits "query" $op.Input }} },
          URIParams:   []string{ {{ locationTraits "uri" $op.Input }} },
          InHeaders:   []string{ {{ locationTraits "header" $op.Input }} },
          OutHeaders:  []string{ {{ locationTraits "header" $op.Output }} },
        }, {{ else if hasRequiredTrait $op.Input }}
        OperationBindings: &aws.OperationBindings{
          Required:    []string{ {{ requiredTraits $op.Input }} },
        }, {{ end }}
      }
    }

    req = aws.NewRequest(c.Service, op{{ exportable $name }}, {{ if $op.Input }}input{{ else }}nil{{ end }}, {{ if $op.Output }}output{{ else }}nil{{ end }})
    {{ if $op.Output }}output = {{ structName $op.Output.Literal }}
    req.Data = output{{ end }}
    return
}

{{ godoc $name $op.Documentation }} func (c *{{ $.Name }}) {{ exportable $name }}({{ if $op.Input }}input {{ $op.Input.Type }}{{ end }}) ({{ if $op.Output }}output {{ structName $op.Output.Type }},{{ end }} err error) {
  req{{ if $op.Output }}, out{{ end }} := c.{{ exportable $name }}Request({{ if $op.Input }}input{{ end }})
  {{ if $op.Output }}output = out
  {{ end }}err = req.Send()
  return
}

{{ end }}

var (
{{ range $name, $op := .Operations }}op{{ exportable $name }} *aws.Operation
{{ end }}
)

{{ end }}
`)
}

func tplAPIShapes(t *template.Template) (*template.Template, error) {
	return t.Parse(`
{{ define "api-shapes" }}

{{ range $_, $s := enumList }}
// Possible values for {{ exportable $s.Alias }}.
const (
    {{ range $name, $value := $s.Shape.Enums }}
    {{ $name }} = {{ $value }}{{ end }}
)
{{ end }}

{{ range $_, $s := shapeList }}
{{ if eq $s.Shape.ShapeType "structure" }}

// {{ $s.Alias }} is undocumented.
type {{ $s.Alias }} struct {
{{ range $name, $m := $s.Shape.Members }}
{{ exportable $name }} {{ $m.Shape.Type }} {{ $m.Tag $.Metadata.Protocol $name }}  {{ end }}
}

{{ end }}
{{ end }}

{{ end }}
`)
}
<|endoftext|>


<commit_before>package modules

import (
	"database/sql"
	"strings"

	"github.com/pajlada/pajbot2/bot"
	"github.com/pajlada/pajbot2/command"
	"github.com/pajlada/pajbot2/common"
	"github.com/pajlada/pajbot2/helper"
	"github.com/pajlada/pajbot2/sqlmanager"
)

/*
Command xD
*/
type Command struct {
	commands []command.Command
}

/*
some ideas:
not sure how this would work but id like to keep things simple

$(user1) = usersource displayname
$(sender) = sender of msg
$(1) = raw arg from msg
$(bot) = bot
$(channel) = channel

users have methods, for example $(sender.points):
	.low = lowercase
	.points
	.level
	etc ...

bot has:
	bot.uptime
	bot.version
	etc...

channel has:
	channel.uptime
	channel.title
	channel.game
	channel.subs
	channel.name
	etc...

!points would look like this :
	"$(user1) has $(user1.points) points."

!uptime:
	"$(sender), $(channel.name) has been online for $(channel.uptime) PogChamp"

*/

// Ensure the module implements the interface properly
var _ Module = (*Command)(nil)

func (module *Command) loadCommands(sql *sqlmanager.SQLManager) {
	// Fetch rows from pb_command
	rows, err := sql.Session.Query("SELECT id, channel_id, triggers, response, response_type FROM pb_command")

	if err != nil {
		log.Error("Error fetching commands:", err)
		return
	}

	module.readCommands(rows)
}

// loadCommand loads a command with a given ID
func (module *Command) loadCommand(sql *sqlmanager.SQLManager, commandID int64) {
	// Fetch rows from pb_command
	rows, err := sql.Session.Query("SELECT id, channel_id, triggers, response, response_type FROM pb_command WHERE `id`=?", commandID)

	if err != nil {
		log.Error("Error fetching commands:", err)
		return
	}

	module.readCommands(rows)
}

func (module *Command) readCommands(rows *sql.Rows) {
	for rows.Next() {
		c := command.ReadSQLCommand(rows)
		if c != nil {
			module.AddCommand(c)
		}
	}
}

// Init initializes something
func (module *Command) Init(sql *sqlmanager.SQLManager) {
	module.loadCommands(sql)

	xdCommand := command.TextCommand{
		BaseCommand: command.BaseCommand{
			Triggers: []string{
				"xd",
				"xdlol",
			},
		},
		Response: "pajaSWA",
	}
	module.AddCommand(&xdCommand)
	testCommand := command.NestedCommand{
		BaseCommand: command.BaseCommand{
			Triggers: []string{
				"lul",
				"xdlul",
			},
		},
		Commands: []command.Command{
			&xdCommand,
			&command.TextCommand{
				BaseCommand: command.BaseCommand{
					Triggers: []string{
						"a",
					},
				},
				Response: "pajaSWA a ;P",
			},
			&command.TextCommand{
				BaseCommand: command.BaseCommand{
					Triggers: []string{
						"b",
					},
				},
				Response: "pajaSWA b ;P",
			},
		},
	}
	module.AddCommand(&testCommand)

	// Temporary !admin prefix while it's in development
	adminCommand := &command.NestedCommand{
		BaseCommand: command.BaseCommand{
			Triggers: []string{
				"admin",
			},
		},
		Commands: []command.Command{
			&command.NestedCommand{
				BaseCommand: command.BaseCommand{
					Triggers: []string{
						"add",
					},
				},
				Commands: []command.Command{
					&command.FuncCommand{
						BaseCommand: command.BaseCommand{
							Triggers: []string{
								"command",
							},
							Level: 500,
						},
						Function: module.createCommand,
					},
				},
			},
			&xdCommand,
			&command.TextCommand{
				BaseCommand: command.BaseCommand{
					Triggers: []string{
						"a",
					},
				},
				Response: "pajaSWA a ;P",
			},
			&command.TextCommand{
				BaseCommand: command.BaseCommand{
					Triggers: []string{
						"b",
					},
				},
				Response: "pajaSWA b ;P",
			},
		},
	}
	module.AddCommand(adminCommand)
}

// AddCommand adds the given command to the list of active commands
func (module *Command) AddCommand(cmd command.Command) {
	module.commands = append(module.commands, cmd)
}

func (module *Command) createCommand(b *bot.Bot, msg *common.Msg, action *bot.Action) {
	// Change to 2 when we remove the !admin prefix
	const triggerLength = 3
	const usageFormat = "Usage: !%s !trigger response"
	triggers := helper.GetTriggersKC(msg.Text)
	if len(triggers) < triggerLength {
		b.Say("Missing arguments")
		return
	}

	if len(triggers) <= triggerLength+2 {
		b.Sayf(usageFormat, strings.Join(triggers[:triggerLength], " "))
		return
	}
	// TODO: use an argument parser so we can have --arguments like --silent and --reply --me --cd=0
	arguments := triggers[triggerLength:]

	trigger := strings.Replace(strings.ToLower(arguments[0]), "!", "", -1)
	if len(trigger) == 0 {
		b.Sayf(usageFormat, strings.Join(triggers[:triggerLength], " "))
		return
	}

	response := arguments[1:]

	sqlCommand := command.SQLCommand{
		ChannelID: 1, // XXX
		Triggers:  trigger,
		Response:  strings.Join(response, " "),
	}
	b.Sayf("CREATING COMMAND XD: %s - user level: %d", msg.Text, msg.User.Level)
	b.Sayf("Triggers: %s", triggers)
	b.Sayf("Arguments: %s", arguments)
	commandID := sqlCommand.Insert(b.SQL.Session)
	module.loadCommand(b.SQL, commandID)
	b.Say("xD")
}

// Check xD
func (module *Command) Check(b *bot.Bot, msg *common.Msg, action *bot.Action) error {
	if len(msg.Text) == 0 {
		// Do nothing with empty messages
		return nil
	}

	m := helper.GetTriggers(msg.Text)
	trigger := m[0]

	if msg.Text[0] != '!' {
		return nil
	}
	for _, command := range module.commands {
		if triggered, c := command.IsTriggered(trigger, m, 0); triggered {
			// Is the user high level enough to use this command?
			bc := c.GetBaseCommand()
			if bc.Level > msg.User.Level {
				log.Warningf("%s tried to use %s, which requires level %d (he is level %d)",
					msg.User.DisplayName, strings.Join(m, " "), bc.Level, msg.User.Level)
				return nil
			}
			// TODO: Get response first, and skip if the response is nil or something of that sort
			r := c.Run(b, msg, action)
			if r != "" {
				action.Response = r
				action.Stop = true
			}
			return nil
		}
	}
	return nil
}
<commit_msg>Improvements to the command system.<commit_after>package modules

import (
	"database/sql"
	"strings"

	"github.com/pajlada/pajbot2/bot"
	"github.com/pajlada/pajbot2/command"
	"github.com/pajlada/pajbot2/common"
	"github.com/pajlada/pajbot2/helper"
	"github.com/pajlada/pajbot2/sqlmanager"
)

/*
Command xD
*/
type Command struct {
	commands []command.Command
}

/*
some ideas:
not sure how this would work but id like to keep things simple

$(user1) = usersource displayname
$(sender) = sender of msg
$(1) = raw arg from msg
$(bot) = bot
$(channel) = channel

users have methods, for example $(sender.points):
	.low = lowercase
	.points
	.level
	etc ...

bot has:
	bot.uptime
	bot.version
	etc...

channel has:
	channel.uptime
	channel.title
	channel.game
	channel.subs
	channel.name
	etc...

!points would look like this :
	"$(user1) has $(user1.points) points."

!uptime:
	"$(sender), $(channel.name) has been online for $(channel.uptime) PogChamp"

*/

// Ensure the module implements the interface properly
var _ Module = (*Command)(nil)

func (module *Command) loadCommands(sql *sqlmanager.SQLManager) {
	// Fetch rows from pb_command
	rows, err := sql.Session.Query("SELECT id, channel_id, triggers, response, response_type FROM pb_command")

	if err != nil {
		log.Error("Error fetching commands:", err)
		return
	}

	module.readCommands(rows)
}

// loadCommand loads a command with a given ID
func (module *Command) loadCommand(sql *sqlmanager.SQLManager, commandID int64) {
	// Fetch rows from pb_command
	rows, err := sql.Session.Query("SELECT id, channel_id, triggers, response, response_type FROM pb_command WHERE `id`=?", commandID)

	if err != nil {
		log.Error("Error fetching commands:", err)
		return
	}

	module.readCommands(rows)
}

func (module *Command) readCommands(rows *sql.Rows) {
	for rows.Next() {
		c := command.ReadSQLCommand(rows)
		if c != nil {
			module.AddCommand(c)
		}
	}
}

// Init initializes something
func (module *Command) Init(sql *sqlmanager.SQLManager) {
	module.loadCommands(sql)

	xdCommand := command.TextCommand{
		BaseCommand: command.BaseCommand{
			Triggers: []string{
				"xd",
				"xdlol",
			},
		},
		Response: "pajaSWA",
	}
	module.AddCommand(&xdCommand)
	testCommand := command.NestedCommand{
		BaseCommand: command.BaseCommand{
			Triggers: []string{
				"lul",
				"xdlul",
			},
		},
		Commands: []command.Command{
			&xdCommand,
			&command.TextCommand{
				BaseCommand: command.BaseCommand{
					Triggers: []string{
						"a",
					},
				},
				Response: "pajaSWA a ;P",
			},
			&command.TextCommand{
				BaseCommand: command.BaseCommand{
					Triggers: []string{
						"b",
					},
				},
				Response: "pajaSWA b ;P",
			},
		},
	}
	module.AddCommand(&testCommand)

	// Temporary !admin prefix while it's in development
	adminCommand := &command.NestedCommand{
		BaseCommand: command.BaseCommand{
			Triggers: []string{
				"admin",
			},
		},
		Commands: []command.Command{
			&command.NestedCommand{
				BaseCommand: command.BaseCommand{
					Triggers: []string{
						"add",
					},
				},
				Commands: []command.Command{
					&command.FuncCommand{
						BaseCommand: command.BaseCommand{
							Triggers: []string{
								"command",
							},
							Level: 500,
						},
						Function: module.createCommand,
					},
				},
			},
			&command.NestedCommand{
				BaseCommand: command.BaseCommand{
					Triggers: []string{
						"remove",
					},
				},
				Commands: []command.Command{
					&command.FuncCommand{
						BaseCommand: command.BaseCommand{
							Triggers: []string{
								"command",
							},
							Level: 500,
						},
						Function: module.removeCommand,
					},
				},
			},
			&xdCommand,
			&command.TextCommand{
				BaseCommand: command.BaseCommand{
					Triggers: []string{
						"a",
					},
				},
				Response: "pajaSWA a ;P",
			},
			&command.TextCommand{
				BaseCommand: command.BaseCommand{
					Triggers: []string{
						"b",
					},
				},
				Response: "pajaSWA b ;P",
			},
		},
	}
	module.AddCommand(adminCommand)
}

// AddCommand adds the given command to the list of active commands
func (module *Command) AddCommand(cmd command.Command) {
	module.commands = append(module.commands, cmd)
}

func (module *Command) createCommand(b *bot.Bot, msg *common.Msg, action *bot.Action) {
	// Change to 2 when we remove the !admin prefix
	const triggerLength = 3
	const usageFormat = "Usage: !%s !trigger response"
	triggers := helper.GetTriggersKC(msg.Text)
	if len(triggers) < triggerLength {
		b.Say("Missing arguments")
		return
	}

	if len(triggers) < triggerLength+2 {
		b.Sayf(usageFormat, strings.Join(triggers[:triggerLength], " "))
		return
	}
	// TODO: use an argument parser so we can have --arguments like --silent and --reply --me --cd=0
	arguments := triggers[triggerLength:]

	// TODO: parse multiple triggers (separated by |)
	triggerString := strings.Replace(strings.ToLower(arguments[0]), "!", "", -1)
	if len(triggerString) == 0 {
		b.Sayf(usageFormat, strings.Join(triggers[:triggerLength], " "))
		return
	}
	var triggerList []string
	for _, t := range strings.Split(triggerString, "|") {
		add := true
		if len(t) > 0 {
			for _, eT := range triggerList {
				if t == eT {
					add = false
					break
				}
			}
			if add {
				triggerList = append(triggerList, t)
			}
		}
	}
	b.Sayf("%s", strings.Join(triggerList, ","))
	triggerString = strings.Join(triggerList, "|")

	response := arguments[1:]

	// See if any of the aliases we want to use is already in use
	for _, trigger := range triggerList {
		c := module.getTriggeredCommand("!" + trigger)
		if c != nil {
			b.Sayf("Command !%s is already in use.", trigger)
			return
		}
	}

	sqlCommand := command.SQLCommand{
		ChannelID: 1, // XXX
		Triggers:  triggerString,
		Response:  strings.Join(response, " "),
	}

	b.Sayf("CREATING COMMAND XD: %s - user level: %d", msg.Text, msg.User.Level)
	b.Sayf("Triggers: %s", triggers)
	b.Sayf("Arguments: %s", arguments)
	commandID := sqlCommand.Insert(b.SQL.Session)
	module.loadCommand(b.SQL, commandID)
	b.Say("xD")
}

func (module *Command) removeCommand(b *bot.Bot, msg *common.Msg, action *bot.Action) {
	// Change to 2 when we remove the !admin prefix
	const triggerLength = 3
	const usageFormat = "Usage: !%s !command"
	triggers := helper.GetTriggersKC(msg.Text)
	if len(triggers) < triggerLength {
		b.Say("Missing arguments")
		return
	}

	if len(triggers) < triggerLength+1 {
		b.Sayf(usageFormat, strings.Join(triggers[:triggerLength], " "))
		return
	}
	// TODO: use an argument parser so we can have --arguments like --silent and --reply --me --cd=0
	arguments := triggers[triggerLength:]

	trigger := strings.Replace(strings.ToLower(arguments[0]), "!", "", -1)

	c := module.getTriggeredCommand("!" + trigger)
	if c == nil {
		b.Sayf("No command with trigger !%s", trigger)
		return
	}

	// TODO: Actually remove the command
	b.Sayf("Remove command with trigger !%s", trigger)
	b.Sayf("Command data: %#v", c)
}

func (module *Command) getTriggeredCommand(text string) command.Command {
	m := helper.GetTriggers(text)
	trigger := m[0]

	for _, command := range module.commands {
		if triggered, c := command.IsTriggered(trigger, m, 0); triggered {
			return c
		}
	}
	return nil
}

// Check xD
func (module *Command) Check(b *bot.Bot, msg *common.Msg, action *bot.Action) error {
	if len(msg.Text) == 0 {
		// Do nothing with empty messages
		return nil
	}

	m := helper.GetTriggers(msg.Text)

	if msg.Text[0] != '!' {
		return nil
	}
	c := module.getTriggeredCommand(msg.Text)
	if c != nil {
		// Is the user high level enough to use this command?
		bc := c.GetBaseCommand()
		if bc.Level > msg.User.Level {
			log.Warningf("%s tried to use %s, which requires level %d (he is level %d)",
				msg.User.DisplayName, strings.Join(m, " "), bc.Level, msg.User.Level)
			return nil
		}
		// TODO: Get response first, and skip if the response is nil or something of that sort
		r := c.Run(b, msg, action)
		if r != "" {
			action.Response = r
			action.Stop = true
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package blockly

import (
	"io"
	"encoding/xml"
	"fmt"
	"strings"
)

const (
	InputDummy = iota
	InputField
	InputValue
	InputStatement
)

type Workspace struct {
	ExportedXml		io.Reader

	// TODO: Variables
	Root			[]*Block
}

type Value struct {
	Name			string
	Connection		*Block
}

type Input struct {
	Type			int
	Name			string
	Text			string
	Value			*Value
	Statement		*Statement
}

type Block struct {
	Type			string

	PreviousConnection	*Block
	NextConnection		*Block
	OutputConnection	*Block

	Inputs			[]*Input
}

type Statement struct {
	Name			string
	Connection		*Block
}

func NewWorkspace(xml io.Reader) Workspace {
	if xml == nil {
		panic("Xml cannot be nil")
	}

	return Workspace{
		ExportedXml:	xml,
		Root:		[]*Block{},
	}
}

func (w *Workspace) Parse() error {
	dec := xml.NewDecoder(w.ExportedXml)

	for {
		t, err := dec.Token()
		if t == nil {
			break
		}else if err != nil {
			return err
		}

		switch t := t.(type) {
		case xml.StartElement:
			if t.Name.Local ==  "block" {
				block := NewBlock()
				block.Type = getAttr(t, "type")
				w.Root = append(w.Root, &block)

				if err := w.parseStatement(dec, &block); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

func (w *Workspace) parseStatement(dec *xml.Decoder, ptr *Block) error{
	for {
		t, err := dec.Token()
		if t == nil {
			break
		}else if err != nil {
			return err
		}

		switch t := t.(type) {
		case xml.StartElement:
			switch t.Name.Local {
			case "block": {
				ptr.Type = getAttr(t, "type")
			}
			case "next": {
				b := NewBlock()

				b.PreviousConnection = ptr
				ptr.NextConnection = &b
				ptr = ptr.NextConnection
			}
			case "field": {
				var field struct {
					Name	string	`xml:"name,attr"`
					Value	string	`xml:",chardata"`
				}

				if err := dec.DecodeElement(&field, &t); err != nil {
					return err
				}

				ptr.Inputs = append(ptr.Inputs, &Input{
					Type: InputField,
					Name: field.Name,
					Text: field.Value,
				})
			}
			case "statement": {
				block := NewBlock()

				name := getAttr(t, "name")
				input := &Input {
					Type: InputStatement,
					Name: name,
					Statement: &Statement {
						Name: name,
						Connection: &block,
					},
				}
				ptr.Inputs = append(ptr.Inputs, input)
				if err := w.parseStatement(dec, &block); err != nil {
					return err
				}
			}
			case "value": {
				block := NewBlock()

				name := getAttr(t, "name")
				input := &Input {
					Type: InputValue,
					Name: name,
					Value: &Value {
						Name: name,
						Connection: &block,
					},
				}
				ptr.Inputs = append(ptr.Inputs, input)
				if err := w.parseStatement(dec, &block); err != nil {
					return err
				}
			}
			}
		case xml.EndElement:
			switch t.Name.Local {
			case "next": {
				ptr = ptr.PreviousConnection

				if ptr.PreviousConnection == nil {
					return nil
				}
			}
			case "statement", "value": {
				return nil
			}
			}
		}
	}
	return nil
}

func NewBlock() Block {
	return Block {
		Inputs: []*Input{},
	}
}

func getAttr(t xml.StartElement, key string) string{
	for _, a := range t.Attr {
		if a.Name.Local == key {
			return a.Value
		}
	}

	return ""
}

func printWorkspace(w Workspace){
	for _, root := range w.Root {
		ptr := root

		printStatement(ptr, 0)
	}
}

func printStatement(block *Block, depth int){
	ptr := block

	for ptr != nil {
		fmt.Println(strings.Repeat("\t", depth) + "<block type=\"" + ptr.Type + "\">")

		for _, input := range ptr.Inputs {
			if input.Type == InputField {
				fmt.Println(strings.Repeat("\t", depth + 1) + "<field name=\"" + input.Name + "\">" + input.Text + "</field>")
			}else if input.Type == InputStatement {
				fmt.Println(strings.Repeat("\t", depth + 1) + "<statement name=\"" + input.Statement.Name + "\">")
				printStatement(input.Statement.Connection, depth + 2)
			}else if input.Type == InputValue {
				fmt.Println(strings.Repeat("\t", depth + 1) + "<value name=\"" + input.Value.Name + "\">")
				printStatement(input.Value.Connection, depth + 2)
			}
		}

		ptr = ptr.NextConnection
		depth++
	}
}
<commit_msg>Fixed exiting 2 levels when statement is end<commit_after>package blockly

import (
	"io"
	"encoding/xml"
	"fmt"
	"strings"
)

const (
	InputDummy = iota
	InputField
	InputValue
	InputStatement
)

type Workspace struct {
	ExportedXml		io.Reader

	// TODO: Variables
	Root			[]*Block
}

type Value struct {
	Name			string
	Connection		*Block
}

type Input struct {
	Type			int
	Name			string
	Text			string
	Value			*Value
	Statement		*Statement
}

type Block struct {
	Type			string

	PreviousConnection	*Block
	NextConnection		*Block
	OutputConnection	*Block

	Inputs			[]*Input
}

type Statement struct {
	Name			string
	Connection		*Block
}

func NewWorkspace(xml io.Reader) Workspace {
	if xml == nil {
		panic("Xml cannot be nil")
	}

	return Workspace{
		ExportedXml:	xml,
		Root:		[]*Block{},
	}
}

func (w *Workspace) Parse() error {
	dec := xml.NewDecoder(w.ExportedXml)

	for {
		t, err := dec.Token()
		if t == nil {
			break
		}else if err != nil {
			return err
		}

		switch t := t.(type) {
		case xml.StartElement:
			if t.Name.Local ==  "block" {
				block := NewBlock()
				block.Type = getAttr(t, "type")
				w.Root = append(w.Root, &block)

				if err := w.parseStatement(dec, &block); err != nil {
					return err
				}
			}
		}
	}

	return nil
}

func (w *Workspace) parseStatement(dec *xml.Decoder, ptr *Block) error{
	for {
		t, err := dec.Token()
		if t == nil {
			break
		}else if err != nil {
			return err
		}

		switch t := t.(type) {
		case xml.StartElement:
			switch t.Name.Local {
				case "block": {
					ptr.Type = getAttr(t, "type")
				}
				case "next": {
					b := NewBlock()

					b.PreviousConnection = ptr
					ptr.NextConnection = &b
					ptr = ptr.NextConnection
				}
				case "field": {
					var field struct {
						Name	string	`xml:"name,attr"`
						Value	string	`xml:",chardata"`
					}

					if err := dec.DecodeElement(&field, &t); err != nil {
						return err
					}

					ptr.Inputs = append(ptr.Inputs, &Input{
						Type: InputField,
						Name: field.Name,
						Text: field.Value,
					})
				}
				case "statement": {
					block := NewBlock()

					name := getAttr(t, "name")
					input := &Input {
						Type: InputStatement,
						Name: name,
						Statement: &Statement {
							Name: name,
							Connection: &block,
						},
					}

					if err := w.parseStatement(dec, &block); err != nil {
						return err
					}

					ptr.Inputs = append(ptr.Inputs, input)
				}
				case "value": {
					block := NewBlock()

					name := getAttr(t, "name")
					input := &Input {
						Type: InputValue,
						Name: name,
						Value: &Value {
							Name: name,
							Connection: &block,
						},
					}

					if err := w.parseStatement(dec, &block); err != nil {
						return err
					}
					ptr.Inputs = append(ptr.Inputs, input)
				}
			}
		case xml.EndElement:
			switch t.Name.Local {
				case "next": {
					ptr = ptr.PreviousConnection

					if ptr == nil {
						return nil
					}
				}
				case "statement", "value": {
					return nil
				}
			}
		}
	}
	return nil
}

func NewBlock() Block {
	return Block {
		Inputs: []*Input{},
	}
}

func getAttr(t xml.StartElement, key string) string{
	for _, a := range t.Attr {
		if a.Name.Local == key {
			return a.Value
		}
	}

	return ""
}

func printWorkspace(w Workspace){
	for _, root := range w.Root {
		ptr := root

		printStatement(ptr, 0)
	}
}

func printStatement(block *Block, depth int){
	ptr := block

	for ptr != nil {
		fmt.Println(strings.Repeat("\t", depth) + "<block type=\"" + ptr.Type + "\">")

		for _, input := range ptr.Inputs {
			if input.Type == InputField {
				fmt.Println(strings.Repeat("\t", depth + 1) + "<field name=\"" + input.Name + "\">" + input.Text + "</field>")
			}else if input.Type == InputStatement {
				fmt.Println(strings.Repeat("\t", depth + 1) + "<statement name=\"" + input.Statement.Name + "\">")
				printStatement(input.Statement.Connection, depth + 2)
			}else if input.Type == InputValue {
				fmt.Println(strings.Repeat("\t", depth + 1) + "<value name=\"" + input.Value.Name + "\">")
				printStatement(input.Value.Connection, depth + 2)
			}
		}

		ptr = ptr.NextConnection
		depth++
	}
}
<|endoftext|>


<commit_before>package temp

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"path"
	"strings"
	"sync"

	"github.com/barakmich/agro"
	"github.com/barakmich/agro/models"
	"github.com/hashicorp/go-immutable-radix"
)

func init() {
	agro.RegisterMetadataProvider("temp", newTempMetadata)
}

type temp struct {
	mut      sync.Mutex
	inode    agro.INodeID
	tree     *iradix.Tree
	volIndex map[string]agro.VolumeID
	vol      agro.VolumeID
}

func newTempMetadata(address string) agro.MetadataService {
	return &temp{
		volIndex: make(map[string]agro.VolumeID),
		tree:     iradix.New(),
	}
}

func (t *temp) Mkfs() error { return nil }

func (t *temp) CreateVolume(volume string) error {
	t.mut.Lock()
	defer t.mut.Unlock()

	tx := t.tree.Txn()

	k := []byte(agro.Path{Volume: volume, Path: "/"}.Key())
	if _, ok := tx.Get(k); !ok {
		tx.Insert(k, (*models.Directory)(nil))
		t.tree = tx.Commit()
		t.volIndex[volume] = t.vol
		t.vol++
	}

	// TODO(jzelinskie): maybe raise volume already exists
	return nil
}

func (t *temp) CommitInodeIndex() (agro.INodeID, error) {
	t.mut.Lock()
	defer t.mut.Unlock()

	t.inode++
	return t.inode, nil
}

func (t *temp) Mkdir(p agro.Path, dir *models.Directory) error {
	if p.Path == "/" {
		return errors.New("can't create the root directory")
	}

	t.mut.Lock()
	defer t.mut.Unlock()

	tx := t.tree.Txn()

	k := []byte(p.Key())
	if _, ok := tx.Get(k); ok {
		return &os.PathError{
			Op:   "mkdir",
			Path: p.Path,
			Err:  os.ErrExist,
		}
	}
	tx.Insert(k, dir)

	for {
		p.Path, _ = path.Split(strings.TrimSuffix(p.Path, "/"))
		if p.Path == "" {
			break
		}
		k = []byte(p.Key())
		if _, ok := tx.Get(k); !ok {
			return &os.PathError{
				Op:   "stat",
				Path: p.Path,
				Err:  os.ErrNotExist,
			}
		}
	}

	t.tree = tx.Commit()
	return nil
}

func (t *temp) Getdir(p agro.Path) (*models.Directory, []agro.Path, error) {
	var (
		tx = t.tree.Txn()
		k  = []byte(p.Key())
	)
	v, ok := tx.Get(k)
	if !ok {
		return nil, nil, &os.PathError{
			Op:   "stat",
			Path: p.Path,
			Err:  os.ErrNotExist,
		}
	}

	var (
		dir     = v.(*models.Directory)
		prefix  = []byte(p.SubdirsPrefix())
		subdirs []agro.Path
	)
	tx.Root().WalkPrefix(prefix, func(k []byte, v interface{}) bool {
		subdirs = append(subdirs, agro.Path{
			Volume: p.Volume,
			Path:   fmt.Sprintf("%s%s", p.Path, bytes.TrimPrefix(k, prefix)),
		})
		return false
	})
	return dir, subdirs, nil
}

func (t *temp) GetVolumes() ([]string, error) {
	var (
		iter = t.tree.Root().Iterator()
		out  []string
		last string
	)
	for {
		k, _, ok := iter.Next()
		if !ok {
			break
		}
		if i := bytes.IndexByte(k, ':'); i != -1 {
			vol := string(k[:i])
			if vol == last {
				continue
			}
			out = append(out, vol)
			last = vol
		}
	}
	return out, nil
}

func (t *temp) GetVolumeID(volume string) (agro.VolumeID, error) {
	if vol, ok := t.volIndex[volume]; ok {
		return vol, nil
	}
	return 0, errors.New("temp: no such volume exists")
}
<commit_msg>Wrap map access in a mutex.<commit_after>package temp

import (
	"bytes"
	"errors"
	"fmt"
	"os"
	"path"
	"strings"
	"sync"

	"github.com/barakmich/agro"
	"github.com/barakmich/agro/models"
	"github.com/hashicorp/go-immutable-radix"
)

func init() {
	agro.RegisterMetadataProvider("temp", newTempMetadata)
}

type temp struct {
	mut      sync.Mutex
	inode    agro.INodeID
	tree     *iradix.Tree
	volIndex map[string]agro.VolumeID
	vol      agro.VolumeID
}

func newTempMetadata(address string) agro.MetadataService {
	return &temp{
		volIndex: make(map[string]agro.VolumeID),
		tree:     iradix.New(),
	}
}

func (t *temp) Mkfs() error { return nil }

func (t *temp) CreateVolume(volume string) error {
	t.mut.Lock()
	defer t.mut.Unlock()

	tx := t.tree.Txn()

	k := []byte(agro.Path{Volume: volume, Path: "/"}.Key())
	if _, ok := tx.Get(k); !ok {
		tx.Insert(k, (*models.Directory)(nil))
		t.tree = tx.Commit()
		t.volIndex[volume] = t.vol
		t.vol++
	}

	// TODO(jzelinskie): maybe raise volume already exists
	return nil
}

func (t *temp) CommitInodeIndex() (agro.INodeID, error) {
	t.mut.Lock()
	defer t.mut.Unlock()

	t.inode++
	return t.inode, nil
}

func (t *temp) Mkdir(p agro.Path, dir *models.Directory) error {
	if p.Path == "/" {
		return errors.New("can't create the root directory")
	}

	t.mut.Lock()
	defer t.mut.Unlock()

	tx := t.tree.Txn()

	k := []byte(p.Key())
	if _, ok := tx.Get(k); ok {
		return &os.PathError{
			Op:   "mkdir",
			Path: p.Path,
			Err:  os.ErrExist,
		}
	}
	tx.Insert(k, dir)

	for {
		p.Path, _ = path.Split(strings.TrimSuffix(p.Path, "/"))
		if p.Path == "" {
			break
		}
		k = []byte(p.Key())
		if _, ok := tx.Get(k); !ok {
			return &os.PathError{
				Op:   "stat",
				Path: p.Path,
				Err:  os.ErrNotExist,
			}
		}
	}

	t.tree = tx.Commit()
	return nil
}

func (t *temp) Getdir(p agro.Path) (*models.Directory, []agro.Path, error) {
	var (
		tx = t.tree.Txn()
		k  = []byte(p.Key())
	)
	v, ok := tx.Get(k)
	if !ok {
		return nil, nil, &os.PathError{
			Op:   "stat",
			Path: p.Path,
			Err:  os.ErrNotExist,
		}
	}

	var (
		dir     = v.(*models.Directory)
		prefix  = []byte(p.SubdirsPrefix())
		subdirs []agro.Path
	)
	tx.Root().WalkPrefix(prefix, func(k []byte, v interface{}) bool {
		subdirs = append(subdirs, agro.Path{
			Volume: p.Volume,
			Path:   fmt.Sprintf("%s%s", p.Path, bytes.TrimPrefix(k, prefix)),
		})
		return false
	})
	return dir, subdirs, nil
}

func (t *temp) GetVolumes() ([]string, error) {
	var (
		iter = t.tree.Root().Iterator()
		out  []string
		last string
	)
	for {
		k, _, ok := iter.Next()
		if !ok {
			break
		}
		if i := bytes.IndexByte(k, ':'); i != -1 {
			vol := string(k[:i])
			if vol == last {
				continue
			}
			out = append(out, vol)
			last = vol
		}
	}
	return out, nil
}

func (t *temp) GetVolumeID(volume string) (agro.VolumeID, error) {
	t.mut.Lock()
	defer t.mut.Unlock()

	if vol, ok := t.volIndex[volume]; ok {
		return vol, nil
	}
	return 0, errors.New("temp: no such volume exists")
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"path"
	"sort"
	"strings"
	"text/template"
	"time"
)

var blogTitle = flag.String("title", "blog", "Blog title")
var destinationExt = flag.String("extension", "", "Destination file extension")
var postsPath = flag.String("posts", "posts", "Posts directory, comma separated for multiple directories")
var templatesPath = flag.String("templates", "templates", "Templates directory")
var destinationPath = flag.String("destination", "destination", "Destination directory")
var siteRoot = flag.String("root", "/", "Site root path")
var templatePrint = flag.String("print", "", "Print out a template for a snippet or a blog post")
var templateAuthor = flag.String("author", "", "Set a default post author")

const templateFileName = "template.html"
const rssTemplateFileName = "rsstemplate.html"

func main() {

	flag.Parse()

	if *templatePrint != "" {
		var article Article
		now := time.Now().Add(15 * time.Minute)
		article.DateModified = &now

		if *templateAuthor != "" {
			article.Author = *templateAuthor
		}

		switch *templatePrint {
		case "post":
			article.Title = "Blog post"
			article.Type = Post
			break
		case "snippet":
			article.Type = Snippet
			break
		default:
			log.Fatal("post and snippet are the only allowed parameters for -print")
		}

		article.Print()

		return
	}

	log.Printf("Blog title is %s", *blogTitle)

	destinationDir, destinationDirErr := os.Open(*destinationPath)

	if destinationDirErr != nil {
		log.Fatal("Destination directory could not be open: ", destinationDirErr)
	}

	defer destinationDir.Close()

	funcMap := template.FuncMap{
		"longDate":     func(args ...interface{}) string { return args[0].(*time.Time).Format("Monday, _2 January 2006, 15:04") },
		"snippetDate":  func(args ...interface{}) string { return args[0].(*time.Time).Format("Jan _2 2006, 15:04") },
		"shortDate":    func(args ...interface{}) string { return args[0].(*time.Time).Format("Jan _2, 2006") },
		"atomDate":     func(args ...interface{}) string { return args[0].(*time.Time).Format("2006-01-02T15:04:05Z07:00") },
		"Snippet":      func(args ...interface{}) bool { return args[0].(*Article).Type == Snippet },
		"last":         func(index, count int) bool { return index == count-1 },
		"tagIndexName": func(tag string) string { return "tag-" + tag + *destinationExt },
		"path": func(article Article) string {
			return article.FullPath()
		},
	}

	mainTemplate := template.Must(template.New("template.html").Funcs(funcMap).ParseFiles(path.Join(*templatesPath, templateFileName)))
	mainRssTemplate := template.Must(template.New("rsstemplate.html").Funcs(funcMap).ParseFiles(path.Join(*templatesPath, rssTemplateFileName)))

	now := time.Now()

	type PostFile struct {
		Name      string
		Extension string
		Path      string
	}

	sourceFiles := []PostFile{}

	log.Println("Using posts directory:", *postsPath)

	for _, postDir := range strings.Split(*postsPath, ",") {
		log.Println("Trying directory", postDir)

		files, filesErr := ioutil.ReadDir(postDir)
		if filesErr != nil {
			log.Fatal("Source directory could not be read")
		}

		for _, file := range files {
			ext := path.Ext(file.Name())

			name := strings.TrimSuffix(path.Base(file.Name()), ext)

			if file.IsDir() || (ext != ".markdown" && ext != ".md" && ext != ".txt") {
				log.Println("Skipping file", file.Name())
				continue
			}

			log.Println("Adding file", file.Name())
			sourceFiles = append(sourceFiles, PostFile{Name: name, Extension: ext, Path: path.Join(postDir, file.Name())})
		}
	}

	var articles Articles

	var indexArticles Articles
	var feedArticles Articles
	var snippetArticles Articles

	for _, sourceFile := range sourceFiles {

		fileContent, fileError := ioutil.ReadFile(sourceFile.Path)

		if fileError != nil {
			log.Fatal("Read file error", fileError)
		}

		sourceBuffer := bytes.NewBuffer(fileContent)

		mdReader := bufio.NewReader(sourceBuffer)

		line, lineErr := mdReader.ReadString('\n')

		if lineErr != nil {
			continue
		}

		var article Article

		if strings.HasPrefix(line, "---") {
			article = ReadArticle(mdReader)

			article.Filename = sourceFile.Name + *destinationExt
		} else {
			log.Fatal("Bad article")
		}

		article.Description = strings.Replace(article.Description, "$SITEROOT", *siteRoot, -1)
		article.Content = strings.Replace(article.Content, "$SITEROOT", *siteRoot, -1)

		if article.DateModified == nil {
			article.DateModified = new(time.Time)
		}

		article.Identifier = sourceFile.Name

		if strings.HasSuffix(sourceFile.Name, "draft") {
			article.Draft = true
		}

		articles = append(articles, &article)

		if article.Type == Page {
			continue
		}

		if article.Draft {
			continue
		}

		if article.Type == Post {
			feedArticles = append(feedArticles, &article)
		}

		if article.Type == Snippet {
			snippetArticles = append(snippetArticles, &article)
		}

		indexArticles = append(indexArticles, &article)
	}

	tags := map[string]bool{}

	sort.Sort(articles)
	sort.Sort(indexArticles)
	sort.Sort(feedArticles)
	sort.Sort(snippetArticles)

	indexBuffer := bytes.NewBufferString("")
	rssIndexBuffer := bytes.NewBufferString("")
	snippetrssIndexBuffer := bytes.NewBufferString("")

	mainTemplate.Execute(indexBuffer, map[string]interface{}{"Title": blogTitle, "Home": true, "Root": siteRoot, "Articles": indexArticles, "CreatedTime": now})
	mainRssTemplate.Execute(rssIndexBuffer, map[string]interface{}{"Title": blogTitle, "Home": true, "Root": siteRoot, "File": "index.xml", "Articles": feedArticles, "CreatedTime": &now})
	mainRssTemplate.Execute(snippetrssIndexBuffer, map[string]interface{}{"Title": blogTitle, "Home": true, "Root": siteRoot, "File": "snippets.xml", "Articles": snippetArticles, "CreatedTime": &now})

	for _, article := range articles {

		destFileBuffer := bytes.NewBufferString("")

		mainTemplate.Execute(destFileBuffer, map[string]interface{}{
			"BlogTitle": blogTitle,
			"Article":   article,
			"Title":     string(article.Title + " – " + *blogTitle),
			"Home":      false,
			"Root":      siteRoot,
		})

		for _, tag := range article.Tags {
			tags[tag] = true
		}

		destinationFileName := path.Join(destinationDir.Name(), article.FullPath())

		os.MkdirAll(path.Join(destinationDir.Name(), article.BasePath()), os.ModePerm)

		writeErr := ioutil.WriteFile(destinationFileName, destFileBuffer.Bytes(), os.ModePerm)

		if writeErr != nil {
			log.Println(writeErr)
		}
	}

	indexFileName := path.Join(destinationDir.Name(), "index.html")
	rssIndexFileName := path.Join(destinationDir.Name(), "index.xml")
	snippetIndexFileName := path.Join(destinationDir.Name(), "snippets.xml")

	ioutil.WriteFile(indexFileName, indexBuffer.Bytes(), os.ModePerm)
	ioutil.WriteFile(rssIndexFileName, rssIndexBuffer.Bytes(), os.ModePerm)
	ioutil.WriteFile(snippetIndexFileName, snippetrssIndexBuffer.Bytes(), os.ModePerm)

	for tag := range tags {

		tagIndexBuffer := bytes.NewBufferString("")
		var tagArticles Articles

		for _, article := range indexArticles {

			if !article.HasTag(tag) {
				continue
			}

			tagArticles = append(tagArticles, article)
		}

		mainTemplate.Execute(tagIndexBuffer, map[string]interface{}{"Articles": tagArticles, "Title": "Tag: " + tag + " – " + *blogTitle, "Home": false, "Root": siteRoot})

		tagIndexFileName := path.Join(destinationDir.Name(), "tag-"+tag+*destinationExt)
		ioutil.WriteFile(tagIndexFileName, tagIndexBuffer.Bytes(), os.ModePerm)
	}
}
<commit_msg>Less logging<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"path"
	"sort"
	"strings"
	"text/template"
	"time"
)

var blogTitle = flag.String("title", "blog", "Blog title")
var destinationExt = flag.String("extension", "", "Destination file extension")
var postsPath = flag.String("posts", "posts", "Posts directory, comma separated for multiple directories")
var templatesPath = flag.String("templates", "templates", "Templates directory")
var destinationPath = flag.String("destination", "destination", "Destination directory")
var siteRoot = flag.String("root", "/", "Site root path")
var templatePrint = flag.String("print", "", "Print out a template for a snippet or a blog post")
var templateAuthor = flag.String("author", "", "Set a default post author")

const templateFileName = "template.html"
const rssTemplateFileName = "rsstemplate.html"

func main() {

	flag.Parse()

	if *templatePrint != "" {
		var article Article
		now := time.Now().Add(15 * time.Minute)
		article.DateModified = &now

		if *templateAuthor != "" {
			article.Author = *templateAuthor
		}

		switch *templatePrint {
		case "post":
			article.Title = "Blog post"
			article.Type = Post
			break
		case "snippet":
			article.Type = Snippet
			break
		default:
			log.Fatal("post and snippet are the only allowed parameters for -print")
		}

		article.Print()

		return
	}

	log.Printf("Blog title is %s", *blogTitle)

	destinationDir, destinationDirErr := os.Open(*destinationPath)

	if destinationDirErr != nil {
		log.Fatal("Destination directory could not be open: ", destinationDirErr)
	}

	defer destinationDir.Close()

	funcMap := template.FuncMap{
		"longDate":     func(args ...interface{}) string { return args[0].(*time.Time).Format("Monday, _2 January 2006, 15:04") },
		"snippetDate":  func(args ...interface{}) string { return args[0].(*time.Time).Format("Jan _2 2006, 15:04") },
		"shortDate":    func(args ...interface{}) string { return args[0].(*time.Time).Format("Jan _2, 2006") },
		"atomDate":     func(args ...interface{}) string { return args[0].(*time.Time).Format("2006-01-02T15:04:05Z07:00") },
		"Snippet":      func(args ...interface{}) bool { return args[0].(*Article).Type == Snippet },
		"last":         func(index, count int) bool { return index == count-1 },
		"tagIndexName": func(tag string) string { return "tag-" + tag + *destinationExt },
		"path": func(article Article) string {
			return article.FullPath()
		},
	}

	mainTemplate := template.Must(template.New("template.html").Funcs(funcMap).ParseFiles(path.Join(*templatesPath, templateFileName)))
	mainRssTemplate := template.Must(template.New("rsstemplate.html").Funcs(funcMap).ParseFiles(path.Join(*templatesPath, rssTemplateFileName)))

	now := time.Now()

	type PostFile struct {
		Name      string
		Extension string
		Path      string
	}

	sourceFiles := []PostFile{}

	for _, postDir := range strings.Split(*postsPath, ",") {

		files, filesErr := ioutil.ReadDir(postDir)
		if filesErr != nil {
			log.Fatal("Source directory could not be read")
		}

		for _, file := range files {
			ext := path.Ext(file.Name())

			name := strings.TrimSuffix(path.Base(file.Name()), ext)

			if file.IsDir() || (ext != ".markdown" && ext != ".md" && ext != ".txt") {
				continue
			}

			sourceFiles = append(sourceFiles, PostFile{Name: name, Extension: ext, Path: path.Join(postDir, file.Name())})
		}
	}

	var articles Articles

	var indexArticles Articles
	var feedArticles Articles
	var snippetArticles Articles

	for _, sourceFile := range sourceFiles {

		fileContent, fileError := ioutil.ReadFile(sourceFile.Path)

		if fileError != nil {
			log.Fatal("Read file error", fileError)
		}

		sourceBuffer := bytes.NewBuffer(fileContent)

		mdReader := bufio.NewReader(sourceBuffer)

		line, lineErr := mdReader.ReadString('\n')

		if lineErr != nil {
			continue
		}

		var article Article

		if strings.HasPrefix(line, "---") {
			article = ReadArticle(mdReader)

			article.Filename = sourceFile.Name + *destinationExt
		} else {
			log.Fatal("Bad article")
		}

		article.Description = strings.Replace(article.Description, "$SITEROOT", *siteRoot, -1)
		article.Content = strings.Replace(article.Content, "$SITEROOT", *siteRoot, -1)

		if article.DateModified == nil {
			article.DateModified = new(time.Time)
		}

		article.Identifier = sourceFile.Name

		if strings.HasSuffix(sourceFile.Name, "draft") {
			article.Draft = true
		}

		articles = append(articles, &article)

		if article.Type == Page {
			continue
		}

		if article.Draft {
			continue
		}

		if article.Type == Post {
			feedArticles = append(feedArticles, &article)
		}

		if article.Type == Snippet {
			snippetArticles = append(snippetArticles, &article)
		}

		indexArticles = append(indexArticles, &article)
	}

	tags := map[string]bool{}

	sort.Sort(articles)
	sort.Sort(indexArticles)
	sort.Sort(feedArticles)
	sort.Sort(snippetArticles)

	indexBuffer := bytes.NewBufferString("")
	rssIndexBuffer := bytes.NewBufferString("")
	snippetrssIndexBuffer := bytes.NewBufferString("")

	mainTemplate.Execute(indexBuffer, map[string]interface{}{"Title": blogTitle, "Home": true, "Root": *siteRoot, "Articles": indexArticles, "CreatedTime": now})
	mainRssTemplate.Execute(rssIndexBuffer, map[string]interface{}{"Title": blogTitle, "Home": true, "Root": *siteRoot, "File": "index.xml", "Articles": feedArticles, "CreatedTime": &now})
	mainRssTemplate.Execute(snippetrssIndexBuffer, map[string]interface{}{"Title": blogTitle, "Home": true, "Root": *siteRoot, "File": "snippets.xml", "Articles": snippetArticles, "CreatedTime": &now})

	for _, article := range articles {

		destFileBuffer := bytes.NewBufferString("")

		mainTemplate.Execute(destFileBuffer, map[string]interface{}{
			"BlogTitle": blogTitle,
			"Article":   article,
			"Title":     string(article.Title + " – " + *blogTitle),
			"Home":      false,
			"Root":      *siteRoot,
		})

		for _, tag := range article.Tags {
			tags[tag] = true
		}

		destinationFileName := path.Join(destinationDir.Name(), article.FullPath())

		os.MkdirAll(path.Join(destinationDir.Name(), article.BasePath()), os.ModePerm)

		writeErr := ioutil.WriteFile(destinationFileName, destFileBuffer.Bytes(), os.ModePerm)

		if writeErr != nil {
			log.Println(writeErr)
		}
	}

	indexFileName := path.Join(destinationDir.Name(), "index.html")
	rssIndexFileName := path.Join(destinationDir.Name(), "index.xml")
	snippetIndexFileName := path.Join(destinationDir.Name(), "snippets.xml")

	ioutil.WriteFile(indexFileName, indexBuffer.Bytes(), os.ModePerm)
	ioutil.WriteFile(rssIndexFileName, rssIndexBuffer.Bytes(), os.ModePerm)
	ioutil.WriteFile(snippetIndexFileName, snippetrssIndexBuffer.Bytes(), os.ModePerm)

	for tag := range tags {

		tagIndexBuffer := bytes.NewBufferString("")
		var tagArticles Articles

		for _, article := range indexArticles {

			if !article.HasTag(tag) {
				continue
			}

			tagArticles = append(tagArticles, article)
		}

		mainTemplate.Execute(tagIndexBuffer, map[string]interface{}{"Articles": tagArticles, "Title": "Tag: " + tag + " – " + *blogTitle, "Home": false, "Root": *siteRoot})

		tagIndexFileName := path.Join(destinationDir.Name(), "tag-"+tag+*destinationExt)
		ioutil.WriteFile(tagIndexFileName, tagIndexBuffer.Bytes(), os.ModePerm)
	}
}
<|endoftext|>


<commit_before>// Package bot implements an IRC bot with plugins.
package bot

import (
	"log"
	"sort"
	"strings"
	"time"

	"github.com/StalkR/goircbot/lib/tls"
	"github.com/fluffle/goirc/client"
	"github.com/fluffle/goirc/state"
)

// Bot represents an IRC bot, with IRC client object, settings, commands.
type Bot interface {
	Run()                                                     // Run bot, reconnect if disconnect.
	Quit(msg string)                                          // Quit bot from IRC with a msg.
	Commands() *Commands                                      // For plugins to Add/Del commands.
	Action(t, msg string)                                     // Shortcut to Conn().Action()
	Connected() bool                                          // Shortcut to Conn().Connected()
	HandleFunc(n string, h client.HandlerFunc) client.Remover // Shortcut to Conn().HandleFunc()
	Invite(nick, channel string)                              // Shortcut to Conn().Nick()
	Join(c string)                                            // Shortcut to Conn().Join()
	Me() *state.Nick                                          // Shortcut to Conn().Me()
	Mode(t string, m ...string)                               // Shortcut to Conn().Mode()
	Nick(nick string)                                         // Shortcut to Conn().Nick()
	Notice(t, msg string)                                     // Shortcut to Conn().Notice()
	Part(c string, m ...string)                               // Shortcut to Conn().Part()
	Privmsg(t, msg string)                                    // Shortcut to Conn().Privmsg()
	Conn() *client.Conn                                       // Conn returns the underlying goirc client connection.
	Channels() []string                                       // Channels returns list of channels which bot has joined.
}

// NewBot creates a new Bot implementation with a set of parameters.
func NewBot(host string, ssl bool, nick, ident string, channels []string) Bot {
	return NewBotWithProxy(host, ssl, nick, ident, channels, "")
}

// NewBotWithProxy creates a new Bot implementation with a set of parameters and a proxy.
func NewBotWithProxy(host string, ssl bool, nick, ident string, channels []string, proxy string) Bot {
	hostPort := strings.SplitN(host, ":", 2)
	cfg := &client.Config{
		Me:          &state.Nick{Nick: nick},
		NewNick:     func(s string) string { return s + "_" },
		PingFreq:    3 * time.Minute,
		QuitMessage: "I have to go.",
		Server:      host,
		SSL:         ssl,
		SSLConfig:   tls.Config(hostPort[0]),
		Version:     "Powered by GoIRCBot",
		Recover:     (*client.Conn).LogPanic,
		SplitLen:    450,
		Proxy:       proxy,
	}
	cfg.Me.Ident = ident
	cfg.Me.Name = nick

	conn := client.Client(cfg)
	conn.EnableStateTracking()
	b := &BotImpl{
		conn:      conn,
		reconnect: true,
		quit:      make(chan bool),
		commands:  NewCommands(),
		channels:  channels,
	}

	// Join channels on connect and mark ourselves as a Bot.
	conn.HandleFunc("connected",
		func(conn *client.Conn, line *client.Line) {
			conn.Mode(conn.Me().Nick, "+B")
			for _, channel := range b.channels {
				conn.Join(channel)
			}
		})

	// Signal disconnect to Bot.Run so it can reconnect.
	conn.HandleFunc("disconnected",
		func(conn *client.Conn, line *client.Line) {
			channels := b.Channels()
			// On the first disconnect, we will remember channels. Then state is
			// reinitialized and another connection is attempted. If that one fails,
			// we end up with an empty state, so no channels and we do not want to
			// save that, it does not make much sense. So ignore empty values.
			if len(channels) > 0 {
				b.channels = b.Channels()
			}
			b.quit <- true
		})

	conn.HandleFunc("privmsg",
		func(conn *client.Conn, line *client.Line) { b.commands.Handle(b, line) })

	b.commands.Add("help", Command{
		Help:    "show commands or detailed help",
		Handler: func(e *Event) { b.commands.Help(e) },
		Pub:     true,
		Priv:    true,
		Hidden:  false})

	return b
}

// BotImpl implements Bot.
type BotImpl struct {
	conn      *client.Conn
	reconnect bool
	quit      chan bool
	commands  *Commands
	channels  []string
}

// Run starts the Bot by connecting it to IRC. It automatically reconnects.
func (b *BotImpl) Run() {
	for b.reconnect {
		if err := b.Conn().Connect(); err != nil {
			log.Println("Connection error:", err, "- reconnecting in 1 minute")
			time.Sleep(time.Minute)
			continue
		}

		// Wait on quit channel for a disconnect event.
		<-b.quit
	}
}

// Quit quits the bot from IRC (and no reconnect).
func (b *BotImpl) Quit(msg string) {
	b.reconnect = false
	b.conn.Quit(msg)
}

// Commands returns the underlying Commands.
func (b *BotImpl) Commands() *Commands { return b.commands }

// Shortcuts to b.Conn to ease mocking of Bot interface.
func (b *BotImpl) Action(t, msg string) { b.Conn().Action(t, msg) }
func (b *BotImpl) Connected() bool      { return b.Conn().Connected() }
func (b *BotImpl) HandleFunc(n string, h client.HandlerFunc) client.Remover {
	return b.Conn().HandleFunc(n, h)
}
func (b *BotImpl) Invite(nick, channel string) { b.Conn().Invite(nick, channel) }
func (b *BotImpl) Join(c string)               { b.Conn().Join(c) }
func (b *BotImpl) Me() *state.Nick             { return b.Conn().Me() }
func (b *BotImpl) Mode(t string, m ...string)  { b.Conn().Mode(t, m...) }
func (b *BotImpl) Nick(nick string)            { b.Conn().Nick(nick) }
func (b *BotImpl) Notice(t, msg string)        { b.Conn().Notice(t, msg) }
func (b *BotImpl) Part(c string, m ...string)  { b.Conn().Part(c, m...) }
func (b *BotImpl) Privmsg(t, msg string)       { b.Conn().Privmsg(t, msg) }
func (b *BotImpl) Conn() *client.Conn          { return b.conn }
func (b *BotImpl) Channels() []string {
	var channels []string
	for name := range b.Conn().Me().Channels {
		channels = append(channels, name)
	}
	sort.Strings(channels)
	return channels
}
<commit_msg>bot: adjust on connect comment<commit_after>// Package bot implements an IRC bot with plugins.
package bot

import (
	"log"
	"sort"
	"strings"
	"time"

	"github.com/StalkR/goircbot/lib/tls"
	"github.com/fluffle/goirc/client"
	"github.com/fluffle/goirc/state"
)

// Bot represents an IRC bot, with IRC client object, settings, commands.
type Bot interface {
	Run()                                                     // Run bot, reconnect if disconnect.
	Quit(msg string)                                          // Quit bot from IRC with a msg.
	Commands() *Commands                                      // For plugins to Add/Del commands.
	Action(t, msg string)                                     // Shortcut to Conn().Action()
	Connected() bool                                          // Shortcut to Conn().Connected()
	HandleFunc(n string, h client.HandlerFunc) client.Remover // Shortcut to Conn().HandleFunc()
	Invite(nick, channel string)                              // Shortcut to Conn().Nick()
	Join(c string)                                            // Shortcut to Conn().Join()
	Me() *state.Nick                                          // Shortcut to Conn().Me()
	Mode(t string, m ...string)                               // Shortcut to Conn().Mode()
	Nick(nick string)                                         // Shortcut to Conn().Nick()
	Notice(t, msg string)                                     // Shortcut to Conn().Notice()
	Part(c string, m ...string)                               // Shortcut to Conn().Part()
	Privmsg(t, msg string)                                    // Shortcut to Conn().Privmsg()
	Conn() *client.Conn                                       // Conn returns the underlying goirc client connection.
	Channels() []string                                       // Channels returns list of channels which bot has joined.
}

// NewBot creates a new Bot implementation with a set of parameters.
func NewBot(host string, ssl bool, nick, ident string, channels []string) Bot {
	return NewBotWithProxy(host, ssl, nick, ident, channels, "")
}

// NewBotWithProxy creates a new Bot implementation with a set of parameters and a proxy.
func NewBotWithProxy(host string, ssl bool, nick, ident string, channels []string, proxy string) Bot {
	hostPort := strings.SplitN(host, ":", 2)
	cfg := &client.Config{
		Me:          &state.Nick{Nick: nick},
		NewNick:     func(s string) string { return s + "_" },
		PingFreq:    3 * time.Minute,
		QuitMessage: "I have to go.",
		Server:      host,
		SSL:         ssl,
		SSLConfig:   tls.Config(hostPort[0]),
		Version:     "Powered by GoIRCBot",
		Recover:     (*client.Conn).LogPanic,
		SplitLen:    450,
		Proxy:       proxy,
	}
	cfg.Me.Ident = ident
	cfg.Me.Name = nick

	conn := client.Client(cfg)
	conn.EnableStateTracking()
	b := &BotImpl{
		conn:      conn,
		reconnect: true,
		quit:      make(chan bool),
		commands:  NewCommands(),
		channels:  channels,
	}

	// On connect, mark ourselves as bot first then join channels (can be long).
	conn.HandleFunc("connected",
		func(conn *client.Conn, line *client.Line) {
			conn.Mode(conn.Me().Nick, "+B")
			for _, channel := range b.channels {
				conn.Join(channel)
			}
		})

	// Signal disconnect to Bot.Run so it can reconnect.
	conn.HandleFunc("disconnected",
		func(conn *client.Conn, line *client.Line) {
			channels := b.Channels()
			// On the first disconnect, we will remember channels. Then state is
			// reinitialized and another connection is attempted. If that one fails,
			// we end up with an empty state, so no channels and we do not want to
			// save that, it does not make much sense. So ignore empty values.
			if len(channels) > 0 {
				b.channels = b.Channels()
			}
			b.quit <- true
		})

	conn.HandleFunc("privmsg",
		func(conn *client.Conn, line *client.Line) { b.commands.Handle(b, line) })

	b.commands.Add("help", Command{
		Help:    "show commands or detailed help",
		Handler: func(e *Event) { b.commands.Help(e) },
		Pub:     true,
		Priv:    true,
		Hidden:  false})

	return b
}

// BotImpl implements Bot.
type BotImpl struct {
	conn      *client.Conn
	reconnect bool
	quit      chan bool
	commands  *Commands
	channels  []string
}

// Run starts the Bot by connecting it to IRC. It automatically reconnects.
func (b *BotImpl) Run() {
	for b.reconnect {
		if err := b.Conn().Connect(); err != nil {
			log.Println("Connection error:", err, "- reconnecting in 1 minute")
			time.Sleep(time.Minute)
			continue
		}

		// Wait on quit channel for a disconnect event.
		<-b.quit
	}
}

// Quit quits the bot from IRC (and no reconnect).
func (b *BotImpl) Quit(msg string) {
	b.reconnect = false
	b.conn.Quit(msg)
}

// Commands returns the underlying Commands.
func (b *BotImpl) Commands() *Commands { return b.commands }

// Shortcuts to b.Conn to ease mocking of Bot interface.
func (b *BotImpl) Action(t, msg string) { b.Conn().Action(t, msg) }
func (b *BotImpl) Connected() bool      { return b.Conn().Connected() }
func (b *BotImpl) HandleFunc(n string, h client.HandlerFunc) client.Remover {
	return b.Conn().HandleFunc(n, h)
}
func (b *BotImpl) Invite(nick, channel string) { b.Conn().Invite(nick, channel) }
func (b *BotImpl) Join(c string)               { b.Conn().Join(c) }
func (b *BotImpl) Me() *state.Nick             { return b.Conn().Me() }
func (b *BotImpl) Mode(t string, m ...string)  { b.Conn().Mode(t, m...) }
func (b *BotImpl) Nick(nick string)            { b.Conn().Nick(nick) }
func (b *BotImpl) Notice(t, msg string)        { b.Conn().Notice(t, msg) }
func (b *BotImpl) Part(c string, m ...string)  { b.Conn().Part(c, m...) }
func (b *BotImpl) Privmsg(t, msg string)       { b.Conn().Privmsg(t, msg) }
func (b *BotImpl) Conn() *client.Conn          { return b.conn }
func (b *BotImpl) Channels() []string {
	var channels []string
	for name := range b.Conn().Me().Channels {
		channels = append(channels, name)
	}
	sort.Strings(channels)
	return channels
}
<|endoftext|>


<commit_before>// Package minhash implements the bottom-k sketch for streaming set similarity.
/*

For more information,
    http://research.neustar.biz/2012/07/09/sketch-of-the-day-k-minimum-values/

    MinHashing:
    http://infolab.stanford.edu/~ullman/mmds/ch3.pdf
    https://en.wikipedia.org/wiki/MinHash

    BottomK:
    http://www.math.tau.ac.il/~haimk/papers/p225-cohen.pdf
    http://cohenwang.org/edith/Papers/metrics394-cohen.pdf

    http://www.mpi-inf.mpg.de/~rgemulla/publications/beyer07distinct.pdf

This package works best when provided with a strong 64-bit hash function, such as CityHash, Spooky, Murmur3, or SipHash.

*/
package minhash

import (
	"container/heap"
	"math"
	"sort"
)

type intHeap []uint64

func (h intHeap) Len() int { return len(h) }

// actually Greater, since we want a max-heap
func (h intHeap) Less(i, j int) bool { return h[i] > h[j] }
func (h intHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

func (h *intHeap) Push(x interface{}) {
	*h = append(*h, x.(uint64))
}

func (h *intHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

// BottomK is a bottom-k sketch of a set
type BottomK struct {
	size     int
	h        Hash64Func
	minimums *intHeap
}

// NewBottomK returns a new BottomK implementation.
func NewBottomK(h Hash64Func, k int) *BottomK {
	return &BottomK{
		size:     k,
		h:        h,
		minimums: &intHeap{},
	}
}

func (m *BottomK) Push(x interface{}) {
	m.PushBytes(toBytes(x))
}

// Push adds an element to the set.
func (m *BottomK) PushBytes(b []byte) {

	i64 := m.h(b)

	if i64 == 0 {
		return
	}

	if len(*m.minimums) < m.size {
		heap.Push(m.minimums, i64)
		return
	}

	if i64 < (*m.minimums)[0] {
		(*m.minimums)[0] = i64
		heap.Fix(m.minimums, 0)
	}
}

// Merge combines the signatures of the second set, creating the signature of their union.
func (m *BottomK) Merge(m2 *BottomK) {
	for _, v := range *m2.minimums {

		if len(*m.minimums) < m.size {
			heap.Push(m.minimums, v)
			continue
		}

		if v < (*m.minimums)[0] {
			(*m.minimums)[0] = v
			heap.Fix(m.minimums, 0)
		}
	}
}

// Cardinality estimates the cardinality of the set
func (m *BottomK) Cardinality() int {
	return int(float64(len(*m.minimums)-1) / (float64((*m.minimums)[0]) / float64(math.MaxUint64)))

}

// Signature returns a signature for the set.
func (m *BottomK) Signature() []uint64 {
	mins := make(intHeap, len(*m.minimums))
	copy(mins, *m.minimums)
	sort.Sort(mins)
	return mins
}

// Similarity computes an estimate for the similarity between the two sets.
func (m *BottomK) Similarity(m2 *BottomK) float64 {

	if m.size != m2.size {
		panic("minhash minimums size mismatch")
	}

	mins := make(map[uint64]int, len(*m.minimums))

	for _, v := range *m.minimums {
		mins[v]++
	}

	intersect := 0

	for _, v := range *m2.minimums {
		if count, ok := mins[v]; ok && count > 0 {
			intersect++
			mins[v] = count - 1
		}
	}

	maxlength := len(*m.minimums)
	if maxlength < len(*m2.minimums) {
		maxlength = len(*m2.minimums)
	}

	return float64(intersect) / float64(maxlength)
}
<commit_msg>Change Has64 type or HashFunc.<commit_after>// Package minhash implements the bottom-k sketch for streaming set similarity.
/*

For more information,
    http://research.neustar.biz/2012/07/09/sketch-of-the-day-k-minimum-values/

    MinHashing:
    http://infolab.stanford.edu/~ullman/mmds/ch3.pdf
    https://en.wikipedia.org/wiki/MinHash

    BottomK:
    http://www.math.tau.ac.il/~haimk/papers/p225-cohen.pdf
    http://cohenwang.org/edith/Papers/metrics394-cohen.pdf

    http://www.mpi-inf.mpg.de/~rgemulla/publications/beyer07distinct.pdf

This package works best when provided with a strong 64-bit hash function, such as CityHash, Spooky, Murmur3, or SipHash.

*/
package minhash

import (
	"container/heap"
	"math"
	"sort"
)

type intHeap []uint64

func (h intHeap) Len() int { return len(h) }

// actually Greater, since we want a max-heap
func (h intHeap) Less(i, j int) bool { return h[i] > h[j] }
func (h intHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

func (h *intHeap) Push(x interface{}) {
	*h = append(*h, x.(uint64))
}

func (h *intHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

// BottomK is a bottom-k sketch of a set
type BottomK struct {
	size     int
	h        HashFuncFunc
	minimums *intHeap
}

// NewBottomK returns a new BottomK implementation.
func NewBottomK(h HashFuncFunc, k int) *BottomK {
	return &BottomK{
		size:     k,
		h:        h,
		minimums: &intHeap{},
	}
}

func (m *BottomK) Push(x interface{}) {
	m.PushBytes(toBytes(x))
}

// Push adds an element to the set.
func (m *BottomK) PushBytes(b []byte) {

	i64 := m.h(b)

	if i64 == 0 {
		return
	}

	if len(*m.minimums) < m.size {
		heap.Push(m.minimums, i64)
		return
	}

	if i64 < (*m.minimums)[0] {
		(*m.minimums)[0] = i64
		heap.Fix(m.minimums, 0)
	}
}

// Merge combines the signatures of the second set, creating the signature of their union.
func (m *BottomK) Merge(m2 *BottomK) {
	for _, v := range *m2.minimums {

		if len(*m.minimums) < m.size {
			heap.Push(m.minimums, v)
			continue
		}

		if v < (*m.minimums)[0] {
			(*m.minimums)[0] = v
			heap.Fix(m.minimums, 0)
		}
	}
}

// Cardinality estimates the cardinality of the set
func (m *BottomK) Cardinality() int {
	return int(float64(len(*m.minimums)-1) / (float64((*m.minimums)[0]) / float64(math.MaxUint64)))

}

// Signature returns a signature for the set.
func (m *BottomK) Signature() []uint64 {
	mins := make(intHeap, len(*m.minimums))
	copy(mins, *m.minimums)
	sort.Sort(mins)
	return mins
}

// Similarity computes an estimate for the similarity between the two sets.
func (m *BottomK) Similarity(m2 *BottomK) float64 {

	if m.size != m2.size {
		panic("minhash minimums size mismatch")
	}

	mins := make(map[uint64]int, len(*m.minimums))

	for _, v := range *m.minimums {
		mins[v]++
	}

	intersect := 0

	for _, v := range *m2.minimums {
		if count, ok := mins[v]; ok && count > 0 {
			intersect++
			mins[v] = count - 1
		}
	}

	maxlength := len(*m.minimums)
	if maxlength < len(*m2.minimums) {
		maxlength = len(*m2.minimums)
	}

	return float64(intersect) / float64(maxlength)
}
<|endoftext|>


<commit_before>package bot

import (
	"errors"

	"github.com/ChimeraCoder/anaconda"
	"github.com/erbridge/gotwit/callback"
	"github.com/erbridge/gotwit/twitter"
)

type (
	Bot struct {
		screenName     string
		consumerConfig twitter.ConsumerConfig
		client         twitter.Client
		callbacks      map[callback.Type]map[int]callback.Callback
		nextCallbackId int
	}
)

var (
	activeBot *Bot
)

func New(screenName string, consumerConfig twitter.ConsumerConfig, accessConfig twitter.AccessConfig) Bot {
	b := Bot{
		screenName:     screenName,
		consumerConfig: consumerConfig,
		callbacks:      make(map[callback.Type]map[int]callback.Callback),
	}
	b.client = twitter.NewClient(accessConfig, b.handle)
	return b
}

func (b *Bot) Start() error {
	if activeBot == nil {
		activeBot = b
	} else if activeBot == b {
		return errors.New("This bot is already started")
	} else {
		return errors.New("Only one bot can be active at once")
	}

	twitter.BindConsumer(b.consumerConfig)
	b.client.Start()

	return nil
}

func (b *Bot) Stop() error {
	if activeBot == b {
		activeBot = nil
	} else {
		return errors.New("This bot hasn't been started")
	}

	twitter.UnbindConsumer()
	b.client.Stop()

	return nil
}

func (b *Bot) RegisterCallback(t callback.Type, cb callback.Callback) (id int) {
	if _, ok := b.callbacks[t]; !ok {
		b.callbacks[t] = make(map[int]callback.Callback)
	}

	id = b.nextCallbackId
	b.callbacks[t][id] = cb
	b.nextCallbackId++
	return
}

func (b *Bot) UnregisterCallback(t callback.Type, id int) {
	delete(b.callbacks[t], id)
}

func (b *Bot) triggerCallback(t callback.Type, tweet anaconda.Tweet) {
	for _, cb := range b.callbacks[t] {
		cb(tweet)
	}
}

func (b *Bot) handle(tweet anaconda.Tweet) {
	if tweet.RetweetedStatus != nil {
		b.triggerCallback(callback.Retweet, tweet)
		return
	}

	if tweet.InReplyToScreenName == b.screenName {
		b.triggerCallback(callback.Reply, tweet)
		return
	}

	for _, entity := range tweet.Entities.User_mentions {
		if entity.Screen_name == b.screenName {
			b.triggerCallback(callback.Mention, tweet)
			return
		}
	}
}

func (b *Bot) Post(message string, nsfw bool) error {
	return b.client.Post(message, nsfw)
}

func (b *Bot) Reply(tweet anaconda.Tweet, message string, nsfw bool) error {
	return b.client.Reply(tweet, message, nsfw)
}
<commit_msg>Add a Bot.ScreenName method.<commit_after>package bot

import (
	"errors"

	"github.com/ChimeraCoder/anaconda"
	"github.com/erbridge/gotwit/callback"
	"github.com/erbridge/gotwit/twitter"
)

type (
	Bot struct {
		screenName     string
		consumerConfig twitter.ConsumerConfig
		client         twitter.Client
		callbacks      map[callback.Type]map[int]callback.Callback
		nextCallbackId int
	}
)

var (
	activeBot *Bot
)

func New(screenName string, consumerConfig twitter.ConsumerConfig, accessConfig twitter.AccessConfig) Bot {
	b := Bot{
		screenName:     screenName,
		consumerConfig: consumerConfig,
		callbacks:      make(map[callback.Type]map[int]callback.Callback),
	}
	b.client = twitter.NewClient(accessConfig, b.handle)
	return b
}

func (b *Bot) ScreenName() string {
	return b.screenName
}

func (b *Bot) Start() error {
	if activeBot == nil {
		activeBot = b
	} else if activeBot == b {
		return errors.New("This bot is already started")
	} else {
		return errors.New("Only one bot can be active at once")
	}

	twitter.BindConsumer(b.consumerConfig)
	b.client.Start()

	return nil
}

func (b *Bot) Stop() error {
	if activeBot == b {
		activeBot = nil
	} else {
		return errors.New("This bot hasn't been started")
	}

	twitter.UnbindConsumer()
	b.client.Stop()

	return nil
}

func (b *Bot) RegisterCallback(t callback.Type, cb callback.Callback) (id int) {
	if _, ok := b.callbacks[t]; !ok {
		b.callbacks[t] = make(map[int]callback.Callback)
	}

	id = b.nextCallbackId
	b.callbacks[t][id] = cb
	b.nextCallbackId++
	return
}

func (b *Bot) UnregisterCallback(t callback.Type, id int) {
	delete(b.callbacks[t], id)
}

func (b *Bot) triggerCallback(t callback.Type, tweet anaconda.Tweet) {
	for _, cb := range b.callbacks[t] {
		cb(tweet)
	}
}

func (b *Bot) handle(tweet anaconda.Tweet) {
	if tweet.RetweetedStatus != nil {
		b.triggerCallback(callback.Retweet, tweet)
		return
	}

	if tweet.InReplyToScreenName == b.screenName {
		b.triggerCallback(callback.Reply, tweet)
		return
	}

	for _, entity := range tweet.Entities.User_mentions {
		if entity.Screen_name == b.screenName {
			b.triggerCallback(callback.Mention, tweet)
			return
		}
	}
}

func (b *Bot) Post(message string, nsfw bool) error {
	return b.client.Post(message, nsfw)
}

func (b *Bot) Reply(tweet anaconda.Tweet, message string, nsfw bool) error {
	return b.client.Reply(tweet, message, nsfw)
}
<|endoftext|>