<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package azure

import (
	"io"
	"launchpad.net/gwacl"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/errors"
)

type azureStorage struct {
	storageContext
}

// storageContext is an abstraction that is there only to accommodate the need
// for using an azureStorage independently from an environ object in tests.
type storageContext interface {
	getContainer() string
	getStorageContext() (*gwacl.StorageContext, error)
}

// environStorageContext is a storageContext which gets its information from
// an azureEnviron object.
type environStorageContext struct {
	environ *azureEnviron
}

var _ storageContext = (*environStorageContext)(nil)

func (context *environStorageContext) getContainer() string {
	return context.environ.getSnapshot().ecfg.StorageContainerName()
}

func (context *environStorageContext) getStorageContext() (*gwacl.StorageContext, error) {
	return context.environ.getStorageContext()
}

func NewStorage(env *azureEnviron) environs.Storage {
	context := &environStorageContext{environ: env}
	return &azureStorage{context}
}

// azureStorage implements Storage.
var _ environs.Storage = (*azureStorage)(nil)

// Get is specified in the StorageReader interface.
func (storage *azureStorage) Get(name string) (io.ReadCloser, error) {
	context, err := storage.getStorageContext()
	if err != nil {
		return nil, err
	}
	reader, err := context.GetBlob(storage.getContainer(), name)
	if gwacl.IsNotFoundError(err) {
		return nil, errors.NotFoundf("file '%s' not found", name)
	}
	return reader, err
}

// List is specified in the StorageReader interface.
func (storage *azureStorage) List(prefix string) ([]string, error) {
	context, err := storage.getStorageContext()
	if err != nil {
		return nil, err
	}
	request := &gwacl.ListBlobsRequest{Container: storage.getContainer(), Prefix: prefix, Marker: ""}
	blobList, err := context.ListAllBlobs(request)
	if err != nil {
		return nil, err
	}
	names := make([]string, len(blobList.Blobs))
	for index, blob := range blobList.Blobs {
		names[index] = blob.Name
	}
	return names, nil
}

// URL is specified in the StorageReader interface.
func (storage *azureStorage) URL(name string) (string, error) {
	panic("unimplemented")
}

// Put is specified in the StorageWriter interface.
func (storage *azureStorage) Put(name string, r io.Reader, length int64) error {
	limitedReader := io.LimitReader(r, length)
	context, err := storage.getStorageContext()
	if err != nil {
		return err
	}
	return context.UploadBlockBlob(storage.getContainer(), name, limitedReader)
}

// Remove is specified in the StorageWriter interface.
func (storage *azureStorage) Remove(name string) error {
	context, err := storage.getStorageContext()
	if err != nil {
		return err
	}
	return context.DeleteBlob(storage.getContainer(), name)
}
<commit_msg>Fix string.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package azure

import (
	"io"
	"launchpad.net/gwacl"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/errors"
)

type azureStorage struct {
	storageContext
}

// storageContext is an abstraction that is there only to accommodate the need
// for using an azureStorage independently from an environ object in tests.
type storageContext interface {
	getContainer() string
	getStorageContext() (*gwacl.StorageContext, error)
}

// environStorageContext is a storageContext which gets its information from
// an azureEnviron object.
type environStorageContext struct {
	environ *azureEnviron
}

var _ storageContext = (*environStorageContext)(nil)

func (context *environStorageContext) getContainer() string {
	return context.environ.getSnapshot().ecfg.StorageContainerName()
}

func (context *environStorageContext) getStorageContext() (*gwacl.StorageContext, error) {
	return context.environ.getStorageContext()
}

func NewStorage(env *azureEnviron) environs.Storage {
	context := &environStorageContext{environ: env}
	return &azureStorage{context}
}

// azureStorage implements Storage.
var _ environs.Storage = (*azureStorage)(nil)

// Get is specified in the StorageReader interface.
func (storage *azureStorage) Get(name string) (io.ReadCloser, error) {
	context, err := storage.getStorageContext()
	if err != nil {
		return nil, err
	}
	reader, err := context.GetBlob(storage.getContainer(), name)
	if gwacl.IsNotFoundError(err) {
		return nil, errors.NotFoundf("file %q not found", name)
	}
	return reader, err
}

// List is specified in the StorageReader interface.
func (storage *azureStorage) List(prefix string) ([]string, error) {
	context, err := storage.getStorageContext()
	if err != nil {
		return nil, err
	}
	request := &gwacl.ListBlobsRequest{Container: storage.getContainer(), Prefix: prefix, Marker: ""}
	blobList, err := context.ListAllBlobs(request)
	if err != nil {
		return nil, err
	}
	names := make([]string, len(blobList.Blobs))
	for index, blob := range blobList.Blobs {
		names[index] = blob.Name
	}
	return names, nil
}

// URL is specified in the StorageReader interface.
func (storage *azureStorage) URL(name string) (string, error) {
	panic("unimplemented")
}

// Put is specified in the StorageWriter interface.
func (storage *azureStorage) Put(name string, r io.Reader, length int64) error {
	limitedReader := io.LimitReader(r, length)
	context, err := storage.getStorageContext()
	if err != nil {
		return err
	}
	return context.UploadBlockBlob(storage.getContainer(), name, limitedReader)
}

// Remove is specified in the StorageWriter interface.
func (storage *azureStorage) Remove(name string) error {
	context, err := storage.getStorageContext()
	if err != nil {
		return err
	}
	return context.DeleteBlob(storage.getContainer(), name)
}
<|endoftext|>


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

// +build integration,!quick

package registry_test

import (
	"path"

	"github.com/control-center/serviced/zzk"
	. "github.com/control-center/serviced/zzk/registry2"
	. "gopkg.in/check.v1"
)

func (t *ZZKTest) TestSyncRegistry(c *C) {
	conn, err := zzk.GetLocalConnection("/")
	c.Assert(err, IsNil)

	// test create
	pub1Key := PublicPortKey{HostID: "master", PortAddress: ":2181"}
	pub1 := PublicPort{
		TenantID:    "tenantid",
		Application: "app1",
		ServiceID:   "serviceid1",
		Enabled:     true,
		Protocol:    "http",
		UseTLS:      false,
	}
	pubs1 := map[PublicPortKey]PublicPort{pub1Key: pub1}

	vhost1Key := VHostKey{HostID: "master", Subdomain: "test1"}
	vhost1 := VHost{
		TenantID:    "tenantid",
		Application: "app1",
		ServiceID:   "serviceid1",
		Enabled:     true,
	}
	vhosts1 := map[VHostKey]VHost{vhost1Key: vhost1}

	err = SyncServiceRegistry(conn, "serviceid1", pubs1, vhosts1)
	c.Assert(err, IsNil)

	actualpub, err := GetPublicPort(conn, pub1Key)
	c.Assert(err, IsNil)
	actualpub.SetVersion(nil)
	c.Check(*actualpub, DeepEquals, pub1)

	actualvhost, err := GetVHost(conn, vhost1Key)
	c.Assert(err, IsNil)
	actualvhost.SetVersion(nil)
	c.Check(*actualvhost, DeepEquals, vhost1)

	pub2Key := PublicPortKey{HostID: "master", PortAddress: ":22181"}
	pub2 := PublicPort{
		TenantID:    "tenantid",
		Application: "app2",
		ServiceID:   "serviceid2",
		Enabled:     true,
		Protocol:    "https",
		UseTLS:      true,
	}
	pubs2 := map[PublicPortKey]PublicPort{pub2Key: pub2}

	vhost2Key := VHostKey{HostID: "master", Subdomain: "test2"}
	vhost2 := VHost{
		TenantID:    "tenantid",
		Application: "app2",
		ServiceID:   "serviceid2",
		Enabled:     true,
	}
	vhosts2 := map[VHostKey]VHost{vhost2Key: vhost2}

	err = SyncServiceRegistry(conn, "serviceid2", pubs2, vhosts2)
	c.Assert(err, IsNil)

	actualpub, err = GetPublicPort(conn, pub1Key)
	c.Assert(err, IsNil)
	actualpub.SetVersion(nil)
	c.Check(*actualpub, DeepEquals, pub1)

	actualvhost, err = GetVHost(conn, vhost1Key)
	c.Assert(err, IsNil)
	actualvhost.SetVersion(nil)
	c.Check(*actualvhost, DeepEquals, vhost1)

	actualpub, err = GetPublicPort(conn, pub2Key)
	c.Assert(err, IsNil)
	actualpub.SetVersion(nil)
	c.Check(*actualpub, DeepEquals, pub2)

	actualvhost, err = GetVHost(conn, vhost2Key)
	c.Assert(err, IsNil)
	actualvhost.SetVersion(nil)
	c.Check(*actualvhost, DeepEquals, vhost2)

	// test update
	pub1Key = PublicPortKey{HostID: "master", PortAddress: ":2181"}
	pub1 = PublicPort{
		TenantID:    "tenantid",
		Application: "app1",
		ServiceID:   "serviceid1",
		Enabled:     false,
		Protocol:    "",
		UseTLS:      true,
	}
	pubs1 = map[PublicPortKey]PublicPort{pub1Key: pub1}

	vhost1Key = VHostKey{HostID: "master", Subdomain: "test1"}
	vhost1 = VHost{
		TenantID:    "tenantid",
		Application: "app1",
		ServiceID:   "serviceid1",
		Enabled:     false,
	}
	vhosts1 = map[VHostKey]VHost{vhost1Key: vhost1}

	err = SyncServiceRegistry(conn, "serviceid1", pubs1, vhosts1)
	c.Assert(err, IsNil)

	actualpub, err = GetPublicPort(conn, pub1Key)
	c.Assert(err, IsNil)
	actualpub.SetVersion(nil)
	c.Check(*actualpub, DeepEquals, pub1)

	actualvhost, err = GetVHost(conn, vhost1Key)
	c.Assert(err, IsNil)
	actualvhost.SetVersion(nil)
	c.Check(*actualvhost, DeepEquals, vhost1)

	actualpub, err = GetPublicPort(conn, pub2Key)
	c.Assert(err, IsNil)
	actualpub.SetVersion(nil)
	c.Check(*actualpub, DeepEquals, pub2)

	actualvhost, err = GetVHost(conn, vhost2Key)
	c.Assert(err, IsNil)
	actualvhost.SetVersion(nil)
	c.Check(*actualvhost, DeepEquals, vhost2)

	// test delete
	err = SyncServiceRegistry(conn, "serviceid2", make(map[PublicPortKey]PublicPort), make(map[VHostKey]VHost))
	c.Assert(err, IsNil)

	actualpub, err = GetPublicPort(conn, pub1Key)
	c.Assert(err, IsNil)
	actualpub.SetVersion(nil)
	c.Check(*actualpub, DeepEquals, pub1)

	actualvhost, err = GetVHost(conn, vhost1Key)
	c.Assert(err, IsNil)
	actualvhost.SetVersion(nil)
	c.Check(*actualvhost, DeepEquals, vhost1)

	ok, err := conn.Exists(path.Join("/net/pub", pub2Key.HostID, pub2Key.PortAddress))
	c.Assert(err, IsNil)
	c.Check(ok, Equals, false)

	ok, err = conn.Exists(path.Join("/net/vhost", vhost2Key.HostID, vhost2Key.Subdomain))
	c.Assert(err, IsNil)
	c.Check(ok, Equals, false)
}
<commit_msg>registry test<commit_after>// Copyright 2016 The Serviced Authors.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build integration,!quick

package registry_test

import (
	"path"

	"github.com/control-center/serviced/zzk"
	. "github.com/control-center/serviced/zzk/registry2"
	. "gopkg.in/check.v1"
)

