1// Package printer implements printing of avo files in various formats.
2package printer
3
4import (
5	"fmt"
6	"os"
7	"path/filepath"
8	"strings"
9
10	"github.com/mmcloughlin/avo/internal/stack"
11	"github.com/mmcloughlin/avo/ir"
12)
13
14// Printer can produce output for an avo File.
15type Printer interface {
16	Print(*ir.File) ([]byte, error)
17}
18
19// Builder can construct a printer.
20type Builder func(Config) Printer
21
22// Config represents general printing configuration.
23type Config struct {
24	// Command-line arguments passed to the generator. If provided, this will be
25	// included in a code generation warning.
26	Argv []string
27
28	// Name of the code generator.
29	Name string
30
31	// Name of Go package the generated code will belong to.
32	Pkg string
33}
34
35// NewDefaultConfig produces a config with Name "avo".
36// The package name is guessed from the current directory.
37func NewDefaultConfig() Config {
38	return Config{
39		Name: "avo",
40		Pkg:  pkg(),
41	}
42}
43
44// NewArgvConfig constructs a Config from os.Args.
45// The package name is guessed from the current directory.
46func NewArgvConfig() Config {
47	return Config{
48		Argv: os.Args,
49		Pkg:  pkg(),
50	}
51}
52
53// NewGoRunConfig produces a Config for a generator that's expected to be
54// executed via "go run ...".
55func NewGoRunConfig() Config {
56	path := mainfile()
57	if path == "" {
58		return NewDefaultConfig()
59	}
60	argv := []string{"go", "run", filepath.Base(path)}
61	if len(os.Args) > 1 {
62		argv = append(argv, os.Args[1:]...)
63	}
64	return Config{
65		Argv: argv,
66		Pkg:  pkg(),
67	}
68}
69
70// GeneratedBy returns a description of the code generator.
71func (c Config) GeneratedBy() string {
72	if c.Argv == nil {
73		return c.Name
74	}
75	return fmt.Sprintf("command: %s", strings.Join(c.Argv, " "))
76}
77
78// GeneratedWarning returns text for a code generation warning. Conforms to https://golang.org/s/generatedcode.
79func (c Config) GeneratedWarning() string {
80	return fmt.Sprintf("Code generated by %s. DO NOT EDIT.", c.GeneratedBy())
81}
82
83// mainfile attempts to determine the file path of the main function by
84// inspecting the stack. Returns empty string on failure.
85func mainfile() string {
86	if m := stack.Main(); m != nil {
87		return m.File
88	}
89	return ""
90}
91
92// pkg guesses the name of the package from the working directory.
93func pkg() string {
94	if cwd, err := os.Getwd(); err == nil {
95		return filepath.Base(cwd)
96	}
97	return ""
98}
99