<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package watcher

import (
	"context"
	"expvar"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"syscall"
	"testing"
	"time"

	"github.com/golang/glog"
	"github.com/google/mtail/internal/testutil"
	"github.com/pkg/errors"
)

type testStubProcessor struct {
	Events chan Event
}

func (t *testStubProcessor) ProcessFileEvent(ctx context.Context, e Event) {
	go func() {
		t.Events <- e
	}()
}

func newStubProcessor() *testStubProcessor {
	return &testStubProcessor{Events: make(chan Event, 1)}
}

func TestLogWatcher(t *testing.T) {
	if testing.Short() {
		// This test is slow due to disk access.
		t.Skip("skipping log watcher test in short mode")
	}

	workdir, rmWorkdir := testutil.TestTempDir(t)
	defer rmWorkdir()

	w, err := NewLogWatcher(0, true)
	testutil.FatalIfErr(t, err)
	defer func() {
		if err = w.Close(); err != nil {
			t.Fatal(err)
		}
	}()

	s := newStubProcessor()

	if err = w.Observe(workdir, s); err != nil {
		t.Fatal(err)
	}
	f, err := os.Create(filepath.Join(workdir, "logfile"))
	testutil.FatalIfErr(t, err)
	select {
	case e := <-s.Events:
		expected := Event{Create, filepath.Join(workdir, "logfile")}
		if diff := testutil.Diff(expected, e); diff != "" {
			t.Errorf("want: %q, got %q; diff:\n%s", expected, e, diff)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatal("no event received before timeout")
	}

	n, err := f.WriteString("hi")
	testutil.FatalIfErr(t, err)
	if n != 2 {
		t.Fatalf("wrote %d instead of 2", n)
	}
	testutil.FatalIfErr(t, f.Close())
	select {
	case e := <-s.Events:
		expected := Event{Update, filepath.Join(workdir, "logfile")}
		if diff := testutil.Diff(expected, e); diff != "" {
			t.Errorf("want: %q, got %q; diff:\n%s", expected, e, diff)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatal("no event received before timeout")
	}

	testutil.FatalIfErr(t, os.Rename(filepath.Join(workdir, "logfile"), filepath.Join(workdir, "logfile2")))
	results := make([]Event, 0)
	for i := 0; i < 2; i++ {
		select {
		case e := <-s.Events:
			results = append(results, e)
		case <-time.After(100 * time.Millisecond):
			t.Fatal("no event received before timeout")
		}
	}
	expected := []Event{
		{Create, filepath.Join(workdir, "logfile2")},
		{Delete, filepath.Join(workdir, "logfile")},
	}
	sorter := func(a, b Event) bool {
		if a.Op < b.Op {
			return true
		}
		if a.Op > b.Op {
			return false
		}
		if a.Pathname < b.Pathname {
			return true
		}
		if a.Pathname > b.Pathname {
			return false
		}
		return true
	}
	if diff := testutil.Diff(expected, results, testutil.SortSlices(sorter)); diff != "" {
		t.Errorf("diff:\n%s", diff)
	}

	testutil.FatalIfErr(t, os.Chmod(filepath.Join(workdir, "logfile2"), os.ModePerm))
	select {
	case e := <-s.Events:
		expected := Event{Update, filepath.Join(workdir, "logfile2")}
		if diff := testutil.Diff(expected, e); diff != "" {
			t.Errorf("want %q got %q; diff:\n%s", expected, e, diff)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatal("no event received before timeout")
	}

	testutil.FatalIfErr(t, os.Remove(filepath.Join(workdir, "logfile2")))
	select {
	case e := <-s.Events:
		expected := Event{Delete, filepath.Join(workdir, "logfile2")}
		if diff := testutil.Diff(expected, e); diff != "" {
			t.Errorf("want %q got %q; diff:\n%s", expected, e, diff)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatal("no event received before timeout")
	}
}

// This test may be OS specific; possibly break it out to a file with build tags.
func TestFsnotifyErrorFallbackToPoll(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}
	// The Warning log isn't created until the first write.  Create it before
	// setting the rlimit on open files or the test will fail trying to open
	// the log file instead of where it should.
	glog.Warning("pre-creating log to avoid too many open file")

	var rLimit syscall.Rlimit
	err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
	testutil.FatalIfErr(t, err)
	var zero = rLimit
	zero.Cur = 0
	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &zero)
	testutil.FatalIfErr(t, err)
	_, err = NewLogWatcher(0, true)
	if err != nil {
		t.Error(err)
	}
	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
	testutil.FatalIfErr(t, err)
}

func TestLogWatcherAddError(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}

	workdir, rmWorkdir := testutil.TestTempDir(t)
	defer rmWorkdir()

	w, err := NewLogWatcher(0, true)
	testutil.FatalIfErr(t, err)
	defer func() {
		if err = w.Close(); err != nil {
			t.Fatal(err)
		}
	}()
	s := &stubProcessor{}
	filename := filepath.Join(workdir, "test")
	err = w.Observe(filename, s)
	if err == nil {
		t.Errorf("did not receive an error for nonexistent file")
	}
}

func TestLogWatcherAddWhilePermissionDenied(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}

	// Can't force a permission denied error if run as root.
	testutil.SkipIfRoot(t)

	workdir, rmWorkdir := testutil.TestTempDir(t)
	defer rmWorkdir()

	w, err := NewLogWatcher(0, true)
	testutil.FatalIfErr(t, err)
	defer func() {
		if err = w.Close(); err != nil {
			t.Fatal(err)
		}
	}()

	filename := filepath.Join(workdir, "test")
	_, err = os.Create(filename)
	testutil.FatalIfErr(t, err)
	err = os.Chmod(filename, 0)
	testutil.FatalIfErr(t, err)
	s := &stubProcessor{}
	err = w.Observe(filename, s)
	if err != nil {
		t.Errorf("failed to add watch on permission denied")
	}
	err = os.Chmod(filename, 0777)
	testutil.FatalIfErr(t, err)
}

func TestWatcherErrors(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}
	orig, err := strconv.ParseInt(expvar.Get("log_watcher_errors_total").String(), 10, 64)
	if err != nil {
		t.Fatalf("couldn't convert expvar %q", expvar.Get("log_watcher_errors_total").String())
	}
	w, err := NewLogWatcher(0, true)
	testutil.FatalIfErr(t, err)
	w.watcher.Errors <- errors.New("Injected error for test")
	err = w.Close()
	testutil.FatalIfErr(t, err)
	expected := strconv.FormatInt(orig+1, 10)
	if diff := testutil.Diff(expected, expvar.Get("log_watcher_errors_total").String()); diff != "" {
		t.Errorf("log watcher error count not increased:\n%s", diff)
	}
}

func TestWatcherNewFile(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}
	tests := []struct {
		period   time.Duration
		fsnotify bool
	}{
		{0, true},
		{10 * time.Millisecond, false},
	}
	for _, test := range tests {
		t.Run(fmt.Sprintf("%s %v", test.period, test.fsnotify), func(t *testing.T) {
			w, err := NewLogWatcher(test.period, test.fsnotify)
			testutil.FatalIfErr(t, err)
			tmpDir, rmTmpDir := testutil.TestTempDir(t)
			defer rmTmpDir()
			s := &stubProcessor{}
			testutil.FatalIfErr(t, w.Observe(tmpDir, s))
			testutil.TestOpenFile(t, path.Join(tmpDir, "log"))
			if !test.fsnotify {
				w.Poll()
			} else {
				// wait a bit for kernel to notice
				time.Sleep(250 * time.Millisecond)
			}
			w.Close()
			expected := []Event{{Op: Create, Pathname: path.Join(tmpDir, "log")}}
			if diff := testutil.Diff(expected, s.Events); diff != "" {
				t.Errorf("event unexpected: diff:\n%s", diff)
				t.Logf("received:\n%v", s.Events)
			}
		})
	}
}
<commit_msg>Work around a race from duplicate events in test.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package watcher

import (
	"context"
	"expvar"
	"fmt"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"syscall"
	"testing"
	"time"

	"github.com/golang/glog"
	"github.com/google/mtail/internal/testutil"
	"github.com/pkg/errors"
)

type testStubProcessor struct {
	Events chan Event
}

func (t *testStubProcessor) ProcessFileEvent(ctx context.Context, e Event) {
	go func() {
		t.Events <- e
	}()
}

func newStubProcessor() *testStubProcessor {
	return &testStubProcessor{Events: make(chan Event, 1)}
}

func TestLogWatcher(t *testing.T) {
	if testing.Short() {
		// This test is slow due to disk access.
		t.Skip("skipping log watcher test in short mode")
	}

	workdir, rmWorkdir := testutil.TestTempDir(t)
	defer rmWorkdir()

	w, err := NewLogWatcher(0, true)
	testutil.FatalIfErr(t, err)
	defer func() {
		if err = w.Close(); err != nil {
			t.Fatal(err)
		}
	}()

	s := newStubProcessor()

	if err = w.Observe(workdir, s); err != nil {
		t.Fatal(err)
	}
	f, err := os.Create(filepath.Join(workdir, "logfile"))
	testutil.FatalIfErr(t, err)
	select {
	case e := <-s.Events:
		expected := Event{Create, filepath.Join(workdir, "logfile")}
		if diff := testutil.Diff(expected, e); diff != "" {
			t.Errorf("want: %q, got %q; diff:\n%s", expected, e, diff)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatal("no event received before timeout")
	}

	n, err := f.WriteString("hi")
	testutil.FatalIfErr(t, err)
	if n != 2 {
		t.Fatalf("wrote %d instead of 2", n)
	}
	testutil.FatalIfErr(t, f.Close())
	select {
	case e := <-s.Events:
		expected := Event{Update, filepath.Join(workdir, "logfile")}
		if diff := testutil.Diff(expected, e); diff != "" {
			t.Errorf("want: %q, got %q; diff:\n%s", expected, e, diff)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatal("no event received before timeout")
	}

	testutil.FatalIfErr(t, os.Rename(filepath.Join(workdir, "logfile"), filepath.Join(workdir, "logfile2")))
	results := make([]Event, 0)
	for i := 0; i < 2; i++ {
		select {
		case e := <-s.Events:
			results = append(results, e)
		case <-time.After(100 * time.Millisecond):
			t.Fatal("no event received before timeout")
		}
	}
	expected := []Event{
		{Create, filepath.Join(workdir, "logfile2")},
		{Delete, filepath.Join(workdir, "logfile")},
	}
	sorter := func(a, b Event) bool {
		if a.Op < b.Op {
			return true
		}
		if a.Op > b.Op {
			return false
		}
		if a.Pathname < b.Pathname {
			return true
		}
		if a.Pathname > b.Pathname {
			return false
		}
		return true
	}
	if diff := testutil.Diff(expected, results, testutil.SortSlices(sorter)); diff != "" {
		t.Errorf("diff:\n%s", diff)
	}

	testutil.FatalIfErr(t, os.Chmod(filepath.Join(workdir, "logfile2"), os.ModePerm))
	select {
	case e := <-s.Events:
		expected := Event{Update, filepath.Join(workdir, "logfile2")}
		if diff := testutil.Diff(expected, e); diff != "" {
			t.Errorf("want %q got %q; diff:\n%s", expected, e, diff)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatal("no event received before timeout")
	}

	testutil.FatalIfErr(t, os.Remove(filepath.Join(workdir, "logfile2")))
	select {
	case e := <-s.Events:
		expected := Event{Delete, filepath.Join(workdir, "logfile2")}
		if diff := testutil.Diff(expected, e); diff != "" {
			t.Errorf("want %q got %q; diff:\n%s", expected, e, diff)
		}
	case <-time.After(100 * time.Millisecond):
		t.Fatal("no event received before timeout")
	}
}

// This test may be OS specific; possibly break it out to a file with build tags.
func TestFsnotifyErrorFallbackToPoll(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}
	// The Warning log isn't created until the first write.  Create it before
	// setting the rlimit on open files or the test will fail trying to open
	// the log file instead of where it should.
	glog.Warning("pre-creating log to avoid too many open file")

	var rLimit syscall.Rlimit
	err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)
	testutil.FatalIfErr(t, err)
	var zero = rLimit
	zero.Cur = 0
	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &zero)
	testutil.FatalIfErr(t, err)
	_, err = NewLogWatcher(0, true)
	if err != nil {
		t.Error(err)
	}
	err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit)
	testutil.FatalIfErr(t, err)
}

func TestLogWatcherAddError(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}

	workdir, rmWorkdir := testutil.TestTempDir(t)
	defer rmWorkdir()

	w, err := NewLogWatcher(0, true)
	testutil.FatalIfErr(t, err)
	defer func() {
		if err = w.Close(); err != nil {
			t.Fatal(err)
		}
	}()
	s := &stubProcessor{}
	filename := filepath.Join(workdir, "test")
	err = w.Observe(filename, s)
	if err == nil {
		t.Errorf("did not receive an error for nonexistent file")
	}
}

