diff --git a/cmd/prototype.go b/cmd/prototype.go index 92f85e91f1425338c58e9f554c5e4b1e08e7c68f..f7076459fe0c38fe9fdd36926671c22218f1fa6f 100644 --- a/cmd/prototype.go +++ b/cmd/prototype.go @@ -151,9 +151,7 @@ var prototypeSearchCmd = &cobra.Command{ return fmt.Errorf("Failed to find any search results for query '%s'", query) } - for _, proto := range protos { - fmt.Println(proto.Name) - } + fmt.Print(protos) return nil }, diff --git a/prototype/index.go b/prototype/index.go index 385dddd377c1e98a0d1dc98c6db1cac5f478161e..de3765b705e69362f837fede82ab9b62b5f5349c 100644 --- a/prototype/index.go +++ b/prototype/index.go @@ -13,7 +13,7 @@ type index struct { prototypes map[string]*SpecificationSchema } -func (idx *index) SearchNames(query string, opts SearchOptions) ([]*SpecificationSchema, error) { +func (idx *index) SearchNames(query string, opts SearchOptions) (SpecificationSchemas, error) { // TODO(hausdorff): This is the world's worst search algorithm. Improve it at // some point. diff --git a/prototype/interface.go b/prototype/interface.go index ad258891efc31a80709e89fd38f8705507db49e8..b5e00b2efcc4e233c528592300ab575303c7a8b6 100644 --- a/prototype/interface.go +++ b/prototype/interface.go @@ -31,7 +31,7 @@ const ( // Index represents a queryable index of prototype specifications. type Index interface { - SearchNames(query string, opts SearchOptions) ([]*SpecificationSchema, error) + SearchNames(query string, opts SearchOptions) (SpecificationSchemas, error) } // NewIndex constructs an index of prototype specifications from a list. diff --git a/prototype/specification.go b/prototype/specification.go index d0322d52c7820ddb498244961ba4ced56fa96bdd..13e5cd7b94444571512c91adcffe7bb80a2069dc 100644 --- a/prototype/specification.go +++ b/prototype/specification.go @@ -2,6 +2,7 @@ package prototype import ( "fmt" + "sort" "strconv" "strings" ) @@ -26,6 +27,52 @@ type SpecificationSchema struct { Template SnippetSchema `json:"template"` } +// SpecificationSchemas is a slice of pointer to `SpecificationSchema`. +type SpecificationSchemas []*SpecificationSchema + +func (ss SpecificationSchemas) String() string { + // + // We want output that's lined up, like: + // + // io.whatever.pkg.foo Foo's main template [jsonnet, yaml] + // io.whatever.pkg.foobar Foobar's main template [jsonnet, yaml, json] + // + // To accomplish this we find (1) the longest prototype name, and (2) the + // longest description, so that we can properly pad the output. + // + + maxNameLen := 0 + for _, proto := range ss { + if l := len(proto.Name); l > maxNameLen { + maxNameLen = l + } + } + + maxNameDescLen := 0 + for _, proto := range ss { + nameDescLen := maxNameLen + 1 + len(proto.Template.ShortDescription) + if nameDescLen > maxNameDescLen { + maxNameDescLen = nameDescLen + } + } + + lines := []string{} + for _, proto := range ss { + // NOTE: If we don't add 1 below, the longest name will look like : + // `io.whatever.pkg.fooDescription is here.` + nameSpace := strings.Repeat(" ", maxNameLen-len(proto.Name)+1) + descSpace := strings.Repeat(" ", maxNameDescLen-maxNameLen-len(proto.Template.ShortDescription)+2) + + avail := fmt.Sprintf("%s", proto.Template.AvailableTemplates()) + + lines = append(lines, proto.Name+nameSpace+proto.Template.ShortDescription+descSpace+avail+"\n") + } + + sort.Slice(lines, func(i, j int) bool { return lines[i] < lines[j] }) + + return strings.Join(lines, "") +} + // RequiredParams retrieves all parameters that are required by a prototype. func (s *SpecificationSchema) RequiredParams() ParamSchemas { reqd := ParamSchemas{} @@ -87,6 +134,9 @@ type SnippetSchema struct { // Description describes what the prototype does. Description string `json:"description"` + // ShortDescription briefly describes what the prototype does. + ShortDescription string `json:"shortDescription"` + // Various body types of the prototype. Follows the TextMate snippets syntax, // with several features disallowed. At least one of these is required to be // filled out. diff --git a/prototype/systemPrototypes.go b/prototype/systemPrototypes.go index 192c575ea239de95cd29a1c6f04e1529876ae031..3526db6e02bf11f9fd12cc46c49d32e694bdf4be 100644 --- a/prototype/systemPrototypes.go +++ b/prototype/systemPrototypes.go @@ -10,6 +10,7 @@ var defaultPrototypes = []*SpecificationSchema{ Template: SnippetSchema{ Description: `A simple namespace. Labels are automatically populated from the name of the namespace.`, + ShortDescription: `Namespace with labels automatically populated from the name`, YAMLBody: []string{ "kind: Namespace", "apiVersion: v1", @@ -53,6 +54,7 @@ namespace.`, Template: SnippetSchema{ Description: `A service that exposes 'servicePort', and directs traffic to 'targetLabelSelector', at 'targetPort'.`, + ShortDescription: `Service that exposes a single port`, YAMLBody: []string{ "kind: Service", "apiVersion: v1", @@ -101,13 +103,14 @@ to 'targetLabelSelector', at 'targetPort'.`, }, &SpecificationSchema{ APIVersion: "0.1", - Name: "io.ksonnet.pkg.empty-configMap", + Name: "io.ksonnet.pkg.configMap", Params: ParamSchemas{ RequiredParam("name", "name", "Name to give the configMap.", String), OptionalParam("data", "data", "Data for the configMap.", "{}", Object), }, Template: SnippetSchema{ - Description: `A simple config map. Contains no data.`, + Description: `A simple config map with optional user-specified data.`, + ShortDescription: `A simple config map with optional user-specified data`, YAMLBody: []string{ "apiVersion: v1", "kind: ConfigMap", @@ -148,6 +151,7 @@ to 'targetLabelSelector', at 'targetPort'.`, Description: `A deployment that replicates container 'image' some number of times (default: 1), and exposes a port (default: 80). Labels are automatically populated from 'name'.`, + ShortDescription: `Replicates a container n times, exposes a single port`, YAMLBody: []string{ "apiVersion: apps/v1beta1", "kind: Deployment",