func (t *ZZKTest) TestSyncRegistry(c *C) {
	conn, err := zzk.GetLocalConnection("/")
	c.Assert(err, IsNil)

	// test create
	pub1Key := PublicPortKey{HostID: "master", PortAddress: ":2181"}
	pub1 := PublicPort{
		TenantID:    "tenantid",
		Application: "app1",
		ServiceID:   "serviceid1",
		Enabled:     true,
		Protocol:    "http",
		UseTLS:      false,
	}
	pubs1 := map[PublicPortKey]PublicPort{pub1Key: pub1}

	vhost1Key := VHostKey{HostID: "master", Subdomain: "test1"}
	vhost1 := VHost{
		TenantID:    "tenantid",
		Application: "app1",
		ServiceID:   "serviceid1",
		Enabled:     true,
	}
	vhosts1 := map[VHostKey]VHost{vhost1Key: vhost1}

	err = SyncServiceRegistry(conn, "serviceid1", pubs1, vhosts1)
	c.Assert(err, IsNil)

	actualpub := &PublicPort{}
	err = conn.Get("/net/pub/master/:2181", actualpub)
	c.Assert(err, IsNil)
	actualpub.SetVersion(nil)
	c.Check(*actualpub, DeepEquals, pub1)

	actualvhost := &VHost{}
	err = conn.Get("/net/vhost/master/test1", actualvhost)
	c.Assert(err, IsNil)
	actualvhost.SetVersion(nil)
	c.Check(*actualvhost, DeepEquals, vhost1)

	pub2Key := PublicPortKey{HostID: "master", PortAddress: ":22181"}
	pub2 := PublicPort{
		TenantID:    "tenantid",
		Application: "app2",
		ServiceID:   "serviceid2",
		Enabled:     true,
		Protocol:    "https",
		UseTLS:      true,
	}
	pubs2 := map[PublicPortKey]PublicPort{pub2Key: pub2}

	vhost2Key := VHostKey{HostID: "master", Subdomain: "test2"}
	vhost2 := VHost{
		TenantID:    "tenantid",
		Application: "app2",
		ServiceID:   "serviceid2",
		Enabled:     true,
	}
	vhosts2 := map[VHostKey]VHost{vhost2Key: vhost2}

	err = SyncServiceRegistry(conn, "serviceid2", pubs2, vhosts2)
	c.Assert(err, IsNil)

	actualpub = &PublicPort{}
	err = conn.Get("/net/pub/master/:2181", actualpub)
	c.Assert(err, IsNil)
	actualpub.SetVersion(nil)
	c.Check(*actualpub, DeepEquals, pub1)

	actualvhost = &VHost{}
	err = conn.Get("/net/vhost/master/test1", actualvhost)
	c.Assert(err, IsNil)
	actualvhost.SetVersion(nil)
	c.Check(*actualvhost, DeepEquals, vhost1)

	actualpub = &PublicPort{}
	err = conn.Get("/net/pub/master/:22181", actualpub)
	c.Assert(err, IsNil)
	actualpub.SetVersion(nil)
	c.Check(*actualpub, DeepEquals, pub2)

	actualvhost = &VHost{}
	err = conn.Get("/net/vhost/master/test2", actualvhost)
	c.Assert(err, IsNil)
	actualvhost.SetVersion(nil)
	c.Check(*actualvhost, DeepEquals, vhost2)

	// test update
	pub1Key = PublicPortKey{HostID: "master", PortAddress: ":2181"}
	pub1 = PublicPort{
		TenantID:    "tenantid",
		Application: "app1",
		ServiceID:   "serviceid1",
		Enabled:     false,
		Protocol:    "",
		UseTLS:      true,
	}
	pubs1 = map[PublicPortKey]PublicPort{pub1Key: pub1}

	vhost1Key = VHostKey{HostID: "master", Subdomain: "test1"}
	vhost1 = VHost{
		TenantID:    "tenantid",
		Application: "app1",
		ServiceID:   "serviceid1",
		Enabled:     false,
	}
	vhosts1 = map[VHostKey]VHost{vhost1Key: vhost1}

	err = SyncServiceRegistry(conn, "serviceid1", pubs1, vhosts1)
	c.Assert(err, IsNil)

	actualpub = &PublicPort{}
	err = conn.Get("/net/pub/master/:2181", actualpub)
	c.Assert(err, IsNil)
	actualpub.SetVersion(nil)
	c.Check(*actualpub, DeepEquals, pub1)

	actualvhost = &VHost{}
	err = conn.Get("/net/vhost/master/test1", actualvhost)
	c.Assert(err, IsNil)
	actualvhost.SetVersion(nil)
	c.Check(*actualvhost, DeepEquals, vhost1)

	actualpub = &PublicPort{}
	err = conn.Get("/net/pub/master/:22181", actualpub)
	c.Assert(err, IsNil)
	actualpub.SetVersion(nil)
	c.Check(*actualpub, DeepEquals, pub2)

	actualvhost = &VHost{}
	err = conn.Get("/net/vhost/master/test2", actualvhost)
	c.Assert(err, IsNil)
	actualvhost.SetVersion(nil)
	c.Check(*actualvhost, DeepEquals, vhost2)

	// test delete
	err = SyncServiceRegistry(conn, "serviceid2", make(map[PublicPortKey]PublicPort), make(map[VHostKey]VHost))
	c.Assert(err, IsNil)

	actualpub = &PublicPort{}
	err = conn.Get("/net/pub/master/:2181", actualpub)
	actualpub.SetVersion(nil)
	c.Check(*actualpub, DeepEquals, pub1)

	actualvhost = &VHost{}
	err = conn.Get("/net/vhost/master/test1", actualvhost)
	actualvhost.SetVersion(nil)
	c.Check(*actualvhost, DeepEquals, vhost1)

	ok, err := conn.Exists(path.Join("/net/pub", pub2Key.HostID, pub2Key.PortAddress))
	c.Assert(err, IsNil)
	c.Check(ok, Equals, false)

	ok, err = conn.Exists(path.Join("/net/vhost", vhost2Key.HostID, vhost2Key.Subdomain))
	c.Assert(err, IsNil)
	c.Check(ok, Equals, false)
}
<|endoftext|>


<commit_before>package mackerel

const (
	CheckStatusOK       = "OK"
	CheckStatusWarning  = "WARNING"
	CheckStatusCritical = "CRITICAL"
	CheckStatusUnknown  = "UNKNOWN"
)

type CheckReport struct {
	Source               CheckSource `json:"source"`
	Name                 string      `json:"name"`
	Status               string      `json:"status"`
	Message              string      `json:"message"`
	OccurredAt           int64       `json:"occurredAt"`
	NotificationInterval uint        `json:"notificationInterval,omitempty"`
	MaxCheckAttempts     uint        `json:"maxCheckAttempts,omitempty"`
}

type CheckSource interface {
	CheckType() string

	isCheck()
}

const checkTypeHost = "host"

// Ensure each check type conforms to the CheckSource interface.
var _ CheckSource = (*checkSourceHost)(nil)

// Ensure only checkSource types defined in this package can be assigned to the
// CheckSource interface.
func (m *checkSourceHost) isCheck() {}

type checkSourceHost struct {
	Type   string `json:"type"`
	HostID string `json:"hostId"`
}

func (cs *checkSourceHost) CheckType() string {
	return checkTypeHost
}

func NewCheckSourceHost(hostID string) CheckSource {
	return &checkSourceHost{
		Type:   checkTypeHost,
		HostID: hostID,
	}
}

type CheckReports struct {
	Reports []*CheckReport `json:"reports"`
}

func (c *Client) ReportCheckMonitors(crs *CheckReports) error {
	resp, err := c.PostJSON("/api/v0/monitoring/checks/report", crs)
	defer closeResponse(resp)
	return err
}
<commit_msg>add comment<commit_after>package mackerel

// CheckStatuses
const (
	CheckStatusOK       = "OK"
	CheckStatusWarning  = "WARNING"
	CheckStatusCritical = "CRITICAL"
	CheckStatusUnknown  = "UNKNOWN"
)

// CheckReport represents a report of check monitoring
type CheckReport struct {
	Source               CheckSource `json:"source"`
	Name                 string      `json:"name"`
	Status               string      `json:"status"`
	Message              string      `json:"message"`
	OccurredAt           int64       `json:"occurredAt"`
	NotificationInterval uint        `json:"notificationInterval,omitempty"`
	MaxCheckAttempts     uint        `json:"maxCheckAttempts,omitempty"`
}

// CheckSource represents interface to which each check source type must confirm to
type CheckSource interface {
	CheckType() string

	isCheck()
}

const checkTypeHost = "host"

// Ensure each check type conforms to the CheckSource interface.
var _ CheckSource = (*checkSourceHost)(nil)

// Ensure only checkSource types defined in this package can be assigned to the
// CheckSource interface.
func (cs *checkSourceHost) isCheck() {}

type checkSourceHost struct {
	Type   string `json:"type"`
	HostID string `json:"hostId"`
}

// CheckType is for satisfying CheckSource interface
func (cs *checkSourceHost) CheckType() string {
	return checkTypeHost
}

// NewCheckSourceHost returns new CheckSource which check type is "host"
func NewCheckSourceHost(hostID string) CheckSource {
	return &checkSourceHost{
		Type:   checkTypeHost,
		HostID: hostID,
	}
}

// CheckReports represents check reports for API
type CheckReports struct {
	Reports []*CheckReport `json:"reports"`
}

// ReportCheckMonitors reports check monitoring results
func (c *Client) ReportCheckMonitors(crs *CheckReports) error {
	resp, err := c.PostJSON("/api/v0/monitoring/checks/report", crs)
	defer closeResponse(resp)
	return err
}
<|endoftext|>


<commit_before>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package fdroidcl

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

type Index struct {
	Repo struct {
		Name        string `xml:"name,attr"`
		PubKey      string `xml:"pubkey,attr"`
		Timestamp   int    `xml:"timestamp,attr"`
		URL         string `xml:"url,attr"`
		Version     int    `xml:"version,attr"`
		MaxAge      int    `xml:"maxage,attr"`
		Description string `xml:"description"`
	} `xml:"repo"`
	Apps []App `xml:"application"`
}

type commaList []string

func (cl *commaList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	var content string
	if err := d.DecodeElement(&content, &start); err != nil {
		return err
	}
	*cl = strings.Split(content, ",")
	return nil
}

type hexVal []byte

func (hv *hexVal) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	var content string
	var err error
	if err = d.DecodeElement(&content, &start); err != nil {
		return err
	}
	*hv, err = hex.DecodeString(content)
	return err
}

// App is an Android application
type App struct {
	ID        string    `xml:"id"`
	Name      string    `xml:"name"`
	Summary   string    `xml:"summary"`
	Desc      string    `xml:"desc"`
	License   string    `xml:"license"`
	Categs    commaList `xml:"categories"`
	Website   string    `xml:"web"`
	Source    string    `xml:"source"`
	Tracker   string    `xml:"tracker"`
	Changelog string    `xml:"changelog"`
	Donate    string    `xml:"donate"`
	Bitcoin   string    `xml:"bitcoin"`
	Litecoin  string    `xml:"litecoin"`
	Dogecoin  string    `xml:"dogecoin"`
	FlattrID  string    `xml:"flattr"`
	Apks      []Apk     `xml:"package"`
	CVName    string    `xml:"marketversion"`
	CVCode    int       `xml:"marketvercode"`
	CurApk    *Apk
}

type Hash struct {
	Type string `xml:"type,attr"`
	Data hexVal `xml:",chardata"`
}

// Apk is an Android package
type Apk struct {
	VName   string    `xml:"version"`
	VCode   int       `xml:"versioncode"`
	Size    int64     `xml:"size"`
	MinSdk  int       `xml:"sdkver"`
	MaxSdk  int       `xml:"maxsdkver"`
	ABIs    commaList `xml:"nativecode"`
	ApkName string    `xml:"apkname"`
	SrcName string    `xml:"srcname"`
	Sig     hexVal    `xml:"sig"`
	Added   string    `xml:"added"`
	Perms   commaList `xml:"permissions"`
	Feats   commaList `xml:"features"`
	Hashes  []Hash    `xml:"hash"`
}

func (app *App) calcCurApk() {
	for _, apk := range app.Apks {
		app.CurApk = &apk
		if app.CVCode >= apk.VCode {
			break
		}
	}
}

func (app *App) TextDesc(w io.Writer) {
	reader := strings.NewReader(app.Desc)
	decoder := xml.NewDecoder(reader)
	firstParagraph := true
	linePrefix := ""
	colsUsed := 0
	var links []string
	linked := false
	for {
		token, err := decoder.Token()
		if err == io.EOF || token == nil {
			break
		}
		switch t := token.(type) {
		case xml.StartElement:
			switch t.Name.Local {
			case "p":
				if firstParagraph {
					firstParagraph = false
				} else {
					fmt.Fprintln(w)
				}
				linePrefix = ""
				colsUsed = 0
			case "li":
				fmt.Fprint(w, "\n *")
				linePrefix = "   "
				colsUsed = 0
			case "a":
				for _, attr := range t.Attr {
					if attr.Name.Local == "href" {
						links = append(links, attr.Value)
						linked = true
						break
					}
				}
			}
		case xml.EndElement:
			switch t.Name.Local {
			case "p":
				fmt.Fprintln(w)
			case "ul":
				fmt.Fprintln(w)
			case "ol":
				fmt.Fprintln(w)
			}
		case xml.CharData:
			left := string(t)
			if linked {
				left += fmt.Sprintf("[%d]", len(links)-1)
				linked = false
			}
			limit := 80 - len(linePrefix) - colsUsed
			firstLine := true
			for len(left) > limit {
				last := 0
				for i, c := range left {
					if i >= limit {
						break
					}
					if c == ' ' {
						last = i
					}
				}
				if firstLine {
					firstLine = false
					limit += colsUsed
				} else {
					fmt.Fprint(w, linePrefix)
				}
				fmt.Fprintln(w, left[:last])
				left = left[last+1:]
				colsUsed = 0
			}
			if firstLine {
				firstLine = false
			} else {
				fmt.Fprint(w, linePrefix)
			}
			fmt.Fprint(w, left)
			colsUsed += len(left)
		}
	}
	if len(links) > 0 {
		fmt.Fprintln(w)
		for i, link := range links {
			fmt.Fprintf(w, "[%d] %s\n", i, link)
		}
	}
}

func (app *App) prepareData() {
	app.calcCurApk()
}

type appList []App

func (al appList) Len() int           { return len(al) }
func (al appList) Swap(i, j int)      { al[i], al[j] = al[j], al[i] }
func (al appList) Less(i, j int) bool { return al[i].ID < al[j].ID }

func LoadIndexXml(r io.Reader) (*Index, error) {
	var index Index
	decoder := xml.NewDecoder(r)
	if err := decoder.Decode(&index); err != nil {
		return nil, err
	}

	sort.Sort(appList(index.Apps))

	for i := range index.Apps {
		app := &index.Apps[i]
		app.prepareData()
	}
	return &index, nil
}
<commit_msg>Add support for date values<commit_after>/* Copyright (c) 2015, Daniel Martí <mvdan@mvdan.cc> */
/* See LICENSE for licensing information */

