Skip to content
Snippets Groups Projects
Commit fd0251bc authored by Alex Clemmer's avatar Alex Clemmer
Browse files

Add pretty-printing for `prototype search` results

When a user runs `prototype search`, we'd like for the output to include
the name, description, and available template types, and we'd like that
output to be padded for readability. For example, if the user runs
`prototype search io.`, we'd like to output something like this:

   io.whatever.pkg.foo    Foo's main template    [jsonnet, yaml]
   io.whatever.pkg.foobar Foobar's main template [jsonnet,   yaml, json]

This commit will introduce this style of padded output to the `prototype
search` subcommand.
parent 358e6c25
No related branches found
No related tags found
No related merge requests found
......@@ -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
},
......
......@@ -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.
......
......@@ -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.
......
......@@ -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.
......
......@@ -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",
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment