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  }