package fdroidcl

import (
	"encoding/hex"
	"encoding/xml"
	"fmt"
	"io"
	"sort"
	"strings"
	"time"
)

type Index struct {
	Repo struct {
		Name        string `xml:"name,attr"`
		PubKey      string `xml:"pubkey,attr"`
		Timestamp   int    `xml:"timestamp,attr"`
		URL         string `xml:"url,attr"`
		Version     int    `xml:"version,attr"`
		MaxAge      int    `xml:"maxage,attr"`
		Description string `xml:"description"`
	} `xml:"repo"`
	Apps []App `xml:"application"`
}

type commaList []string

func (cl *commaList) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	var content string
	if err := d.DecodeElement(&content, &start); err != nil {
		return err
	}
	*cl = strings.Split(content, ",")
	return nil
}

type hexVal []byte

func (hv *hexVal) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	var content string
	var err error
	if err = d.DecodeElement(&content, &start); err != nil {
		return err
	}
	*hv, err = hex.DecodeString(content)
	return err
}

// App is an Android application
type App struct {
	ID        string    `xml:"id"`
	Name      string    `xml:"name"`
	Summary   string    `xml:"summary"`
	Desc      string    `xml:"desc"`
	License   string    `xml:"license"`
	Categs    commaList `xml:"categories"`
	Website   string    `xml:"web"`
	Source    string    `xml:"source"`
	Tracker   string    `xml:"tracker"`
	Changelog string    `xml:"changelog"`
	Donate    string    `xml:"donate"`
	Bitcoin   string    `xml:"bitcoin"`
	Litecoin  string    `xml:"litecoin"`
	Dogecoin  string    `xml:"dogecoin"`
	FlattrID  string    `xml:"flattr"`
	Apks      []Apk     `xml:"package"`
	CVName    string    `xml:"marketversion"`
	CVCode    int       `xml:"marketvercode"`
	CurApk    *Apk
}

type Hash struct {
	Type string `xml:"type,attr"`
	Data hexVal `xml:",chardata"`
}

type dateVal struct {
	time.Time
}

func (dv *dateVal) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	var content string
	if err := d.DecodeElement(&content, &start); err != nil {
		return err
	}
	t, err := time.Parse("2006-01-02", content)
	*dv = dateVal{t}
	return err
}

// Apk is an Android package
type Apk struct {
	VName   string    `xml:"version"`
	VCode   int       `xml:"versioncode"`
	Size    int64     `xml:"size"`
	MinSdk  int       `xml:"sdkver"`
	MaxSdk  int       `xml:"maxsdkver"`
	ABIs    commaList `xml:"nativecode"`
	ApkName string    `xml:"apkname"`
	SrcName string    `xml:"srcname"`
	Sig     hexVal    `xml:"sig"`
	Added   dateVal   `xml:"added"`
	Perms   commaList `xml:"permissions"`
	Feats   commaList `xml:"features"`
	Hashes  []Hash    `xml:"hash"`
}

func (app *App) calcCurApk() {
	for _, apk := range app.Apks {
		app.CurApk = &apk
		if app.CVCode >= apk.VCode {
			break
		}
	}
}

func (app *App) TextDesc(w io.Writer) {
	reader := strings.NewReader(app.Desc)
	decoder := xml.NewDecoder(reader)
	firstParagraph := true
	linePrefix := ""
	colsUsed := 0
	var links []string
	linked := false
	for {
		token, err := decoder.Token()
		if err == io.EOF || token == nil {
			break
		}
		switch t := token.(type) {
		case xml.StartElement:
			switch t.Name.Local {
			case "p":
				if firstParagraph {
					firstParagraph = false
				} else {
					fmt.Fprintln(w)
				}
				linePrefix = ""
				colsUsed = 0
			case "li":
				fmt.Fprint(w, "\n *")
				linePrefix = "   "
				colsUsed = 0
			case "a":
				for _, attr := range t.Attr {
					if attr.Name.Local == "href" {
						links = append(links, attr.Value)
						linked = true
						break
					}
				}
			}
		case xml.EndElement:
			switch t.Name.Local {
			case "p":
				fmt.Fprintln(w)
			case "ul":
				fmt.Fprintln(w)
			case "ol":
				fmt.Fprintln(w)
			}
		case xml.CharData:
			left := string(t)
			if linked {
				left += fmt.Sprintf("[%d]", len(links)-1)
				linked = false
			}
			limit := 80 - len(linePrefix) - colsUsed
			firstLine := true
			for len(left) > limit {
				last := 0
				for i, c := range left {
					if i >= limit {
						break
					}
					if c == ' ' {
						last = i
					}
				}
				if firstLine {
					firstLine = false
					limit += colsUsed
				} else {
					fmt.Fprint(w, linePrefix)
				}
				fmt.Fprintln(w, left[:last])
				left = left[last+1:]
				colsUsed = 0
			}
			if firstLine {
				firstLine = false
			} else {
				fmt.Fprint(w, linePrefix)
			}
			fmt.Fprint(w, left)
			colsUsed += len(left)
		}
	}
	if len(links) > 0 {
		fmt.Fprintln(w)
		for i, link := range links {
			fmt.Fprintf(w, "[%d] %s\n", i, link)
		}
	}
}

func (app *App) prepareData() {
	app.calcCurApk()
}

type appList []App

func (al appList) Len() int           { return len(al) }
func (al appList) Swap(i, j int)      { al[i], al[j] = al[j], al[i] }
func (al appList) Less(i, j int) bool { return al[i].ID < al[j].ID }

func LoadIndexXml(r io.Reader) (*Index, error) {
	var index Index
	decoder := xml.NewDecoder(r)
	if err := decoder.Decode(&index); err != nil {
		return nil, err
	}

	sort.Sort(appList(index.Apps))

	for i := range index.Apps {
		app := &index.Apps[i]
		app.prepareData()
	}
	return &index, nil
}
<|endoftext|>


<commit_before>package gnosis

import (
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"
	"time"

	"github.com/JackKnifed/blackfriday"
	"github.com/blevesearch/bleve"
	"github.com/mschoch/blackfriday-text"
	"gopkg.in/fsnotify.v1"
)

type gnosisIndex struct {
	TrueIndex bleve.Index
	Config    IndexSection
}

type indexedPage struct {
	Name     string    `json:"name"`
	Filepath string    `json:"path"`
	Body     string    `json:"body"`
	Topics   string    `json:"topic"`
	Keywords string    `json:"keyword"`
	Modified time.Time `json:"modified"`
}

func openIndex(config IndexSection) *gnosisIndex {
	index := new(gnosisIndex)
	index.Config = config
	newIndex, err := bleve.Open(path.Clean(index.Config.IndexPath))
	if err == nil {
		log.Printf("Opening existing index...")
		index.TrueIndex = newIndex
	} else if err == bleve.ErrorIndexPathDoesNotExist {
		log.Printf("Creating new index...")
		// create a mapping
		indexMapping := index.buildIndexMapping()
		newIndex, err := bleve.New(path.Clean(index.Config.IndexPath), indexMapping)
		if err != nil {
			log.Fatal(err)
		} else {
			index.TrueIndex = newIndex
		}
	} else {
		log.Fatal(err)
	}
	return index
}

func (index *gnosisIndex) buildIndexMapping() *bleve.IndexMapping {

	// create a text field type
	enTextFieldMapping := bleve.NewTextFieldMapping()
	enTextFieldMapping.Analyzer = index.Config.IndexType

	// create a date field type
	dateTimeMapping := bleve.NewDateTimeFieldMapping()

	// map out the wiki page
	wikiMapping := bleve.NewDocumentMapping()
	wikiMapping.AddFieldMappingsAt("path", enTextFieldMapping)
	wikiMapping.AddFieldMappingsAt("body", enTextFieldMapping)
	wikiMapping.AddFieldMappingsAt("topic", enTextFieldMapping)
	wikiMapping.AddFieldMappingsAt("keyword", enTextFieldMapping)
	wikiMapping.AddFieldMappingsAt("modified", dateTimeMapping)

	// add the wiki page mapping to a new index
	// ##TODO## revisit? move out to config?
	indexMapping := bleve.NewIndexMapping()
	indexMapping.AddDocumentMapping("wiki", wikiMapping)
	indexMapping.DefaultAnalyzer = index.Config.IndexType

	return indexMapping
}

func (index *gnosisIndex) cleanupMarkdown(input []byte) []byte {
	extensions := 0
	renderer := blackfridaytext.TextRenderer()
	output := blackfriday.Markdown(input, renderer, extensions)
	return output
}

func (index *gnosisIndex) relativePath(filePath string) string {
	filePath = strings.TrimPrefix(filePath, index.Config.WatchDir)
	filePath = path.Clean(filePath)
	return filePath
}

func (index *gnosisIndex) generateWikiFromFile(filePath string) (*indexedPage, error) {
	pdata := new(PageMetadata)
	err := pdata.LoadPage(filePath)
	//fileBytes, err := ioutil.ReadFile(filePath)
	if err != nil {
		return nil, err
	}

	// ##TODO## I need to look up the actual index that I'm building and hit all of the fields here
	cleanedUpPage := index.cleanupMarkdown(pdata.Page)
	topics, keywords := pdata.ListMeta()
	rv := indexedPage{
		Name: pdata.Title,
		Body: string(cleanedUpPage),
		Filepath: filePath,
		Topics: strings.Join(topics, " "),
		Keywords: strings.Join(keywords, " "),
		Modified: pdata.FileStats.ModTime(),
	}
	return &rv, nil
}

func (index *gnosisIndex) processUpdate(path string) {
	log.Printf("updated: %s", path)
	rp := index.relativePath(path)
	wiki, err := index.generateWikiFromFile(path)
	if err != nil {
		log.Print(err)
	} else {
		index.TrueIndex.Index(rp, wiki)
	}
}

func (index *gnosisIndex) processDelete(path string) {
	log.Printf("delete: %s", path)
	rp := index.relativePath(path)
	err := index.TrueIndex.Delete(rp)
	if err != nil {
		log.Print(err)
	}
}

func (index *gnosisIndex) walkForIndexing(path string) {
	// ##TODO## reevaulate by finding all files within a path?
	dirEntries, err := ioutil.ReadDir(path)
	if err != nil {
		log.Fatal(err)
	}
	for _, dirEntry := range dirEntries {
		dirEntryPath := path + string(os.PathSeparator) + dirEntry.Name()
		if dirEntry.IsDir() {
			index.walkForIndexing(dirEntryPath)
		} else if strings.HasSuffix(dirEntry.Name(), ".md") {
			index.processUpdate(dirEntryPath)
		}
	}
}

func (index *gnosisIndex) startWatching(filePath string) *fsnotify.Watcher {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	// maybe rework the index so the Watcher is inside the index? idk

	// start a go routine to process events
	go func() {
		idleTimer := time.NewTimer(10 * time.Second)
		queuedEvents := make([]fsnotify.Event, 0)
		for {
			select {
			case ev := <-watcher.Events:
				queuedEvents = append(queuedEvents, ev)
				idleTimer.Reset(10 * time.Second)
			case err := <-watcher.Errors:
				log.Fatal(err)
			case <-idleTimer.C:
				for _, ev := range queuedEvents {
					if strings.HasSuffix(ev.Name, ".md") {
						switch ev.Op {
						case fsnotify.Remove, fsnotify.Rename:
							// delete the filePath
							index.processDelete(ev.Name)
						case fsnotify.Create, fsnotify.Write:
							// update the filePath
							index.processUpdate(ev.Name)
						default:
							// ignore
						}
					}
				}
				queuedEvents = make([]fsnotify.Event, 0)
				idleTimer.Reset(10 * time.Second)
			}
		}
	}()

	// now actually watch the filePath requested
	err = watcher.Add(filePath)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("watching '%s' for changes...", filePath)

	return watcher
}
<commit_msg>made a bunch of chnages to the indexer - i believe i quashed all bugs and got it to watch multiple directories<commit_after>package gnosis

import (
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"
	"time"

	"github.com/JackKnifed/blackfriday"
	"github.com/blevesearch/bleve"
	"github.com/mschoch/blackfriday-text"
	"gopkg.in/fsnotify.v1"
)

type gnosisIndex struct {
	TrueIndex bleve.Index
	Config    IndexSection
	openWatchers fsnotify.Watcher[]
}

type indexedPage struct {
	Name     string    `json:"name"`
	Filepath string    `json:"path"`
	Body     string    `json:"body"`
	Topics   string    `json:"topic"`
	Keywords string    `json:"keyword"`
	Modified time.Time `json:"modified"`
}

func openIndex(config IndexSection) (*gnosisIndex, err) {
	index := new(gnosisIndex)
	index.Config = config
	newIndex, err := bleve.Open(path.Clean(index.Config.IndexPath))
	if err == nil {
		log.Printf("Opening existing index %s", index.Config.IndexName)
		index.TrueIndex = newIndex
	} else if err == bleve.ErrorIndexPathDoesNotExist {
		log.Printf("Creating new index %s", index.Config.IndexName)
		// create a mapping
		indexMapping := index.buildIndexMapping()
		newIndex, err := bleve.New(path.Clean(index.Config.IndexPath), indexMapping)
		if err != nil {
			log.Fatal(err)
		} else {
			index.TrueIndex = newIndex
		}
	} else {
		log.Error(err)
		return _, err
	}
	// You only got here if you opened an existing index, or a new index
	for _, dir := range index.Config.WatchDir {
		log.Printf("Watching and walking dir %s index %s", dir, index.Config.IndexName)
		watcher := index.startWatching(dir)
		index.openWatchers = append(index.openWatchers, watcher)
		index.walkForIndexing(dir)
	}
	return index
}

