CLI and configuration library built especially for the parallelcoin pod
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

types.go 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. // Package Tri is a library for defining the configuration parameters for a CLI application, managing the configuration files, application data directory, defining subcommands, parameters, default values and automatically loading configuration structures with the processed configuration.
  2. //
  3. // The primary construct used in Go to implement generic types is the interface, and to implement any kind of structure that involves lists, the slice-of-interface []interface{} is a structure that can hold zero or more items of any type whatsoever.
  4. //
  5. // By deriving from this primary primitive using names, the type aliases can become metadata that can be used to specify how its contents are to be interpreted.
  6. //
  7. // Implementation Notes
  8. //
  9. // In this implementation you can see I take advantage of the possibility to put any type into the list to place string labels at the heads of the lists as identifiers, which then can form a tagged tree structure that can be addressed by providing a list of the identifier strings at each node.
  10. //
  11. // Go's strict static typing means that such hierarchies cannot be written without a complete set of types already pre-defined, so in each case the implementation has to be written specifically for the types of data used.
  12. //
  13. // In spite of it being perhaps less logical, all elements of a Tri are a derivative of a Tri. This way one never has to type assert the container, only the contents, and in most cases, the possible types depend on the type of a branch.
  14. //
  15. // Declaration Syntax Validation
  16. //
  17. // Validators for each type in this package automatically trigger the validators for their (valid set of) constituent elements, so only one Valid() invocation is needed, on the Tri type. If Valid functions return an error, it passes back through the cascade to the root where it is printed to tell (the proogrammer) that their declaration was erroneous.
  18. //
  19. // Without validity checks, in a strict static typing language, dynamic variables can cause errors when assuming the input is correct, and such errors can potentially escape notice for a long time, so by adding these runtime bounds and set membership tests, such errors are caught before the application they configure even starts execution of the main() - the Tri type's Valid() method should be invoked in a init() so it runs after the calls in any var blocks and before the main() tries to parse the CLI flags.
  20. //
  21. // NOTE: all types use the Tri []interface{} type because, while it is possible to omit fields by using field tags, if the object is another composite object, its type must be specified unless it is an array and the members are implicitly typed. By using interface slice instead, the declaration syntax has minimum redundant type specifications.
  22. package tri
  23. import (
  24. )
  25. // Branch is an interface that all Tri nodes implement to do runtime checks on the contents of a Tri derived type, basically something like Assert for the constraints required of the subtype.
  26. //
  27. // The validation method is used at runtime and basically is a declaration syntax check, and only the root Valid() function must be called, it does the rest and returns as soon as it finds an error when walking the tree, or proceeds to next step of reading CLI args and compositing defaults, config file, env together filling the configuration structure.
  28. //
  29. // This validation has no benefit if the Tri declaration is valid, but if it is not checked, the parse/configure process will almost certainly panic when it finds the wrong type of object in the wrong position, so it may seem overly verbose but this ensures that no further checking is required before parsing the several inputs that result in a filled out configuration struct.
  30. //
  31. // Constraints are not the most visible feature of generic types, but if constraints aren't applied to generic types they can be very hard bugs to spot, and simply do not occur if you first check the parts of the structures are correct.
  32. type Branch interface {
  33. Validate() error
  34. }
  35. // TODO: write the english version of what structure each of these has
  36. // Brief is a short description up to 80 characters long containing one string with no control characters, that is intended to describe the item it is embedded in.
  37. type Brief Tri
  38. // Command is the specification for an individual subcommand. Shown below is the full set of allowable items, the metadata items may only appear once, there must be a Brief, the name at the start, and a Handler function.
  39. /*
  40. {"name",
  41. Short{"c"}, // single character shortcut for full length name
  42. Brief{"brief"},
  43. Usage{"usage"},
  44. Help{"help"},
  45. Examples{
  46. "example 1", "explaining text",
  47. ...
  48. },
  49. Var{...
  50. },
  51. Trigger{...
  52. },
  53. func(Tri) int {
  54. ...
  55. return 0
  56. },
  57. }
  58. */
  59. type Command Tri
  60. // Commands is just an array of Command, providing a symbol-free and human-friendly name for the array of commands in an application declaration.
  61. type Commands []Command
  62. // Default is specifies the default value for a Variable, it must contain only one variable inside its first element.
  63. type Default Tri
  64. // DefaultCommand specifies the Command that should run when no subcommand is specified on the commandline.
  65. type DefaultCommand Tri
  66. // DefaultOn specifies that the trigger it is inside is disabled by its name appearing in the invocation.
  67. type DefaultOn Tri
  68. // Examples is is a list of pairs of strings containing a snippet of an example invocation and a short description of the effect of this example.
  69. type Examples Tri
  70. // Group is a single string tag with the same format as name fields that functions as a tag to gather related items in the help output.
  71. type Group Tri
  72. // Help is a free-form text that is interpreted as markdown syntax and may optionally be formatted using ANSI codes by a preprocessor to represent the structured text that a markdown parser will produce, by default all markdown annotations will be removed.
  73. type Help Tri
  74. // RunAfter is a flag indicating that a Trigger element of a Command should be run during shutdown instead of before startup.
  75. type RunAfter Tri
  76. // Short is a single character symbol that can be used instead of the name at the top of the Tri-derived type in invocation.
  77. type Short Tri
  78. // Slot can contain pointers to one or more items of the same type and is intended to allow the parser to directly populate the value in a possibly external struct.
  79. type Slot Tri
  80. // Terminates is a flag for Trigger types that indicates that the function will terminate execution of the application once it completes its work.
  81. type Terminates Tri
  82. // Tri is the root type where the base of an application parameter definition starts.
  83. /*
  84. var exampleTri = Tri{
  85. "appname", // only letters in Tri tags
  86. Brief{"up to 80 char string, no control characters, not nil"},
  87. Usage{"up to 80 char string, no control characters, not nil"},
  88. Version{0, 1, 1, "alpha"},
  89. DefaultCommand{"help"},
  90. Var{"datadir",
  91. Short{"d"},
  92. Brief{"brief"},
  93. Usage{"usage"},
  94. Help{"help"},
  95. Default{"~/.pod"},
  96. Slot{""},
  97. },
  98. Trigger{"trigger",
  99. ...
  100. },
  101. Commands{},
  102. }
  103. */
  104. // Note that this base specification may have variables and triggers associated with it that can be used to set configuration values common to all (or most) of the Commands specified in the declaration.
  105. type Tri []interface{}
  106. // Trigger is for initiating the execution of one-shot functions that often terminate execution, or rewrite, sort, re-index, and this kind of thing.
  107. type Trigger Tri
  108. // Usage is is an example showing the invocation of a Tri CLI flag.
  109. type Usage Tri
  110. // Var is defines a configuration variable and the means to populate this variable in an optionally separate configuration structure.
  111. type Var Tri
  112. // Version is a short specification implementing a semver version string.
  113. type Version Tri