1package manifest
2
3import (
4	"encoding/json"
5	"fmt"
6	"io"
7	"io/ioutil"
8	"os"
9	"path/filepath"
10)
11
12// NoManifestFound is an erro returned when a build manifest file was not found in the designated directory path.
13type NoManifestFound struct {
14	Path string
15}
16
17// Error returns the error description
18func (n *NoManifestFound) Error() string {
19	return "build artifact manifest not found"
20}
21
22// Manifest is a description of a Smithy build artifact produced
23// by the github.com/aws/smithy-go plugin.
24type Manifest struct {
25	Module       string            `json:"module"`
26	Go           string            `json:"go"`
27	Dependencies map[string]string `json:"dependencies"`
28	Files        []string          `json:"files"`
29	Unstable     bool              `json:"unstable"`
30}
31
32// ValidateManifest validates that the build artifact description
33// has the minimum required information to produce a valid Go module description.
34func ValidateManifest(manifest Manifest) error {
35	if len(manifest.Go) == 0 {
36		return fmt.Errorf("missing Go minimum version")
37	}
38	if len(manifest.Module) == 0 {
39		return fmt.Errorf("missing module path")
40	}
41	return nil
42}
43
44// LoadManifest loads the manifest description from the file located at the given path.
45func LoadManifest(path string) (Manifest, error) {
46	mf, err := os.Open(path)
47	if err != nil && os.IsNotExist(err) {
48		return Manifest{}, &NoManifestFound{Path: path}
49	} else if err != nil {
50		return Manifest{}, fmt.Errorf("failed to open manifest: %w", err)
51	}
52	defer mf.Close()
53	return ReadManifest(mf)
54}
55
56// ReadManifest parses the manifest bytes from the provided reader and returns the manifest description.
57func ReadManifest(reader io.Reader) (m Manifest, err error) {
58	data, err := ioutil.ReadAll(reader)
59	if err != nil {
60		return Manifest{}, err
61	}
62	if err = json.Unmarshal(data, &m); err != nil {
63		return Manifest{}, err
64	}
65	if err = ValidateManifest(m); err != nil {
66		return Manifest{}, err
67	}
68	return m, nil
69}
70
71// SmithyArtifactPaths is a slice of smithy-go build artifacts.
72// See the Walk method which can be used for finding the generated Go
73// source code from the Smithy build plugins projection.
74type SmithyArtifactPaths []string
75
76// Walk is a filepath.WalkFunc compatible method that can be used for finding
77// smithy-go plugin build artifacts.
78func (a *SmithyArtifactPaths) Walk(path string, info os.FileInfo, err error) error {
79	if err != nil {
80		return err
81	}
82
83	if !info.IsDir() {
84		return nil
85	}
86
87	pluginOutput := filepath.Join(path, "go-codegen")
88	stat, err := os.Stat(pluginOutput)
89	if err != nil {
90		return nil
91	}
92
93	if !stat.IsDir() {
94		return nil
95	}
96
97	*a = append(*a, pluginOutput)
98
99	return filepath.SkipDir
100}
101