func (index *gnosisIndex) closeIndex() {
	for _, watcher := range index.openWatchers {
		watcher.Close()
	}
}

func (index *gnosisIndex) buildIndexMapping() *bleve.IndexMapping {

	// create a text field type
	enTextFieldMapping := bleve.NewTextFieldMapping()
	enTextFieldMapping.Analyzer = index.Config.IndexType

	// create a date field type
	dateTimeMapping := bleve.NewDateTimeFieldMapping()

	// map out the wiki page
	wikiMapping := bleve.NewDocumentMapping()
	wikiMapping.AddFieldMappingsAt("path", enTextFieldMapping)
	wikiMapping.AddFieldMappingsAt("body", enTextFieldMapping)
	wikiMapping.AddFieldMappingsAt("topic", enTextFieldMapping)
	wikiMapping.AddFieldMappingsAt("keyword", enTextFieldMapping)
	wikiMapping.AddFieldMappingsAt("modified", dateTimeMapping)

	// add the wiki page mapping to a new index
	indexMapping := bleve.NewIndexMapping()
	indexMapping.AddDocumentMapping(index.Config.IndexName, wikiMapping)
	indexMapping.DefaultAnalyzer = index.Config.IndexType

	return indexMapping
}

func (index *gnosisIndex) cleanupMarkdown(input []byte) []byte {
	extensions := 0
	renderer := blackfridaytext.TextRenderer()
	output := blackfriday.Markdown(input, renderer, extensions)
	return output
}

func (index *gnosisIndex) relativePath(filePath string) string {
	filePath = strings.TrimPrefix(filePath, index.Config.WatchDir)
	filePath = path.Clean(filePath)
	return filePath
}

func (index *gnosisIndex) generateWikiFromFile(filePath string) (*indexedPage, error) {
	pdata := new(PageMetadata)
	err := pdata.LoadPage(filePath)
	//fileBytes, err := ioutil.ReadFile(filePath)
	if err != nil {
		return nil, err
	}

	if MatchedTag(index.Config.Restricted) == true {
		return nil, sprintf("Hit a restricted page - %s", filePath)
	} else {
	cleanedUpPage := index.cleanupMarkdown(pdata.Page)
	topics, keywords := pdata.ListMeta()
	rv := indexedPage{
		Name: pdata.Title,
		Body: string(cleanedUpPage),
		Filepath: filePath,
		Topics: strings.Join(topics, " "),
		Keywords: strings.Join(keywords, " "),
		Modified: pdata.FileStats.ModTime(),
	}
	return &rv, nil
	}
}

// Update the entry in the index to the output from a given file
func (index *gnosisIndex) processUpdate(path string) {
	log.Printf("updated: %s", path)
	rp := index.relativePath(path)
	wiki, err := index.generateWikiFromFile(path)
	if err != nil {
		log.Print(err)
	} else {
		index.TrueIndex.Index(rp, wiki)
	}
}

// Deletes a given path from the wiki entry
func (index *gnosisIndex) processDelete(path string) {
	log.Printf("delete: %s", path)
	rp := index.relativePath(path)
	err := index.TrueIndex.Delete(rp)
	if err != nil {
		log.Print(err)
	}
}

// walks a given path, and runs processUpdate on each File
func (index *gnosisIndex) walkForIndexing(path string) {
	dirEntries, err := ioutil.ReadDir(path)
	if err != nil {
		log.Fatal(err)
	}
	for _, dirEntry := range dirEntries {
		dirEntryPath := path + string(os.PathSeparator) + dirEntry.Name()
		if dirEntry.IsDir() {
			index.walkForIndexing(dirEntryPath)
		} else if strings.HasSuffix(dirEntry.Name(), index.Config.WatchExtension) {
			index.processUpdate(dirEntryPath)
		}
	}
}

// watches a given filepath for an index for changes
func (index *gnosisIndex) startWatching(filePath string) *fsnotify.Watcher {
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}

	// maybe rework the index so the Watcher is inside the index? idk

	// start a go routine to process events
	go func() {
		idleTimer := time.NewTimer(10 * time.Second)
		queuedEvents := make([]fsnotify.Event, 0)
		for {
			select {
			case ev := <-watcher.Events:
				queuedEvents = append(queuedEvents, ev)
				idleTimer.Reset(10 * time.Second)
			case err := <-watcher.Errors:
				log.Fatal(err)
			case <-idleTimer.C:
				for _, ev := range queuedEvents {
					if strings.HasSuffix(ev.Name, index.Config.WatchExtension) {
						switch ev.Op {
						case fsnotify.Remove, fsnotify.Rename:
							// delete the filePath
							index.processDelete(ev.Name)
						case fsnotify.Create, fsnotify.Write:
							// update the filePath
							index.processUpdate(ev.Name)
						default:
							// ignore
						}
					}
				}
				queuedEvents = make([]fsnotify.Event, 0)
				idleTimer.Reset(10 * time.Second)
			}
		}
	}()

	// now actually watch the filePath requested
	err = watcher.Add(filePath)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("watching '%s' for changes...", filePath)

	return watcher
}
<|endoftext|>


<commit_before>package logutils

import (
	"bytes"
	"io"
	"sync"
)

type LogLevel string

// LevelFilter is an io.Writer that can be used with a logger that
// will filter out log messages that aren't at least a certain level.
//
// Once the filter is in use somewhere, it is not safe to modify
// the structure.
type LevelFilter struct {
	// Levels is the list of log levels, in increasing order of
	// severity. Example might be: {"DEBUG", "WARN", "ERROR"}.
	Levels []LogLevel

	// MinLevel is the minimum level allowed through
	MinLevel LogLevel

	// The underlying io.Writer where log messages that pass the filter
	// will be set.
	Writer io.Writer

	badLevels map[LogLevel]struct{}
	once      sync.Once
}

// Check will check a given line if it would be included in the level
// filter.
func (f *LevelFilter) Check(line []byte) bool {
	f.once.Do(f.init)

	// Check for a log level
	var level LogLevel
	x := bytes.IndexByte(line, '[')
	if x >= 0 {
		y := bytes.IndexByte(line[x:], ']')
		if y >= 0 {
			level = LogLevel(line[x+1 : x+y])
		}
	}

	_, ok := f.badLevels[level]
	return !ok
}

func (f *LevelFilter) Write(p []byte) (n int, err error) {
	// Note in general that io.Writer can receive any byte sequence
	// to write, but the "log" package always guarantees that we only
	// get a single line. We use that as a slight optimization within
	// this method, assuming we're dealing with a single, complete line
	// of log data.

	if !f.Check(p) {
		return len(p), nil
	}

	return f.Writer.Write(p)
}

// SetMinLevel is used to update the minimum log level
func (f *LevelFilter) SetMinLevel(min LogLevel) {
	f.MinLevel = min
	f.init()
}

func (f *LevelFilter) init() {
	badLevels := make(map[LogLevel]struct{})
	for _, level := range f.Levels {
		if level == f.MinLevel {
			break
		}
		badLevels[level] = struct{}{}
	}
	f.badLevels = badLevels
}
<commit_msg>Add package doc.<commit_after>// Package logutils augments the standard log package with levels.
package logutils

import (
	"bytes"
	"io"
	"sync"
)

type LogLevel string

// LevelFilter is an io.Writer that can be used with a logger that
// will filter out log messages that aren't at least a certain level.
//
// Once the filter is in use somewhere, it is not safe to modify
// the structure.
type LevelFilter struct {
	// Levels is the list of log levels, in increasing order of
	// severity. Example might be: {"DEBUG", "WARN", "ERROR"}.
	Levels []LogLevel

	// MinLevel is the minimum level allowed through
	MinLevel LogLevel

	// The underlying io.Writer where log messages that pass the filter
	// will be set.
	Writer io.Writer

	badLevels map[LogLevel]struct{}
	once      sync.Once
}

// Check will check a given line if it would be included in the level
// filter.
func (f *LevelFilter) Check(line []byte) bool {
	f.once.Do(f.init)

	// Check for a log level
	var level LogLevel
	x := bytes.IndexByte(line, '[')
	if x >= 0 {
		y := bytes.IndexByte(line[x:], ']')
		if y >= 0 {
			level = LogLevel(line[x+1 : x+y])
		}
	}

	_, ok := f.badLevels[level]
	return !ok
}

func (f *LevelFilter) Write(p []byte) (n int, err error) {
	// Note in general that io.Writer can receive any byte sequence
	// to write, but the "log" package always guarantees that we only
	// get a single line. We use that as a slight optimization within
	// this method, assuming we're dealing with a single, complete line
	// of log data.

	if !f.Check(p) {
		return len(p), nil
	}

	return f.Writer.Write(p)
}

// SetMinLevel is used to update the minimum log level
func (f *LevelFilter) SetMinLevel(min LogLevel) {
	f.MinLevel = min
	f.init()
}

func (f *LevelFilter) init() {
	badLevels := make(map[LogLevel]struct{})
	for _, level := range f.Levels {
		if level == f.MinLevel {
			break
		}
		badLevels[level] = struct{}{}
	}
	f.badLevels = badLevels
}
<|endoftext|>


<commit_before>/*

	gamepkg is a package that helps locate, validate, and modify game package
	imports.

*/
package gamepkg

import (
	"errors"
	"github.com/jkomoros/boardgame/boardgame-util/lib/path"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"path/filepath"
)

type GamePkg struct {
	//Every contstructo sets absolutePath to something that at least exists on
	//disk.
	absolutePath         string
	importPath           string
	calculatedIsGamePkg  bool
	memoizedIsGamePkg    bool
	memoizedIsGamePkgErr error
}

//New tries to interpret the input as an import. If that files, tries to
//interpret it as a path (rel or absolute), and if that fails, bails.
func New(importOrPath string) (*GamePkg, error) {
	pkg, err := NewFromImport(importOrPath)
	if err == nil {
		return pkg, nil
	}
	return NewFromPath(importOrPath)
}

//NewFromPath takes path (either relative or absolute path) and returns a new
//GamePkg. Will error if the given path does not appear to denote a valid game
//package for any reason.
func NewFromPath(path string) (*GamePkg, error) {

	if !filepath.IsAbs(path) {

		cwd, err := os.Getwd()

		if err != nil {
			return nil, errors.New("Couldn't get working directory: " + err.Error())
		}

		path = filepath.Join(cwd, path)
	}

	return newGamePkg(path, "")

}

//NewFromImport will return a new GamePkg pointing to that import. Will error
//if the given path does not appear to denote a valid game package for any
//reason.
func NewFromImport(importPath string) (*GamePkg, error) {

	absPath, err := path.AbsoluteGoPkgPath(importPath)

	if err != nil {
		return nil, errors.New("Absolute path couldn't be found: " + err.Error())
	}

	//If no error, then absPath must point to a valid thing

	return newGamePkg(absPath, importPath)

}

func newGamePkg(absPath, importPath string) (*GamePkg, error) {
	result := &GamePkg{
		absolutePath: absPath,
		importPath:   importPath,
	}

	if info, err := os.Stat(absPath); err != nil {
		return nil, errors.New("Path doesn't point to valid location on disk: " + err.Error())
	} else if !info.IsDir() {
		return nil, errors.New("Path points to an object but it's not a directory.")
	}

	if !result.goPkg() {
		return nil, errors.New(absPath + " denotes a folder with no go source files")
	}

	isGamePkg, err := result.isGamePkg()

	if !isGamePkg {
		return nil, errors.New(absPath + " was not a valid game package: " + err.Error())
	}

	return result, nil
}

//AbsolutePath returns the absolute path where the package in question resides
//on disk. All constructors will have errored if AbsolutePath doesn't at the
//very least point to a valid location on disk.
func (g *GamePkg) AbsolutePath() string {
	return g.absolutePath
}

//goPkg validates that the absolutePath denotes a package with at least one go
//file. If there's an error will default to false.
func (g *GamePkg) goPkg() bool {

	infos, _ := ioutil.ReadDir(g.AbsolutePath())

	for _, info := range infos {
		if filepath.Ext(info.Name()) == ".go" {
			return true
		}
	}

	return false

}

//Import returns the string that could be used in your source to import this
//package.
func (g *GamePkg) Import() (string, error) {
	//Calculate it if not already calculated (for example via NewFromImport constructor)
	if g.importPath == "" {

		goPkg, err := build.ImportDir(g.AbsolutePath(), 0)

		if err != nil {
			return "", errors.New("Couldn't read package: " + err.Error())
		}

		//TODO: factor this into a helper that also sets the package name in
		//case it's asked for later.
		g.importPath = goPkg.ImportPath
	}

	return g.importPath, nil
}

//isGamePkg verifies that the package appears to be a valid game package.
//Specifically it checks for
func (g *GamePkg) isGamePkg() (bool, error) {
	if !g.calculatedIsGamePkg {
		g.memoizedIsGamePkg, g.memoizedIsGamePkgErr = g.calculateIsGamePkg()
	}
	return g.memoizedIsGamePkg, g.memoizedIsGamePkgErr
}

func (g *GamePkg) calculateIsGamePkg() (bool, error) {
	pkgs, err := parser.ParseDir(token.NewFileSet(), g.AbsolutePath(), nil, 0)

	if err != nil {
		return false, errors.New("Couldn't parse folder: " + err.Error())
	}

	if len(pkgs) < 1 {
		return false, errors.New("No packages in that directory")
	}

	if len(pkgs) > 1 {
		return false, errors.New("More than one package in that directory")
	}

	var pkg *ast.Package

	for _, p := range pkgs {
		pkg = p
	}

	foundNewDelegate := false

	for _, file := range pkg.Files {
		for _, decl := range file.Decls {
			fun, ok := decl.(*ast.FuncDecl)
			if !ok {
				continue
			}

			if fun.Name.String() != "NewDelegate" {
				continue
			}

			//OK, it might be the function. Does it have the right signature?

			if fun.Recv != nil {
				return false, errors.New("NewDelegate had a receiver")
			}

			if fun.Type.Params.NumFields() > 0 {
				return false, errors.New("NewDelegate took more than 0 items")
			}

			if fun.Type.Results.NumFields() != 1 {
				return false, errors.New("NewDelegate didn't return exactly one item")
			}

			//TODO: check that the returned item implements
			//boardgame.GameDelegate.

			foundNewDelegate = true
			break

		}

		if foundNewDelegate {
			break
		}
	}

	if !foundNewDelegate {
		return false, errors.New("Couldn't find NewDelegate")
	}

	return true, nil
}
<commit_msg>Switch the top=level struct to be Pkg, not GamePkg. Part of #694.<commit_after>/*

	gamepkg is a package that helps locate, validate, and modify game package
	imports.

*/
package gamepkg

import (
	"errors"
	"github.com/jkomoros/boardgame/boardgame-util/lib/path"
	"go/ast"
	"go/build"
	"go/parser"
	"go/token"
	"io/ioutil"
	"os"
	"path/filepath"
)

type Pkg struct {
	//Every contstructo sets absolutePath to something that at least exists on
	//disk.
	absolutePath         string
	importPath           string
	calculatedIsGamePkg  bool
	memoizedIsGamePkg    bool
	memoizedIsGamePkgErr error
}

//New tries to interpret the input as an import. If that files, tries to
//interpret it as a path (rel or absolute), and if that fails, bails.
func New(importOrPath string) (*Pkg, error) {
	pkg, err := NewFromImport(importOrPath)
	if err == nil {
		return pkg, nil
	}
	return NewFromPath(importOrPath)
}

//NewFromPath takes path (either relative or absolute path) and returns a new
//Pkg. Will error if the given path does not appear to denote a valid game
//package for any reason.
func NewFromPath(path string) (*Pkg, error) {

	if !filepath.IsAbs(path) {

		cwd, err := os.Getwd()

		if err != nil {
			return nil, errors.New("Couldn't get working directory: " + err.Error())
		}

		path = filepath.Join(cwd, path)
	}

	return newPkg(path, "")

}

//NewFromImport will return a new Pkg pointing to that import. Will error
//if the given path does not appear to denote a valid game package for any
//reason.
func NewFromImport(importPath string) (*Pkg, error) {

	absPath, err := path.AbsoluteGoPkgPath(importPath)

	if err != nil {
		return nil, errors.New("Absolute path couldn't be found: " + err.Error())
	}

	//If no error, then absPath must point to a valid thing

	return newPkg(absPath, importPath)

}

func newPkg(absPath, importPath string) (*Pkg, error) {
	result := &Pkg{
		absolutePath: absPath,
		importPath:   importPath,
	}

	if info, err := os.Stat(absPath); err != nil {
		return nil, errors.New("Path doesn't point to valid location on disk: " + err.Error())
	} else if !info.IsDir() {
		return nil, errors.New("Path points to an object but it's not a directory.")
	}

	if !result.goPkg() {
		return nil, errors.New(absPath + " denotes a folder with no go source files")
	}

	isGamePkg, err := result.isGamePkg()

	if !isGamePkg {
		return nil, errors.New(absPath + " was not a valid game package: " + err.Error())
	}

	return result, nil
}

//AbsolutePath returns the absolute path where the package in question resides
//on disk. All constructors will have errored if AbsolutePath doesn't at the
//very least point to a valid location on disk.
func (p *Pkg) AbsolutePath() string {
	return p.absolutePath
}

//goPkg validates that the absolutePath denotes a package with at least one go
//file. If there's an error will default to false.
func (g *Pkg) goPkg() bool {

	infos, _ := ioutil.ReadDir(g.AbsolutePath())

	for _, info := range infos {
		if filepath.Ext(info.Name()) == ".go" {
			return true
		}
	}

	return false

}

//Import returns the string that could be used in your source to import this
//package.
func (p *Pkg) Import() (string, error) {
	//Calculate it if not already calculated (for example via NewFromImport constructor)
	if p.importPath == "" {

		goPkg, err := build.ImportDir(p.AbsolutePath(), 0)

		if err != nil {
			return "", errors.New("Couldn't read package: " + err.Error())
		}

		//TODO: factor this into a helper that also sets the package name in
		//case it's asked for later.
		p.importPath = goPkg.ImportPath
	}

	return p.importPath, nil
}

//isPkg verifies that the package appears to be a valid game package.
//Specifically it checks for
func (g *Pkg) isGamePkg() (bool, error) {
	if !g.calculatedIsGamePkg {
		g.memoizedIsGamePkg, g.memoizedIsGamePkgErr = g.calculateIsGamePkg()
	}
	return g.memoizedIsGamePkg, g.memoizedIsGamePkgErr
}

func (g *Pkg) calculateIsGamePkg() (bool, error) {
	pkgs, err := parser.ParseDir(token.NewFileSet(), g.AbsolutePath(), nil, 0)

	if err != nil {
		return false, errors.New("Couldn't parse folder: " + err.Error())
	}

	if len(pkgs) < 1 {
		return false, errors.New("No packages in that directory")
	}

	if len(pkgs) > 1 {
		return false, errors.New("More than one package in that directory")
	}

	var pkg *ast.Package

	for _, p := range pkgs {
		pkg = p
	}

	foundNewDelegate := false

	for _, file := range pkg.Files {
		for _, decl := range file.Decls {
			fun, ok := decl.(*ast.FuncDecl)
			if !ok {
				continue
			}

			if fun.Name.String() != "NewDelegate" {
				continue
			}

			//OK, it might be the function. Does it have the right signature?

			if fun.Recv != nil {
				return false, errors.New("NewDelegate had a receiver")
			}

			if fun.Type.Params.NumFields() > 0 {
				return false, errors.New("NewDelegate took more than 0 items")
			}

			if fun.Type.Results.NumFields() != 1 {
				return false, errors.New("NewDelegate didn't return exactly one item")
			}

			//TODO: check that the returned item implements
			//boardgame.GameDelegate.

			foundNewDelegate = true
			break

		}

		if foundNewDelegate {
			break
		}
	}

	if !foundNewDelegate {
		return false, errors.New("Couldn't find NewDelegate")
	}

	return true, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 The present-tex Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"image"
	"os"

	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"

	_ "golang.org/x/image/bmp"
	_ "golang.org/x/image/tiff"
	_ "golang.org/x/image/vp8"
	_ "golang.org/x/image/webp"

	"golang.org/x/tools/present"
	"golang.org/x/xerrors"
)

type Image struct {
	present.Image
	HasCaption bool
	Caption    present.Caption
}

func parseImages(doc *present.Doc) error {
	var err error
	for i := range doc.Sections {
		section := &doc.Sections[i]
		for j := range section.Elem {
			elem := section.Elem[j]
			switch elem := elem.(type) {
			default:
				continue
			case present.Image:
				err = parseImage(&elem)
				if err != nil {
					return xerrors.Errorf("could not parse image %w: %w", elem.URL, err)
				}
				img := Image{Image: elem}
				if j+1 < len(section.Elem) {
					if elem, ok := section.Elem[j+1].(present.Caption); ok {
						err = parseCaption(&elem)
						if err != nil {
							return err
						}
						img.HasCaption = true
						img.Caption = elem
					}
				}
				section.Elem[j] = img
			}
		}
	}

	if err != nil {
		return xerrors.Errorf("could not parse images: %w", err)
	}

	return parseCaptions(doc)
}

func parseImage(elem *present.Image) error {
	var err error

	if elem.Height == 0 || elem.Width == 0 {
		f, err := os.Open(elem.URL)
		if err != nil {
			return xerrors.Errorf(
				"error opening file [%s]: %w",
				elem.URL,
				err,
			)
		}
		defer f.Close()
		img, _, err := image.Decode(f)
		if err != nil {
			return xerrors.Errorf(
				"error decoding image file [%s]: %w",
				elem.URL,
				err,
			)
		}
		h := img.Bounds().Dy()
		w := img.Bounds().Dx()

		switch {
		case elem.Height == 0 && elem.Width == 0:
			elem.Height = h
			elem.Width = w
		case elem.Height == 0 && elem.Width != 0:
			// rescale, keeping ratio
			ratio := float64(elem.Width) / float64(w)
			elem.Height = int(float64(h) * ratio)
		case elem.Height != 0 && elem.Width == 0:
			// rescale, keeping ratio
			ratio := float64(elem.Height) / float64(h)
			elem.Width = int(float64(w) * ratio)

		}

	}

	// rescale height/width to a (default=72) DPI resolution
	// height and width are now in inches.
	elem.Height /= *dpi
	elem.Width /= *dpi

	return err
}
<commit_msg>image: add support for SVG<commit_after>// Copyright 2015 The present-tex Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"image"
	"os"
	"os/exec"
	"strings"

	_ "image/gif"
	_ "image/jpeg"
	_ "image/png"

	_ "golang.org/x/image/bmp"
	_ "golang.org/x/image/tiff"
	_ "golang.org/x/image/vp8"
	_ "golang.org/x/image/webp"

	"golang.org/x/tools/present"
	"golang.org/x/xerrors"
)

type Image struct {
	present.Image
	HasCaption bool
	Caption    present.Caption
}

func parseImages(doc *present.Doc) error {
	var err error
	for i := range doc.Sections {
		section := &doc.Sections[i]
		for j := range section.Elem {
			elem := section.Elem[j]
			switch elem := elem.(type) {
			default:
				continue
			case present.Image:
				err = parseImage(&elem)
				if err != nil {
					return xerrors.Errorf("could not parse image %w: %w", elem.URL, err)
				}
				img := Image{Image: elem}
				if j+1 < len(section.Elem) {
					if elem, ok := section.Elem[j+1].(present.Caption); ok {
						err = parseCaption(&elem)
						if err != nil {
							return err
						}
						img.HasCaption = true
						img.Caption = elem
					}
				}
				section.Elem[j] = img
			}
		}
	}

	if err != nil {
		return xerrors.Errorf("could not parse images: %w", err)
	}

	return parseCaptions(doc)
}

func parseImage(elem *present.Image) error {
	var err error

	if strings.HasSuffix(elem.URL, ".svg") {
		oname := elem.URL[:len(elem.URL)-len(".svg")] + "_svg.png"
		err := exec.Command("convert", elem.URL, oname).Run()
		if err != nil {
			return xerrors.Errorf(
				"could not convert SVG image %q to PNG: %w",
				elem.URL, err,
			)
		}
		elem.URL = oname
	}

	if elem.Height == 0 || elem.Width == 0 {
		f, err := os.Open(elem.URL)
		if err != nil {
			return xerrors.Errorf(
				"error opening file [%s]: %w",
				elem.URL,
				err,
			)
		}
		defer f.Close()
		img, _, err := image.Decode(f)
		if err != nil {
			return xerrors.Errorf(
				"error decoding image file [%s]: %w",
				elem.URL,
				err,
			)
		}
		h := img.Bounds().Dy()
		w := img.Bounds().Dx()

		switch {
		case elem.Height == 0 && elem.Width == 0:
			elem.Height = h
			elem.Width = w
		case elem.Height == 0 && elem.Width != 0:
			// rescale, keeping ratio
			ratio := float64(elem.Width) / float64(w)
			elem.Height = int(float64(h) * ratio)
		case elem.Height != 0 && elem.Width == 0:
			// rescale, keeping ratio
			ratio := float64(elem.Height) / float64(h)
			elem.Width = int(float64(w) * ratio)

		}

	}

	// rescale height/width to a (default=72) DPI resolution
	// height and width are now in inches.
	elem.Height /= *dpi
	elem.Width /= *dpi

	return err
}
<|endoftext|>


<commit_before>// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff

package draw2d

import (
	"errors"
	"image"
	"image/color"
	"image/draw"
	"log"
	"math"

	"code.google.com/p/freetype-go/freetype/raster"
	"code.google.com/p/freetype-go/freetype/truetype"
)

type Painter interface {
	raster.Painter
	SetColor(color color.Color)
}

var (
	defaultFontData = FontData{"luxi", FontFamilySans, FontStyleNormal}
)

type ImageGraphicContext struct {
	*StackGraphicContext
	img              draw.Image
	painter          Painter
	fillRasterizer   *raster.Rasterizer
	strokeRasterizer *raster.Rasterizer
	glyphBuf         *truetype.GlyphBuf
	DPI              int
}