func TestLogWatcherAddWhilePermissionDenied(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}

	// Can't force a permission denied error if run as root.
	testutil.SkipIfRoot(t)

	workdir, rmWorkdir := testutil.TestTempDir(t)
	defer rmWorkdir()

	w, err := NewLogWatcher(0, true)
	testutil.FatalIfErr(t, err)
	defer func() {
		if err = w.Close(); err != nil {
			t.Fatal(err)
		}
	}()

	filename := filepath.Join(workdir, "test")
	_, err = os.Create(filename)
	testutil.FatalIfErr(t, err)
	err = os.Chmod(filename, 0)
	testutil.FatalIfErr(t, err)
	s := &stubProcessor{}
	err = w.Observe(filename, s)
	if err != nil {
		t.Errorf("failed to add watch on permission denied")
	}
	err = os.Chmod(filename, 0777)
	testutil.FatalIfErr(t, err)
}

func TestWatcherErrors(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}
	orig, err := strconv.ParseInt(expvar.Get("log_watcher_errors_total").String(), 10, 64)
	if err != nil {
		t.Fatalf("couldn't convert expvar %q", expvar.Get("log_watcher_errors_total").String())
	}
	w, err := NewLogWatcher(0, true)
	testutil.FatalIfErr(t, err)
	w.watcher.Errors <- errors.New("Injected error for test")
	err = w.Close()
	testutil.FatalIfErr(t, err)
	expected := strconv.FormatInt(orig+1, 10)
	if diff := testutil.Diff(expected, expvar.Get("log_watcher_errors_total").String()); diff != "" {
		t.Errorf("log watcher error count not increased:\n%s", diff)
	}
}

func TestWatcherNewFile(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping log watcher test in short mode")
	}
	tests := []struct {
		period   time.Duration
		fsnotify bool
	}{
		{0, true},
		{10 * time.Millisecond, false},
	}
	for _, test := range tests {
		t.Run(fmt.Sprintf("%s %v", test.period, test.fsnotify), func(t *testing.T) {
			w, err := NewLogWatcher(test.period, test.fsnotify)
			testutil.FatalIfErr(t, err)
			tmpDir, rmTmpDir := testutil.TestTempDir(t)
			defer rmTmpDir()
			s := &stubProcessor{}
			testutil.FatalIfErr(t, w.Observe(tmpDir, s))
			testutil.TestOpenFile(t, path.Join(tmpDir, "log"))
			if !test.fsnotify {
				w.Poll()
			} else {
				// wait a bit for kernel to notice
				time.Sleep(250 * time.Millisecond)
			}
			w.Close()
			expected := []Event{{Op: Create, Pathname: path.Join(tmpDir, "log")}}
			// Fetching the start of the slice is a hack because we get duplicate events.
			if diff := testutil.Diff(expected, s.Events[0:1]); diff != "" {
				t.Errorf("event unexpected: diff:\n%s", diff)
				t.Logf("received:\n%v", s.Events)
			}
		})
	}
}
<|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT

package mock

import (
	"bytes"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"strconv"

	"github.com/issue9/is"

	"github.com/caixw/apidoc/v7/core"
	"github.com/caixw/apidoc/v7/internal/ast"
	"github.com/caixw/apidoc/v7/internal/locale"
)

func validXML(ns []*ast.XMLNamespace, p *ast.Request, content []byte) error {
	if len(content) == 0 {
		if p == nil || p.Type.V() == ast.TypeNone {
			return nil
		}
		return core.NewSyntaxError(core.Location{}, "", locale.ErrInvalidFormat)
	}

	d := xml.NewDecoder(bytes.NewReader(content))
	for {
		token, err := d.Token()
		if errors.Is(err, io.EOF) && token == nil { // 正常结束
			return nil
		}
		if err != nil {
			return err
		}

		switch v := token.(type) {
		case xml.StartElement:
			if err := validXMLNamespaces(ns, v); err != nil {
				return err
			}
			return validElement(ns, v, p.Param(), true, d, v.Name.Local)
		case xml.EndElement:
			return core.NewSyntaxError(core.Location{}, "", locale.ErrInvalidFormat)
		}
	}
}

func validXMLNamespaces(ns []*ast.XMLNamespace, start xml.StartElement) error {
ATTR:
	for _, attr := range start.Attr {
		if attr.Name.Local == "xmlns" && attr.Name.Space == "" { // 默认
			for _, item := range ns {
				if item.Auto.V() && item.URN.V() == attr.Value {
					continue ATTR
				}
			}
			return core.NewSyntaxError(core.Location{}, "xmlns", locale.ErrNotFound)
		} else if attr.Name.Space == "xmlns" && attr.Name.Local != "" {
			for _, item := range ns {
				if item.Prefix.V() == attr.Name.Local && item.URN.V() == attr.Value {
					continue ATTR
				}
			}
			return core.NewSyntaxError(core.Location{}, "xmlns:"+attr.Name.Local, locale.ErrNotFound)
		}
	}
	return nil
}

func validName(name xml.Name, ns []*ast.XMLNamespace, p *ast.Param) bool {
	if name.Local != p.Name.V() {
		return false
	}

	if name.Space == "" {
		return true
	}

	for _, item := range ns {
		if item.URN.V() == name.Space {
			return item.Prefix.V() == p.XMLNSPrefix.V() || item.Auto.V()
		}
	}
	return false
}

func validElement(
	ns []*ast.XMLNamespace,
	start xml.StartElement,
	p *ast.Param,
	allowArray bool,
	d *xml.Decoder,
	field string,
) error {
	if err := validStartElement(ns, start, p, allowArray, d, field); err != nil {
		return err
	}

	for {
		token, err := d.Token()
		if errors.Is(err, io.EOF) && token == nil { // 正常结束
			return nil
		}
		if err != nil {
			return err
		}

		var chardata []byte
		switch v := token.(type) {
		case xml.StartElement:
			if allowArray && p.Array.V() && validName(v.Name, ns, p) {
				return validElement(ns, v, p, false, d, buildXMLField(field, p))
			}

			for _, pp := range p.Items {
				if pp.Name.V() == v.Name.Local {
					if pp.XMLExtract.V() {
						return validElement(ns, v, p, true, d, buildXMLField(field, pp))
					}
					return validElement(ns, v, pp, true, d, buildXMLField(field, p))
				}
			}
		case xml.EndElement:
			if allowArray && p.Array.V() {
				if p.XMLWrapped.V() != "" && p.XMLWrapped.V() != v.Name.Local {
					return core.NewSyntaxError(core.Location{}, v.Name.Local, locale.ErrNotFound)
				}
				return nil
			}
			if !validName(v.Name, ns, p) {
				return core.NewSyntaxError(core.Location{}, v.Name.Local, locale.ErrNotFound)
			}

			if chardata != nil {
				return validXMLParamValue(p, p.Name.V(), string(chardata))
			}
			return nil
		case xml.CharData:
			chardata = v
		}
	}
}

func validStartElement(
	ns []*ast.XMLNamespace,
	start xml.StartElement,
	p *ast.Param,
	allowArray bool,
	d *xml.Decoder,
	field string,
) error {
	for _, attr := range start.Attr {
		for _, pp := range p.Items {
			if !validName(attr.Name, ns, pp) {
				continue
			}
			if err := validXMLParamValue(pp, buildXMLField(field, pp), attr.Value); err != nil {
				return err
			}
			break
		}
	}

	if allowArray && p.Array.V() {
		if p.XMLWrapped.V() != "" && p.XMLWrapped.V() != start.Name.Local {
			return core.NewSyntaxError(core.Location{}, start.Name.Local, locale.ErrNotFound)
		}

		start.Attr = start.Attr[:0] // 在 allowArray == true 已经处理过 start.Attr
		return validElement(ns, start, p, false, d, buildXMLField(field, p))
	}

	if (!validName(start.Name, ns, p)) &&
		(!allowArray && p.XMLWrapped.V() != start.Name.Local) {
		return core.NewSyntaxError(core.Location{}, start.Name.Local, locale.ErrNotFound)
	}
	return nil
}

func buildXMLField(field string, p *ast.Param) string {
	if p.XMLAttr.V() {
		return field + "@" + p.Name.V()
	}
	return field + p.Name.V()
}

// 验证 p 描述的类型与 v 是否匹配，如果不匹配返回错误信息。
// field 表示 p 在整个对象中的位置信息。
func validXMLParamValue(p *ast.Param, field, v string) error {
	switch p.Type.V() {
	case ast.TypeNone:
		if v != "" {
			return core.NewSyntaxError(core.Location{}, field, locale.ErrInvalidValue)
		}
	case ast.TypeNumber:
		if !is.Number(v) {
			return core.NewSyntaxError(core.Location{}, field, locale.ErrInvalidFormat)
		}
	case ast.TypeBool:
		if _, err := strconv.ParseBool(v); err != nil {
			return core.NewSyntaxError(core.Location{}, field, locale.ErrInvalidFormat)
		}
	case ast.TypeString, ast.TypeObject:
		return nil
	default:
		panic(fmt.Sprintf("文档中类型定义错误 %s", p.Type.V()))
	}

	if isEnum(p) {
		for _, enum := range p.Enums {
			if enum.Value.V() == v {
				return nil
			}
		}
		return core.NewSyntaxError(core.Location{}, field, locale.ErrInvalidValue)
	}

	return nil
}

type xmlBuilder struct {
	start xml.StartElement
	items []*xmlBuilder

	chardata interface{}
	cdata    bool // 表示 chardata 是一个 cdata 数据
}

