github.com/machinebox/remoto@v0.1.2-0.20191024144331-eff21a7d321f/generator/definition/definition.go (about) 1 package definition 2 3 import ( 4 "errors" 5 "fmt" 6 "strings" 7 ) 8 9 // Definition is the definition of one or more services. 10 // In templates, it is usually accessed via the `def` variable. 11 // Package name is <%= def.PackageName %> 12 type Definition struct { 13 Services []Service `json:"services"` 14 PackageName string `json:"packageName"` 15 PackageComment string `json:"packageComment"` 16 } 17 18 // Source gets the Remoto source for this definition. 19 func (d Definition) Source() string { 20 s := printComments(d.PackageComment) 21 s += "package " + d.PackageName + "\n\n" 22 for i := range d.Services { 23 s += d.Services[i].String() 24 } 25 return s 26 } 27 28 func (d Definition) String() string { 29 return d.Source() 30 } 31 32 // Valid gets whether this Definition is valid or not. 33 func (d Definition) Valid() error { 34 if len(d.Services) == 0 { 35 return errors.New("must provide at least one service") 36 } 37 for _, service := range d.Services { 38 if len(service.Methods) == 0 { 39 return errors.New("service " + service.Name + " must have at least one method") 40 } 41 } 42 return nil 43 } 44 45 // Structure gets a Structure by name. 46 func (d Definition) Structure(name string) *Structure { 47 for _, service := range d.Services { 48 for _, structure := range service.Structures { 49 if structure.Name == name { 50 return &structure 51 } 52 } 53 } 54 return nil 55 } 56 57 // Service describes a logically grouped set of endpoints. 58 type Service struct { 59 Name string `json:"name"` 60 Comment string `json:"comment"` 61 Methods []Method `json:"methods"` 62 Structures []Structure `json:"structures"` 63 } 64 65 // EnsureStructure adds the Structure to the service if it isn't 66 // already there. 67 func (s *Service) EnsureStructure(structure Structure) { 68 for i := range s.Structures { 69 if s.Structures[i].Name == structure.Name { 70 return 71 } 72 } 73 s.Structures = append(s.Structures, structure) 74 } 75 76 func (s Service) String() string { 77 str := printComments(s.Comment) 78 str += "type " + s.Name + " interface {\n" 79 for i := range s.Methods { 80 str += indent(1, s.Methods[i].String()) 81 } 82 str += "}\n\n" 83 for i := range s.Structures { 84 str += s.Structures[i].String() 85 } 86 return str 87 } 88 89 // Method is a single method. 90 type Method struct { 91 Name string `json:"name"` 92 Comment string `json:"comment"` 93 RequestStructure Structure `json:"requestStructure"` 94 ResponseStructure Structure `json:"responseStructure"` 95 } 96 97 func (m Method) String() string { 98 str := printComments(m.Comment) 99 str += m.Name + "(" + m.RequestStructure.Name + ") " + m.ResponseStructure.Name 100 return str 101 } 102 103 // Structure describes a data structure. 104 type Structure struct { 105 Name string `json:"name"` 106 Comment string `json:"comment"` 107 Fields []Field `json:"fields"` 108 IsImported bool `json:"isImported"` 109 110 IsRequestObject bool `json:"isRequestObject"` 111 IsResponseObject bool `json:"isResponseObject"` 112 } 113 114 func (s Structure) String() string { 115 str := printComments(s.Comment) 116 str += "type " + s.Name + " struct {\n" 117 for i := range s.Fields { 118 str += indent(1, s.Fields[i].String()) 119 } 120 str += "}\n\n" 121 return str 122 } 123 124 // HasFields gets whether the Structure has any fields or not. 125 func (s Structure) HasFields() bool { 126 return len(s.Fields) > 0 127 } // TODO: test 128 129 // HasField gets whether the Structure has a specific field or not. 130 func (s Structure) HasField(field string) bool { 131 for _, f := range s.Fields { 132 if f.Name == field { 133 return true 134 } 135 } 136 return false 137 } // TODO: test 138 139 // FieldsOfType gets all Field objects that have a specific type. 140 func (s Structure) FieldsOfType(typename string) []Field { 141 var fields []Field 142 for _, field := range s.Fields { 143 if field.Type.Name == typename { 144 fields = append(fields, field) 145 } 146 } 147 return fields 148 } 149 150 // Field describes a structure field. 151 type Field struct { 152 Name string `json:"name"` 153 Comment string `json:"comment"` 154 Type Type `json:"type"` 155 } 156 157 func (f Field) String() string { 158 return fmt.Sprintf("%s%s %s", printComments(f.Comment), f.Name, f.Type.code()) 159 } 160 161 // IsExported gets whether the field is exported or not. 162 func (f Field) IsExported() bool { 163 return strings.ToUpper(f.Name[0:1]) == f.Name[0:1] 164 } 165 166 // Type describes the type of a Field. 167 type Type struct { 168 Name string `json:"name"` 169 IsMultiple bool `json:"isMultiple"` 170 IsStruct bool `json:"isStruct"` 171 IsImported bool `json:"isImported"` 172 } 173 174 func (t Type) code() string { 175 str := t.Name 176 if t.IsMultiple { 177 str = "[]" + str 178 } 179 return str 180 } 181 182 func printComments(comment string) string { 183 if comment == "" { 184 return "" 185 } 186 var out string 187 for _, line := range strings.Split(comment, "\n") { 188 out += "// " + line + "\n" 189 } 190 return out 191 } 192 193 func indent(n int, s string) string { 194 var out string 195 prefix := strings.Repeat("\t", n) 196 for _, line := range strings.Split(s, "\n") { 197 out += prefix + line + "\n" 198 } 199 return out 200 }