/**
 * Create a new Graphic context from an image
 */
func NewGraphicContext(img draw.Image) *ImageGraphicContext {
	var painter Painter
	switch selectImage := img.(type) {
	case *image.RGBA:
		painter = raster.NewRGBAPainter(selectImage)
	default:
		panic("Image type not supported")
	}
	return NewGraphicContextWithPainter(img, painter)
}

// Create a new Graphic context from an image and a Painter (see Freetype-go)
func NewGraphicContextWithPainter(img draw.Image, painter Painter) *ImageGraphicContext {
	width, height := img.Bounds().Dx(), img.Bounds().Dy()
	dpi := 92
	gc := &ImageGraphicContext{
		NewStackGraphicContext(),
		img,
		painter,
		raster.NewRasterizer(width, height),
		raster.NewRasterizer(width, height),
		truetype.NewGlyphBuf(),
		dpi,
	}
	return gc
}

func (gc *ImageGraphicContext) GetDPI() int {
	return gc.DPI
}

func (gc *ImageGraphicContext) Clear() {
	width, height := gc.img.Bounds().Dx(), gc.img.Bounds().Dy()
	gc.ClearRect(0, 0, width, height)
}

func (gc *ImageGraphicContext) ClearRect(x1, y1, x2, y2 int) {
	imageColor := image.NewUniform(gc.Current.FillColor)
	draw.Draw(gc.img, image.Rect(x1, y1, x2, y2), imageColor, image.ZP, draw.Over)
}

func (gc *ImageGraphicContext) DrawImage(img image.Image) {
	DrawImage(img, gc.img, gc.Current.Tr, draw.Over, BilinearFilter)
}

func (gc *ImageGraphicContext) FillString(text string) (cursor float64) {
	return gc.FillStringAt(text, 0, 0)
}

func (gc *ImageGraphicContext) FillStringAt(text string, x, y float64) (cursor float64) {
	width := gc.CreateStringPath(text, x, y)
	gc.Fill()
	return width
}

func (gc *ImageGraphicContext) StrokeString(text string) (cursor float64) {
	return gc.StrokeStringAt(text, 0, 0)
}

func (gc *ImageGraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) {
	width := gc.CreateStringPath(text, x, y)
	gc.Stroke()
	return width
}

func (gc *ImageGraphicContext) loadCurrentFont() (*truetype.Font, error) {
	font := GetFont(gc.Current.FontData)
	if font == nil {
		font = GetFont(defaultFontData)
	}
	if font == nil {
		return nil, errors.New("No font set, and no default font available.")
	}
	gc.SetFont(font)
	gc.SetFontSize(gc.Current.FontSize)
	return font, nil
}

func fUnitsToFloat64(x int32) float64 {
	scaled := x << 2
	return float64(scaled/256) + float64(scaled%256)/256.0
}

// p is a truetype.Point measured in FUnits and positive Y going upwards.
// The returned value is the same thing measured in floating point and positive Y
// going downwards.
func pointToF64Point(p truetype.Point) (x, y float64) {
	return fUnitsToFloat64(p.X), -fUnitsToFloat64(p.Y)
}

// drawContour draws the given closed contour at the given sub-pixel offset.
func (gc *ImageGraphicContext) drawContour(ps []truetype.Point, dx, dy float64) {
	if len(ps) == 0 {
		return
	}
	startX, startY := pointToF64Point(ps[0])
	gc.MoveTo(startX+dx, startY+dy)
	q0X, q0Y, on0 := startX, startY, true
	for _, p := range ps[1:] {
		qX, qY := pointToF64Point(p)
		on := p.Flags&0x01 != 0
		if on {
			if on0 {
				gc.LineTo(qX+dx, qY+dy)
			} else {
				gc.QuadCurveTo(q0X+dx, q0Y+dy, qX+dx, qY+dy)
			}
		} else {
			if on0 {
				// No-op.
			} else {
				midX := (q0X + qX) / 2
				midY := (q0Y + qY) / 2
				gc.QuadCurveTo(q0X+dx, q0Y+dy, midX+dx, midY+dy)
			}
		}
		q0X, q0Y, on0 = qX, qY, on
	}
	// Close the curve.
	if on0 {
		gc.LineTo(startX+dx, startY+dy)
	} else {
		gc.QuadCurveTo(q0X+dx, q0Y+dy, startX+dx, startY+dy)
	}
}

func (gc *ImageGraphicContext) drawGlyph(glyph truetype.Index, dx, dy float64) error {
	if err := gc.glyphBuf.Load(gc.Current.Font, int32(gc.Current.Scale), glyph, truetype.NoHinting); err != nil {
		return err
	}
	e0 := 0
	for _, e1 := range gc.glyphBuf.End {
		gc.drawContour(gc.glyphBuf.Point[e0:e1], dx, dy)
		e0 = e1
	}
	return nil
}

// CreateStringPath creates a path from the string s at x, y, and returns the string width.
// The text is placed so that the left edge of the em square of the first character of s
// and the baseline intersect at x, y. The majority of the affected pixels will be
// above and to the right of the point, but some may be below or to the left.
// For example, drawing a string that starts with a 'J' in an italic font may
// affect pixels below and left of the point.
func (gc *ImageGraphicContext) CreateStringPath(s string, x, y float64) float64 {
	font, err := gc.loadCurrentFont()
	if err != nil {
		log.Println(err)
		return 0.0
	}
	startx := x
	prev, hasPrev := truetype.Index(0), false
	for _, rune := range s {
		index := font.Index(rune)
		if hasPrev {
			x += fUnitsToFloat64(font.Kerning(int32(gc.Current.Scale), prev, index))
		}
		err := gc.drawGlyph(index, x, y)
		if err != nil {
			log.Println(err)
			return startx - x
		}
		x += fUnitsToFloat64(font.HMetric(int32(gc.Current.Scale), index).AdvanceWidth)
		prev, hasPrev = index, true
	}
	return x - startx
}

// GetStringBounds returns the approximate pixel bounds of the string s at x, y.
// The the left edge of the em square of the first character of s
// and the baseline intersect at 0, 0 in the returned coordinates.
// Therefore the top and left coordinates may well be negative.
func (gc *ImageGraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) {
	font, err := gc.loadCurrentFont()
	if err != nil {
		log.Println(err)
		return 0, 0, 0, 0
	}
	top, left, bottom, right = 10e6, 10e6, -10e6, -10e6
	cursor := 0.0
	prev, hasPrev := truetype.Index(0), false
	for _, rune := range s {
		index := font.Index(rune)
		if hasPrev {
			cursor += fUnitsToFloat64(font.Kerning(int32(gc.Current.Scale), prev, index))
		}
		if err := gc.glyphBuf.Load(gc.Current.Font, int32(gc.Current.Scale), index, truetype.NoHinting); err != nil {
			log.Println(err)
			return 0, 0, 0, 0
		}
		e0 := 0
		for _, e1 := range gc.glyphBuf.End {
			ps := gc.glyphBuf.Point[e0:e1]
			for _, p := range ps {
				x, y := pointToF64Point(p)
				top = math.Min(top, y)
				bottom = math.Max(bottom, y)
				left = math.Min(left, x+cursor)
				right = math.Max(right, x+cursor)
			}
		}
		cursor += fUnitsToFloat64(font.HMetric(int32(gc.Current.Scale), index).AdvanceWidth)
		prev, hasPrev = index, true
	}
	return left, top, right, bottom
}

// recalc recalculates scale and bounds values from the font size, screen
// resolution and font metrics, and invalidates the glyph cache.
func (gc *ImageGraphicContext) recalc() {
	int32(gc.Current.Scale) = int32(gc.Current.FontSize * float64(gc.DPI) * (64.0 / 72.0))
}

// SetDPI sets the screen resolution in dots per inch.
func (gc *ImageGraphicContext) SetDPI(dpi int) {
	gc.DPI = dpi
	gc.recalc()
}

// SetFont sets the font used to draw text.
func (gc *ImageGraphicContext) SetFont(font *truetype.Font) {
	gc.Current.Font = font
}

// SetFontSize sets the font size in points (as in ``a 12 point font'').
func (gc *ImageGraphicContext) SetFontSize(fontSize float64) {
	gc.Current.FontSize = fontSize
	gc.recalc()
}

func (gc *ImageGraphicContext) paint(rasterizer *raster.Rasterizer, color color.Color) {
	gc.painter.SetColor(color)
	rasterizer.Rasterize(gc.painter)
	rasterizer.Clear()
	gc.Current.Path.Clear()
}

/**** second method ****/
func (gc *ImageGraphicContext) Stroke(paths ...*PathStorage) {
	paths = append(paths, gc.Current.Path)
	gc.strokeRasterizer.UseNonZeroWinding = true

	stroker := NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.strokeRasterizer)))
	stroker.HalfLineWidth = gc.Current.LineWidth / 2
	var pathConverter *PathConverter
	if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
		dasher := NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
		pathConverter = NewPathConverter(dasher)
	} else {
		pathConverter = NewPathConverter(stroker)
	}
	pathConverter.ApproximationScale = gc.Current.Tr.GetScale()
	pathConverter.Convert(paths...)

	gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
}

/**** second method ****/
func (gc *ImageGraphicContext) Fill(paths ...*PathStorage) {
	paths = append(paths, gc.Current.Path)
	gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding()

	/**** first method ****/
	pathConverter := NewPathConverter(NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer)))
	pathConverter.ApproximationScale = gc.Current.Tr.GetScale()
	pathConverter.Convert(paths...)

	gc.paint(gc.fillRasterizer, gc.Current.FillColor)
}

/* second method */
func (gc *ImageGraphicContext) FillStroke(paths ...*PathStorage) {
	gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding()
	gc.strokeRasterizer.UseNonZeroWinding = true

	filler := NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer))

	stroker := NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.strokeRasterizer)))
	stroker.HalfLineWidth = gc.Current.LineWidth / 2

	demux := NewDemuxConverter(filler, stroker)
	paths = append(paths, gc.Current.Path)
	pathConverter := NewPathConverter(demux)
	pathConverter.ApproximationScale = gc.Current.Tr.GetScale()
	pathConverter.Convert(paths...)

	gc.paint(gc.fillRasterizer, gc.Current.FillColor)
	gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
}

func (f FillRule) UseNonZeroWinding() bool {
	switch f {
	case FillRuleEvenOdd:
		return false
	case FillRuleWinding:
		return true
	}
	return false
}

func (c Cap) Convert() raster.Capper {
	switch c {
	case RoundCap:
		return raster.RoundCapper
	case ButtCap:
		return raster.ButtCapper
	case SquareCap:
		return raster.SquareCapper
	}
	return raster.RoundCapper
}

func (j Join) Convert() raster.Joiner {
	switch j {
	case RoundJoin:
		return raster.RoundJoiner
	case BevelJoin:
		return raster.BevelJoiner
	}
	return raster.RoundJoiner
}
<commit_msg>fix recalc<commit_after>// Copyright 2010 The draw2d Authors. All rights reserved.
// created: 21/11/2010 by Laurent Le Goff

package draw2d

import (
	"errors"
	"image"
	"image/color"
	"image/draw"
	"log"
	"math"

	"code.google.com/p/freetype-go/freetype/raster"
	"code.google.com/p/freetype-go/freetype/truetype"
)

type Painter interface {
	raster.Painter
	SetColor(color color.Color)
}

var (
	defaultFontData = FontData{"luxi", FontFamilySans, FontStyleNormal}
)

type ImageGraphicContext struct {
	*StackGraphicContext
	img              draw.Image
	painter          Painter
	fillRasterizer   *raster.Rasterizer
	strokeRasterizer *raster.Rasterizer
	glyphBuf         *truetype.GlyphBuf
	DPI              int
}

/**
 * Create a new Graphic context from an image
 */
func NewGraphicContext(img draw.Image) *ImageGraphicContext {
	var painter Painter
	switch selectImage := img.(type) {
	case *image.RGBA:
		painter = raster.NewRGBAPainter(selectImage)
	default:
		panic("Image type not supported")
	}
	return NewGraphicContextWithPainter(img, painter)
}

// Create a new Graphic context from an image and a Painter (see Freetype-go)
func NewGraphicContextWithPainter(img draw.Image, painter Painter) *ImageGraphicContext {
	width, height := img.Bounds().Dx(), img.Bounds().Dy()
	dpi := 92
	gc := &ImageGraphicContext{
		NewStackGraphicContext(),
		img,
		painter,
		raster.NewRasterizer(width, height),
		raster.NewRasterizer(width, height),
		truetype.NewGlyphBuf(),
		dpi,
	}
	return gc
}

func (gc *ImageGraphicContext) GetDPI() int {
	return gc.DPI
}

func (gc *ImageGraphicContext) Clear() {
	width, height := gc.img.Bounds().Dx(), gc.img.Bounds().Dy()
	gc.ClearRect(0, 0, width, height)
}

func (gc *ImageGraphicContext) ClearRect(x1, y1, x2, y2 int) {
	imageColor := image.NewUniform(gc.Current.FillColor)
	draw.Draw(gc.img, image.Rect(x1, y1, x2, y2), imageColor, image.ZP, draw.Over)
}

