Пример #1
0
func (g *GenWriter) WriteHeader(w io.Writer, t typewriter.Type) {
	err := g.ensureValidation(t)

	if err != nil {
		panic(err)
	}

	m, exists := g.models[t.String()]

	if !exists {
		return
	}

	s := `// See http://clipperhouse.github.io/gen for documentation

`
	w.Write([]byte(s))

	if includeSortSupport(m.methods) {
		s := `// Sort implementation is a modification of http://golang.org/pkg/sort/#Sort
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found at http://golang.org/LICENSE.

`
		w.Write([]byte(s))
	}

	return
}
Пример #2
0
// genwriter prepares models for later use in the .Validate() method. It must be called prior.
func (g *GenWriter) ensureValidation(t typewriter.Type) error {
	if !g.validated[t.String()] {
		return fmt.Errorf("Type '%s' has not been previously validated. TypeWriter.Validate() must be called on all types before using them in subsequent methods.", t.String())
	}

	return nil
}
Пример #3
0
func (c *ContainerWriter) Validate(t typewriter.Type) (bool, error) {
	tag, found, err := t.Tags.ByName("containers")

	if !found || err != nil {
		return false, err
	}

	// must include at least one item that we recognize
	any := false
	for _, item := range tag.Items {
		if templates.Contains(item) {
			// found one, move on
			any = true
			break
		}
	}

	if !any {
		// not an error, but irrelevant
		return false, nil
	}

	c.tagsByType[t.String()] = tag
	return true, nil
}
Пример #4
0
func (c ContainerWriter) Validate(t typewriter.Type) (bool, error) {
	tag, found, err := t.Tags.ByName("containers")
	if found && err == nil {
		c.tagsByType[t.String()] = tag
	}
	return found, err
}
Пример #5
0
func (g *GenWriter) Imports(t typewriter.Type) (result []typewriter.ImportSpec) {
	err := g.ensureValidation(t)

	if err != nil {
		panic(err)
	}

	m, exists := g.models[t.String()]

	if !exists {
		return
	}

	imports := make(map[string]bool)

	methodRequiresErrors := map[string]bool{
		"First":   true,
		"Single":  true,
		"Max":     true,
		"Min":     true,
		"MaxBy":   true,
		"MinBy":   true,
		"Average": true,
	}

	methodRequiresSort := map[string]bool{
		"Sort": true,
	}

	for _, s := range m.methods {
		if methodRequiresErrors[s] {
			imports["errors"] = true
		}
		if methodRequiresSort[s] {
			imports["sort"] = true
		}
	}

	for _, p := range m.projections {
		if methodRequiresErrors[p.Method] {
			imports["errors"] = true
		}
		if methodRequiresSort[p.Method] {
			imports["sort"] = true
		}
	}

	for s := range imports {
		result = append(result, typewriter.ImportSpec{
			Path: s,
		})
	}

	return
}
Пример #6
0
func (c *ContainerWriter) WriteHeader(w io.Writer, t typewriter.Type) {
	tag := c.tagsByType[t.String()] // validated above

	s := `// See http://clipperhouse.github.io/gen for documentation

`
	w.Write([]byte(s))

	var list, ring, set bool

	for _, s := range tag.Items {
		if s == "List" {
			list = true
		}
		if s == "Ring" {
			ring = true
		}
		if s == "Set" {
			set = true
		}
	}

	if list {
		license := `// List is a modification of http://golang.org/pkg/container/list/
`
		w.Write([]byte(license))
	}

	if ring {
		license := `// Ring is a modification of http://golang.org/pkg/container/ring/
`
		w.Write([]byte(license))
	}

	if list || ring {
		license := `// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found at http://golang.org/LICENSE

`
		w.Write([]byte(license))
	}

	if set {
		license := `// Set is a modification of https://github.com/deckarep/golang-set
// The MIT License (MIT)
// Copyright (c) 2013 Ralph Caraveo ([email protected])
`
		w.Write([]byte(license))
	}

	return
}
Пример #7
0
func (g *GenWriter) WriteBody(w io.Writer, t typewriter.Type) {
	err := g.ensureValidation(t)

	if err != nil {
		panic(err)
	}

	m, exists := g.models[t.String()]

	if !exists {
		return
	}

	tmpl, _ := standardTemplates.Get("plural")
	if err := tmpl.Execute(w, m); err != nil {
		panic(err)
	}

	for _, s := range m.methods {
		tmpl, _ := standardTemplates.Get(s) // already validated above
		err := tmpl.Execute(w, m)
		if err != nil {
			panic(err)
		}
	}

	for _, p := range m.projections {
		tmpl, _ := projectionTemplates.Get(p.Method) // already validated above
		err := tmpl.Execute(w, p)
		if err != nil {
			panic(err)
		}
	}

	if includeSortInterface(m.methods) {
		tmpl, _ := standardTemplates.Get("sortInterface") // already validated above
		err := tmpl.Execute(w, m)
		if err != nil {
			panic(err)
		}
	}

	if includeSortSupport(m.methods) {
		tmpl, _ := standardTemplates.Get("sortSupport") // already validated above
		err := tmpl.Execute(w, m)
		if err != nil {
			panic(err)
		}
	}
}
Пример #8
0
func (c *ContainerWriter) WriteBody(w io.Writer, t typewriter.Type) {
	tag := c.tagsByType[t.String()] // validated above

	for _, s := range tag.Items {
		tmpl, err := templates.Get(s) // validate above to avoid err check here?
		if err != nil {
			continue
		}
		err = tmpl.Execute(w, t)
		if err != nil {
			fmt.Println(err)
			continue
		}
	}

	return
}
Пример #9
0
// Validates that the tag on the gen type has correctly instructed this
// typewriter to generate code.  If the first return value is false, then
// none of the write methods are called.
func (tw *SimpleTypewriter) Validate(twt typewriter.Type) (bool, error) {
	tag, found, err := twt.Tags.ByName(plural(tw.name))

	if !found || err != nil {
		return false, err
	}

	items := tw.itemsToGenerate(tag.Items)
	fmt.Printf("%s: items for %s: %v\n", tw.Name(), twt.String(), items)

	if len(items) == 0 {
		// not an error, but irrelevant to generation
		return false, nil
	}

	tw.itemsForTypeName[twt.String()] = items
	return true, nil
}
Пример #10
0
func (f *fooWriter) WriteBody(w io.Writer, t typewriter.Type) {
	w.Write([]byte(fmt.Sprintf(`func pointless%s(){
		fmt.Println("pointless!")
		}`, t.LocalName())))
	return
}
Пример #11
0
func TestValidate(t *testing.T) {
	g := NewGenWriter()

	pkg := typewriter.NewPackage("dummy", "SomePackage")

	typ := typewriter.Type{
		Package: pkg,
		Name:    "SomeType",
		Tags:    typewriter.Tags{},
	}

	if g.validated[typ.String()] {
		t.Errorf("type should not show having been validated yet")
	}

	if err := g.ensureValidation(typ); err == nil {
		t.Errorf("ensure validation should return err prior to validation")
	}

	valid, err := g.Validate(typ)

	if !valid || err != nil {
		t.Errorf("type should be valid")
	}

	if !g.validated[typ.String()] {
		t.Errorf("type should show having been validated")
	}

	if err := g.ensureValidation(typ); err != nil {
		t.Errorf("ensure validation should not return err after validation")
	}

	if _, ok := g.models[typ.String()]; !ok {
		t.Errorf("type should appear in g.models")
	}

	if m := g.models[typ.String()]; len(m.methods) == 0 {
		t.Errorf("model without tags should have methods")
	}

	if m := g.models[typ.String()]; len(m.projections) != 0 {
		t.Errorf("model without tags should have no projections")
	}

	typ2 := typewriter.Type{
		Package: pkg,
		Name:    "SomeType2",
		Tags: typewriter.Tags{
			typewriter.Tag{
				Name:  "projections",
				Items: []string{"int", "string"},
			},
		},
	}

	valid2, err2 := g.Validate(typ2)

	if !valid2 || err2 != nil {
		t.Errorf("type should be valid")
	}

	if m := g.models[typ2.String()]; len(m.projections) == 0 {
		t.Errorf("model with projections tag should have projections")
	}

	typ3 := typewriter.Type{
		Package: pkg,
		Name:    "SomeType3",
		Tags: typewriter.Tags{
			typewriter.Tag{
				Name:  "projections",
				Items: []string{"int", "Foo"},
			},
		},
	}

	valid3, err3 := g.Validate(typ3)

	if valid3 {
		t.Errorf("type with unknown projection type should be invalid")
	}

	if err3 == nil {
		t.Errorf("type with unknown projection should return error")
	}

	if !strings.Contains(err3.Error(), "Foo") {
		t.Errorf("type with unknown projection type should mention the unknown projection type; got %v", err3)
	}

	if !strings.Contains(err3.Error(), typ3.Name) {
		t.Errorf("type with unknown projection type should mention the type on which it was declared; got %v", err3)
	}

	if m := g.models[typ2.String()]; len(m.projections) == 0 {
		t.Errorf("model with projections tag should have projections")
	}

	typ4 := typewriter.Type{
		Package: pkg,
		Name:    "SomeType4",
		Tags: typewriter.Tags{
			typewriter.Tag{
				Name:  "methods",
				Items: []string{"All", "Foo"},
			},
		},
	}

	valid4, err4 := g.Validate(typ4)

	if valid4 {
		t.Errorf("type with unknown method should be invalid")
	}

	if err4 == nil {
		t.Errorf("type with unknown method should return error")
	}

	if !strings.Contains(err4.Error(), "Foo") {
		t.Errorf("type with unknown method should mention the unknown projection type; got %v", err4)
	}

	if !strings.Contains(err4.Error(), typ4.Name) {
		t.Errorf("type with unknown method should mention the type on which it was declared; got %v", err4)
	}
}
Пример #12
0
// This business exists because I overload the methods tag to specify both standard and projection methods.
// Kind of a mess, but for the end user, arguably simpler. And arguably not.
func evaluateTags(t typewriter.Type) (standardMethods, projectionMethods []string, err error) {
	var nilMethods, nilProjections bool

	methods, found, methodsErr := t.Tags.ByName("methods")

	if methodsErr != nil {
		err = methodsErr
		return
	}

	nilMethods = !found // non-existent methods tag is different than empty

	_, found, projectionsErr := t.Tags.ByName("projections")

	if projectionsErr != nil {
		err = projectionsErr
		return
	}

	nilProjections = !found

	if nilMethods || methods.Negated {
		// default to all
		standardMethods = standardTemplates.GetAllKeys()
		if !nilProjections {
			projectionMethods = projectionTemplates.GetAllKeys()
		}
	}

	if !nilMethods {
		// categorize subsetted methods as standard or projection
		std := make([]string, 0)
		prj := make([]string, 0)

		// collect unknowns for err later
		unknown := make([]string, 0)

		for _, m := range methods.Items {
			isStd := standardTemplates.Contains(m)
			if isStd {
				std = append(std, m)
			}

			// only consider projection methods in presence of projected types
			isPrj := !nilProjections && projectionTemplates.Contains(m)
			if isPrj {
				prj = append(prj, m)
			}

			if !isStd && !isPrj {
				unknown = append(unknown, m)
			}
		}

		if methods.Negated {
			standardMethods = remove(standardMethods, std...)
			projectionMethods = remove(projectionMethods, prj...)
		} else {
			standardMethods = std
			projectionMethods = prj
		}

		if len(unknown) > 0 {
			err = fmt.Errorf("method(s) %v on type %s are unknown", unknown, t.String())
			return
		}
	}

	return
}
Пример #13
0
func TestEvaluateTags(t *testing.T) {
	typ := typewriter.Type{
		Name:    "TestType",
		Package: typewriter.NewPackage("dummy", "TestPackage"),
	}

	typ.Tags = typewriter.Tags{}

	standardMethods1, projectionMethods1, err1 := evaluateTags(typ)

	if err1 != nil {
		t.Errorf("empty methods should be ok, instead got '%v'", err1)
	}

	if len(standardMethods1) != len(standardTemplates.GetAllKeys()) {
		t.Errorf("standard methods should default to all")
	}

	if len(projectionMethods1) != 0 {
		t.Errorf("projection methods without projected type should be none, instead got %v", projectionMethods1)
	}

	typ.Tags = typewriter.Tags{
		{
			Name:  "methods",
			Items: []string{"Count", "Where"},
		},
	}

	standardMethods2, projectionMethods2, err2 := evaluateTags(typ)

	if err2 != nil {
		t.Errorf("empty methods should be ok, instead got %v", err2)
	}

	if len(standardMethods2) != 2 {
		t.Errorf("standard methods should be parsed")
	}

	if len(projectionMethods2) != 0 {
		t.Errorf("projection methods without projected typs should be none")
	}

	typ.Tags = typewriter.Tags{
		{
			Name:  "methods",
			Items: []string{"Count", "Unknown"},
		},
	}

	standardMethods3, projectionMethods3, err3 := evaluateTags(typ)

	if err3 == nil {
		t.Errorf("unknown method should be error")
	}

	if len(standardMethods3) != 1 {
		t.Errorf("standard methods (except unknown) should be 1, got %v", len(standardMethods3))
	}

	if len(projectionMethods3) != 0 {
		t.Errorf("projection methods without projected types should be none")
	}

	typ.Tags = typewriter.Tags{
		{
			Name:  "projections",
			Items: []string{"SomeType"},
		},
	}

	standardMethods4, projectionMethods4, err4 := evaluateTags(typ)

	if err4 != nil {
		t.Errorf("projected types without subsetted methods should be ok, instead got: '%v'", err4)
	}

	if len(standardMethods4) != len(standardTemplates.GetAllKeys()) {
		t.Errorf("standard methods should default to all")
	}

	if len(projectionMethods4) != len(projectionTemplates.GetAllKeys()) {
		t.Errorf("projection methods should default to all in presence of projected types")
	}

	typ.Tags = typewriter.Tags{
		{
			Name:  "methods",
			Items: []string{"GroupBy"},
		},
		{
			Name:  "projections",
			Items: []string{"SomeType"},
		},
	}

	standardMethods5, projectionMethods5, err5 := evaluateTags(typ)

	if err5 != nil {
		t.Errorf("projected types with subsetted methods should be ok, instead got: '%v'", err5)
	}

	if len(standardMethods5) != 0 {
		t.Errorf("standard methods should be none")
	}

	if len(projectionMethods5) != 1 {
		t.Errorf("projection methods should be subsetted")
	}

	typ.Tags = typewriter.Tags{
		{
			Name: "methods",
		},
	}

	standardMethods6, projectionMethods6, err6 := evaluateTags(typ)

	if err6 != nil {
		t.Errorf("empty subsetted methods should be ok, instead got: '%v'", err6)
	}

	if len(standardMethods6) != 0 {
		t.Errorf("standard methods should be empty when the tag is empty")
	}

	if len(projectionMethods6) != 0 {
		t.Errorf("projection methods should be none")
	}

	typ.Tags = typewriter.Tags{
		{
			Name:    "methods",
			Items:   []string{"Sort", "Any"},
			Negated: true,
		},
	}

	standardMethods7, projectionMethods7, err7 := evaluateTags(typ)

	if err7 != nil {
		t.Errorf("subsetted methods should be ok, instead got: '%v'", err7)
	}

	expected7 := []string{"All", "Count", "Distinct", "DistinctBy", "Each", "First", "IsSorted", "IsSortedBy", "IsSortedByDesc", "IsSortedDesc", "Max", "MaxBy", "Min", "MinBy", "Single", "SortBy", "SortByDesc", "SortDesc", "Where"}
	if !sliceEqual(standardMethods7, expected7) {
		t.Errorf("standard methods should be negatively subsetted, expected %v, got %v", expected7, standardMethods7)
	}

	if len(projectionMethods7) != 0 {
		t.Errorf("projection methods should be none")
	}

	typ.Tags = typewriter.Tags{
		{
			Name:    "methods",
			Items:   []string{"Sort", "Where", "GroupBy"},
			Negated: true,
		},
		{
			Name:  "projections",
			Items: []string{"int"},
		},
	}

	standardMethods8, projectionMethods8, err8 := evaluateTags(typ)

	if err8 != nil {
		t.Errorf("subsetted methods should be ok, instead got: '%v'", err8)
	}

	expectedStd8 := []string{"All", "Any", "Count", "Distinct", "DistinctBy", "Each", "First", "IsSorted", "IsSortedBy", "IsSortedByDesc", "IsSortedDesc", "Max", "MaxBy", "Min", "MinBy", "Single", "SortBy", "SortByDesc", "SortDesc"}
	if !sliceEqual(standardMethods8, expectedStd8) {
		t.Errorf("standard methods should be negatively subsetted, expected %v, got %v", expectedStd8, standardMethods8)
	}

	expectedPrj8 := []string{"Aggregate", "Average", "Max", "Min", "Select", "Sum"}
	if !sliceEqual(projectionMethods8, expectedPrj8) {
		t.Errorf("projection methods should be negatively subsetted, expected %v, got %v", expectedPrj8, projectionMethods8)
	}
}
Пример #14
0
func (g *GenWriter) Validate(t typewriter.Type) (bool, error) {
	standardMethods, projectionMethods, err := evaluateTags(t)
	if err != nil {
		return false, err
	}

	// filter methods applicable to type
	for _, s := range standardMethods {
		tmpl, ok := standardTemplates[s]

		if !ok {
			err = fmt.Errorf("unknown method %v on type %s", s, t.String())
			return false, err
		}

		if !tmpl.ApplicableTo(t) {
			standardMethods = remove(standardMethods, s)
		}
	}

	projectionTag, found, err := t.Tags.ByName("projections")

	if err != nil {
		return false, err
	}

	m := model{
		Type:    t,
		methods: standardMethods,
	}

	if found {
		for _, s := range projectionTag.Items {
			projectionType, err := t.Package.Eval(s)

			if err != nil {
				return false, fmt.Errorf("unable to identify type %s, projected on %s (%s)", s, t.Name, err)
			}

			for _, pm := range projectionMethods {
				tmpl, ok := projectionTemplates[pm]

				if !ok {
					return false, fmt.Errorf("unknown projection method %v", pm)
				}

				if tmpl.ApplicableTo(projectionType) {
					m.projections = append(m.projections, Projection{
						Method: pm,
						Type:   s,
						Parent: &m,
					})
				}
			}
		}
	}

	g.validated[t.String()] = true

	// only add to models if we are going to do something with it
	if len(m.methods) > 0 || len(m.projections) > 0 {
		g.models[t.String()] = m
	}

	return true, nil
}
Пример #15
0
// Write the actual body.
func (tw *SimpleTypewriter) WriteBody(wrt io.Writer, twt typewriter.Type) {
	for _, item := range tw.itemsForTypeName[twt.String()] {
		tw.writeTemplateBody(item, wrt, twt)
	}
	return
}
Пример #16
0
// Write headersForItemType for the generated file.  This would include licensing, credits
// or anything else that you wanted to put in there.  This can depend upon the
// items selected.
func (tw *SimpleTypewriter) WriteHeader(wrt io.Writer, twt typewriter.Type) {
	for _, item := range tw.itemsForTypeName[twt.String()] {
		tw.writeHeaderForItem(item, wrt)
	}
}