func buildXML(ns []*ast.XMLNamespace, p *ast.Request, indent string, g *GenOptions) ([]byte, error) {
	if p == nil || p.Type.V() == ast.TypeNone {
		return nil, nil
	}

	builder, err := parseXML(ns, p.Param(), true, true, g)
	if err != nil {
		return nil, err
	}

	buf := new(bytes.Buffer)
	e := xml.NewEncoder(buf)
	e.Indent("", indent)

	if err = builder.encode(e); err != nil {
		return nil, err
	}

	if err := e.Flush(); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func parseXML(ns []*ast.XMLNamespace, p *ast.Param, chkArray, root bool, g *GenOptions) (*xmlBuilder, error) {
	builder := &xmlBuilder{
		start: xml.StartElement{
			Name: buildXMLName(ns, p),
			Attr: make([]xml.Attr, 0, len(p.Items)+len(ns)),
		},
		items: []*xmlBuilder{},
		cdata: p.XMLCData.V(),
	}

	if p.Array.V() && chkArray {
		if err := parseArray(ns, p, builder, g); err != nil {
			return nil, err
		}
		if root {
			return builder.items[0], nil
		}
		return builder, nil
	}

	if p.Type.V() != ast.TypeObject {
		builder.chardata = genXMLValue(g, p)
		goto RET
	}

	for _, item := range p.Items {
		switch {
		case item.XMLAttr.V():
			attr := xml.Attr{
				Name:  buildXMLName(ns, item),
				Value: fmt.Sprint(genXMLValue(g, item)),
			}
			builder.start.Attr = append(builder.start.Attr, attr)
		case item.XMLExtract.V():
			builder.chardata = genXMLValue(g, item)
			builder.cdata = item.XMLCData.V()
		case item.Array.V():
			if err := parseArray(ns, item, builder, g); err != nil {
				return nil, err
			}
		default:
			b, err := parseXML(ns, item, true, false, g)
			if err != nil {
				return nil, err
			}
			builder.items = append(builder.items, b)
		}
	} // end for

RET:
	if root { // namespace
		for _, item := range ns {
			name := "xmlns"
			if !item.Auto.V() && item.Prefix.V() != "" {
				name += ":" + item.Prefix.V()
			}

			builder.start.Attr = append(builder.start.Attr, xml.Attr{
				Name:  xml.Name{Local: name},
				Value: item.URN.V(),
			})
		}
	}

	return builder, nil
}

func buildXMLName(ns []*ast.XMLNamespace, p *ast.Param) xml.Name {
	if p.XMLNSPrefix.V() != "" {
		return xml.Name{Local: p.XMLNSPrefix.V() + ":" + p.Name.V()}
	}

	for _, item := range ns {
		if item.Auto.V() {
			return xml.Name{Local: p.Name.V()}
		}

	}
	return xml.Name{Local: p.Name.V()}
}

func parseArray(ns []*ast.XMLNamespace, p *ast.Param, parent *xmlBuilder, g *GenOptions) error {
	b := parent
	if p.XMLWrapped.V() != "" {
		b = &xmlBuilder{items: []*xmlBuilder{}}
		var prefix string
		if p.XMLNSPrefix != nil {
			prefix = p.XMLNSPrefix.V() + ":"
		}
		if p.XMLWrapped != nil {
			b.start.Name.Local = prefix + p.XMLWrapped.V()
		}
		parent.items = append(parent.items, b)
	}

	for i := 0; i < g.generateSliceSize(); i++ {
		bb, err := parseXML(ns, p, false, false, g)
		if err != nil {
			return err
		}
		b.items = append(b.items, bb)
	}

	return nil
}

func (builder *xmlBuilder) encode(e *xml.Encoder) error {
	if builder == nil {
		return nil
	}

	if builder.cdata && builder.chardata != nil {
		return e.EncodeElement(struct {
			string `xml:",cdata"`
		}{fmt.Sprint(builder.chardata)}, builder.start)
	} else if builder.chardata != nil {
		return e.EncodeElement(builder.chardata, builder.start)
	}

	if err := e.EncodeToken(builder.start); err != nil {
		return err
	}
	for _, item := range builder.items {
		if err := item.encode(e); err != nil {
			return err
		}
	}
	return e.EncodeToken(builder.start.End())
}

func genXMLValue(g *GenOptions, p *ast.Param) interface{} {
	switch p.Type.V() {
	case ast.TypeNone:
		return ""
	case ast.TypeBool:
		return g.generateBool()
	case ast.TypeNumber:
		return g.generateNumber(p)
	case ast.TypeString:
		return g.generateString(p)
	default: // ast.TypeObject:
		panic(fmt.Sprintf("无效的类型 %s", p.Type.V())) // 加载的时候已经作语法验证，此处还出错则直接 panic
	}
}
<commit_msg>style(internal/mock): 调整函数名称<commit_after>// SPDX-License-Identifier: MIT

package mock

import (
	"bytes"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"strconv"

	"github.com/issue9/is"

	"github.com/caixw/apidoc/v7/core"
	"github.com/caixw/apidoc/v7/internal/ast"
	"github.com/caixw/apidoc/v7/internal/locale"
)

func validXML(ns []*ast.XMLNamespace, p *ast.Request, content []byte) error {
	if len(content) == 0 {
		if p == nil || p.Type.V() == ast.TypeNone {
			return nil
		}
		return core.NewSyntaxError(core.Location{}, "", locale.ErrInvalidFormat)
	}

	d := xml.NewDecoder(bytes.NewReader(content))
	for {
		token, err := d.Token()
		if errors.Is(err, io.EOF) && token == nil { // 正常结束
			return nil
		}
		if err != nil {
			return err
		}

		switch v := token.(type) {
		case xml.StartElement:
			if err := validXMLNamespaces(ns, v); err != nil {
				return err
			}
			return validXMLElement(ns, v, p.Param(), true, d, v.Name.Local)
		case xml.EndElement:
			return core.NewSyntaxError(core.Location{}, "", locale.ErrInvalidFormat)
		}
	}
}

func validXMLNamespaces(ns []*ast.XMLNamespace, start xml.StartElement) error {
ATTR:
	for _, attr := range start.Attr {
		if attr.Name.Local == "xmlns" && attr.Name.Space == "" { // 默认
			for _, item := range ns {
				if item.Auto.V() && item.URN.V() == attr.Value {
					continue ATTR
				}
			}
			return core.NewSyntaxError(core.Location{}, "xmlns", locale.ErrNotFound)
		} else if attr.Name.Space == "xmlns" && attr.Name.Local != "" {
			for _, item := range ns {
				if item.Prefix.V() == attr.Name.Local && item.URN.V() == attr.Value {
					continue ATTR
				}
			}
			return core.NewSyntaxError(core.Location{}, "xmlns:"+attr.Name.Local, locale.ErrNotFound)
		}
	}
	return nil
}

func validXMLName(name xml.Name, ns []*ast.XMLNamespace, p *ast.Param) bool {
	if name.Local != p.Name.V() {
		return false
	}

	if name.Space == "" {
		return true
	}

	for _, item := range ns {
		if item.URN.V() == name.Space {
			return item.Prefix.V() == p.XMLNSPrefix.V() || item.Auto.V()
		}
	}
	return false
}

func validXMLElement(
	ns []*ast.XMLNamespace,
	start xml.StartElement,
	p *ast.Param,
	allowArray bool,
	d *xml.Decoder,
	field string,
) error {
	if err := validStartElement(ns, start, p, allowArray, d, field); err != nil {
		return err
	}

	for {
		token, err := d.Token()
		if errors.Is(err, io.EOF) && token == nil { // 正常结束
			return nil
		}
		if err != nil {
			return err
		}

		var chardata []byte
		switch v := token.(type) {
		case xml.StartElement:
			if allowArray && p.Array.V() && validXMLName(v.Name, ns, p) {
				return validXMLElement(ns, v, p, false, d, buildXMLField(field, p))
			}

			for _, pp := range p.Items {
				if pp.Name.V() == v.Name.Local {
					if pp.XMLExtract.V() {
						return validXMLElement(ns, v, p, true, d, buildXMLField(field, pp))
					}
					return validXMLElement(ns, v, pp, true, d, buildXMLField(field, p))
				}
			}
		case xml.EndElement:
			if allowArray && p.Array.V() {
				if p.XMLWrapped.V() != "" && p.XMLWrapped.V() != v.Name.Local {
					return core.NewSyntaxError(core.Location{}, v.Name.Local, locale.ErrNotFound)
				}
				return nil
			}
			if !validXMLName(v.Name, ns, p) {
				return core.NewSyntaxError(core.Location{}, v.Name.Local, locale.ErrNotFound)
			}

			if chardata != nil {
				return validXMLParamValue(p, p.Name.V(), string(chardata))
			}
			return nil
		case xml.CharData:
			chardata = v
		}
	}
}

func validStartElement(
	ns []*ast.XMLNamespace,
	start xml.StartElement,
	p *ast.Param,
	allowArray bool,
	d *xml.Decoder,
	field string,
) error {
	for _, attr := range start.Attr {
		for _, pp := range p.Items {
			if !validXMLName(attr.Name, ns, pp) {
				continue
			}
			if err := validXMLParamValue(pp, buildXMLField(field, pp), attr.Value); err != nil {
				return err
			}
			break
		}
	}

	if allowArray && p.Array.V() {
		if p.XMLWrapped.V() != "" && p.XMLWrapped.V() != start.Name.Local {
			return core.NewSyntaxError(core.Location{}, start.Name.Local, locale.ErrNotFound)
		}

		start.Attr = start.Attr[:0] // 在 allowArray == true 已经处理过 start.Attr
		return validXMLElement(ns, start, p, false, d, buildXMLField(field, p))
	}

	if (!validXMLName(start.Name, ns, p)) &&
		(!allowArray && p.XMLWrapped.V() != start.Name.Local) {
		return core.NewSyntaxError(core.Location{}, start.Name.Local, locale.ErrNotFound)
	}
	return nil
}

func buildXMLField(field string, p *ast.Param) string {
	if p.XMLAttr.V() {
		return field + "@" + p.Name.V()
	}
	return field + p.Name.V()
}

// 验证 p 描述的类型与 v 是否匹配，如果不匹配返回错误信息。
// field 表示 p 在整个对象中的位置信息。
func validXMLParamValue(p *ast.Param, field, v string) error {
	switch p.Type.V() {
	case ast.TypeNone:
		if v != "" {
			return core.NewSyntaxError(core.Location{}, field, locale.ErrInvalidValue)
		}
	case ast.TypeNumber:
		if !is.Number(v) {
			return core.NewSyntaxError(core.Location{}, field, locale.ErrInvalidFormat)
		}
	case ast.TypeBool:
		if _, err := strconv.ParseBool(v); err != nil {
			return core.NewSyntaxError(core.Location{}, field, locale.ErrInvalidFormat)
		}
	case ast.TypeString, ast.TypeObject:
		return nil
	default:
		panic(fmt.Sprintf("文档中类型定义错误 %s", p.Type.V()))
	}

	if isEnum(p) {
		for _, enum := range p.Enums {
			if enum.Value.V() == v {
				return nil
			}
		}
		return core.NewSyntaxError(core.Location{}, field, locale.ErrInvalidValue)
	}

	return nil
}

type xmlBuilder struct {
	start xml.StartElement
	items []*xmlBuilder

	chardata interface{}
	cdata    bool // 表示 chardata 是一个 cdata 数据
}

func buildXML(ns []*ast.XMLNamespace, p *ast.Request, indent string, g *GenOptions) ([]byte, error) {
	if p == nil || p.Type.V() == ast.TypeNone {
		return nil, nil
	}

	builder, err := parseXML(ns, p.Param(), true, true, g)
	if err != nil {
		return nil, err
	}

	buf := new(bytes.Buffer)
	e := xml.NewEncoder(buf)
	e.Indent("", indent)

	if err = builder.encode(e); err != nil {
		return nil, err
	}

	if err := e.Flush(); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

func parseXML(ns []*ast.XMLNamespace, p *ast.Param, chkArray, root bool, g *GenOptions) (*xmlBuilder, error) {
	builder := &xmlBuilder{
		start: xml.StartElement{
			Name: buildXMLName(ns, p),
			Attr: make([]xml.Attr, 0, len(p.Items)+len(ns)),
		},
		items: []*xmlBuilder{},
		cdata: p.XMLCData.V(),
	}

	if p.Array.V() && chkArray {
		if err := parseArray(ns, p, builder, g); err != nil {
			return nil, err
		}
		if root {
			return builder.items[0], nil
		}
		return builder, nil
	}

	if p.Type.V() != ast.TypeObject {
		builder.chardata = genXMLValue(g, p)
		goto RET
	}

	for _, item := range p.Items {
		switch {
		case item.XMLAttr.V():
			attr := xml.Attr{
				Name:  buildXMLName(ns, item),
				Value: fmt.Sprint(genXMLValue(g, item)),
			}
			builder.start.Attr = append(builder.start.Attr, attr)
		case item.XMLExtract.V():
			builder.chardata = genXMLValue(g, item)
			builder.cdata = item.XMLCData.V()
		case item.Array.V():
			if err := parseArray(ns, item, builder, g); err != nil {
				return nil, err
			}
		default:
			b, err := parseXML(ns, item, true, false, g)
			if err != nil {
				return nil, err
			}
			builder.items = append(builder.items, b)
		}
	} // end for

RET:
	if root { // namespace
		for _, item := range ns {
			name := "xmlns"
			if !item.Auto.V() && item.Prefix.V() != "" {
				name += ":" + item.Prefix.V()
			}

			builder.start.Attr = append(builder.start.Attr, xml.Attr{
				Name:  xml.Name{Local: name},
				Value: item.URN.V(),
			})
		}
	}

	return builder, nil
}

func buildXMLName(ns []*ast.XMLNamespace, p *ast.Param) xml.Name {
	if p.XMLNSPrefix.V() != "" {
		return xml.Name{Local: p.XMLNSPrefix.V() + ":" + p.Name.V()}
	}

	for _, item := range ns {
		if item.Auto.V() {
			return xml.Name{Local: p.Name.V()}
		}

	}
	return xml.Name{Local: p.Name.V()}
}

func parseArray(ns []*ast.XMLNamespace, p *ast.Param, parent *xmlBuilder, g *GenOptions) error {
	b := parent
	if p.XMLWrapped.V() != "" {
		b = &xmlBuilder{items: []*xmlBuilder{}}
		var prefix string
		if p.XMLNSPrefix != nil {
			prefix = p.XMLNSPrefix.V() + ":"
		}
		if p.XMLWrapped != nil {
			b.start.Name.Local = prefix + p.XMLWrapped.V()
		}
		parent.items = append(parent.items, b)
	}

	for i := 0; i < g.generateSliceSize(); i++ {
		bb, err := parseXML(ns, p, false, false, g)
		if err != nil {
			return err
		}
		b.items = append(b.items, bb)
	}

	return nil
}

func (builder *xmlBuilder) encode(e *xml.Encoder) error {
	if builder == nil {
		return nil
	}

	if builder.cdata && builder.chardata != nil {
		return e.EncodeElement(struct {
			string `xml:",cdata"`
		}{fmt.Sprint(builder.chardata)}, builder.start)
	} else if builder.chardata != nil {
		return e.EncodeElement(builder.chardata, builder.start)
	}

	if err := e.EncodeToken(builder.start); err != nil {
		return err
	}
	for _, item := range builder.items {
		if err := item.encode(e); err != nil {
			return err
		}
	}
	return e.EncodeToken(builder.start.End())
}

func genXMLValue(g *GenOptions, p *ast.Param) interface{} {
	switch p.Type.V() {
	case ast.TypeNone:
		return ""
	case ast.TypeBool:
		return g.generateBool()
	case ast.TypeNumber:
		return g.generateNumber(p)
	case ast.TypeString:
		return g.generateString(p)
	default: // ast.TypeObject:
		panic(fmt.Sprintf("无效的类型 %s", p.Type.V())) // 加载的时候已经作语法验证，此处还出错则直接 panic
	}
}
<|endoftext|>


<commit_before>//Package subcommand is an option parsing utility a la git/mercurial/go loosely
//inspired by Ruby's OptionParser
package subcommand

import (
	"fmt"
	"strings"
)

//FlagType defines the different flag types. Options have values associated to the flag, Switches have no value associated.
type FlagType int

const (
	Option FlagType = iota
	Switch
)

//Command aggregates different flags under a common name. Every time a command is found during the parsing process the associated function is executed.
type Command struct {
	//Name
	Name            string
	Description     string //Command help line
	Params          string //Description of the parameters
	innerFlagsLong  map[string]*Flag
	innerFlagsShort map[string]*Flag
	fn              func(command string, leftOvers ...string) error
	parent          *Command
}

//Access to flags
type Flagged interface {
	Flags() []Flag
}

//getFlags returns a slice containing the c's flags
func (c *Command) Flags() []Flag {
	//return c.Name
	flags := make([]Flag, 0)
	for _, val := range c.innerFlagsLong {
		flags = append(flags, *val)
	}

	return flags
}

//Returns the command parent
func (c Command) Parent() *Command {
	return c.parent
}

//Parser contains other commands. It's the data structure and its name should be the program's name.
type Parser struct {
	Command
	Commands map[string]*Command
	help     Command
}

func newCommand(parent *Command, name string, description string, fn func(string, ...string) error) *Command {
	return &Command{
		Name:            name,
		innerFlagsShort: make(map[string]*Flag),
		innerFlagsLong:  make(map[string]*Flag),
		fn:              fn,
		Description:     description,
		parent:          parent,
	}
}

//Sets the help command. There is one default implementation automatically added when the parser is created.
func (p *Parser) SetHelp(name string, description string, fn func(string, ...string) error) *Command {
	command := newCommand(&p.Command, name, description, fn)
	p.help = *command
	return command

}
func (p *Parser) OnCommand(fn func() error) {
	p.Command.fn = func(string, ...string) error {
		return fn()
	}
}

//Returns the help command
func (p Parser) Help() Command {
	return p.help
}

type CommandFunction func(string, ...string) error

//NewParser constructs a parser for program name given
func NewParser(program string) *Parser {
	parser := &Parser{
		Command:  *newCommand(nil, program, "", func(string, ...string) error { return nil }),
		Commands: make(map[string]*Command),
	}
	parser.SetHelp("help", fmt.Sprintf("Type %v help [command] for detailed information about a command", program), defaultHelp(parser))
	return parser
}

func defaultHelp(p *Parser) func(string, ...string) error {
	return func(help string, args ...string) error {
		if len(args) > 0 {
			if cmd, ok := p.Commands[args[0]]; ok {
				visitCommand(*cmd)
				return nil
			} else {
				fmt.Printf("help: command not found %v\n", args[0])
			}
		}
		visitParser(*p)
		return nil
	}
}

//AddCommand inserts a new subcommand to the parser. The callback fn receives as first argument
//the command name followed by the left overs of the parsing process
//Example:
// command "hello" prints the non flags (options and switches) arguments.
// The associated callback should be something like
// func processCommand(commandName string,args ...string){
//      fmt.Printf("The command %v says:\n",commandName)
//      for _,arg:= rage args{
//              fmt.Printf("%v \n",arg)
//      }
//}
func (p *Parser) AddCommand(name string, description string, fn func(string, ...string) error) *Command {
	if _, exists := p.Commands[name]; exists {
		panic(fmt.Sprintf("Command '%s' already exists ", name))
	}
	//create the command
	command := newCommand(&p.Command, name, description, fn)
	//add it to the parser
	p.Commands[name] = command
	return command
}

//Adds a new option to the command to be used as "--option OPTION" (expects a value after the flag) in the command line
//The short definition has no length restriction but it should be significantly shorter that its long counterpart
//The function fn receives the name of the option and its value
//Example:
//command.AddOption("path","p",setPath)//option
//[...]
// func setPath(option,value string){
//      printf("According the option %v the path is set to %v",option,value);
//}
func (c *Command) AddOption(long string, short string, description string, fn func(string, string) error) *Flag {
	flag := buildFlag(long, short, description, fn, Option)
	c.addFlag(flag)
	return flag
}

//Adds a new switch to the command to be used as "--switch" (expects no value after the flag) in the command line
//The short definition has no length restriction but it should be significantly shorter that its long counterpart
//The function fn receives two string, the first is the switch name and the second is just an empty string
//Example:
//command.AddSwitch("verbose","v",setVerbose)//option
//[...]
// func setVerbose(switch string){
//      printf("I'm get to get quite talkative! I'm set to be %v ",switch);
//}
func (c *Command) AddSwitch(long string, short string, description string, fn func(string, string) error) *Flag {
	flag := buildFlag(long, short, description, fn, Switch)
	c.addFlag(flag)
	return flag
}

//Adds a flag to the command
func (c *Command) addFlag(flag *Flag) {

	if _, exists := c.innerFlagsLong[flag.Long]; exists {
		panic(fmt.Errorf("Flag '%s' already exists ", flag.Long))
	}
	if _, exists := c.innerFlagsShort[flag.Short]; exists {
		panic(fmt.Errorf("Flag '%s' already exists ", flag.Short))
	}
	c.innerFlagsLong[flag.Long] = flag
	if flag.Short != "" {
		c.innerFlagsShort[flag.Short] = flag
	}

}

func (c *Command) String() string {
	return fmt.Sprintf("%v\t %v", c.Name, c.Description)
}

//Parse parses the arguments executing the associated functions for each command and flag.
//It returns the left overs if some non-option strings or commands  were not processed.
//Errors are returned in case an unknown flag is found or a mandatory flag was not supplied.
// The set of function calls to be performed are carried in order and once the parsing process is done
func (p *Parser) Parse(args []string) (leftOvers []string, err error) {
	//get the delayed functions to call
	//for every flag//command
	fns, leftOvers, err := p.parse(args)
	if err != nil {
		return
	}
	for _, fn := range fns {
		if err := fn(); err != nil {
			return leftOvers, err
		}

	}
	return
}

//The actual parsing process
func (p *Parser) parse(args []string) (functions []func() error, leftOvers []string, err error) {
	//TODO : rewrite the parsing algorithm to make it a bit more clean and clever...
	//visited flags
	var visited []Flag
	//functions to call once the parsing process is over
	var currentCommand Command = p.Command
	var currentFunc func() error = nil
	var onCommand func() error = commandCaller(p.Name, &leftOvers, p.Command.fn)
	//go comsuming options commands and sub-options
	for i := 0; i < len(args); i++ {
		arg := args[i]
		if strings.HasPrefix(arg, "-") { //flag
			var opt *Flag
			var ok bool
			if strings.HasPrefix(arg, "--") {
				opt, ok = currentCommand.innerFlagsLong[arg[2:]]
			} else {
				opt, ok = currentCommand.innerFlagsShort[arg[1:]]
			}
			//not present
			if !ok {
				err = fmt.Errorf("%v is not a valid flag for %v", arg, currentCommand.Name)
				return
			}

			if opt.Type == Option { //option
				if i+1 >= len(args) {
					err = fmt.Errorf("No value for option %v", arg)
					return
				}
				functions = append(functions, flagCaller(opt.Long, args[i+1], opt.fn))
				i++
			} else { //switch
				functions = append(functions, flagCaller(opt.Long, "", opt.fn))
			}
			//add to visited options
			visited = append(visited, *opt)
		} else {
			//_,isParser:=currentCommand.(Parser)
			if err = checkVisited(visited, currentCommand); err != nil {
				return
			}
			for _, fn := range functions {
				if err = fn(); err != nil {
					return
				}

			}
			functions = make([]func() error, 0)
			if onCommand != nil {

				if err = onCommand(); err != nil {
					return
				}
				onCommand = nil
				//functions = append(functions, currentFunc)
			}
			cmd, ok := p.Commands[arg]
			//if its a command
			if isHelp := (arg == p.help.Name); (ok || isHelp) && currentCommand.Name != p.help.Name {
				visited = []Flag{}
				if !isHelp {
					currentCommand = *cmd
					currentFunc = commandCaller(arg, &leftOvers, cmd.fn)
				} else {
					currentCommand = p.help
					currentFunc = commandCaller(arg, &leftOvers, p.help.fn)
				}
			} else {
				leftOvers = append(leftOvers, arg)
			}

		}

	}
	if currentFunc != nil {
		functions = append(functions, currentFunc)
	}
	//last check for visited
	err = checkVisited(visited, currentCommand)

	return
}

func flagCaller(name, value string, fn func(string, string) error) func() error {
	return func() error { return fn(name, value) }
}
func commandCaller(command string, leftOvers *[]string, fn func(string, ...string) error) func() error {
	return func() error { return fn(command, *leftOvers...) }
}

//checks if the mandatory flags were visited
func checkVisited(visited []Flag, command Command) error {
	for _, flag := range command.Flags() {
		if flag.Mandatory {
			ok := false
			for _, vFlag := range visited {
				if vFlag.Long == flag.Long {
					ok = true
					break
				}
			}
			if !ok {
				return fmt.Errorf("%v was not found and is mandatory for %v", flag, command)
			}
		}
	}
	return nil
}

//Flag structure
type Flag struct {
	//long definition (--option OPTION)
	Long string
	//Short definition (-o )
	Short string
	//Description
	Description string
	//FlagType, option or switch
	Type FlagType
	//Function to call when the flag is found during the parsing process
	fn func(string, string) error
	//Says if the flag is optional or mandatory
	Mandatory bool
}

//Must sets the flag as mandatory. The parser will raise an error in case it isn't present in the arguments
//TODO make sure that switches are not allowed to get mandatory
func (f *Flag) Must(isIt bool) {
	f.Mandatory = isIt
}

//Gets a help friendly flag representation:
//-o,--option  OPTION           This option does this and that
//-s,--switch                   This is a switch
//-i,--ignoreme [IGNOREME]      Optional option
func (f Flag) String() string {
	var format string
	var help string
	shortFormat := "%v"
	if f.Short != "" {
		shortFormat = "-%v,"
	}
	if f.Type == Option {
		if f.Mandatory {
			format = "--%v %v\t%v"
		} else {
			format = "--%v [%v]\t%v"
		}
		help = fmt.Sprintf(shortFormat+format, f.Short, f.Long, strings.ToUpper(f.Long), f.Description)
	} else {
		format = "--%v \t%v"
		help = fmt.Sprintf(shortFormat+format, f.Short, f.Long, f.Description)
	}
	return help
}

//Checks that the definition is just one word
func checkDefinition(flag string) bool {
	parts := strings.Split(flag, " ")
	return len(parts) == 1
}

//builds the flag struct panicking if errors are encountered
func buildFlag(long string, short string, desc string, fn func(string, string) error, kind FlagType) *Flag {
	long = strings.Trim(long, " ")
	short = strings.Trim(short, " ")
	if len(long) == 0 {
		panic("Long definition is empty")
	}

	if !checkDefinition(long) {
		panic(fmt.Sprintf("Long definition %v has two words. Only one is accepted", long))
	}

	if !checkDefinition(short) {
		panic(fmt.Sprintf("Short definition %v has two words. Only one is accepted", long))
	}
	return &Flag{
		Type:        kind,
		Long:        long,
		Short:       short,
		fn:          fn,
		Description: desc,
		Mandatory:   false,
	}
}

//Help printing functions
func visitParser(p Parser) {
	fmt.Printf("Usage: %v [global_options] command [arguments]\n", p.Name)
	fmt.Printf("\n")
	fmt.Printf("Global Options\n")
	fmt.Printf("--------------\n")
	fmt.Printf("\n")
	for _, flag := range p.Flags() {
		fmt.Printf("\t%v\n", flag)
	}
	fmt.Printf("Commands\n")
	fmt.Printf("--------\n")
	fmt.Printf("\n")
	for _, cmd := range p.Commands {
		fmt.Printf("\t%v\t\t%v\n", cmd.Name, cmd.Description)
	}

	fmt.Printf("\n")
	fmt.Printf("\t%v\t\t%v\n", p.help.Name, p.help.Description)
}

func visitCommand(c Command) {
	fmt.Printf("%v\t\t%v\n", c.Name, c.Description)
	fmt.Printf("\n")
	for _, flag := range c.Flags() {
		fmt.Printf("\t%v\n", flag)
	}
}
<commit_msg>Add function to get the prefix for flag help<commit_after>//Package subcommand is an option parsing utility a la git/mercurial/go loosely
//inspired by Ruby's OptionParser
package subcommand

import (
	"fmt"
	"strings"
)

//FlagType defines the different flag types. Options have values associated to the flag, Switches have no value associated.
type FlagType int

const (
	Option FlagType = iota
	Switch
)

//Command aggregates different flags under a common name. Every time a command is found during the parsing process the associated function is executed.
type Command struct {
	//Name
	Name            string
	Description     string //Command help line
	Params          string //Description of the parameters
	innerFlagsLong  map[string]*Flag
	innerFlagsShort map[string]*Flag
	fn              func(command string, leftOvers ...string) error
	parent          *Command
}

//Access to flags
type Flagged interface {
	Flags() []Flag
}

//getFlags returns a slice containing the c's flags
func (c *Command) Flags() []Flag {
	//return c.Name
	flags := make([]Flag, 0)
	for _, val := range c.innerFlagsLong {
		flags = append(flags, *val)
	}

	return flags
}

//Returns the command parent
func (c Command) Parent() *Command {
	return c.parent
}

//Parser contains other commands. It's the data structure and its name should be the program's name.
type Parser struct {
	Command
	Commands map[string]*Command
	help     Command
}

func newCommand(parent *Command, name string, description string, fn func(string, ...string) error) *Command {
	return &Command{
		Name:            name,
		innerFlagsShort: make(map[string]*Flag),
		innerFlagsLong:  make(map[string]*Flag),
		fn:              fn,
		Description:     description,
		parent:          parent,
	}
}

//Sets the help command. There is one default implementation automatically added when the parser is created.
func (p *Parser) SetHelp(name string, description string, fn func(string, ...string) error) *Command {
	command := newCommand(&p.Command, name, description, fn)
	p.help = *command
	return command

}
func (p *Parser) OnCommand(fn func() error) {
	p.Command.fn = func(string, ...string) error {
		return fn()
	}
}

//Returns the help command
func (p Parser) Help() Command {
	return p.help
}

type CommandFunction func(string, ...string) error

//NewParser constructs a parser for program name given
func NewParser(program string) *Parser {
	parser := &Parser{
		Command:  *newCommand(nil, program, "", func(string, ...string) error { return nil }),
		Commands: make(map[string]*Command),
	}
	parser.SetHelp("help", fmt.Sprintf("Type %v help [command] for detailed information about a command", program), defaultHelp(parser))
	return parser
}

func defaultHelp(p *Parser) func(string, ...string) error {
	return func(help string, args ...string) error {
		if len(args) > 0 {
			if cmd, ok := p.Commands[args[0]]; ok {
				visitCommand(*cmd)
				return nil
			} else {
				fmt.Printf("help: command not found %v\n", args[0])
			}
		}
		visitParser(*p)
		return nil
	}
}

//AddCommand inserts a new subcommand to the parser. The callback fn receives as first argument
//the command name followed by the left overs of the parsing process
//Example:
// command "hello" prints the non flags (options and switches) arguments.
// The associated callback should be something like
// func processCommand(commandName string,args ...string){
//      fmt.Printf("The command %v says:\n",commandName)
//      for _,arg:= rage args{
//              fmt.Printf("%v \n",arg)
//      }
//}
func (p *Parser) AddCommand(name string, description string, fn func(string, ...string) error) *Command {
	if _, exists := p.Commands[name]; exists {
		panic(fmt.Sprintf("Command '%s' already exists ", name))
	}
	//create the command
	command := newCommand(&p.Command, name, description, fn)
	//add it to the parser
	p.Commands[name] = command
	return command
}

//Adds a new option to the command to be used as "--option OPTION" (expects a value after the flag) in the command line
//The short definition has no length restriction but it should be significantly shorter that its long counterpart
//The function fn receives the name of the option and its value
//Example:
//command.AddOption("path","p",setPath)//option
//[...]
// func setPath(option,value string){
//      printf("According the option %v the path is set to %v",option,value);
//}
func (c *Command) AddOption(long string, short string, description string, fn func(string, string) error) *Flag {
	flag := buildFlag(long, short, description, fn, Option)
	c.addFlag(flag)
	return flag
}

//Adds a new switch to the command to be used as "--switch" (expects no value after the flag) in the command line
//The short definition has no length restriction but it should be significantly shorter that its long counterpart
//The function fn receives two string, the first is the switch name and the second is just an empty string
//Example:
//command.AddSwitch("verbose","v",setVerbose)//option
//[...]
// func setVerbose(switch string){
//      printf("I'm get to get quite talkative! I'm set to be %v ",switch);
//}
func (c *Command) AddSwitch(long string, short string, description string, fn func(string, string) error) *Flag {
	flag := buildFlag(long, short, description, fn, Switch)
	c.addFlag(flag)
	return flag
}

//Adds a flag to the command
func (c *Command) addFlag(flag *Flag) {

	if _, exists := c.innerFlagsLong[flag.Long]; exists {
		panic(fmt.Errorf("Flag '%s' already exists ", flag.Long))
	}
	if _, exists := c.innerFlagsShort[flag.Short]; exists {
		panic(fmt.Errorf("Flag '%s' already exists ", flag.Short))
	}
	c.innerFlagsLong[flag.Long] = flag
	if flag.Short != "" {
		c.innerFlagsShort[flag.Short] = flag
	}

}

func (c *Command) String() string {
	return fmt.Sprintf("%v\t %v", c.Name, c.Description)
}

//Parse parses the arguments executing the associated functions for each command and flag.
//It returns the left overs if some non-option strings or commands  were not processed.
//Errors are returned in case an unknown flag is found or a mandatory flag was not supplied.
// The set of function calls to be performed are carried in order and once the parsing process is done
func (p *Parser) Parse(args []string) (leftOvers []string, err error) {
	//get the delayed functions to call
	//for every flag//command
	fns, leftOvers, err := p.parse(args)
	if err != nil {
		return
	}
	for _, fn := range fns {
		if err := fn(); err != nil {
			return leftOvers, err
		}

	}
	return
}

//The actual parsing process
func (p *Parser) parse(args []string) (functions []func() error, leftOvers []string, err error) {
	//TODO : rewrite the parsing algorithm to make it a bit more clean and clever...
	//visited flags
	var visited []Flag
	//functions to call once the parsing process is over
	var currentCommand Command = p.Command
	var currentFunc func() error = nil
	var onCommand func() error = commandCaller(p.Name, &leftOvers, p.Command.fn)
	//go comsuming options commands and sub-options
	for i := 0; i < len(args); i++ {
		arg := args[i]
		if strings.HasPrefix(arg, "-") { //flag
			var opt *Flag
			var ok bool
			if strings.HasPrefix(arg, "--") {
				opt, ok = currentCommand.innerFlagsLong[arg[2:]]
			} else {
				opt, ok = currentCommand.innerFlagsShort[arg[1:]]
			}
			//not present
			if !ok {
				err = fmt.Errorf("%v is not a valid flag for %v", arg, currentCommand.Name)
				return
			}

			if opt.Type == Option { //option
				if i+1 >= len(args) {
					err = fmt.Errorf("No value for option %v", arg)
					return
				}
				functions = append(functions, flagCaller(opt.Long, args[i+1], opt.fn))
				i++
			} else { //switch
				functions = append(functions, flagCaller(opt.Long, "", opt.fn))
			}
			//add to visited options
			visited = append(visited, *opt)
		} else {
			//_,isParser:=currentCommand.(Parser)
			if err = checkVisited(visited, currentCommand); err != nil {
				return
			}
			for _, fn := range functions {
				if err = fn(); err != nil {
					return
				}

			}
			functions = make([]func() error, 0)
			if onCommand != nil {

				if err = onCommand(); err != nil {
					return
				}
				onCommand = nil
				//functions = append(functions, currentFunc)
			}
			cmd, ok := p.Commands[arg]
			//if its a command
			if isHelp := (arg == p.help.Name); (ok || isHelp) && currentCommand.Name != p.help.Name {
				visited = []Flag{}
				if !isHelp {
					currentCommand = *cmd
					currentFunc = commandCaller(arg, &leftOvers, cmd.fn)
				} else {
					currentCommand = p.help
					currentFunc = commandCaller(arg, &leftOvers, p.help.fn)
				}
			} else {
				leftOvers = append(leftOvers, arg)
			}

		}

	}
	if currentFunc != nil {
		functions = append(functions, currentFunc)
	}
	//last check for visited
	err = checkVisited(visited, currentCommand)

	return
}

func flagCaller(name, value string, fn func(string, string) error) func() error {
	return func() error { return fn(name, value) }
}
func commandCaller(command string, leftOvers *[]string, fn func(string, ...string) error) func() error {
	return func() error { return fn(command, *leftOvers...) }
}

//checks if the mandatory flags were visited
func checkVisited(visited []Flag, command Command) error {
	for _, flag := range command.Flags() {
		if flag.Mandatory {
			ok := false
			for _, vFlag := range visited {
				if vFlag.Long == flag.Long {
					ok = true
					break
				}
			}
			if !ok {
				return fmt.Errorf("%v was not found and is mandatory for %v", flag, command)
			}
		}
	}
	return nil
}

//Flag structure
type Flag struct {
	//long definition (--option OPTION)
	Long string
	//Short definition (-o )
	Short string
	//Description
	Description string
	//FlagType, option or switch
	Type FlagType
	//Function to call when the flag is found during the parsing process
	fn func(string, string) error
	//Says if the flag is optional or mandatory
	Mandatory bool
}

//Must sets the flag as mandatory. The parser will raise an error in case it isn't present in the arguments
//TODO make sure that switches are not allowed to get mandatory
func (f *Flag) Must(isIt bool) {
	f.Mandatory = isIt
}

//Gets a help friendly flag representation:
//-o,--option  OPTION           This option does this and that
//-s,--switch                   This is a switch
//-i,--ignoreme [IGNOREME]      Optional option
func (f Flag) String() string {
	return fmt.Sprintf("%s\t%s", f.FlagStringPrefix(), f.Description)
}

func (f Flag) FlagStringPrefix() string {
	var format string
	var prefix string
	shortFormat := "%v"
	if f.Short != "" {
		shortFormat = "-%v,"
	}
	if f.Type == Option {
		if f.Mandatory {
			format = "--%v %v"
		} else {
			format = "--%v [%v]"
		}
		prefix = fmt.Sprintf(shortFormat+format, f.Short, f.Long, strings.ToUpper(f.Long))
	} else {
		format = "--%v \t%v"
		prefix = fmt.Sprintf(shortFormat+format, f.Short, f.Long)
	}
	return prefix
}

//Checks that the definition is just one word
func checkDefinition(flag string) bool {
	parts := strings.Split(flag, " ")
	return len(parts) == 1
}

//builds the flag struct panicking if errors are encountered
func buildFlag(long string, short string, desc string, fn func(string, string) error, kind FlagType) *Flag {
	long = strings.Trim(long, " ")
	short = strings.Trim(short, " ")
	if len(long) == 0 {
		panic("Long definition is empty")
	}

	if !checkDefinition(long) {
		panic(fmt.Sprintf("Long definition %v has two words. Only one is accepted", long))
	}

	if !checkDefinition(short) {
		panic(fmt.Sprintf("Short definition %v has two words. Only one is accepted", long))
	}
	return &Flag{
		Type:        kind,
		Long:        long,
		Short:       short,
		fn:          fn,
		Description: desc,
		Mandatory:   false,
	}
}

//Help printing functions
func visitParser(p Parser) {
	fmt.Printf("Usage: %v [global_options] command [arguments]\n", p.Name)
	fmt.Printf("\n")
	fmt.Printf("Global Options\n")
	fmt.Printf("--------------\n")
	fmt.Printf("\n")
	for _, flag := range p.Flags() {
		fmt.Printf("\t%v\n", flag)
	}
	fmt.Printf("Commands\n")
	fmt.Printf("--------\n")
	fmt.Printf("\n")
	for _, cmd := range p.Commands {
		fmt.Printf("\t%v\t\t%v\n", cmd.Name, cmd.Description)
	}

	fmt.Printf("\n")
	fmt.Printf("\t%v\t\t%v\n", p.help.Name, p.help.Description)
}

func visitCommand(c Command) {
	fmt.Printf("%v\t\t%v\n", c.Name, c.Description)
	fmt.Printf("\n")
	for _, flag := range c.Flags() {
		fmt.Printf("\t%v\n", flag)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

type substitute struct {
	searchPattern  string
	replacePattern string
	paths          []string
}

func (s *substitute) Run() error {
	for _, file := range s.matchingFiles() {
		contents, err := ioutil.ReadFile(file)
		if err != nil {
			return err
		}
		err = ioutil.WriteFile(file, s.replace(contents), 0777)
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *substitute) replace(source []byte) []byte {
	// Go uses $N for backreferences, not \N
	goPattern := regexp.MustCompile(`\\(\d)`).ReplaceAllString(s.replacePattern, `$$$1`)
	return s.regex().ReplaceAll(source, []byte(goPattern))
}

func (s *substitute) regex() *regexp.Regexp {
	return regexp.MustCompilePOSIX(s.searchPattern)
}

func (s *substitute) matchingFiles() []string {
	grepArgs := []string{"grep", "--extended-regexp", "--files-with-matches", s.searchPattern}
	if len(s.paths) > 0 {
		grepArgs = append(grepArgs, s.paths...)
	}
	output, err := exec.Command("git", grepArgs...).CombinedOutput()
	if err != nil {
		if len(output) != 0 {
			fmt.Println(output)
		}
		os.Exit(1)
	}
	splitOut := strings.Split(string(output), "\n")
	return splitOut[:len(splitOut)-1]
}
<commit_msg>print git-grep errors as strings<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

type substitute struct {
	searchPattern  string
	replacePattern string
	paths          []string
}

func (s *substitute) Run() error {
	for _, file := range s.matchingFiles() {
		contents, err := ioutil.ReadFile(file)
		if err != nil {
			return err
		}
		err = ioutil.WriteFile(file, s.replace(contents), 0777)
		if err != nil {
			return err
		}
	}
	return nil
}

func (s *substitute) replace(source []byte) []byte {
	// Go uses $N for backreferences, not \N
	goPattern := regexp.MustCompile(`\\(\d)`).ReplaceAllString(s.replacePattern, `$$$1`)
	return s.regex().ReplaceAll(source, []byte(goPattern))
}

func (s *substitute) regex() *regexp.Regexp {
	return regexp.MustCompilePOSIX(s.searchPattern)
}

func (s *substitute) matchingFiles() []string {
	grepArgs := []string{"grep", "--extended-regexp", "--files-with-matches", s.searchPattern}
	if len(s.paths) > 0 {
		grepArgs = append(grepArgs, s.paths...)
	}
	output, err := exec.Command("git", grepArgs...).CombinedOutput()
	if err != nil {
		if len(output) != 0 {
			fmt.Println(string(output))
		}
		os.Exit(1)
	}
	splitOut := strings.Split(string(output), "\n")
	return splitOut[:len(splitOut)-1]
}
<|endoftext|>


<commit_before><commit_msg>Check bbl up session out content.<commit_after><|endoftext|>


<commit_before>package movieController

import (
	"context"
	"time"

	"github.com/gilcrest/errs"
	"github.com/gilcrest/go-api-basic/app"
	"github.com/gilcrest/go-api-basic/controller"
	"github.com/gilcrest/go-api-basic/datastore/movieDatastore"
	"github.com/gilcrest/go-api-basic/domain/movie"
)

// MovieController is used as the base controller for the Movie logic
type MovieController struct {
	App *app.Application
	SRF controller.StandardResponseFields
}

// MovieRequest is the request struct
type MovieRequest struct {
	Title    string `json:"title"`
	Year     int    `json:"year"`
	Rated    string `json:"rated"`
	Released string `json:"release_date"`
	RunTime  int    `json:"run_time"`
	Director string `json:"director"`
	Writer   string `json:"writer"`
}

// MovieResponse is the response struct for a single Movie
type MovieResponse struct {
	ExternalID      string `json:"external_id"`
	Title           string `json:"title"`
	Year            int    `json:"year"`
	Rated           string `json:"rated"`
	Released        string `json:"release_date"`
	RunTime         int    `json:"run_time"`
	Director        string `json:"director"`
	Writer          string `json:"writer"`
	CreateTimestamp string `json:"create_timestamp"`
	UpdateTimestamp string `json:"update_timestamp"`
}

// ListMovieResponse is the response struct for multiple Movies
type ListMovieResponse struct {
	controller.StandardResponseFields
	Data []*MovieResponse `json:"data"`
}

// SingleMovieResponse is the response struct for multiple Movies
type SingleMovieResponse struct {
	controller.StandardResponseFields
	Data *MovieResponse `json:"data"`
}

// DeleteMovieResponse is the response struct for deleted Movies
type DeleteMovieResponse struct {
	controller.StandardResponseFields
	Data struct {
		ExtlID  string `json:"extl_id"`
		Deleted bool   `json:"deleted"`
	} `json:"data"`
}

func newDeleteMovieResponse(m *movie.Movie, srf controller.StandardResponseFields) *DeleteMovieResponse {
	return &DeleteMovieResponse{
		StandardResponseFields: srf,
		Data: struct {
			ExtlID  string "json:\"extl_id\""
			Deleted bool   "json:\"deleted\""
		}{
			ExtlID:  m.ExternalID,
			Deleted: true,
		},
	}
}

// newMovieResponse is an initializer for MovieResponse
func newMovieResponse(m *movie.Movie) *MovieResponse {
	return &MovieResponse{
		ExternalID:      m.ExternalID,
		Title:           m.Title,
		Year:            m.Year,
		Rated:           m.Rated,
		Released:        m.Released.Format(time.RFC3339),
		RunTime:         m.RunTime,
		Director:        m.Director,
		Writer:          m.Writer,
		CreateTimestamp: m.CreateTimestamp.Format(time.RFC3339),
		UpdateTimestamp: m.UpdateTimestamp.Format(time.RFC3339),
	}
}

// NewMovieController initializes MovieController
func NewMovieController(app *app.Application, srf controller.StandardResponseFields) *MovieController {
	return &MovieController{App: app, SRF: srf}
}

// Add adds a movie to the catalog.
func (ctl *MovieController) Add(ctx context.Context, r *MovieRequest) (*SingleMovieResponse, error) {
	const op errs.Op = "controller/movieController/MovieController.Add"

	// Convert request into a Movie struct
	m, err := newMovie(r)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Validate the input given from the request
	err = m.Validate()
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Call the Add method to perform domain business logic
	err = m.Add(ctx)
	if err != nil {
		return nil, errs.E(err)
	}

	// Begin a DB Tx, if the underlying struct is a MockDatastore then
	// the Tx will be nil
	tx, err := ctl.App.Datastorer.BeginTx(ctx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// NewTransactor gives back either a MockTx or a concrete
	// Tx, depending upon whether the Tx from Datastorer is nil or not
	t, err := movieDatastore.NewTransactor(tx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Call the Create method of the Transactor to insert data to
	// the database (unless mocked, of course). If an error occurs,
	// rollback the transaction
	err = t.Create(ctx, m)
	if err != nil {
		return nil, errs.E(op, ctl.App.Datastorer.RollbackTx(tx, err))
	}

	// Commit the Transaction
	if err := ctl.App.Datastorer.CommitTx(tx); err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	// Populate the response
	response := ctl.NewSingleMovieResponse(newMovieResponse(m))

	return response, nil
}

// Update updates the movie given the id sent in
func (ctl *MovieController) Update(ctx context.Context, id string, r *MovieRequest) (*SingleMovieResponse, error) {
	const op errs.Op = "controller/movieController/MovieController.Update"

	// Convert request into a Movie struct
	m, err := newMovie(r)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Validate the input given from the request
	err = m.Validate()
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Perform domain Update "business logic"
	err = m.Update(ctx, id)
	if err != nil {
		return nil, errs.E(err)
	}

	// Begin a DB Tx, if the underlying struct is a MockDatastore then
	// the Tx will be nil
	tx, err := ctl.App.Datastorer.BeginTx(ctx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// NewTransactor gives back either a MockTx or a concrete
	// Tx, depending upon whether the Tx from Datastorer is nil or not
	t, err := movieDatastore.NewTransactor(tx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Call the Update method of the Transactor to update data on
	// the database (unless mocked, of course). If an error occurs,
	// rollback the transaction
	err = t.Update(ctx, m)
	if err != nil {
		return nil, errs.E(op, ctl.App.Datastorer.RollbackTx(tx, err))
	}

	// Commit the Transaction
	if err := ctl.App.Datastorer.CommitTx(tx); err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	// Populate the response
	response := ctl.NewSingleMovieResponse(newMovieResponse(m))

	return response, nil
}

// Delete removes the movie given the id sent in
func (ctl *MovieController) Delete(ctx context.Context, id string) (*DeleteMovieResponse, error) {
	const op errs.Op = "controller/movieController/MovieController.Update"

	// NewSelector returns either a concrete DB or a MockDB,
	// depending on whether the Datastorer sql.DB is nil or not
	s, err := movieDatastore.NewSelector(ctl.App.Datastorer.DB())
	if err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	// Find the Movie by ID using the selector.FindByID method
	m, err := s.FindByID(ctx, id)
	if err != nil {
		return nil, errs.E(op, err)
	}

	tx, err := ctl.App.Datastorer.BeginTx(ctx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// NewTransactor gives back either a MockTx or a concrete
	// Tx, depending upon whether the Tx from Datastorer is nil or not
	t, err := movieDatastore.NewTransactor(tx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Delete method of Transactor physically deletes the record
	// from the DB, unless mocked
	err = t.Delete(ctx, m)
	if err != nil {
		return nil, errs.E(op, ctl.App.Datastorer.RollbackTx(tx, err))
	}

	// Commit the Transaction
	if err := ctl.App.Datastorer.CommitTx(tx); err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	// Populate the response
	response := newDeleteMovieResponse(m, ctl.SRF)

	return response, nil
}

// FindByID finds a movie given its' unique ID
func (ctl *MovieController) FindByID(ctx context.Context, id string) (*SingleMovieResponse, error) {
	const op errs.Op = "controller/movieController/MovieController.FindByID"

	// NewSelector returns either a concrete DB or a MockDB,
	// depending on whether the Datastorer sql.DB is nil or not
	s, err := movieDatastore.NewSelector(ctl.App.Datastorer.DB())
	if err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	// Find the Movie by ID using the selector.FindByID method
	m, err := s.FindByID(ctx, id)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Populate the response
	response := ctl.NewSingleMovieResponse(newMovieResponse(m))

	return response, nil
}

// FindAll finds the entire set of Movies
func (ctl *MovieController) FindAll(ctx context.Context) (*ListMovieResponse, error) {
	const op errs.Op = "controller/movieController/MovieController.FindAll"

	// NewSelector returns either a concrete DB or a MockDB,
	// depending on whether the Datastorer sql.DB is nil or not
	s, err := movieDatastore.NewSelector(ctl.App.Datastorer.DB())
	if err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	// Find the list of all Movies using the selector.FindAll method
	movies, err := s.FindAll(ctx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Populate the response
	response := ctl.NewListMovieResponse(movies)

	return response, nil
}

// NewListMovieResponse is an initializer for ListMovieResponse
func (ctl *MovieController) NewListMovieResponse(ms []*movie.Movie) *ListMovieResponse {

	var s []*MovieResponse

	for _, m := range ms {
		mr := newMovieResponse(m)
		s = append(s, mr)
	}

	return &ListMovieResponse{StandardResponseFields: ctl.SRF, Data: s}
}

// NewSingleMovieResponse is an initializer for SingleMovieResponse
func (ctl *MovieController) NewSingleMovieResponse(mr *MovieResponse) *SingleMovieResponse {
	return &SingleMovieResponse{StandardResponseFields: ctl.SRF, Data: mr}
}

// NewMovie is an initializer for the Movie struct
func newMovie(am *MovieRequest) (*movie.Movie, error) {
	const op errs.Op = "controller/movieController/newMovie"

	// Parse Release Date according to RFC3339
	t, err := time.Parse(time.RFC3339, am.Released)
	if err != nil {
		return nil, errs.E(op,
			errs.Validation,
			errs.Code("invalid_date_format"),
			errs.Parameter("ReleaseDate"),
			err)
	}

	return &movie.Movie{
		Title:    am.Title,
		Year:     am.Year,
		Rated:    am.Rated,
		Released: t,
		RunTime:  am.RunTime,
		Director: am.Director,
		Writer:   am.Writer,
	}, nil
}
<commit_msg>Sew in mock and interface changes<commit_after>package movieController

import (
	"context"
	"fmt"
	"time"

	"github.com/gilcrest/errs"
	"github.com/gilcrest/go-api-basic/app"
	"github.com/gilcrest/go-api-basic/controller"
	"github.com/gilcrest/go-api-basic/datastore/movieDatastore"
	"github.com/gilcrest/go-api-basic/domain/movie"
)

// MovieController is used as the base controller for the Movie logic
type MovieController struct {
	App *app.Application
	SRF controller.StandardResponseFields
}

// RequestBody is the request struct
type RequestData struct {
	Title    string `json:"title"`
	Year     int    `json:"year"`
	Rated    string `json:"rated"`
	Released string `json:"release_date"`
	RunTime  int    `json:"run_time"`
	Director string `json:"director"`
	Writer   string `json:"writer"`
}

// ResponseData is the response struct for a single Movie
type ResponseData struct {
	ExternalID      string `json:"external_id"`
	Title           string `json:"title"`
	Year            int    `json:"year"`
	Rated           string `json:"rated"`
	Released        string `json:"release_date"`
	RunTime         int    `json:"run_time"`
	Director        string `json:"director"`
	Writer          string `json:"writer"`
	CreateTimestamp string `json:"create_timestamp"`
	UpdateTimestamp string `json:"update_timestamp"`
}

// ListMovieResponse is the response struct for multiple Movies
type ListMovieResponse struct {
	controller.StandardResponseFields
	Data []*ResponseData `json:"data"`
}

// SingleMovieResponse is the response struct for multiple Movies
type SingleMovieResponse struct {
	controller.StandardResponseFields
	Data *ResponseData `json:"data"`
}

// DeleteMovieResponse is the response struct for deleted Movies
type DeleteMovieResponse struct {
	controller.StandardResponseFields
	Data struct {
		ExtlID  string `json:"extl_id"`
		Deleted bool   `json:"deleted"`
	} `json:"data"`
}

func newDeleteMovieResponse(m *movie.Movie, srf controller.StandardResponseFields) *DeleteMovieResponse {
	return &DeleteMovieResponse{
		StandardResponseFields: srf,
		Data: struct {
			ExtlID  string "json:\"extl_id\""
			Deleted bool   "json:\"deleted\""
		}{
			ExtlID:  m.ExternalID,
			Deleted: true,
		},
	}
}

// newMovieResponse is an initializer for MovieResponse
func newMovieResponse(i interface{}) (*ResponseData, error) {
	const op errs.Op = "controller/movieController/newMovieResponse"

	switch m := i.(type) {
	case *movie.Movie:
		return &ResponseData{
			ExternalID:      m.ExternalID,
			Title:           m.Title,
			Year:            m.Year,
			Rated:           m.Rated,
			Released:        m.Released.Format(time.RFC3339),
			RunTime:         m.RunTime,
			Director:        m.Director,
			Writer:          m.Writer,
			CreateTimestamp: m.CreateTimestamp.Format(time.RFC3339),
			UpdateTimestamp: m.UpdateTimestamp.Format(time.RFC3339),
		}, nil
	case *movie.MockMovie:
		return &ResponseData{
			ExternalID:      m.ExternalID,
			Title:           m.Title,
			Year:            m.Year,
			Rated:           m.Rated,
			Released:        m.Released.Format(time.RFC3339),
			RunTime:         m.RunTime,
			Director:        m.Director,
			Writer:          m.Writer,
			CreateTimestamp: m.CreateTimestamp.Format(time.RFC3339),
			UpdateTimestamp: m.UpdateTimestamp.Format(time.RFC3339),
		}, nil
	default:
		return nil, errs.E(op, fmt.Errorf("unknown type %T", m))
	}
}

// NewMovieController initializes MovieController
func NewMovieController(app *app.Application, srf controller.StandardResponseFields) *MovieController {
	return &MovieController{App: app, SRF: srf}
}

// Add adds a movie to the catalog.
func (ctl *MovieController) Add(ctx context.Context, r *RequestData) (*SingleMovieResponse, error) {
	const op errs.Op = "controller/movieController/MovieController.Add"

	// Convert request into a Movie struct
	m, err := ctl.newAdder(r)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Call the Add method to perform domain business logic
	err = m.Add(ctx)
	if err != nil {
		return nil, errs.E(err)
	}

	// Begin a DB Tx, if the underlying struct is a MockDatastore then
	// the Tx will be nil
	tx, err := ctl.App.Datastorer.BeginTx(ctx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// NewTransactor gives back either a MockTx or a concrete
	// Tx, depending upon whether the Tx from Datastorer is nil or not
	t, err := movieDatastore.NewTransactor(tx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Call the Create method of the Transactor to insert data to
	// the database (unless mocked, of course). If an error occurs,
	// rollback the transaction
	err = t.Create(ctx, m)
	if err != nil {
		return nil, errs.E(op, ctl.App.Datastorer.RollbackTx(tx, err))
	}

	// Commit the Transaction
	if err := ctl.App.Datastorer.CommitTx(tx); err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	rd, err := newMovieResponse(m)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Populate the response
	response := ctl.NewSingleMovieResponse(rd)

	return response, nil
}

// Update updates the movie given the id sent in
func (ctl *MovieController) Update(ctx context.Context, id string, r *RequestData) (*SingleMovieResponse, error) {
	const op errs.Op = "controller/movieController/MovieController.Update"

	// Convert request into a Movie struct
	m, err := ctl.newUpdater(r)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Perform domain Update "business logic"
	err = m.Update(ctx, id)
	if err != nil {
		return nil, errs.E(err)
	}

	// Begin a DB Tx, if the underlying struct is a MockDatastore then
	// the Tx will be nil
	tx, err := ctl.App.Datastorer.BeginTx(ctx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// NewTransactor gives back either a MockTx or a concrete
	// Tx, depending upon whether the Tx from Datastorer is nil or not
	t, err := movieDatastore.NewTransactor(tx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Call the Update method of the Transactor to update data on
	// the database (unless mocked, of course). If an error occurs,
	// rollback the transaction
	err = t.Update(ctx, m)
	if err != nil {
		return nil, errs.E(op, ctl.App.Datastorer.RollbackTx(tx, err))
	}

	// Commit the Transaction
	if err := ctl.App.Datastorer.CommitTx(tx); err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	rd, err := newMovieResponse(m)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Populate the response
	response := ctl.NewSingleMovieResponse(rd)

	return response, nil
}

// Delete removes the movie given the id sent in
func (ctl *MovieController) Delete(ctx context.Context, id string) (*DeleteMovieResponse, error) {
	const op errs.Op = "controller/movieController/MovieController.Update"

	// NewSelector returns either a concrete DB or a MockDB,
	// depending on whether the Datastorer sql.DB is nil or not
	s, err := movieDatastore.NewSelector(ctl.App.Datastorer.DB())
	if err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	// Find the Movie by ID using the selector.FindByID method
	m, err := s.FindByID(ctx, id)
	if err != nil {
		return nil, errs.E(op, err)
	}

	tx, err := ctl.App.Datastorer.BeginTx(ctx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// NewTransactor gives back either a MockTx or a concrete
	// Tx, depending upon whether the Tx from Datastorer is nil or not
	t, err := movieDatastore.NewTransactor(tx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Delete method of Transactor physically deletes the record
	// from the DB, unless mocked
	err = t.Delete(ctx, m)
	if err != nil {
		return nil, errs.E(op, ctl.App.Datastorer.RollbackTx(tx, err))
	}

	// Commit the Transaction
	if err := ctl.App.Datastorer.CommitTx(tx); err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	// Populate the response
	response := newDeleteMovieResponse(m, ctl.SRF)

	return response, nil
}

// FindByID finds a movie given its' unique ID
func (ctl *MovieController) FindByID(ctx context.Context, id string) (*SingleMovieResponse, error) {
	const op errs.Op = "controller/movieController/MovieController.FindByID"

	// NewSelector returns either a concrete DB or a MockDB,
	// depending on whether the Datastorer sql.DB is nil or not
	s, err := movieDatastore.NewSelector(ctl.App.Datastorer.DB())
	if err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	// Find the Movie by ID using the selector.FindByID method
	m, err := s.FindByID(ctx, id)
	if err != nil {
		return nil, errs.E(op, err)
	}

	rd, err := newMovieResponse(m)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Populate the response
	response := ctl.NewSingleMovieResponse(rd)

	return response, nil
}

// FindAll finds the entire set of Movies
func (ctl *MovieController) FindAll(ctx context.Context) (*ListMovieResponse, error) {
	const op errs.Op = "controller/movieController/MovieController.FindAll"

	// NewSelector returns either a concrete DB or a MockDB,
	// depending on whether the Datastorer sql.DB is nil or not
	s, err := movieDatastore.NewSelector(ctl.App.Datastorer.DB())
	if err != nil {
		return nil, errs.E(op, errs.Database, err)
	}

	// Find the list of all Movies using the selector.FindAll method
	movies, err := s.FindAll(ctx)
	if err != nil {
		return nil, errs.E(op, err)
	}

	// Populate the response
	response, err := ctl.NewListMovieResponse(movies)
	if err != nil {
		return nil, errs.E(op, err)
	}

	return response, nil
}

// NewListMovieResponse is an initializer for ListMovieResponse
func (ctl *MovieController) NewListMovieResponse(ms []*movie.Movie) (*ListMovieResponse, error) {
	const op errs.Op = "controller/movieController/MovieController.NewListMovieResponse"

	var s []*ResponseData

	for _, m := range ms {
		mr, err := newMovieResponse(m)
		if err != nil {
			return nil, errs.E(op, err)
		}
		s = append(s, mr)
	}

	return &ListMovieResponse{StandardResponseFields: ctl.SRF, Data: s}, nil
}

// NewSingleMovieResponse is an initializer for SingleMovieResponse
func (ctl *MovieController) NewSingleMovieResponse(mr *ResponseData) *SingleMovieResponse {
	return &SingleMovieResponse{StandardResponseFields: ctl.SRF, Data: mr}
}

// NewMovie is an initializer for the Movie struct
func (ctl *MovieController) newAdder(am *RequestData) (movie.Adder, error) {
	const op errs.Op = "controller/movieController/newMovie"

	// Parse Release Date according to RFC3339
	t, err := time.Parse(time.RFC3339, am.Released)
	if err != nil {
		return nil, errs.E(op,
			errs.Validation,
			errs.Code("invalid_date_format"),
			errs.Parameter("ReleaseDate"),
			err)
	}

	if ctl.App.Mock {
		return &movie.MockMovie{
			Title:    am.Title,
			Year:     am.Year,
			Rated:    am.Rated,
			Released: t,
			RunTime:  am.RunTime,
			Director: am.Director,
			Writer:   am.Writer,
		}, nil
	}

	return &movie.Movie{
		Title:    am.Title,
		Year:     am.Year,
		Rated:    am.Rated,
		Released: t,
		RunTime:  am.RunTime,
		Director: am.Director,
		Writer:   am.Writer,
	}, nil
}

// NewMovie is an initializer for the Movie struct
func (ctl *MovieController) newUpdater(am *RequestData) (movie.Updater, error) {
	const op errs.Op = "controller/movieController/newMovie"

	// Parse Release Date according to RFC3339
	t, err := time.Parse(time.RFC3339, am.Released)
	if err != nil {
		return nil, errs.E(op,
			errs.Validation,
			errs.Code("invalid_date_format"),
			errs.Parameter("ReleaseDate"),
			err)
	}

	if ctl.App.Mock {
		return &movie.MockMovie{
			Title:    am.Title,
			Year:     am.Year,
			Rated:    am.Rated,
			Released: t,
			RunTime:  am.RunTime,
			Director: am.Director,
			Writer:   am.Writer,
		}, nil
	}

	return &movie.Movie{
		Title:    am.Title,
		Year:     am.Year,
		Rated:    am.Rated,
		Released: t,
		RunTime:  am.RunTime,
		Director: am.Director,
		Writer:   am.Writer,
	}, nil
}
<|endoftext|>


<commit_before>package xmlrpc

import ("time"; "fmt"; "encoding/base64";  "strconv"; "strings"; "xml"; "os"; "io")

// The interface that all values that want to be transmitted must conform to.
type MarshallUnmarshaller interface {
    // Get trasmittable value of this item
    Marshall() string;
    // Populate this item based on the provided value
    Unmarshall(string) (MarshallUnmarshaller,os.Error);
}

// Simple types
type IntValue int;
type BooleanValue bool;
type StringValue string;
type DoubleValue float;
type DateTimeValue time.Time;
type Base64Value []byte;
type StructValue map[string] MarshallUnmarshaller;
type ArrayValue []MarshallUnmarshaller;

// An error returned from the rpc server
type Fault struct {
    FaultCode int;
    FaultString string;
}

// Make fault fit os.Error
func (f Fault) String() string {
    return f.FaultString
}

type error string;

func (e error) String() string {
    return string(e)
}

// Marshal and Unmarshall functions
func (i IntValue) Marshall() string {
    return fmt.Sprintf("<int>%v</int>", i)
}

func (i IntValue) Unmarshall(s string) (MarshallUnmarshaller,os.Error) {
    tempInt, err := strconv.Atoi(s);
    i = IntValue(tempInt);
    return i,err
}

func (b BooleanValue) Marshall() string {
    return fmt.Sprintf("<boolean>%v</boolean>", b)
}

func (b BooleanValue) Unmarshall(s string) (MarshallUnmarshaller,os.Error) {
    switch s {
        case "0":
            b = BooleanValue(false)
        case "1":
            b = BooleanValue(true)
        default:
            return nil,error(fmt.Sprintf("Unrecognized boolean: %s", s))
    }
    return b,nil;
}

func (s StringValue) Marshall() string {
    return fmt.Sprintf("<string>%v</string>", s)
}

func (s StringValue) Unmarshall(val string) (MarshallUnmarshaller,os.Error) {
    s = StringValue(val);
    return s,nil;
}

func (d DoubleValue) Marshall() string {
    return fmt.Sprintf("<double>%v</double>", d)
}

func (d DoubleValue) Unmarshall(val string) (MarshallUnmarshaller, os.Error) {
    tempDouble, err := strconv.Atof(val);
    d = DoubleValue(tempDouble);
    return d,err
}

func (d DateTimeValue) Marshall() string {
    // TODO try to get ISO8601 in stdlib
    return fmt.Sprintf("<dateTime.iso8601>%s</dateTime.iso8601>", "NOT IMPLEMENTED")
}

func (d DateTimeValue) Unmarshall(val string) (MarshallUnmarshaller, os.Error) {
    return d,error("date Not Implemented")
}

func (b Base64Value) Marshall() string {
    encLen := base64.StdEncoding.EncodedLen(len(b));
    enc := make([]byte, encLen);
    base64.StdEncoding.Encode(enc, b);
    return fmt.Sprintf("<base64>%s</base64>", string(enc));
}

func (b Base64Value) Unmarshall(s string) (MarshallUnmarshaller, os.Error) {
    decLen := base64.StdEncoding.DecodedLen(len(s));
    b = Base64Value(make([]byte, decLen));
    rLen,err := base64.StdEncoding.Decode(b, strings.Bytes(s));
    b = b[0:rLen];
    return b,err
}

func (s StructValue) Marshall() (ret string) {
    ret = "<struct>";
    for key, value := range s {
        ret += fmt.Sprintf("<member><name>%s</name><value>%s</value></member>", key, value.Marshall())
    }
    ret += "</struct>";
    return
}
func (s ArrayValue) Marshall() (ret string) {
    ret = "<array><data>";
    for _, value := range s {
        ret += fmt.Sprintf("<value>%s</value>", value.Marshall())
    }
    ret += "</data></array>";
    return
}
func (f Fault) Marshall() string {
    faultStruct := StructValue{"faultCode": IntValue(f.FaultCode), "faultString": StringValue(f.FaultString)};
    return fmt.Sprintf("<fault>%s</fault>", faultStruct.Marshall())
}

func ParseMessage(r io.Reader) (MarshallUnmarshaller, os.Error) {
    p := xml.NewParser(r);
    return parseMessage(p)
}

func parseMessage(p *xml.Parser) (MarshallUnmarshaller, os.Error) {
    t, err := p.Token();
    if err != nil {return nil, err}
    start,ok := t.(xml.StartElement);
    if !ok {return nil, error("Unexpected symbol")}
    switch start.Name.Local {
        case "int":
            fallthrough
        case "i4":
            s, er := readBody(p);
            if er != nil { return nil, er }
            return IntValue(0).Unmarshall(s)
        case "boolean":
            s, er := readBody(p);
            if er != nil { return nil, er }
            return BooleanValue(false).Unmarshall(s)
        case "string":
            s, er := readBody(p);
            if er != nil { return nil, er }
            return StringValue(s), nil
        case "double":
            s, er := readBody(p);
            if er != nil { return nil, er }
            return DoubleValue(0).Unmarshall(s)
        case "dateTime.iso8601":
            s, er := readBody(p);
            if er != nil { return nil, er }
            return DateTimeValue{}.Unmarshall(s)
        case "base64":
            s, er := readBody(p);
            if er != nil { return nil, er }
            return Base64Value(make([]byte,0)).Unmarshall(s)
    }
    return nil, nil
}

func readBody(p *xml.Parser) (string, os.Error) {
    ret := "";
    for {
        t,err := p.Token();
        if (err != nil) { return "", err }
        switch v := t.(type) {
            case xml.CharData:
                ret += string(v)
            case xml.EndElement:
                return ret, nil
            case xml.ProcInst:
            case xml.Comment:
            case xml.Directive:
            default:
                return "", error("Unexpected token")
        }
    }
    return "", error("UNREACHABLE CODE")
}
<commit_msg>Changed Unmarsal to use parser directly<commit_after>package xmlrpc

import ("time"; "fmt"; "encoding/base64";  "strconv"; "strings"; "xml"; "os"; "io")

// The interface that all values that want to be transmitted must conform to.
type MarshallUnmarshaller interface {
    // Get trasmittable value of this item
    Marshall() string;
    // Populate this item based on the provided value
    Unmarshall(*xml.Parser) (MarshallUnmarshaller,os.Error);
}

// Simple types
type IntValue int;
type BooleanValue bool;
type StringValue string;
type DoubleValue float;
type DateTimeValue time.Time;
type Base64Value []byte;
type StructValue map[string] MarshallUnmarshaller;
type ArrayValue []MarshallUnmarshaller;

// An error returned from the rpc server
type Fault struct {
    FaultCode int;
    FaultString string;
}

// Make fault fit os.Error
func (f Fault) String() string {
    return f.FaultString
}

type error string;

func (e error) String() string {
    return string(e)
}

// Marshal and Unmarshall functions
func (i IntValue) Marshall() string {
    return fmt.Sprintf("<int>%v</int>", i)
}

func (i IntValue) Unmarshall(p *xml.Parser) (MarshallUnmarshaller,os.Error) {
    s, er := readBody(p);
    if er != nil { return nil, er }
    tempInt, err := strconv.Atoi(s);
    i = IntValue(tempInt);
    return i,err
}

func (b BooleanValue) Marshall() string {
    return fmt.Sprintf("<boolean>%v</boolean>", b)
}

func (b BooleanValue) Unmarshall(p *xml.Parser) (MarshallUnmarshaller,os.Error) {
    s, er := readBody(p);
    if er != nil { return nil, er }
    switch s {
        case "0":
            b = BooleanValue(false)
        case "1":
            b = BooleanValue(true)
        default:
            return nil,error(fmt.Sprintf("Unrecognized boolean: %s", s))
    }
    return b,nil;
}

func (s StringValue) Marshall() string {
    return fmt.Sprintf("<string>%v</string>", s)
}

func (s StringValue) Unmarshall(p *xml.Parser) (MarshallUnmarshaller,os.Error) {
    val, er := readBody(p);
    if er != nil { return nil, er }
    s = StringValue(val);
    return s,nil;
}

func (d DoubleValue) Marshall() string {
    return fmt.Sprintf("<double>%v</double>", d)
}

func (d DoubleValue) Unmarshall(p *xml.Parser) (MarshallUnmarshaller, os.Error) {
    val, er := readBody(p);
    if er != nil { return nil, er }
    tempDouble, err := strconv.Atof(val);
    d = DoubleValue(tempDouble);
    return d,err
}

func (d DateTimeValue) Marshall() string {
    // TODO try to get ISO8601 in stdlib
    return fmt.Sprintf("<dateTime.iso8601>%s</dateTime.iso8601>", "NOT IMPLEMENTED")
}

func (d DateTimeValue) Unmarshall(p *xml.Parser) (MarshallUnmarshaller, os.Error) {
    return d,error("date Not Implemented")
}

func (b Base64Value) Marshall() string {
    encLen := base64.StdEncoding.EncodedLen(len(b));
    enc := make([]byte, encLen);
    base64.StdEncoding.Encode(enc, b);
    return fmt.Sprintf("<base64>%s</base64>", string(enc));
}

func (b Base64Value) Unmarshall(p *xml.Parser) (MarshallUnmarshaller, os.Error) {
    s, er := readBody(p);
    if er != nil { return nil, er }
    decLen := base64.StdEncoding.DecodedLen(len(s));
    b = Base64Value(make([]byte, decLen));
    rLen,err := base64.StdEncoding.Decode(b, strings.Bytes(s));
    b = b[0:rLen];
    return b,err
}

func (s StructValue) Marshall() (ret string) {
    ret = "<struct>";
    for key, value := range s {
        ret += fmt.Sprintf("<member><name>%s</name><value>%s</value></member>", key, value.Marshall())
    }
    ret += "</struct>";
    return
}
func (s ArrayValue) Marshall() (ret string) {
    ret = "<array><data>";
    for _, value := range s {
        ret += fmt.Sprintf("<value>%s</value>", value.Marshall())
    }
    ret += "</data></array>";
    return
}
func (f Fault) Marshall() string {
    faultStruct := StructValue{"faultCode": IntValue(f.FaultCode), "faultString": StringValue(f.FaultString)};
    return fmt.Sprintf("<fault>%s</fault>", faultStruct.Marshall())
}

func ParseMessage(r io.Reader) (MarshallUnmarshaller, os.Error) {
    p := xml.NewParser(r);
    return parseMessage(p)
}

func parseMessage(p *xml.Parser) (MarshallUnmarshaller, os.Error) {
    t, err := p.Token();
    if err != nil {return nil, err}
    start,ok := t.(xml.StartElement);
    if !ok {return nil, error("Unexpected symbol")}
    switch start.Name.Local {
        case "int":
            fallthrough
        case "i4":
            return IntValue(0).Unmarshall(p)
        case "boolean":
            return BooleanValue(false).Unmarshall(p)
        case "string":
            return StringValue("").Unmarshall(p)
        case "double":
            return DoubleValue(0).Unmarshall(p)
        case "dateTime.iso8601":
            return DateTimeValue{}.Unmarshall(p)
        case "base64":
            return Base64Value(make([]byte,0)).Unmarshall(p)
    }
    return nil, nil
}

func readBody(p *xml.Parser) (string, os.Error) {
    ret := "";
    for {
        t,err := p.Token();
        if (err != nil) { return "", err }
        switch v := t.(type) {
            case xml.CharData:
                ret += string(v)
            case xml.EndElement:
                return ret, nil
            case xml.ProcInst:
            case xml.Comment:
            case xml.Directive:
            default:
                return "", error("Unexpected token")
        }
    }
    return "", error("UNREACHABLE CODE")
}
<|endoftext|>


<commit_before>package jstmpl

import (
	"bytes"
	"go/format"
	"text/template"

	"github.com/go-jstmpl/go-jstmpl/helpers"
	"github.com/go-jstmpl/go-jstmpl/types"
)

type Generator struct{}

func NewGenerator() *Generator {
	return &Generator{}
}

func (g *Generator) Process(model *types.Root, tmpl []byte, ext string) ([]byte, error) {
	out := bytes.NewBuffer([]byte{})
	t := template.Must(template.New("").Funcs(map[string]interface{}{
		"notLast":            helpers.NotLast,
		"joinTypes":          helpers.JoinTypes,
		"serialize":          helpers.Serialize,
		"toStringLiteral":    helpers.ToStringLiteral,
		"convertTypeForGo":   helpers.ConvertTypeForGo,
		"convertTagsForGo":   helpers.ConvertTagsForGo,
		"convertArrayForGo":  helpers.ConvertArrayForGo,
		"convertTypeForJS":   helpers.ConvertTypeForJS,
		"linkTitle":          helpers.LinkTitle,
		"getKeyFromJSONPath": helpers.GetKeyFromJSONPath,
		"snakeCase":          helpers.SnakeCase,
		"lowerSnakeCase":     helpers.LowerSnakeCase,
		"upperSnakeCase":     helpers.UpperSnakeCase,
		"lowerCamelCase":     helpers.LowerCamelCase,
		"upperCamelCase":     helpers.UpperCamelCase,
		// Deprecated
		"spaceToUpperCamelCase": helpers.SpaceToUpperCamelCase,
		"snakeToUpperCamelCase": helpers.SnakeToUpperCamelCase,
		"snakeToLowerCamelCase": helpers.SnakeToLowerCamelCase,
		"toUpperFirst":          helpers.ToUpperFirst,
		"toLowerFirst":          helpers.ToLowerFirst,
	}).Parse(string(tmpl)))
	if err := t.Execute(out, model); err != nil {
		return nil, err
	}

	// Format for each language
	b := out.Bytes()
	switch ext {
	case ".go":
		var err error
		b, err = format.Source(b)
		if err != nil {
			return nil, err
		}
	}

	return b, nil
}
<commit_msg>Add general helper<commit_after>package jstmpl

import (
	"bytes"
	"go/format"
	"os"
	"text/template"

	"github.com/go-jstmpl/go-jstmpl/helpers"
	"github.com/go-jstmpl/go-jstmpl/types"
)

type Generator struct{}

func NewGenerator() *Generator {
	return &Generator{}
}

func (g *Generator) Process(model *types.Root, tmpl []byte, ext string) ([]byte, error) {
	out := bytes.NewBuffer([]byte{})
	t := template.Must(template.New("").Funcs(map[string]interface{}{
		"getEnv":             os.Getenv,
		"notLast":            helpers.NotLast,
		"joinTypes":          helpers.JoinTypes,
		"serialize":          helpers.Serialize,
		"toStringLiteral":    helpers.ToStringLiteral,
		"convertTypeForGo":   helpers.ConvertTypeForGo,
		"convertTagsForGo":   helpers.ConvertTagsForGo,
		"convertArrayForGo":  helpers.ConvertArrayForGo,
		"convertTypeForJS":   helpers.ConvertTypeForJS,
		"linkTitle":          helpers.LinkTitle,
		"getKeyFromJSONPath": helpers.GetKeyFromJSONPath,
		"snakeCase":          helpers.SnakeCase,
		"lowerSnakeCase":     helpers.LowerSnakeCase,
		"upperSnakeCase":     helpers.UpperSnakeCase,
		"lowerCamelCase":     helpers.LowerCamelCase,
		"upperCamelCase":     helpers.UpperCamelCase,
		// Deprecated
		"spaceToUpperCamelCase": helpers.SpaceToUpperCamelCase,
		"snakeToUpperCamelCase": helpers.SnakeToUpperCamelCase,
		"snakeToLowerCamelCase": helpers.SnakeToLowerCamelCase,
		"toUpperFirst":          helpers.ToUpperFirst,
		"toLowerFirst":          helpers.ToLowerFirst,
	}).Parse(string(tmpl)))
	if err := t.Execute(out, model); err != nil {
		return nil, err
	}

	// Format for each language
	b := out.Bytes()
	switch ext {
	case ".go":
		var err error
		b, err = format.Source(b)
		if err != nil {
			return nil, err
		}
	}

	return b, nil
}
<|endoftext|>