func (gc *ImageGraphicContext) DrawImage(img image.Image) {
	DrawImage(img, gc.img, gc.Current.Tr, draw.Over, BilinearFilter)
}

func (gc *ImageGraphicContext) FillString(text string) (cursor float64) {
	return gc.FillStringAt(text, 0, 0)
}

func (gc *ImageGraphicContext) FillStringAt(text string, x, y float64) (cursor float64) {
	width := gc.CreateStringPath(text, x, y)
	gc.Fill()
	return width
}

func (gc *ImageGraphicContext) StrokeString(text string) (cursor float64) {
	return gc.StrokeStringAt(text, 0, 0)
}

func (gc *ImageGraphicContext) StrokeStringAt(text string, x, y float64) (cursor float64) {
	width := gc.CreateStringPath(text, x, y)
	gc.Stroke()
	return width
}

func (gc *ImageGraphicContext) loadCurrentFont() (*truetype.Font, error) {
	font := GetFont(gc.Current.FontData)
	if font == nil {
		font = GetFont(defaultFontData)
	}
	if font == nil {
		return nil, errors.New("No font set, and no default font available.")
	}
	gc.SetFont(font)
	gc.SetFontSize(gc.Current.FontSize)
	return font, nil
}

func fUnitsToFloat64(x int32) float64 {
	scaled := x << 2
	return float64(scaled/256) + float64(scaled%256)/256.0
}

// p is a truetype.Point measured in FUnits and positive Y going upwards.
// The returned value is the same thing measured in floating point and positive Y
// going downwards.
func pointToF64Point(p truetype.Point) (x, y float64) {
	return fUnitsToFloat64(p.X), -fUnitsToFloat64(p.Y)
}

// drawContour draws the given closed contour at the given sub-pixel offset.
func (gc *ImageGraphicContext) drawContour(ps []truetype.Point, dx, dy float64) {
	if len(ps) == 0 {
		return
	}
	startX, startY := pointToF64Point(ps[0])
	gc.MoveTo(startX+dx, startY+dy)
	q0X, q0Y, on0 := startX, startY, true
	for _, p := range ps[1:] {
		qX, qY := pointToF64Point(p)
		on := p.Flags&0x01 != 0
		if on {
			if on0 {
				gc.LineTo(qX+dx, qY+dy)
			} else {
				gc.QuadCurveTo(q0X+dx, q0Y+dy, qX+dx, qY+dy)
			}
		} else {
			if on0 {
				// No-op.
			} else {
				midX := (q0X + qX) / 2
				midY := (q0Y + qY) / 2
				gc.QuadCurveTo(q0X+dx, q0Y+dy, midX+dx, midY+dy)
			}
		}
		q0X, q0Y, on0 = qX, qY, on
	}
	// Close the curve.
	if on0 {
		gc.LineTo(startX+dx, startY+dy)
	} else {
		gc.QuadCurveTo(q0X+dx, q0Y+dy, startX+dx, startY+dy)
	}
}

func (gc *ImageGraphicContext) drawGlyph(glyph truetype.Index, dx, dy float64) error {
	if err := gc.glyphBuf.Load(gc.Current.Font, int32(gc.Current.Scale), glyph, truetype.NoHinting); err != nil {
		return err
	}
	e0 := 0
	for _, e1 := range gc.glyphBuf.End {
		gc.drawContour(gc.glyphBuf.Point[e0:e1], dx, dy)
		e0 = e1
	}
	return nil
}

// CreateStringPath creates a path from the string s at x, y, and returns the string width.
// The text is placed so that the left edge of the em square of the first character of s
// and the baseline intersect at x, y. The majority of the affected pixels will be
// above and to the right of the point, but some may be below or to the left.
// For example, drawing a string that starts with a 'J' in an italic font may
// affect pixels below and left of the point.
func (gc *ImageGraphicContext) CreateStringPath(s string, x, y float64) float64 {
	font, err := gc.loadCurrentFont()
	if err != nil {
		log.Println(err)
		return 0.0
	}
	startx := x
	prev, hasPrev := truetype.Index(0), false
	for _, rune := range s {
		index := font.Index(rune)
		if hasPrev {
			x += fUnitsToFloat64(font.Kerning(int32(gc.Current.Scale), prev, index))
		}
		err := gc.drawGlyph(index, x, y)
		if err != nil {
			log.Println(err)
			return startx - x
		}
		x += fUnitsToFloat64(font.HMetric(int32(gc.Current.Scale), index).AdvanceWidth)
		prev, hasPrev = index, true
	}
	return x - startx
}

// GetStringBounds returns the approximate pixel bounds of the string s at x, y.
// The the left edge of the em square of the first character of s
// and the baseline intersect at 0, 0 in the returned coordinates.
// Therefore the top and left coordinates may well be negative.
func (gc *ImageGraphicContext) GetStringBounds(s string) (left, top, right, bottom float64) {
	font, err := gc.loadCurrentFont()
	if err != nil {
		log.Println(err)
		return 0, 0, 0, 0
	}
	top, left, bottom, right = 10e6, 10e6, -10e6, -10e6
	cursor := 0.0
	prev, hasPrev := truetype.Index(0), false
	for _, rune := range s {
		index := font.Index(rune)
		if hasPrev {
			cursor += fUnitsToFloat64(font.Kerning(int32(gc.Current.Scale), prev, index))
		}
		if err := gc.glyphBuf.Load(gc.Current.Font, int32(gc.Current.Scale), index, truetype.NoHinting); err != nil {
			log.Println(err)
			return 0, 0, 0, 0
		}
		e0 := 0
		for _, e1 := range gc.glyphBuf.End {
			ps := gc.glyphBuf.Point[e0:e1]
			for _, p := range ps {
				x, y := pointToF64Point(p)
				top = math.Min(top, y)
				bottom = math.Max(bottom, y)
				left = math.Min(left, x+cursor)
				right = math.Max(right, x+cursor)
			}
		}
		cursor += fUnitsToFloat64(font.HMetric(int32(gc.Current.Scale), index).AdvanceWidth)
		prev, hasPrev = index, true
	}
	return left, top, right, bottom
}

// recalc recalculates scale and bounds values from the font size, screen
// resolution and font metrics, and invalidates the glyph cache.
func (gc *ImageGraphicContext) recalc() {
	gc.Current.Scale = gc.Current.FontSize * float64(gc.DPI) * (64.0 / 72.0)
}

// SetDPI sets the screen resolution in dots per inch.
func (gc *ImageGraphicContext) SetDPI(dpi int) {
	gc.DPI = dpi
	gc.recalc()
}

// SetFont sets the font used to draw text.
func (gc *ImageGraphicContext) SetFont(font *truetype.Font) {
	gc.Current.Font = font
}

// SetFontSize sets the font size in points (as in ``a 12 point font'').
func (gc *ImageGraphicContext) SetFontSize(fontSize float64) {
	gc.Current.FontSize = fontSize
	gc.recalc()
}

func (gc *ImageGraphicContext) paint(rasterizer *raster.Rasterizer, color color.Color) {
	gc.painter.SetColor(color)
	rasterizer.Rasterize(gc.painter)
	rasterizer.Clear()
	gc.Current.Path.Clear()
}

/**** second method ****/
func (gc *ImageGraphicContext) Stroke(paths ...*PathStorage) {
	paths = append(paths, gc.Current.Path)
	gc.strokeRasterizer.UseNonZeroWinding = true

	stroker := NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.strokeRasterizer)))
	stroker.HalfLineWidth = gc.Current.LineWidth / 2
	var pathConverter *PathConverter
	if gc.Current.Dash != nil && len(gc.Current.Dash) > 0 {
		dasher := NewDashConverter(gc.Current.Dash, gc.Current.DashOffset, stroker)
		pathConverter = NewPathConverter(dasher)
	} else {
		pathConverter = NewPathConverter(stroker)
	}
	pathConverter.ApproximationScale = gc.Current.Tr.GetScale()
	pathConverter.Convert(paths...)

	gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
}

/**** second method ****/
func (gc *ImageGraphicContext) Fill(paths ...*PathStorage) {
	paths = append(paths, gc.Current.Path)
	gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding()

	/**** first method ****/
	pathConverter := NewPathConverter(NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer)))
	pathConverter.ApproximationScale = gc.Current.Tr.GetScale()
	pathConverter.Convert(paths...)

	gc.paint(gc.fillRasterizer, gc.Current.FillColor)
}

/* second method */
func (gc *ImageGraphicContext) FillStroke(paths ...*PathStorage) {
	gc.fillRasterizer.UseNonZeroWinding = gc.Current.FillRule.UseNonZeroWinding()
	gc.strokeRasterizer.UseNonZeroWinding = true

	filler := NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.fillRasterizer))

	stroker := NewLineStroker(gc.Current.Cap, gc.Current.Join, NewVertexMatrixTransform(gc.Current.Tr, NewVertexAdder(gc.strokeRasterizer)))
	stroker.HalfLineWidth = gc.Current.LineWidth / 2

	demux := NewDemuxConverter(filler, stroker)
	paths = append(paths, gc.Current.Path)
	pathConverter := NewPathConverter(demux)
	pathConverter.ApproximationScale = gc.Current.Tr.GetScale()
	pathConverter.Convert(paths...)

	gc.paint(gc.fillRasterizer, gc.Current.FillColor)
	gc.paint(gc.strokeRasterizer, gc.Current.StrokeColor)
}

func (f FillRule) UseNonZeroWinding() bool {
	switch f {
	case FillRuleEvenOdd:
		return false
	case FillRuleWinding:
		return true
	}
	return false
}

func (c Cap) Convert() raster.Capper {
	switch c {
	case RoundCap:
		return raster.RoundCapper
	case ButtCap:
		return raster.ButtCapper
	case SquareCap:
		return raster.SquareCapper
	}
	return raster.RoundCapper
}

func (j Join) Convert() raster.Joiner {
	switch j {
	case RoundJoin:
		return raster.RoundJoiner
	case BevelJoin:
		return raster.BevelJoiner
	}
	return raster.RoundJoiner
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/binary"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"runtime"
	"syscall"
)

func main() {
	var port = flag.Int("p", 0, "port to listen on")
	flag.Parse()

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		for range c {
			os.Exit(0)
		}
	}()

	runtime.GOMAXPROCS(runtime.NumCPU())
	ln, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
	if err != nil {
		log.Printf("failed to listen on port %d: %v", port, err)
		return
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println("failed to accept connection:", err)
			continue
		}
		go handleConnection(conn)
	}
}

func handleConnection(c net.Conn) {
	f, err := c.(*net.TCPConn).File()
	c.Close()
	if err != nil {
		log.Println(err)
	}
	defer f.Close()

	var (
		challenge uint32
		buf       = make([]byte, 4)
	)

	for {
		if _, err = f.Read(buf); err != nil {
			if err != io.EOF {
				fmt.Println("read error: ", err)
			}
			return
		}

		if challenge = binary.BigEndian.Uint32(buf); challenge == 0 {
			os.Exit(0)
		}
		binary.BigEndian.PutUint32(buf, challenge+1)

		if _, err = f.Write(buf); err != nil {
			log.Println("write error: ", err)
			return
		}
	}
}
<commit_msg>go-blocking: Increase threads for connections<commit_after>package main

import (
	"encoding/binary"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"runtime"
	"sync/atomic"
	"syscall"
)

var (
	numConns   int32 = 0
	numThreads int
)

func main() {
	var port = flag.Int("p", 0, "port to listen on")
	flag.Parse()

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		for range c {
			os.Exit(0)
		}
	}()

	numThreads = runtime.NumCPU()
	runtime.GOMAXPROCS(numThreads)

	ln, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))
	if err != nil {
		log.Printf("failed to listen on port %d: %v", port, err)
		return
	}

	for {
		conn, err := ln.Accept()
		if err != nil {
			log.Println("failed to accept connection:", err)
			continue
		}
		nc := atomic.AddInt32(&numConns, 1)
		if int(nc) > numThreads {
			numThreads *= 2
			runtime.GOMAXPROCS(numThreads)
		}
		go handleConnection(conn)
	}
}

func handleConnection(c net.Conn) {
	runtime.LockOSThread()

	f, err := c.(*net.TCPConn).File()
	c.Close()
	if err != nil {
		log.Println(err)
		return
	}

	defer func() {
		f.Close()
		atomic.AddInt32(&numConns, -1)
	}()

	var (
		challenge uint32
		buf       = make([]byte, 4)
	)

	for {
		if _, err = f.Read(buf); err != nil {
			if err != io.EOF {
				fmt.Println("read error: ", err)
			}
			return
		}

		if challenge = binary.BigEndian.Uint32(buf); challenge == 0 {
			os.Exit(0)
		}
		binary.BigEndian.PutUint32(buf, challenge+1)

		if _, err = f.Write(buf); err != nil {
			log.Println("write error: ", err)
			return
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"
)

type stateFn func(*CoffeeLex) stateFn

type CoffeeLex struct {
	// the line
	input string
	line  int
	start int
	pos   int
	state stateFn

	// current space prefix length (used to calculate indentation level)
	space     int
	lastSpace int

	// XXX
	width int
	items chan *CoffeeSymType
}

type TokenType int

const eof = -1
const lexError = -99

func (self *CoffeeSymType) String() string {
	switch self.typ {
	case T_EOF:
		return "EOF"
	}
	return fmt.Sprintf("[%d] %q", self.typ, self.val)
}

func (l *CoffeeLex) emit(t TokenType) {
	l.items <- &CoffeeSymType{
		typ:  t,
		val:  l.input[l.start:l.pos],
		pos:  l.start,
		line: l.line,
	}
	l.start = l.pos
}

// peek returns but does not consume
// the next rune in the input.
func (l *CoffeeLex) peek() (r rune) {
	r = l.next()
	l.backup()
	return r
}

func (l *CoffeeLex) peekMore(p int) (r rune) {
	var w = 0
	for i := p; i > 0; i-- {
		r = l.next()
		w += l.width
	}
	l.pos -= w
	return r
}

// ignore skips over the pending input before this point.
func (l *CoffeeLex) ignore() {
	l.start = l.pos
}

// accept consumes the next rune
// if it's from the valid set.
func (l *CoffeeLex) accept(valid string) bool {
	if strings.IndexRune(valid, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

// backup steps back one rune.
// Can be called only once per call of next.
func (l *CoffeeLex) backup() {
	l.pos -= l.width
}

// next returns the next rune in the input.
func (l *CoffeeLex) next() (r rune) {
	if l.pos >= len(l.input) {
		l.width = 0
		return eof
	}
	r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
	l.pos += l.width
	return r
}

func (l *CoffeeLex) run() {
	for l.state = lexStart; l.state != nil; {
		fn := l.state(l)
		if fn != nil {
			l.state = fn
		} else {
			break
		}
	}
	l.emit(T_EOF)
}

func (l *CoffeeLex) close() {
	close(l.items)
}

func lexStartLine(l *CoffeeLex) stateFn {
	var c rune = l.peek()
	if c == ' ' || c == '\t' {
		return lexIndentSpaces
	}
	// default start state.
	return lexStart
}

func lexStart(l *CoffeeLex) stateFn {
	var c rune = l.peek()
	if unicode.IsDigit(c) {
		return lexNumber
	} else if c == ' ' || c == '\t' {
		// return lexSpaces
		return lexIgnoreSpaces
	} else if c == '\n' || c == '\r' {
		l.line++
		l.next()
		l.emit(T_NEWLINE)
		l.lastSpace = l.space
		l.space = 0
		return lexStartLine
	} else if c == '=' && l.peekMore(2) != '=' {
		l.next()
		l.emit(T_ASSIGN)
		return lexStart
	} else if l.consumeIfMatch("//") {
		return lexOnelineComment
	} else if l.consumeIfMatch("/*") {
		return lexComment
	} else if l.emitIfMatch("if ", T_IF) {
		return lexStart
	} else if l.emitIfMatch("if else ", T_ELSEIF) {
		return lexStart
	} else if l.emitIfMatch("class ", T_CLASS) {
		return lexStart
	} else if unicode.IsLetter(c) {
		return lexIdentifier
	} else if c == eof {
		l.emit(T_EOF)
		return nil
	}
	return nil
}

func lexComment(l *CoffeeLex) stateFn {
	var c rune
	for {
		c = l.next()
		if c == eof {
			break
		}
		if c == '*' && l.peek() == '/' {
			break
		}
	}
	l.next()
	l.emit(T_COMMENT)
	return lexStart
}

func lexOnelineComment(l *CoffeeLex) stateFn {
	var c rune
	for {
		c = l.next()
		if c == '\n' || c == eof {
			break
		}
	}
	l.backup()
	l.emit(T_ONELINE_COMMENT)
	return lexStartLine
}

func lexIdentifier(l *CoffeeLex) stateFn {
	for {
		c := l.next()
		if unicode.IsLetter(c) || unicode.IsDigit(c) {
		} else {
			break
		}
	}
	l.backup()
	l.emit(T_IDENTIFIER)
	return lexStart
}

func lexIgnoreSpaces(l *CoffeeLex) stateFn {
	var c rune
	for {
		c = l.next()
		if c != ' ' || c == eof {
			break
		}
	}
	l.backup()
	l.ignore()
	return lexStart
}

func lexIndentSpaces(l *CoffeeLex) stateFn {
	l.space = 0
	for {
		c := l.next()
		if c == ' ' || c == '\t' {
			l.space++
		} else {
			break
		}
	}
	l.backup()
	l.emit(T_SPACE)
	return lexStart
}

func lexSpaces(l *CoffeeLex) stateFn {
	for c := l.next(); c == ' ' || c == '\t'; {
	}
	l.backup()
	l.emit(T_SPACE)
	return lexStart
}

func lexFloating(l *CoffeeLex) stateFn {
	for {
		c := l.next()
		if !unicode.IsDigit(c) {
			break
		}
	}
	l.backup()
	l.emit(T_FLOATING)
	return lexStart
}

func lexNumber(l *CoffeeLex) stateFn {
	var c rune
	for c = l.next(); true; c = l.next() {
		if unicode.IsDigit(c) {
			continue
		} else if c == '.' && unicode.IsDigit(l.peek()) {
			return lexFloating
		} else {
			break
		}
	}
	l.backup()
	l.emit(T_NUMBER)
	return lexStart
}

func (l *CoffeeLex) emitIfMatch(str string, typ TokenType) bool {
	if l.consumeIfMatch(str) {
		l.emit(typ)
		return true
	}
	return false
}

func (l *CoffeeLex) consumeIfMatch(str string) bool {
	var c rune
	var width = 0
	for _, sc := range str {
		c = l.next()
		width += l.width
		if sc != c {
			// rollback
			l.pos -= width
			return false
		}
	}
	return true
}

/*
lookahead match method
*/
func (l *CoffeeLex) match(str string) bool {
	var c rune
	var width = 0
	for _, sc := range str {
		c = l.next()
		width += l.width
		if sc != c {
			l.pos -= width
			return false
		}
	}
	l.pos -= width
	return true
}

// set token in lval, return the token type id
func (l *CoffeeLex) Lex(lval *CoffeeSymType) int {
	var c rune = l.next()
	if unicode.IsDigit(c) {
		lval.val = string(c)
		lval.typ = T_DIGIT
		return T_DIGIT
	} else if unicode.IsLower(c) {
		lval.val = string(c)
		lval.typ = T_LETTER
		return T_LETTER
	}
	return int(c)
}

func (l *CoffeeLex) Error(s string) {
	fmt.Printf("syntax error: %s\n", s)
}
<commit_msg>Lexer: add "for", "foreach" token<commit_after>package main

import (
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"
)

type stateFn func(*CoffeeLex) stateFn

type CoffeeLex struct {
	// the line
	input string
	line  int
	start int
	pos   int
	state stateFn

	// current space prefix length (used to calculate indentation level)
	space     int
	lastSpace int

	// XXX
	width int
	items chan *CoffeeSymType
}

type TokenType int

const eof = -1
const lexError = -99

func (self *CoffeeSymType) String() string {
	switch self.typ {
	case T_EOF:
		return "EOF"
	}
	return fmt.Sprintf("[%d] %q", self.typ, self.val)
}

func (l *CoffeeLex) emit(t TokenType) {
	l.items <- &CoffeeSymType{
		typ:  t,
		val:  l.input[l.start:l.pos],
		pos:  l.start,
		line: l.line,
	}
	l.start = l.pos
}

// peek returns but does not consume
// the next rune in the input.
func (l *CoffeeLex) peek() (r rune) {
	r = l.next()
	l.backup()
	return r
}

func (l *CoffeeLex) peekMore(p int) (r rune) {
	var w = 0
	for i := p; i > 0; i-- {
		r = l.next()
		w += l.width
	}
	l.pos -= w
	return r
}

// ignore skips over the pending input before this point.
func (l *CoffeeLex) ignore() {
	l.start = l.pos
}

// accept consumes the next rune
// if it's from the valid set.
func (l *CoffeeLex) accept(valid string) bool {
	if strings.IndexRune(valid, l.next()) >= 0 {
		return true
	}
	l.backup()
	return false
}

// backup steps back one rune.
// Can be called only once per call of next.
func (l *CoffeeLex) backup() {
	l.pos -= l.width
}

// next returns the next rune in the input.
func (l *CoffeeLex) next() (r rune) {
	if l.pos >= len(l.input) {
		l.width = 0
		return eof
	}
	r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
	l.pos += l.width
	return r
}

func (l *CoffeeLex) run() {
	for l.state = lexStart; l.state != nil; {
		fn := l.state(l)
		if fn != nil {
			l.state = fn
		} else {
			break
		}
	}
	l.emit(T_EOF)
}

func (l *CoffeeLex) close() {
	close(l.items)
}

func lexStartLine(l *CoffeeLex) stateFn {
	var c rune = l.peek()
	if c == ' ' || c == '\t' {
		return lexIndentSpaces
	}
	// default start state.
	return lexStart
}

func lexStart(l *CoffeeLex) stateFn {
	var c rune = l.peek()
	if unicode.IsDigit(c) {
		return lexNumber
	} else if c == ' ' || c == '\t' {
		// return lexSpaces
		return lexIgnoreSpaces
	} else if c == '\n' || c == '\r' {
		l.line++
		l.next()
		l.emit(T_NEWLINE)
		l.lastSpace = l.space
		l.space = 0
		return lexStartLine
	} else if c == '=' && l.peekMore(2) != '=' {
		l.next()
		l.emit(T_ASSIGN)
		return lexStart
	} else if l.consumeIfMatch("//") {
		return lexOnelineComment
	} else if l.consumeIfMatch("/*") {
		return lexComment
	} else if l.emitIfMatch("if ", T_IF) {
		return lexStart
	} else if l.emitIfMatch("if else ", T_ELSEIF) {
		return lexStart
	} else if l.emitIfMatch("class ", T_CLASS) {
		return lexStart
	} else if l.emitIfMatch("for ", T_FOR) {
		return lexStart
	} else if l.emitIfMatch("foreach ", T_FOREACH) {
		return lexStart
	} else if unicode.IsLetter(c) {
		return lexIdentifier
	} else if c == eof {
		l.emit(T_EOF)
		return nil
	}
	return nil
}

func lexComment(l *CoffeeLex) stateFn {
	var c rune
	for {
		c = l.next()
		if c == eof {
			break
		}
		if c == '*' && l.peek() == '/' {
			break
		}
	}
	l.next()
	l.emit(T_COMMENT)
	return lexStart
}

func lexOnelineComment(l *CoffeeLex) stateFn {
	var c rune
	for {
		c = l.next()
		if c == '\n' || c == eof {
			break
		}
	}
	l.backup()
	l.emit(T_ONELINE_COMMENT)
	return lexStartLine
}

func lexIdentifier(l *CoffeeLex) stateFn {
	for {
		c := l.next()
		if unicode.IsLetter(c) || unicode.IsDigit(c) {
		} else {
			break
		}
	}
	l.backup()
	l.emit(T_IDENTIFIER)
	return lexStart
}

func lexIgnoreSpaces(l *CoffeeLex) stateFn {
	var c rune
	for {
		c = l.next()
		if c != ' ' || c == eof {
			break
		}
	}
	l.backup()
	l.ignore()
	return lexStart
}

func lexIndentSpaces(l *CoffeeLex) stateFn {
	l.space = 0
	for {
		c := l.next()
		if c == ' ' || c == '\t' {
			l.space++
		} else {
			break
		}
	}
	l.backup()
	l.emit(T_SPACE)
	return lexStart
}

func lexSpaces(l *CoffeeLex) stateFn {
	for c := l.next(); c == ' ' || c == '\t'; {
	}
	l.backup()
	l.emit(T_SPACE)
	return lexStart
}

func lexFloating(l *CoffeeLex) stateFn {
	for {
		c := l.next()
		if !unicode.IsDigit(c) {
			break
		}
	}
	l.backup()
	l.emit(T_FLOATING)
	return lexStart
}

func lexNumber(l *CoffeeLex) stateFn {
	var c rune
	for c = l.next(); true; c = l.next() {
		if unicode.IsDigit(c) {
			continue
		} else if c == '.' && unicode.IsDigit(l.peek()) {
			return lexFloating
		} else {
			break
		}
	}
	l.backup()
	l.emit(T_NUMBER)
	return lexStart
}

func (l *CoffeeLex) emitIfMatch(str string, typ TokenType) bool {
	if l.consumeIfMatch(str) {
		l.emit(typ)
		return true
	}
	return false
}

func (l *CoffeeLex) consumeIfMatch(str string) bool {
	var c rune
	var width = 0
	for _, sc := range str {
		c = l.next()
		width += l.width
		if sc != c {
			// rollback
			l.pos -= width
			return false
		}
	}
	return true
}

/*
lookahead match method
*/
func (l *CoffeeLex) match(str string) bool {
	var c rune
	var width = 0
	for _, sc := range str {
		c = l.next()
		width += l.width
		if sc != c {
			l.pos -= width
			return false
		}
	}
	l.pos -= width
	return true
}

// set token in lval, return the token type id
func (l *CoffeeLex) Lex(lval *CoffeeSymType) int {
	var c rune = l.next()
	if unicode.IsDigit(c) {
		lval.val = string(c)
		lval.typ = T_DIGIT
		return T_DIGIT
	} else if unicode.IsLower(c) {
		lval.val = string(c)
		lval.typ = T_LETTER
		return T_LETTER
	}
	return int(c)
}

func (l *CoffeeLex) Error(s string) {
	fmt.Printf("syntax error: %s\n", s)
}
<|endoftext|>