├── .github └── dependabot.yml ├── .gitignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── cmd ├── convert.go ├── diff.go ├── encrypt.go ├── merge.go ├── root.go ├── run.go └── version.go ├── compare ├── compare.go ├── compare_test.go └── init_test.go ├── compile ├── compile.go ├── compile_test.go └── init_test.go ├── debug └── debug.go ├── dynaml ├── addition.go ├── addition_test.go ├── archive.go ├── auto.go ├── auto_test.go ├── base64.go ├── basename.go ├── bcrypt.go ├── boolean.go ├── boolean_test.go ├── call.go ├── call_test.go ├── catch.go ├── compact.go ├── comparison.go ├── comparison_test.go ├── concatenation.go ├── concatenation_test.go ├── cond.go ├── cond_test.go ├── contains.go ├── control.go ├── control │ ├── for.go │ ├── if.go │ ├── merge.go │ ├── switch.go │ └── typeswitch.go ├── convert.go ├── crypt │ ├── md5crypt.go │ └── salt.go ├── default.go ├── defined.go ├── division.go ├── division_test.go ├── dns.go ├── dynamic_expression.go ├── dynamic_expression_test.go ├── dynaml.peg ├── dynaml.peg.go ├── element.go ├── env.go ├── error.go ├── eval.go ├── evaluate_as_helper_test.go ├── exec.go ├── expression.go ├── fail_to_evaluate_helper_test.go ├── failing_expr_helper_test.go ├── fake_binding_helper_test.go ├── features.go ├── files.go ├── float.go ├── floatmath.go ├── format.go ├── grouped.go ├── index.go ├── init_test.go ├── integer.go ├── integer_test.go ├── intersect.go ├── ip.go ├── ipset.go ├── join.go ├── json.go ├── keys.go ├── lambda.go ├── length.go ├── list.go ├── list_test.go ├── list_to_map.go ├── listexpansion.go ├── log_and.go ├── log_and_test.go ├── log_or.go ├── log_or_test.go ├── lookup.go ├── lower.go ├── makemap.go ├── map.go ├── map_test.go ├── mapmerge.go ├── mapping.go ├── mapping_test.go ├── marker.go ├── match.go ├── md5.go ├── md5crypt.go ├── merge.go ├── merge_test.go ├── mkdir.go ├── modulo.go ├── modulo_test.go ├── multiplication.go ├── multiplication_test.go ├── nil.go ├── nil_test.go ├── node.go ├── not.go ├── not_test.go ├── or.go ├── or_test.go ├── parser.go ├── parser_test.go ├── parseurl.go ├── passwd │ ├── method_des1.go │ └── passwd.go ├── pipe.go ├── prefer.go ├── projection.go ├── qualified_expression.go ├── rand.go ├── range.go ├── range_test.go ├── read.go ├── reference.go ├── reference_test.go ├── registry.go ├── replace.go ├── require.go ├── reverse.go ├── scope.go ├── select.go ├── semver │ ├── compare.go │ ├── match.go │ ├── mod.go │ ├── semver.go │ ├── sort.go │ └── validate.go ├── slice.go ├── sort.go ├── split.go ├── static_ips.go ├── string.go ├── string_test.go ├── stub.go ├── substr.go ├── subtraction.go ├── subtraction_test.go ├── sum.go ├── sum_test.go ├── sync.go ├── tag.go ├── tagdef.go ├── tempfile.go ├── template.go ├── trim.go ├── type.go ├── undefined.go ├── uniq.go ├── unresolved_check.go ├── unresolved_check_test.go ├── validate.go ├── validor.go ├── validor_test.go ├── value.go ├── wireguard │ ├── genkey.go │ ├── key.go │ └── pubkey.go ├── write.go └── x509 │ ├── certificate.go │ ├── fields.go │ ├── genkey.go │ ├── parsecert.go │ ├── publickey.go │ ├── utils.go │ └── validators.go ├── examples ├── cf-aws.yml ├── graph │ ├── closures.yaml │ ├── graph.yaml │ └── utilities.yaml ├── multi-az-cf-aws.yml ├── spifflib │ └── lib-example.go └── tutorial.md ├── features └── features.go ├── flow ├── cascade.go ├── cascade_as_helper_test.go ├── cascade_default_test.go ├── cascade_graph_test.go ├── cascade_subst_test.go ├── cascade_test.go ├── environment.go ├── environment_test.go ├── error_check_test.go ├── flow.go ├── flow2_test.go ├── flow_as_helper_test.go ├── flow_control.go ├── flow_control_test.go ├── flow_namedarg_test.go ├── flow_semver_test.go ├── flow_tag_test.go ├── flow_test.go ├── flow_vararg_test.go ├── init_test.go ├── outer_test.go ├── state.go ├── val_test.go └── version.go ├── go.mod ├── go.sum ├── hack ├── peg └── tools.go ├── init_test.go ├── legacy ├── .gitignore └── candiedyaml │ ├── .gitignore │ ├── LICENSE │ ├── NOTICE │ ├── README.md │ ├── api.go │ ├── candiedyaml_suite_test.go │ ├── decode.go │ ├── decode_test.go │ ├── emitter.go │ ├── encode.go │ ├── encode_test.go │ ├── fixtures │ └── specification │ │ ├── example2_1.yaml │ │ ├── example2_10.yaml │ │ ├── example2_11.yaml │ │ ├── example2_12.yaml │ │ ├── example2_13.yaml │ │ ├── example2_14.yaml │ │ ├── example2_15.yaml │ │ ├── example2_15_dumped.yaml │ │ ├── example2_16.yaml │ │ ├── example2_17.yaml │ │ ├── example2_17_control.yaml │ │ ├── example2_17_hexesc.yaml │ │ ├── example2_17_quoted.yaml │ │ ├── example2_17_single.yaml │ │ ├── example2_17_tie_fighter.yaml │ │ ├── example2_17_unicode.yaml │ │ ├── example2_18.yaml │ │ ├── example2_19.yaml │ │ ├── example2_2.yaml │ │ ├── example2_20.yaml │ │ ├── example2_21.yaml │ │ ├── example2_22.yaml │ │ ├── example2_23.yaml │ │ ├── example2_23_application.yaml │ │ ├── example2_23_non_date.yaml │ │ ├── example2_23_picture.yaml │ │ ├── example2_24.yaml │ │ ├── example2_24_dumped.yaml │ │ ├── example2_25.yaml │ │ ├── example2_26.yaml │ │ ├── example2_27.yaml │ │ ├── example2_27_dumped.yaml │ │ ├── example2_28.yaml │ │ ├── example2_3.yaml │ │ ├── example2_4.yaml │ │ ├── example2_5.yaml │ │ ├── example2_6.yaml │ │ ├── example2_7.yaml │ │ ├── example2_8.yaml │ │ ├── example2_9.yaml │ │ ├── example_empty.yaml │ │ └── types │ │ ├── map.yaml │ │ ├── map_mixed_tags.yaml │ │ ├── merge.yaml │ │ ├── omap.yaml │ │ ├── pairs.yaml │ │ ├── seq.yaml │ │ ├── set.yaml │ │ ├── v.yaml │ │ └── value.yaml │ ├── libyaml-LICENSE │ ├── parser.go │ ├── parser_test.go │ ├── reader.go │ ├── reader_test.go │ ├── resolver.go │ ├── resolver_test.go │ ├── run_parser.go │ ├── scanner.go │ ├── scanner_test.go │ ├── tags.go │ ├── writer.go │ ├── yaml_definesh.go │ ├── yaml_privateh.go │ └── yamlh.go ├── libraries ├── README.md ├── certs │ ├── README.md │ └── certs.yaml ├── generate │ ├── README.md │ └── generate.yaml ├── graph │ ├── README.md │ └── graph.yaml └── state │ ├── README.md │ ├── gen.sh │ └── state.yaml ├── scripts ├── build └── test ├── spiff++.go ├── spiff_test.go ├── spiffing ├── example_test.go ├── examples_test.go ├── init_test.go ├── interface.go ├── spiff.go ├── spiffing_test.go └── utils.go ├── travis.sh ├── vendor ├── github.com │ ├── Masterminds │ │ └── semver │ │ │ └── v3 │ │ │ ├── .gitignore │ │ │ ├── .golangci.yml │ │ │ ├── CHANGELOG.md │ │ │ ├── LICENSE.txt │ │ │ ├── Makefile │ │ │ ├── README.md │ │ │ ├── collection.go │ │ │ ├── constraints.go │ │ │ ├── doc.go │ │ │ ├── fuzz.go │ │ │ └── version.go │ ├── cloudfoundry-incubator │ │ └── candiedyaml │ │ │ ├── .gitignore │ │ │ ├── LICENSE │ │ │ ├── NOTICE │ │ │ ├── README.md │ │ │ ├── api.go │ │ │ ├── decode.go │ │ │ ├── emitter.go │ │ │ ├── encode.go │ │ │ ├── libyaml-LICENSE │ │ │ ├── parser.go │ │ │ ├── reader.go │ │ │ ├── resolver.go │ │ │ ├── run_parser.go │ │ │ ├── scanner.go │ │ │ ├── tags.go │ │ │ ├── writer.go │ │ │ ├── yaml_definesh.go │ │ │ ├── yaml_privateh.go │ │ │ └── yamlh.go │ ├── google │ │ └── go-cmp │ │ │ ├── LICENSE │ │ │ └── cmp │ │ │ ├── compare.go │ │ │ ├── export_panic.go │ │ │ ├── export_unsafe.go │ │ │ ├── internal │ │ │ ├── diff │ │ │ │ ├── debug_disable.go │ │ │ │ ├── debug_enable.go │ │ │ │ └── diff.go │ │ │ ├── flags │ │ │ │ └── flags.go │ │ │ ├── function │ │ │ │ └── func.go │ │ │ └── value │ │ │ │ ├── name.go │ │ │ │ ├── pointer_purego.go │ │ │ │ ├── pointer_unsafe.go │ │ │ │ └── sort.go │ │ │ ├── options.go │ │ │ ├── path.go │ │ │ ├── report.go │ │ │ ├── report_compare.go │ │ │ ├── report_references.go │ │ │ ├── report_reflect.go │ │ │ ├── report_slices.go │ │ │ ├── report_text.go │ │ │ └── report_value.go │ ├── mandelsoft │ │ ├── filepath │ │ │ ├── LICENSE │ │ │ └── pkg │ │ │ │ └── filepath │ │ │ │ ├── eval.go │ │ │ │ ├── orig.go │ │ │ │ └── path.go │ │ └── vfs │ │ │ ├── LICENSE │ │ │ └── pkg │ │ │ ├── osfs │ │ │ ├── doc.go │ │ │ ├── osfs.go │ │ │ ├── osfs_unix.go │ │ │ ├── osfs_windows.go │ │ │ └── tempfs.go │ │ │ ├── projectionfs │ │ │ ├── doc.go │ │ │ └── projectionfs.go │ │ │ ├── utils │ │ │ ├── base.go │ │ │ ├── eval.go │ │ │ ├── file.go │ │ │ ├── fileInfo.go │ │ │ ├── fssupport.go │ │ │ ├── mappedfs.go │ │ │ ├── sorter.go │ │ │ └── utils.go │ │ │ └── vfs │ │ │ ├── doc.go │ │ │ ├── errors.go │ │ │ ├── errors_unix.go │ │ │ ├── errors_windows.go │ │ │ ├── eval.go │ │ │ ├── interface.go │ │ │ ├── iofs.go │ │ │ ├── tempfile.go │ │ │ ├── utils.go │ │ │ ├── utils_regular.go │ │ │ ├── utils_windows.go │ │ │ ├── vfs.go │ │ │ └── walk.go │ ├── modern-go │ │ └── reflect2 │ │ │ ├── .gitignore │ │ │ ├── .travis.yml │ │ │ ├── Gopkg.lock │ │ │ ├── Gopkg.toml │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── go_above_118.go │ │ │ ├── go_above_19.go │ │ │ ├── go_below_118.go │ │ │ ├── reflect2.go │ │ │ ├── reflect2_amd64.s │ │ │ ├── reflect2_kind.go │ │ │ ├── relfect2_386.s │ │ │ ├── relfect2_amd64p32.s │ │ │ ├── relfect2_arm.s │ │ │ ├── relfect2_arm64.s │ │ │ ├── relfect2_mips64x.s │ │ │ ├── relfect2_mipsx.s │ │ │ ├── relfect2_ppc64x.s │ │ │ ├── relfect2_s390x.s │ │ │ ├── safe_field.go │ │ │ ├── safe_map.go │ │ │ ├── safe_slice.go │ │ │ ├── safe_struct.go │ │ │ ├── safe_type.go │ │ │ ├── type_map.go │ │ │ ├── unsafe_array.go │ │ │ ├── unsafe_eface.go │ │ │ ├── unsafe_field.go │ │ │ ├── unsafe_iface.go │ │ │ ├── unsafe_link.go │ │ │ ├── unsafe_map.go │ │ │ ├── unsafe_ptr.go │ │ │ ├── unsafe_slice.go │ │ │ ├── unsafe_struct.go │ │ │ └── unsafe_type.go │ ├── nxadm │ │ └── tail │ │ │ ├── .gitignore │ │ │ ├── CHANGES.md │ │ │ ├── Dockerfile │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── ratelimiter │ │ │ ├── Licence │ │ │ ├── leakybucket.go │ │ │ ├── memory.go │ │ │ └── storage.go │ │ │ ├── tail.go │ │ │ ├── tail_posix.go │ │ │ ├── tail_windows.go │ │ │ ├── util │ │ │ └── util.go │ │ │ ├── watch │ │ │ ├── filechanges.go │ │ │ ├── inotify.go │ │ │ ├── inotify_tracker.go │ │ │ ├── polling.go │ │ │ └── watch.go │ │ │ └── winfile │ │ │ └── winfile.go │ └── subosito │ │ └── gotenv │ │ ├── .env │ │ ├── .env.invalid │ │ ├── .gitignore │ │ ├── .golangci.yaml │ │ ├── CHANGELOG.md │ │ ├── LICENSE │ │ ├── README.md │ │ └── gotenv.go └── modules.txt └── yaml ├── equals.go ├── find.go ├── find_test.go ├── init_test.go ├── interpolation.go ├── interpolation_test.go ├── marshal.go ├── node.go ├── node_test.go ├── parser.go ├── parser_test.go └── value.go /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | # Raise pull requests for version updates 8 | # to pip against the `develop` branch 9 | target-branch: "dev" 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sw[pq] 2 | tags 3 | .idea 4 | .godev 5 | /local 6 | /spiff 7 | /spiff++ 8 | /godoc 9 | *.coverprofile 10 | spiff_darwin_amd64.zip 11 | spiff_linux_amd64.zip 12 | vendor/gopkg.in/ 13 | vendor/golang.org/ 14 | vendor/github.com/fsnotify/ 15 | vendor/github.com/hashicorp/ 16 | vendor/github.com/hpcloud/ 17 | vendor/github.com/inconshreveable/ 18 | vendor/github.com/magiconair/ 19 | vendor/github.com/mitchellh/ 20 | vendor/github.com/onsi/ 21 | vendor/github.com/pelletier/ 22 | vendor/github.com/pointlander/ 23 | vendor/github.com/spf13/ 24 | /.project 25 | /.settings/ 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.15.x 5 | 6 | matrix: 7 | include: 8 | - arch: amd64 9 | go: 1.18.x 10 | 11 | install: 12 | - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh 13 | - ./travis.sh 14 | 15 | script: 16 | - make 17 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "net/http" 7 | "os" 8 | "strings" 9 | 10 | "github.com/spf13/cobra" 11 | "github.com/spf13/viper" 12 | 13 | "github.com/mandelsoft/spiff/flow" 14 | ) 15 | 16 | var cfgFile string 17 | 18 | // rootCmd represents the base command when called without any subcommands 19 | var rootCmd = &cobra.Command{ 20 | Use: "spiff", 21 | Short: "YAML in-domain templating processor", 22 | Version: flow.VERSION, 23 | } 24 | 25 | // Execute adds all child commands to the root command and sets flags appropriately. 26 | // This is called by main.main(). It only needs to happen once to the rootCmd. 27 | func Execute() { 28 | if err := rootCmd.Execute(); err != nil { 29 | fmt.Println(err) 30 | os.Exit(1) 31 | } 32 | } 33 | 34 | func init() { 35 | cobra.OnInitialize(initConfig) 36 | } 37 | 38 | // initConfig reads in config file and ENV variables if set. 39 | func initConfig() { 40 | viper.AutomaticEnv() // read in environment variables that match 41 | } 42 | 43 | func ReadFile(file string) ([]byte, error) { 44 | if strings.HasPrefix(file, "http:") || strings.HasPrefix(file, "https:") { 45 | response, err := http.Get(file) 46 | if err != nil { 47 | return nil, fmt.Errorf("error getting [%s]: %s", file, err) 48 | } else { 49 | if response.StatusCode != http.StatusOK { 50 | defer response.Body.Close() 51 | msg, _ := ioutil.ReadAll(response.Body) 52 | return nil, fmt.Errorf("[status %d]: %s", response.StatusCode, msg) 53 | } 54 | return ioutil.ReadAll(response.Body) 55 | } 56 | } else { 57 | return ioutil.ReadFile(file) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | ) 8 | 9 | var versionQuiet bool 10 | 11 | // versionCmd represents the version command 12 | var versionCmd = &cobra.Command{ 13 | Use: "version", 14 | Short: "Prints version for spiff++", 15 | Run: func(cmd *cobra.Command, args []string) { 16 | if versionQuiet { 17 | fmt.Println(rootCmd.Version) 18 | } else { 19 | fmt.Printf("%s version %s\n", rootCmd.Name(), rootCmd.Version) 20 | } 21 | }, 22 | } 23 | 24 | func init() { 25 | rootCmd.AddCommand(versionCmd) 26 | 27 | versionCmd.Flags().BoolVarP(&versionQuiet, "quiet", "q", false, "print version only") 28 | } 29 | -------------------------------------------------------------------------------- /compare/init_test.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | 9 | "github.com/mandelsoft/spiff/yaml" 10 | ) 11 | 12 | func Test(t *testing.T) { 13 | RegisterFailHandler(Fail) 14 | RunSpecs(t, "Diffing") 15 | } 16 | 17 | func parseYAML(source string) yaml.Node { 18 | return parseYAMLFrom(source, "compare test") 19 | } 20 | 21 | func parseYAMLFrom(source string, name string) yaml.Node { 22 | parsed, err := yaml.Parse(name, []byte(source)) 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | return parsed 28 | } 29 | -------------------------------------------------------------------------------- /compile/compile_test.go: -------------------------------------------------------------------------------- 1 | package compile 2 | 3 | import ( 4 | "strings" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | var _ = Describe("Compile", func() { 11 | Context("wrong types", func() { 12 | It("rejects unknown type", func() { 13 | _, err := Compile("test", map[string]interface{}{ 14 | "val": struct{}{}, 15 | }) 16 | Expect(err).To(HaveOccurred()) 17 | Expect(err.Error()).To(Equal("val: unknown type (struct {})")) 18 | }) 19 | }) 20 | Context("Dynaml", func() { 21 | It("accepts plain yaml", func() { 22 | source := parseYAML(` 23 | --- 24 | val: alice 25 | `) 26 | _, err := Compile("test", source) 27 | Expect(err).To(Not(HaveOccurred())) 28 | }) 29 | 30 | It("detects compilation error", func() { 31 | source := parseYAML(` 32 | --- 33 | val: (( blub( )) 34 | `) 35 | _, err := Compile("test", source) 36 | Expect(err).To(HaveOccurred()) 37 | Expect(err.Error()).To(Equal("val: parse error near symbol 7 - symbol 8: ' '")) 38 | }) 39 | 40 | It("detects nested compilation error", func() { 41 | source := parseYAML(` 42 | --- 43 | val: 44 | alice: (( blub( )) 45 | nested: 46 | bob: (( map[] )) 47 | `) 48 | _, err := Compile("test", source) 49 | Expect(err).To(HaveOccurred()) 50 | Expect(strings.Split(err.Error(), "\n")).To(And( 51 | ContainElement(`val.nested.bob: parse error near symbol 5 - symbol 6: '['`), 52 | ContainElement(`val.alice: parse error near symbol 7 - symbol 8: ' '`))) 53 | }) 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /compile/init_test.go: -------------------------------------------------------------------------------- 1 | package compile 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | 7 | . "github.com/onsi/ginkgo" 8 | . "github.com/onsi/gomega" 9 | 10 | "github.com/mandelsoft/spiff/legacy/candiedyaml" 11 | ) 12 | 13 | func Test(t *testing.T) { 14 | RegisterFailHandler(Fail) 15 | RunSpecs(t, "Compiling") 16 | } 17 | 18 | func parseYAML(source string) interface{} { 19 | r := bytes.NewBuffer([]byte(source)) 20 | d := candiedyaml.NewDecoder(r) 21 | for d.HasNext() { 22 | var parsed interface{} 23 | err := d.Decode(&parsed) 24 | if err != nil { 25 | panic(err) 26 | } 27 | return parsed 28 | } 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /debug/debug.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "log" 5 | ) 6 | 7 | var DebugFlag bool 8 | 9 | func Debug(fmt string, args ...interface{}) { 10 | if DebugFlag { 11 | log.Printf(fmt, args...) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /dynaml/auto.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | ) 6 | 7 | var ( 8 | refJobs = NewReferenceExpr("", "jobs") 9 | ) 10 | 11 | type AutoExpr struct { 12 | Path []string 13 | } 14 | 15 | func (e AutoExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 16 | info := DefaultInfo() 17 | 18 | if len(e.Path) == 3 && e.Path[0] == "resource_pools" && e.Path[2] == "size" { 19 | jobs, info, found := refJobs.Evaluate(binding, false) 20 | if !found { 21 | info.Issue = yaml.NewIssue("no jobs found") 22 | return nil, info, false 23 | } 24 | 25 | if !isResolvedValue(jobs, binding) { 26 | return e, info, true 27 | } 28 | jobsList, ok := jobs.([]yaml.Node) 29 | if !ok { 30 | return info.Error("jobs must be a list") 31 | } 32 | 33 | var size int64 34 | 35 | for _, job := range jobsList { 36 | poolName, ok := yaml.FindString(job, binding.GetFeatures(), "resource_pool") 37 | if !ok { 38 | continue 39 | } 40 | 41 | if poolName != yaml.PathComponent(e.Path[1]) { 42 | continue 43 | } 44 | 45 | instances, ok := yaml.FindInt(job, binding.GetFeatures(), "instances") 46 | if !ok { 47 | return nil, info, false 48 | } 49 | 50 | size += instances 51 | } 52 | 53 | return size, info, true 54 | } 55 | 56 | return info.Error("auto only allowed for size entry in resource pools") 57 | } 58 | 59 | func (e AutoExpr) String() string { 60 | return "auto" 61 | } 62 | -------------------------------------------------------------------------------- /dynaml/auto_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | var _ = Describe("autos", func() { 11 | Context("when the path is resource_pools.*.size", func() { 12 | expr := AutoExpr{[]string{"resource_pools", "some_pool", "size"}} 13 | 14 | It("sums up the instances of the jobs in the pool", func() { 15 | binding := FakeBinding{ 16 | FoundFromRoot: map[string]yaml.Node{ 17 | "": NewNode("dummy", nil), 18 | "jobs": parseYAML(` 19 | - name: some_job 20 | resource_pool: some_pool 21 | instances: 3 22 | - name: some_other_job 23 | resource_pool: some_pool 24 | instances: 5 25 | - name: some_other_job 26 | resource_pool: some_other_pool 27 | instances: 5 28 | `), 29 | }, 30 | } 31 | 32 | Expect(expr).To(EvaluateAs(8, binding)) 33 | }) 34 | 35 | Context("when one of the jobs has non-numeric instances", func() { 36 | It("returns nil", func() { 37 | binding := FakeBinding{ 38 | FoundFromRoot: map[string]yaml.Node{ 39 | "": NewNode("dummy", nil), 40 | "jobs": parseYAML(` 41 | - name: some_job 42 | resource_pool: some_pool 43 | instances: 3 44 | - name: some_other_job 45 | resource_pool: some_pool 46 | instances: not-evaluated-yet 47 | - name: some_other_job 48 | resource_pool: some_other_pool 49 | instances: 5 50 | `), 51 | }, 52 | } 53 | 54 | Expect(expr).To(FailToEvaluate(binding)) 55 | }) 56 | }) 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /dynaml/basename.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "net/url" 5 | "path" 6 | ) 7 | 8 | func init() { 9 | RegisterFunction("basename", func_basename) 10 | RegisterFunction("dirname", func_dirname) 11 | } 12 | 13 | func func_basename(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 14 | info := DefaultInfo() 15 | 16 | if len(arguments) != 1 { 17 | return info.Error("function basename takes exactly one argument") 18 | } 19 | 20 | switch val := arguments[0].(type) { 21 | case string: 22 | u, err := url.Parse(val) 23 | if err == nil { 24 | if u.Path == "" { 25 | return "/", info, true 26 | } 27 | return path.Base(u.Path), info, true 28 | } 29 | return path.Base(val), info, true 30 | default: 31 | return info.Error("string argument expected for function basename") 32 | } 33 | } 34 | 35 | func func_dirname(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 36 | info := DefaultInfo() 37 | 38 | if len(arguments) != 1 { 39 | return info.Error("function dirname takes exactly one argument") 40 | } 41 | 42 | switch val := arguments[0].(type) { 43 | case string: 44 | u, err := url.Parse(val) 45 | if err == nil { 46 | if u.Path == "" { 47 | return "/", info, true 48 | } 49 | return path.Dir(u.Path), info, true 50 | } 51 | return path.Dir(val), info, true 52 | default: 53 | return info.Error("string argument expected for function dirname") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /dynaml/bcrypt.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | "golang.org/x/crypto/bcrypt" 6 | ) 7 | 8 | func func_bcrypt(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 9 | info := DefaultInfo() 10 | cost := 10 11 | 12 | if len(arguments) < 1 || len(arguments) > 2 { 13 | return info.Error("bcrypt takes one or two arguments") 14 | } 15 | 16 | str, ok := arguments[0].(string) 17 | if !ok { 18 | return info.Error("first argument for bcrypt must be a string") 19 | } 20 | 21 | if len(arguments) > 1 { 22 | c, ok := arguments[1].(int64) 23 | if !ok { 24 | return info.Error("second argument for bcrypt must be an integer") 25 | } 26 | cost = int(c) 27 | } 28 | result, err := bcrypt.GenerateFromPassword([]byte(str), cost) 29 | if err != nil { 30 | return info.Error("bcrypt error: %s", err) 31 | } 32 | 33 | return fmt.Sprintf("%s", result), info, true 34 | } 35 | 36 | func func_bcrypt_check(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 37 | info := DefaultInfo() 38 | 39 | if len(arguments) != 2 { 40 | return info.Error("bcrypt_check takes two arguments") 41 | } 42 | 43 | passwd, ok := arguments[0].(string) 44 | if !ok { 45 | return info.Error("first argument for bcrypt_check must be a string") 46 | } 47 | 48 | hash, ok := arguments[1].(string) 49 | if !ok { 50 | return info.Error("second argument for bcrypt_check must be a string") 51 | } 52 | 53 | err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(passwd)) 54 | return err == nil, info, true 55 | } 56 | -------------------------------------------------------------------------------- /dynaml/boolean.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type BooleanExpr struct { 8 | Value bool 9 | } 10 | 11 | func (e BooleanExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 12 | return e.Value, DefaultInfo(), true 13 | } 14 | 15 | func (e BooleanExpr) String() string { 16 | return fmt.Sprintf("%v", e.Value) 17 | } 18 | -------------------------------------------------------------------------------- /dynaml/boolean_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("booleans", func() { 9 | It("evaluates to a bool", func() { 10 | Expect(BooleanExpr{true}).To(EvaluateAs(true, FakeBinding{})) 11 | Expect(BooleanExpr{false}).To(EvaluateAs(false, FakeBinding{})) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /dynaml/compact.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | ) 6 | 7 | func init() { 8 | RegisterFunction("compact", func_compact) 9 | } 10 | 11 | func func_compact(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 12 | info := DefaultInfo() 13 | 14 | if len(arguments) != 1 { 15 | return info.Error("compact takes exactly 1 argument") 16 | } 17 | 18 | list, ok := arguments[0].([]yaml.Node) 19 | if !ok { 20 | return info.Error("invalid type for function compact") 21 | } 22 | 23 | var newList []yaml.Node 24 | 25 | for _, v := range list { 26 | found := true 27 | 28 | if v != nil && v.Value() != nil { 29 | switch elem := v.Value().(type) { 30 | case string: 31 | found = len(elem) > 0 32 | case int64: 33 | case map[string]yaml.Node: 34 | found = len(elem) > 0 35 | case []yaml.Node: 36 | found = len(elem) > 0 37 | } 38 | if found { 39 | newList = append(newList, v) 40 | } 41 | } 42 | } 43 | return newList, info, true 44 | } 45 | -------------------------------------------------------------------------------- /dynaml/comparison_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func compareIt(op string, result []bool) { 11 | for first := 5; first <= 7; first++ { 12 | expected := result[first-5] 13 | arg := int64(first) 14 | It(fmt.Sprintf("evaluates %d%s6", first, op), func() { 15 | expr := ComparisonExpr{ 16 | IntegerExpr{arg}, 17 | op, 18 | IntegerExpr{6}, 19 | } 20 | 21 | Expect(expr).To(EvaluateAs(expected, FakeBinding{})) 22 | }) 23 | } 24 | } 25 | 26 | var _ = Describe("comparison operators", func() { 27 | Context("<=", func() { 28 | compareIt("<=", []bool{true, true, false}) 29 | compareIt("<", []bool{true, false, false}) 30 | compareIt(">=", []bool{false, true, true}) 31 | compareIt(">", []bool{false, false, true}) 32 | compareIt("==", []bool{false, true, false}) 33 | compareIt("!=", []bool{true, false, true}) 34 | }) 35 | 36 | Context("when one side fails", func() { 37 | It("fails for left side failing", func() { 38 | expr := ComparisonExpr{ 39 | FailingExpr{}, 40 | "<=", 41 | IntegerExpr{6}, 42 | } 43 | 44 | Expect(expr).To(FailToEvaluate(FakeBinding{})) 45 | }) 46 | 47 | It("fails for right side failing", func() { 48 | expr := ComparisonExpr{ 49 | IntegerExpr{6}, 50 | "<=", 51 | FailingExpr{}, 52 | } 53 | 54 | Expect(expr).To(FailToEvaluate(FakeBinding{})) 55 | }) 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /dynaml/cond.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/mandelsoft/spiff/yaml" 7 | ) 8 | 9 | type CondExpr struct { 10 | C Expression 11 | T Expression 12 | F Expression 13 | } 14 | 15 | func (e CondExpr) IncludesDirectMerge() bool { 16 | return IncludesDirectMerge(e.T) || IncludesDirectMerge(e.F) 17 | } 18 | 19 | func (e CondExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 20 | resolved := true 21 | info := DefaultInfo() 22 | var infoc EvaluationInfo 23 | var result interface{} 24 | 25 | a, info, ok := ResolveExpressionOrPushEvaluation(&e.C, &resolved, &info, binding, false) 26 | if !ok { 27 | // fmt.Printf("*** COND failed: %s\n", info.Issue) 28 | return nil, info, false 29 | } 30 | if !resolved { 31 | return e, info, true 32 | } 33 | if toBool(a) { 34 | result, infoc, ok = e.T.Evaluate(binding, false) 35 | } else { 36 | result, infoc, ok = e.F.Evaluate(binding, false) 37 | } 38 | return result, infoc.Join(info), ok 39 | } 40 | 41 | func (e CondExpr) String() string { 42 | return fmt.Sprintf("%s ? %s : %s", e.C, e.T, e.F) 43 | } 44 | 45 | func toBool(v interface{}) bool { 46 | if v == nil { 47 | return false 48 | } 49 | 50 | switch eff := v.(type) { 51 | case bool: 52 | return eff 53 | case string: 54 | return len(eff) > 0 55 | case int64: 56 | return eff != 0 57 | case []yaml.Node: 58 | return len(eff) != 0 59 | case map[string]yaml.Node: 60 | return len(eff) != 0 61 | default: 62 | return true 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /dynaml/contains.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | func func_contains(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 11 | info := DefaultInfo() 12 | 13 | if len(arguments) != 2 { 14 | return info.Error("function contains takes exactly two arguments") 15 | } 16 | 17 | switch val := arguments[0].(type) { 18 | case map[string]yaml.Node: 19 | elem := arguments[1] 20 | if elem == nil { 21 | return false, info, true 22 | } 23 | key, ok := elem.(string) 24 | if !ok { 25 | return false, info, true 26 | } 27 | _, ok = val[key] 28 | return ok, info, true 29 | 30 | case []yaml.Node: 31 | if arguments[1] == nil { 32 | return false, info, true 33 | } 34 | 35 | elem := arguments[1] 36 | 37 | for _, v := range val { 38 | r, _, _ := compareEquals(v.Value(), elem) 39 | if r { 40 | return true, info, true 41 | } 42 | } 43 | case string: 44 | switch elem := arguments[1].(type) { 45 | case string: 46 | return strings.Contains(val, elem), info, true 47 | case int64: 48 | return strings.Contains(val, strconv.FormatInt(elem, 10)), info, true 49 | case bool: 50 | return strings.Contains(val, strconv.FormatBool(elem)), info, true 51 | default: 52 | return info.Error("invalid type for check string") 53 | } 54 | default: 55 | return info.Error("list or string expected for argument one of function contains") 56 | } 57 | return false, info, true 58 | } 59 | -------------------------------------------------------------------------------- /dynaml/control/if.go: -------------------------------------------------------------------------------- 1 | package control 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/dynaml" 5 | "github.com/mandelsoft/spiff/yaml" 6 | ) 7 | 8 | func init() { 9 | dynaml.RegisterControl("if", flowIf, "then", "else") 10 | } 11 | 12 | func flowIf(ctx *dynaml.ControlContext) (yaml.Node, bool) { 13 | if node, ok := dynaml.ControlReady(ctx, false); !ok { 14 | return node, false 15 | } 16 | if ctx.Value.Value() == nil { 17 | if e := ctx.Option("else"); e != nil { 18 | return dynaml.ControlValue(ctx, e) 19 | } 20 | return yaml.UndefinedNode(dynaml.NewNode(nil, ctx)), true 21 | } 22 | switch v := ctx.Value.Value().(type) { 23 | case bool: 24 | if v { 25 | if e := ctx.Option("then"); e != nil { 26 | return dynaml.ControlValue(ctx, e) 27 | } 28 | return yaml.UndefinedNode(dynaml.NewNode(nil, ctx)), true 29 | } else { 30 | if e := ctx.Option("else"); e != nil { 31 | return dynaml.ControlValue(ctx, e) 32 | } 33 | return yaml.UndefinedNode(dynaml.NewNode(nil, ctx)), true 34 | } 35 | default: 36 | return dynaml.ControlIssue(ctx, "invalid condition value type: %s", dynaml.ExpressionType(v)) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /dynaml/control/merge.go: -------------------------------------------------------------------------------- 1 | package control 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/dynaml" 5 | "github.com/mandelsoft/spiff/yaml" 6 | ) 7 | 8 | func init() { 9 | dynaml.RegisterControl("merge", flowMerge) 10 | } 11 | 12 | func flowMerge(ctx *dynaml.ControlContext) (yaml.Node, bool) { 13 | if node, ok := dynaml.ControlReady(ctx, true); !ok { 14 | return node, false 15 | } 16 | fields := ctx.DefinedFields() 17 | if ctx.Value.Value() != nil { 18 | switch v := ctx.Value.Value().(type) { 19 | case map[string]yaml.Node: 20 | for k, e := range v { 21 | fields[k] = e 22 | } 23 | case []yaml.Node: 24 | for i, l := range v { 25 | if l.Value() != nil { 26 | if m, ok := l.Value().(map[string]yaml.Node); ok { 27 | for k, e := range m { 28 | fields[k] = e 29 | } 30 | } else { 31 | return dynaml.ControlIssue(ctx, "entry %d: invalid entry type: %s", i, dynaml.ExpressionType(v)) 32 | } 33 | } 34 | } 35 | 36 | default: 37 | if v != nil { 38 | return dynaml.ControlIssue(ctx, "invalid value type: %s", dynaml.ExpressionType(v)) 39 | } 40 | } 41 | } 42 | return dynaml.NewNode(fields, ctx), true 43 | } 44 | -------------------------------------------------------------------------------- /dynaml/control/typeswitch.go: -------------------------------------------------------------------------------- 1 | package control 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/dynaml" 5 | "github.com/mandelsoft/spiff/yaml" 6 | ) 7 | 8 | func init() { 9 | dynaml.RegisterControl("type", flowType, "default") 10 | } 11 | 12 | func flowType(ctx *dynaml.ControlContext) (yaml.Node, bool) { 13 | if node, ok := dynaml.ControlReady(ctx, true); !ok { 14 | return node, false 15 | } 16 | return selected(ctx, dynaml.ExpressionType(ctx.Value)) 17 | } 18 | -------------------------------------------------------------------------------- /dynaml/crypt/md5crypt.go: -------------------------------------------------------------------------------- 1 | package crypt 2 | 3 | // taken from github.com/abbot/go-http-auth.go 4 | 5 | import "crypto/md5" 6 | 7 | const MD5_MAGIC = "$apr1$" 8 | const itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" 9 | 10 | var md5CryptSwaps = [16]int{12, 6, 0, 13, 7, 1, 14, 8, 2, 15, 9, 3, 5, 10, 4, 11} 11 | 12 | // MD5Crypt is the MD5 password crypt implementation. 13 | func MD5Crypt(password, salt, magic []byte) []byte { 14 | d := md5.New() 15 | 16 | d.Write(password) 17 | d.Write(magic) 18 | d.Write(salt) 19 | 20 | d2 := md5.New() 21 | d2.Write(password) 22 | d2.Write(salt) 23 | d2.Write(password) 24 | 25 | for i, mixin := 0, d2.Sum(nil); i < len(password); i++ { 26 | d.Write([]byte{mixin[i%16]}) 27 | } 28 | 29 | for i := len(password); i != 0; i >>= 1 { 30 | if i&1 == 0 { 31 | d.Write([]byte{password[0]}) 32 | } else { 33 | d.Write([]byte{0}) 34 | } 35 | } 36 | 37 | final := d.Sum(nil) 38 | 39 | for i := 0; i < 1000; i++ { 40 | d2 := md5.New() 41 | if i&1 == 0 { 42 | d2.Write(final) 43 | } else { 44 | d2.Write(password) 45 | } 46 | 47 | if i%3 != 0 { 48 | d2.Write(salt) 49 | } 50 | 51 | if i%7 != 0 { 52 | d2.Write(password) 53 | } 54 | 55 | if i&1 == 0 { 56 | d2.Write(password) 57 | } else { 58 | d2.Write(final) 59 | } 60 | final = d2.Sum(nil) 61 | } 62 | 63 | result := make([]byte, 0, 22) 64 | v := uint(0) 65 | bits := uint(0) 66 | for _, i := range md5CryptSwaps { 67 | v |= (uint(final[i]) << bits) 68 | for bits = bits + 8; bits > 6; bits -= 6 { 69 | result = append(result, itoa64[v&0x3f]) 70 | v >>= 6 71 | } 72 | } 73 | result = append(result, itoa64[v&0x3f]) 74 | 75 | return append(append(append(magic, salt...), '$'), result...) 76 | } 77 | -------------------------------------------------------------------------------- /dynaml/crypt/salt.go: -------------------------------------------------------------------------------- 1 | package crypt 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | func GenerateSALT(length int) []byte { 9 | rand.Seed(time.Now().UnixNano()) 10 | s := "" 11 | for i := 0; i < length; i++ { 12 | s += string(itoa64[rand.Uint64()%64]) 13 | } 14 | return []byte(s) 15 | } 16 | -------------------------------------------------------------------------------- /dynaml/default.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | type DefaultExpr struct { 4 | } 5 | 6 | func (e DefaultExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 7 | info := DefaultInfo() 8 | return info.Error("OOPS: default value not handled") 9 | } 10 | 11 | func (e DefaultExpr) String() string { 12 | return "" 13 | } 14 | 15 | func isDefaulted(e Expression) bool { 16 | if e == nil { 17 | return true 18 | } 19 | _, ok := e.(DefaultExpr) 20 | return ok 21 | } 22 | -------------------------------------------------------------------------------- /dynaml/defined.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | func (e CallExpr) valid(binding Binding) (interface{}, EvaluationInfo, bool) { 4 | pushed := make([]Expression, len(e.Arguments)) 5 | ok := true 6 | resolved := true 7 | valid := true 8 | var val interface{} 9 | 10 | copy(pushed, e.Arguments) 11 | for i, _ := range pushed { 12 | val, _, ok = ResolveExpressionOrPushEvaluation(&pushed[i], &resolved, nil, binding, true) 13 | if resolved && !ok { 14 | return false, DefaultInfo(), true 15 | } 16 | valid = valid && (val != nil) 17 | } 18 | if !resolved { 19 | return e, DefaultInfo(), true 20 | } 21 | return valid, DefaultInfo(), ok 22 | } 23 | 24 | func (e CallExpr) defined(binding Binding) (interface{}, EvaluationInfo, bool) { 25 | info := DefaultInfo() 26 | pushed := make([]Expression, len(e.Arguments)) 27 | ok := true 28 | resolved := true 29 | 30 | copy(pushed, e.Arguments) 31 | for i, _ := range pushed { 32 | _, info, ok = ResolveExpressionOrPushEvaluation(&pushed[i], &resolved, nil, binding, true) 33 | if resolved { 34 | if !ok { 35 | return false, DefaultInfo(), true 36 | } 37 | if info.Undefined { 38 | return false, DefaultInfo(), true 39 | } 40 | } 41 | } 42 | if !resolved { 43 | return e, DefaultInfo(), true 44 | } 45 | return true, DefaultInfo(), ok 46 | } 47 | -------------------------------------------------------------------------------- /dynaml/element.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | "strconv" 6 | ) 7 | 8 | func func_element(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 9 | info := DefaultInfo() 10 | 11 | if len(arguments) != 2 { 12 | return info.Error("element takes exactly two arguments") 13 | } 14 | 15 | if arguments[0] == nil || arguments[1] == nil { 16 | return info.Error("function element does not take nil arguments") 17 | } 18 | switch data := arguments[0].(type) { 19 | case []yaml.Node: 20 | var index int64 = 0 21 | var err error 22 | switch v := arguments[1].(type) { 23 | case int64: 24 | index = v 25 | case string: 26 | index, err = strconv.ParseInt(v, 10, 64) 27 | if err != nil { 28 | return info.Error("invalid value '%s' of index argument for function element", v) 29 | } 30 | default: 31 | return info.Error("invalid type of index argument for function element") 32 | } 33 | if index < 0 { 34 | return info.Error("index lower than 0") 35 | } 36 | if index >= int64(len(data)) { 37 | return info.Error("index greater or equal list size") 38 | } 39 | return data[index].Value(), info, true 40 | 41 | case map[string]yaml.Node: 42 | index, ok := arguments[1].(string) 43 | if !ok { 44 | return info.Error("map key (%v) must be of type string", arguments[1]) 45 | } 46 | e, ok := data[index] 47 | if !ok { 48 | return info.Error("map key '%s' not found", index) 49 | } 50 | return e.Value(), info, true 51 | default: 52 | return info.Error("invalid type for first argument of function element") 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /dynaml/error.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | func func_error(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 4 | n, info, ok := format("error", arguments, binding) 5 | if !ok { 6 | return n, info, ok 7 | } 8 | return info.Error("%s", n) 9 | } 10 | -------------------------------------------------------------------------------- /dynaml/eval.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | func func_eval(arguments []interface{}, binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 4 | info := DefaultInfo() 5 | 6 | if len(arguments) != 1 { 7 | return info.Error("one argument required for 'eval'") 8 | } 9 | 10 | str, ok := arguments[0].(string) 11 | if !ok { 12 | return info.Error("string argument required for 'eval'") 13 | } 14 | 15 | expr, err := Parse(str, binding.Path(), binding.StubPath()) 16 | if err != nil { 17 | return info.Error("(%s)\t %s", str, err) 18 | } 19 | return expr.Evaluate(binding, locally) 20 | } 21 | -------------------------------------------------------------------------------- /dynaml/evaluate_as_helper_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func EvaluateAs(expected interface{}, binding Binding) *EvaluateAsMatcher { 8 | return &EvaluateAsMatcher{Expected: expected, Binding: binding} 9 | } 10 | 11 | type EvaluateAsMatcher struct { 12 | Expected interface{} 13 | Binding Binding 14 | actual interface{} 15 | } 16 | 17 | func (matcher *EvaluateAsMatcher) Match(source interface{}) (success bool, err error) { 18 | if source == nil && matcher.Expected == nil { 19 | return false, fmt.Errorf("Refusing to compare to .") 20 | } 21 | 22 | expr, ok := source.(Expression) 23 | if !ok { 24 | return false, fmt.Errorf("Not an expression: %v\n", source) 25 | } 26 | 27 | var info EvaluationInfo 28 | matcher.actual, info, ok = expr.Evaluate(matcher.Binding, false) 29 | if !ok { 30 | return false, fmt.Errorf("Node failed to evaluate: %s.", info.Issue.Issue) 31 | } 32 | 33 | if NewNode(matcher.actual, nil).EquivalentToNode(NewNode(matcher.Expected, nil)) { 34 | return true, nil 35 | } else { 36 | return false, nil 37 | } 38 | 39 | return 40 | } 41 | 42 | func formatMessage(actual interface{}, message string, expected interface{}) string { 43 | return fmt.Sprintf("Expected %s %#v, got %#v", message, expected, actual) 44 | } 45 | 46 | func (matcher *EvaluateAsMatcher) FailureMessage(actual interface{}) (message string) { 47 | return formatMessage(matcher.actual, "to evaluate to", matcher.Expected) 48 | } 49 | 50 | func (matcher *EvaluateAsMatcher) NegatedFailureMessage(actual interface{}) (message string) { 51 | return formatMessage(matcher.actual, "not to evaluate to", matcher.Expected) 52 | } 53 | -------------------------------------------------------------------------------- /dynaml/fail_to_evaluate_helper_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func FailToEvaluate(binding Binding) *FailToEvaluateMatcher { 8 | return &FailToEvaluateMatcher{binding} 9 | } 10 | 11 | type FailToEvaluateMatcher struct { 12 | Binding Binding 13 | } 14 | 15 | func (matcher *FailToEvaluateMatcher) Match(source interface{}) (success bool, err error) { 16 | expr, ok := source.(Expression) 17 | if !ok { 18 | return false, fmt.Errorf("Not an expression: %v", source) 19 | } 20 | 21 | actual, _, ok := expr.Evaluate(matcher.Binding, false) 22 | if ok { 23 | return false, fmt.Errorf("Node evaluated to: %#v", actual) 24 | } 25 | 26 | return true, nil 27 | } 28 | 29 | func (matcher *FailToEvaluateMatcher) FailureMessage(actual interface{}) (message string) { 30 | return "" 31 | } 32 | 33 | func (matcher *FailToEvaluateMatcher) NegatedFailureMessage(actual interface{}) (message string) { 34 | return "" 35 | } 36 | -------------------------------------------------------------------------------- /dynaml/failing_expr_helper_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | type FailingExpr struct{} 4 | 5 | func (FailingExpr) Evaluate(Binding, bool) (interface{}, EvaluationInfo, bool) { 6 | return nil, DefaultInfo(), false 7 | } 8 | -------------------------------------------------------------------------------- /dynaml/features.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | ) 6 | 7 | func init() { 8 | RegisterFunction("features", func_features) 9 | } 10 | 11 | func func_features(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 12 | info := DefaultInfo() 13 | 14 | switch len(arguments) { 15 | case 0: 16 | result := []yaml.Node{} 17 | for f := range binding.GetFeatures() { 18 | result = append(result, NewNode(f, binding)) 19 | } 20 | return result, info, true 21 | case 1: 22 | name, ok := arguments[0].(string) 23 | if !ok { 24 | return info.Error("features: argument must be a string") 25 | } 26 | return binding.GetFeatures().Enabled(name), info, true 27 | default: 28 | return info.Error("features acctepts a maximum of one arguments") 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /dynaml/files.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | ) 6 | 7 | func func_listFiles(directory bool, arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 8 | info := DefaultInfo() 9 | 10 | if !binding.GetState().FileAccessAllowed() { 11 | return info.DenyOSOperation("listFiles") 12 | } 13 | 14 | if len(arguments) != 1 { 15 | return info.Error("list requires exactly one arguments") 16 | } 17 | 18 | name, ok := arguments[0].(string) 19 | if !ok { 20 | return info.Error("list: argument must be a string") 21 | } 22 | 23 | if name == "" { 24 | return info.Error("list: argument is empty string") 25 | } 26 | 27 | if !checkExistence(binding, name, true) { 28 | return info.Error("list: %q is no directory or does not exist", name) 29 | } 30 | 31 | files, err := binding.GetState().FileSystem().ReadDir(name) 32 | if err != nil { 33 | return info.Error("list: %q: error reading directory", name, err) 34 | } 35 | result := []yaml.Node{} 36 | for _, f := range files { 37 | if f.IsDir() == directory { 38 | result = append(result, NewNode(f.Name(), binding)) 39 | } 40 | } 41 | return result, info, true 42 | } 43 | -------------------------------------------------------------------------------- /dynaml/float.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type FloatExpr struct { 8 | Value float64 9 | } 10 | 11 | func (e FloatExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 12 | return e.Value, DefaultInfo(), true 13 | } 14 | 15 | func (e FloatExpr) String() string { 16 | return strconv.FormatFloat(e.Value, 'g', -1, 64) 17 | } 18 | -------------------------------------------------------------------------------- /dynaml/format.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | 9 | "github.com/mandelsoft/spiff/legacy/candiedyaml" 10 | ) 11 | 12 | func func_format(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 13 | return format("format", arguments, binding) 14 | } 15 | 16 | func format(name string, arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 17 | info := DefaultInfo() 18 | 19 | if len(arguments) < 1 { 20 | return info.Error("alt least one argument required for '%s'", name) 21 | } 22 | 23 | args := make([]interface{}, len(arguments)) 24 | for i, arg := range arguments { 25 | switch v := arg.(type) { 26 | case []yaml.Node: 27 | yaml, err := candiedyaml.Marshal(NewNode(v, nil)) 28 | if err != nil { 29 | log.Fatalln("error marshalling yaml fragment:", err) 30 | } 31 | args[i] = string(yaml) 32 | case map[string]yaml.Node: 33 | yaml, err := candiedyaml.Marshal(NewNode(v, nil)) 34 | if err != nil { 35 | log.Fatalln("error marshalling yaml fragment:", err) 36 | } 37 | args[i] = string(yaml) 38 | case TemplateValue: 39 | yaml, err := candiedyaml.Marshal(v.Orig) 40 | if err != nil { 41 | log.Fatalln("error marshalling template:", err) 42 | } 43 | args[i] = string(yaml) 44 | case LambdaValue: 45 | args[i] = v.String() 46 | default: 47 | args[i] = arg 48 | } 49 | } 50 | 51 | f, ok := args[0].(string) 52 | if !ok { 53 | return info.Error("%s: format must be string", format) 54 | } 55 | return fmt.Sprintf(f, args[1:]...), info, true 56 | } 57 | -------------------------------------------------------------------------------- /dynaml/grouped.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type GroupedExpr struct { 8 | Expr Expression 9 | } 10 | 11 | func (e GroupedExpr) IncludesDirectMerge() bool { 12 | return IncludesDirectMerge(e.Expr) 13 | } 14 | 15 | func (e GroupedExpr) String() string { 16 | return fmt.Sprintf("( %s )", e.Expr) 17 | } 18 | 19 | func (e GroupedExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 20 | return e.Expr.Evaluate(binding, locally) 21 | } 22 | -------------------------------------------------------------------------------- /dynaml/index.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | func func_index(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 10 | return _index(true, strings.Index, arguments, binding) 11 | } 12 | func func_lastindex(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 13 | return _index(false, strings.LastIndex, arguments, binding) 14 | } 15 | 16 | func _index(first bool, f func(string, string) int, arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 17 | info := DefaultInfo() 18 | found := int64(-1) 19 | 20 | if len(arguments) != 2 { 21 | return info.Error("function index takes exactly two arguments") 22 | } 23 | 24 | switch val := arguments[0].(type) { 25 | case []yaml.Node: 26 | if arguments[1] == nil { 27 | return -1, info, true 28 | } 29 | 30 | elem := arguments[1] 31 | 32 | for i, v := range val { 33 | r, _, _ := compareEquals(v.Value(), elem) 34 | if r { 35 | found = int64(i) 36 | if first { 37 | break 38 | } 39 | } 40 | } 41 | case string: 42 | switch elem := arguments[1].(type) { 43 | case string: 44 | return int64(f(val, elem)), info, true 45 | case int64: 46 | return int64(f(val, strconv.FormatInt(elem, 10))), info, true 47 | case bool: 48 | return int64(f(val, strconv.FormatBool(elem))), info, true 49 | default: 50 | return info.Error("invalid type for check string") 51 | } 52 | default: 53 | return info.Error("list or string expected for argument one of function index") 54 | } 55 | return int64(found), info, true 56 | } 57 | -------------------------------------------------------------------------------- /dynaml/init_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | 9 | "github.com/mandelsoft/spiff/yaml" 10 | ) 11 | 12 | func Test(t *testing.T) { 13 | RegisterFailHandler(Fail) 14 | RunSpecs(t, "Dynaml") 15 | } 16 | 17 | func parseYAML(source string) yaml.Node { 18 | parsed, err := yaml.Parse("dynaml test", []byte(source)) 19 | if err != nil { 20 | panic(err) 21 | } 22 | 23 | return parsed 24 | } 25 | -------------------------------------------------------------------------------- /dynaml/integer.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | type IntegerExpr struct { 8 | Value int64 9 | } 10 | 11 | func (e IntegerExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 12 | return e.Value, DefaultInfo(), true 13 | } 14 | 15 | func (e IntegerExpr) String() string { 16 | return strconv.FormatInt(e.Value, 10) 17 | } 18 | -------------------------------------------------------------------------------- /dynaml/integer_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("integers", func() { 9 | It("evaluates to an int", func() { 10 | Expect(IntegerExpr{42}).To(EvaluateAs(42, FakeBinding{})) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /dynaml/intersect.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | ) 6 | 7 | func init() { 8 | RegisterFunction("intersect", func_intersect) 9 | } 10 | 11 | func func_intersect(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 12 | var result []yaml.Node 13 | info := DefaultInfo() 14 | 15 | for i, l := range arguments { 16 | switch alist := l.(type) { 17 | case []yaml.Node: 18 | if result == nil { 19 | result = alist 20 | } else { 21 | newList := []yaml.Node{} 22 | for _, e := range result { 23 | found := false 24 | for _, n := range alist { 25 | r, _, _ := compareEquals(e.Value(), n.Value()) 26 | if r { 27 | found = true 28 | break 29 | } 30 | } 31 | if found { 32 | newList = append(newList, e) 33 | } 34 | } 35 | result = newList 36 | } 37 | case nil: 38 | default: 39 | return info.Error("intersect: argument %d: type '%s'(%s) cannot be intersected", i, ExpressionType(l)) 40 | } 41 | } 42 | 43 | return result, info, true 44 | } 45 | -------------------------------------------------------------------------------- /dynaml/join.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "strconv" 5 | "strings" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | func func_join(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 11 | info := DefaultInfo() 12 | 13 | if len(arguments) < 1 { 14 | return nil, info, false 15 | } 16 | 17 | args := make([]string, 0) 18 | for i, arg := range arguments { 19 | switch v := arg.(type) { 20 | case string: 21 | args = append(args, v) 22 | case int64: 23 | args = append(args, strconv.FormatInt(v, 10)) 24 | case bool: 25 | args = append(args, strconv.FormatBool(v)) 26 | case []yaml.Node: 27 | if i == 0 { 28 | return info.Error("first argument for join must be a string") 29 | } 30 | for _, elem := range v { 31 | switch e := elem.Value().(type) { 32 | case string: 33 | args = append(args, e) 34 | case int64: 35 | args = append(args, strconv.FormatInt(e, 10)) 36 | case bool: 37 | args = append(args, strconv.FormatBool(e)) 38 | default: 39 | return info.Error("elements of list(arg %d) to join must be simple values", i) 40 | } 41 | } 42 | case nil: 43 | default: 44 | return info.Error("argument %d to join must be simple value or list", i) 45 | } 46 | } 47 | 48 | return strings.Join(args[1:], args[0]), info, true 49 | } 50 | -------------------------------------------------------------------------------- /dynaml/json.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/mandelsoft/spiff/legacy/candiedyaml" 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | func func_as_json(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 11 | info := DefaultInfo() 12 | 13 | if len(arguments) != 1 { 14 | return info.Error("asjson takes exactly one argument") 15 | } 16 | 17 | result, err := yaml.ValueToJSON(arguments[0]) 18 | if err != nil { 19 | return info.Error("cannot jsonencode: %s", err) 20 | } 21 | return string(result), info, true 22 | } 23 | 24 | func func_as_yaml(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 25 | info := DefaultInfo() 26 | 27 | if len(arguments) != 1 { 28 | return info.Error("asyaml takes exactly one argument") 29 | } 30 | 31 | result, err := candiedyaml.Marshal(arguments[0]) 32 | if err != nil { 33 | return info.Error("cannot yamlencode: %s", err) 34 | } 35 | return string(result), info, true 36 | } 37 | 38 | func func_parse_yaml(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 39 | info := DefaultInfo() 40 | 41 | if len(arguments) < 1 || len(arguments) > 2 { 42 | return info.Error("parse takes one or two arguments") 43 | } 44 | 45 | str, ok := arguments[0].(string) 46 | if !ok { 47 | return info.Error("first argument for parse must be a string") 48 | } 49 | 50 | mode := "import" 51 | if len(arguments) > 1 { 52 | mode, ok = arguments[1].(string) 53 | if !ok { 54 | return info.Error("second argument for parse must be a string") 55 | } 56 | } 57 | 58 | name := strings.Join(binding.Path(), ".") 59 | return ParseData(name, []byte(str), mode, binding) 60 | } 61 | -------------------------------------------------------------------------------- /dynaml/keys.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | ) 6 | 7 | func func_keys(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 8 | info := DefaultInfo() 9 | 10 | if len(arguments) != 1 { 11 | return info.Error("one argument required for keys") 12 | } 13 | 14 | m, ok := arguments[0].(map[string]yaml.Node) 15 | if !ok { 16 | return info.Error("map argument required for keys") 17 | } 18 | 19 | result := []yaml.Node{} 20 | for _, k := range getSortedKeys(m) { 21 | result = append(result, NewNode(k, binding)) 22 | } 23 | return result, info, true 24 | } 25 | -------------------------------------------------------------------------------- /dynaml/length.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | ) 6 | 7 | func func_length(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 8 | var result interface{} 9 | info := DefaultInfo() 10 | 11 | if len(arguments) != 1 { 12 | return info.Error("length takes exactly 1 argument") 13 | } 14 | 15 | switch v := arguments[0].(type) { 16 | case []yaml.Node: 17 | result = len(v) 18 | case map[string]yaml.Node: 19 | result = len(v) 20 | case string: 21 | result = len(v) 22 | default: 23 | return info.Error("invalid type for function length") 24 | } 25 | return yaml.MassageType(result), info, true 26 | } 27 | -------------------------------------------------------------------------------- /dynaml/list.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | type ListExpr struct { 11 | Contents []Expression 12 | } 13 | 14 | func (e ListExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 15 | resolved := true 16 | 17 | values, info, ok := ResolveExpressionListOrPushEvaluation(&e.Contents, &resolved, nil, binding, false) 18 | 19 | if !ok { 20 | return nil, info, false 21 | } 22 | if !resolved { 23 | return e, info, true 24 | } 25 | 26 | nodes := []yaml.Node{} 27 | for i, _ := range values { 28 | nodes = append(nodes, NewNode(values[i], binding)) 29 | } 30 | return nodes, info, true 31 | } 32 | 33 | func (e ListExpr) String() string { 34 | vals := make([]string, len(e.Contents)) 35 | for i, e := range e.Contents { 36 | vals[i] = fmt.Sprintf("%s", e) 37 | } 38 | 39 | return fmt.Sprintf("[%s]", strings.Join(vals, ", ")) 40 | } 41 | -------------------------------------------------------------------------------- /dynaml/list_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | var _ = Describe("lists", func() { 11 | It("evaluates to an array of nodes", func() { 12 | expr := ListExpr{ 13 | []Expression{ 14 | IntegerExpr{1}, 15 | StringExpr{"two"}, 16 | }, 17 | } 18 | 19 | Expect(expr).To(EvaluateAs([]yaml.Node{NewNode(1, nil), NewNode("two", nil)}, FakeBinding{})) 20 | }) 21 | 22 | Context("when empty", func() { 23 | It("evaluates to an empty array", func() { 24 | Expect(ListExpr{}).To(EvaluateAs([]yaml.Node{}, FakeBinding{})) 25 | }) 26 | }) 27 | 28 | Context("when an entry does not resolve", func() { 29 | It("fails", func() { 30 | expr := ListExpr{ 31 | []Expression{ 32 | ReferenceExpr{Path: []string{"foo"}}, 33 | }, 34 | } 35 | 36 | Expect(expr).To(FailToEvaluate(FakeBinding{})) 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /dynaml/listexpansion.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import "fmt" 4 | 5 | type ListExpansion interface { 6 | Expression 7 | IsListExpansion() bool 8 | } 9 | 10 | type ListExpansionExpr struct { 11 | Expression 12 | } 13 | 14 | func (e ListExpansionExpr) String() string { 15 | return fmt.Sprintf("%s...", e.Expression) 16 | } 17 | 18 | func (e ListExpansionExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 19 | return e.Expression.Evaluate(binding, locally) 20 | } 21 | 22 | func (e ListExpansionExpr) IsListExpansion() bool { 23 | return true 24 | } 25 | 26 | func IsListExpansion(e Expression) bool { 27 | va, ok := e.(ListExpansion) 28 | return ok && va.IsListExpansion() 29 | } 30 | 31 | func KeepArgWrapper(e Expression, orig Expression) Expression { 32 | if va, ok := orig.(ListExpansion); ok && va.IsListExpansion() { 33 | if _, ok := e.(ListExpansion); !ok { 34 | return ListExpansionExpr{e} 35 | } 36 | } 37 | if na, ok := orig.(NameArgument); ok { 38 | return NameArgument{na.Name, e} 39 | } 40 | return e 41 | } 42 | -------------------------------------------------------------------------------- /dynaml/log_and.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/mandelsoft/spiff/debug" 7 | ) 8 | 9 | const ( 10 | OpAnd = "-and" 11 | ) 12 | 13 | type LogAndExpr struct { 14 | A Expression 15 | B Expression 16 | } 17 | 18 | func (e LogAndExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 19 | a, b, info, resolved, first_ok, all_ok := resolveLOperands(e.A, e.B, binding) 20 | if !first_ok { 21 | return nil, info, false 22 | } 23 | if !resolved { 24 | return e, info, true 25 | } 26 | debug.Debug("AND: %#v, %#v\n", a, b) 27 | inta, ok := a.(int64) 28 | if ok { 29 | if !all_ok { 30 | return nil, info, false 31 | } 32 | return inta & b.(int64), info, true 33 | } 34 | if !toBool(a) { 35 | return false, info, true 36 | } 37 | if !all_ok { 38 | return nil, info, false 39 | } 40 | return toBool(b), info, true 41 | } 42 | 43 | func (e LogAndExpr) String() string { 44 | return fmt.Sprintf("%s %s %s", e.A, OpAnd, e.B) 45 | } 46 | -------------------------------------------------------------------------------- /dynaml/lower.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import "strings" 4 | 5 | func func_lower(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 6 | return _modifystring("lower", strings.ToLower, arguments, binding) 7 | } 8 | 9 | func func_upper(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 10 | return _modifystring("upper", strings.ToUpper, arguments, binding) 11 | } 12 | 13 | func _modifystring(name string, mod func(string) string, arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 14 | 15 | info := DefaultInfo() 16 | 17 | if len(arguments) != 1 { 18 | return info.Error("%s requires one argument", name) 19 | } 20 | 21 | str, ok := arguments[0].(string) 22 | if !ok { 23 | return info.Error("first argument for %s must be a string", name) 24 | } 25 | 26 | return mod(str), info, true 27 | } 28 | -------------------------------------------------------------------------------- /dynaml/makemap.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | "strconv" 6 | ) 7 | 8 | func func_makemap(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 9 | info := DefaultInfo() 10 | 11 | result := make(map[string]yaml.Node) 12 | var key string 13 | 14 | if len(arguments) == 1 { 15 | list, ok := arguments[0].([]yaml.Node) 16 | if !ok { 17 | return info.Error("single argument flavor of mapentry requires a list") 18 | } 19 | for i, elem := range list { 20 | m, ok := elem.Value().(map[string]yaml.Node) 21 | if !ok { 22 | return info.Error("entry %d is no map entry", i) 23 | } 24 | k, ok := m["key"] 25 | if !ok { 26 | return info.Error("entry %d has no entry 'key'", i) 27 | } 28 | switch elem := k.Value().(type) { 29 | case string: 30 | key = elem 31 | case int64: 32 | key = strconv.FormatInt(elem, 10) 33 | case bool: 34 | key = strconv.FormatBool(elem) 35 | default: 36 | return info.Error("invalid type for 'key' value of entry %d", i) 37 | } 38 | v, ok := m["value"] 39 | if !ok { 40 | return info.Error("entry %d has no entry 'value'", i) 41 | } 42 | result[key] = v 43 | } 44 | } else if len(arguments)%2 == 0 { 45 | for i := 0; i < len(arguments); i += 2 { 46 | switch elem := arguments[i].(type) { 47 | case string: 48 | key = elem 49 | case int64: 50 | key = strconv.FormatInt(elem, 10) 51 | case bool: 52 | key = strconv.FormatBool(elem) 53 | default: 54 | return info.Error("invalid type for key value of arument pair %d", i/2) 55 | } 56 | 57 | result[key] = NewNode(arguments[i+1], binding) 58 | } 59 | } else { 60 | return info.Error("mapentry takes one or two arguments") 61 | } 62 | 63 | return result, info, true 64 | } 65 | -------------------------------------------------------------------------------- /dynaml/map.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | "github.com/mandelsoft/spiff/yaml" 6 | ) 7 | 8 | type CreateMapExpr struct { 9 | Assignments []Assignment 10 | } 11 | 12 | func (e CreateMapExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 13 | 14 | newMap := make(map[string]yaml.Node) 15 | info := DefaultInfo() 16 | resolved := true 17 | 18 | for _, a := range e.Assignments { 19 | key, info, ok := ResolveExpressionOrPushEvaluation(&a.Key, &resolved, &info, binding, locally) 20 | if !ok || !resolved { 21 | return e, info, ok 22 | } 23 | 24 | kstr, ok := key.(string) 25 | if !ok { 26 | return info.Error("assignment target must evaluate to string") 27 | } 28 | val, info, ok := ResolveExpressionOrPushEvaluation(&a.Value, &resolved, &info, binding, locally) 29 | if !ok || !resolved { 30 | return e, info, ok 31 | } 32 | newMap[kstr] = NewNode(val, binding) 33 | } 34 | return newMap, DefaultInfo(), true 35 | } 36 | 37 | func (e CreateMapExpr) String() string { 38 | result := "{" 39 | sep := " " 40 | for _, a := range e.Assignments { 41 | result += sep + a.String() 42 | sep = ", " 43 | } 44 | return result + " }" 45 | } 46 | 47 | type Assignment struct { 48 | Key Expression 49 | Value Expression 50 | } 51 | 52 | func (e Assignment) String() string { 53 | return fmt.Sprintf("%s = %s", e.Key, e.Value) 54 | } 55 | -------------------------------------------------------------------------------- /dynaml/map_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | var _ = Describe("empty", func() { 11 | It("evaluates to empty hash", func() { 12 | Expect(CreateMapExpr{}).To(EvaluateAs(make(map[string]yaml.Node), FakeBinding{})) 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /dynaml/mapmerge.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | "github.com/mandelsoft/spiff/yaml" 6 | ) 7 | 8 | func func_merge(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 9 | info := DefaultInfo() 10 | 11 | if len(arguments) < 1 { 12 | return info.Error("at least one argument required for merge function") 13 | } 14 | 15 | args := []yaml.Node{} 16 | 17 | if len(arguments) == 1 { 18 | l, ok := arguments[0].([]yaml.Node) 19 | if ok { 20 | for i, e := range l { 21 | m, err := getMap(i, e.Value()) 22 | if err != nil { 23 | return info.Error("merge: entry of list argument: %s", err) 24 | } 25 | args = append(args, yaml.NewNode(m, "dynaml")) 26 | } 27 | if len(args) == 0 { 28 | return info.Error("merge: no map found for merge") 29 | } 30 | } 31 | } 32 | if len(args) == 0 { 33 | for i, arg := range arguments { 34 | m, err := getMap(i, arg) 35 | if err != nil { 36 | return info.Error("merge: argument %s", err) 37 | } 38 | args = append(args, yaml.NewNode(m, "dynaml")) 39 | } 40 | } 41 | result, err := binding.Cascade(binding, args[0], false, args[1:]...) 42 | if err != nil { 43 | info.SetError("merging failed: %s", err) 44 | return nil, info, false 45 | } 46 | 47 | return result.Value(), info, true 48 | } 49 | 50 | func getMap(n int, arg interface{}) (map[string]yaml.Node, error) { 51 | temp, ok := arg.(TemplateValue) 52 | if ok { 53 | arg, ok := node_copy(temp.Prepared).Value().(map[string]yaml.Node) 54 | if !ok { 55 | return nil, fmt.Errorf("%d: template is not a map template", n+1) 56 | } 57 | return arg, nil 58 | } 59 | m, ok := arg.(map[string]yaml.Node) 60 | if ok { 61 | return m, nil 62 | } 63 | return nil, fmt.Errorf("%d: no map or map template, but %s", n+1, ExpressionType(arg)) 64 | } 65 | -------------------------------------------------------------------------------- /dynaml/mapping_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("mapping expressions", func() { 9 | It("prints mapping expression", func() { 10 | desc := MappingExpr{ 11 | ReferenceExpr{Path: []string{"list"}}, 12 | ConcatenationExpr{ 13 | ReferenceExpr{Path: []string{"x"}}, 14 | StringExpr{".*"}, 15 | }, 16 | MapToListContext, 17 | }.String() 18 | Expect(desc).To(Equal("map[list|x \".*\"]")) 19 | }) 20 | 21 | It("simplifies lambda mapping expression", func() { 22 | desc := MappingExpr{ 23 | ReferenceExpr{Path: []string{"list"}}, 24 | LambdaExpr{ 25 | []Parameter{Parameter{Name: "x"}}, 26 | false, 27 | ConcatenationExpr{ 28 | ReferenceExpr{Path: []string{"x"}}, 29 | StringExpr{".*"}, 30 | }, 31 | }, 32 | MapToListContext, 33 | }.String() 34 | Expect(desc).To(Equal("map[list|x|->x \".*\"]")) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /dynaml/md5crypt.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | "github.com/mandelsoft/spiff/dynaml/crypt" 6 | "strings" 7 | ) 8 | 9 | func func_md5crypt(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 10 | info := DefaultInfo() 11 | 12 | if len(arguments) != 1 { 13 | return info.Error("md5crypt takes one argument") 14 | } 15 | 16 | passwd, ok := arguments[0].(string) 17 | if !ok { 18 | return info.Error("first argument for md5crypt must be a string") 19 | } 20 | 21 | result := crypt.MD5Crypt([]byte(passwd), crypt.GenerateSALT(8), []byte(crypt.MD5_MAGIC)) 22 | 23 | return fmt.Sprintf("%s", result), info, true 24 | } 25 | 26 | func func_md5crypt_check(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 27 | info := DefaultInfo() 28 | 29 | if len(arguments) != 2 { 30 | return info.Error("md5crypt_check takes two arguments") 31 | } 32 | 33 | passwd, ok := arguments[0].(string) 34 | if !ok { 35 | return info.Error("first argument for md5crypt_check must be a string") 36 | } 37 | 38 | hash, ok := arguments[1].(string) 39 | if !ok { 40 | return info.Error("second argument for md5crypt_check must be a string") 41 | } 42 | 43 | parts := strings.Split(hash, "$") 44 | if len(parts) != 4 { 45 | return info.Error("invalid md5crypt hash: must contain three '$' characters") 46 | } 47 | check := crypt.MD5Crypt([]byte(passwd), []byte(parts[2]), []byte("$"+parts[1]+"$")) 48 | return string(check) == hash, info, true 49 | } 50 | -------------------------------------------------------------------------------- /dynaml/merge.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/mandelsoft/spiff/debug" 7 | ) 8 | 9 | type MergeExpr struct { 10 | Path []string 11 | Redirect bool 12 | Replace bool 13 | Required bool 14 | None bool 15 | KeyName string 16 | } 17 | 18 | func (e MergeExpr) IncludesDirectMerge() bool { 19 | return !e.Redirect 20 | } 21 | 22 | func (e MergeExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 23 | var info EvaluationInfo 24 | info.KeyName = e.KeyName 25 | debug.Debug("/// lookup %v\n", e.Path) 26 | if e.Redirect { 27 | info.RedirectPath = e.Path 28 | } 29 | // if len(e.Path) == 0 { 30 | if e.None { 31 | info.Merged = true 32 | return nil, info, true 33 | } 34 | node, ok := binding.FindInStubs(e.Path) 35 | if ok { 36 | info.Replace = e.Replace 37 | info.Merged = true 38 | info.Source = node.SourceName() 39 | info.NodeFlags = node.Flags() 40 | return node.Value(), info, ok 41 | } else { 42 | return info.Error("'%s' not found in any stub", strings.Join(e.Path, ".")) 43 | } 44 | } 45 | 46 | func (e MergeExpr) String() string { 47 | rep := "" 48 | if e.Replace { 49 | rep = " replace" 50 | } 51 | 52 | if e.KeyName != "" { 53 | rep += " on " + e.KeyName 54 | } 55 | if e.Required && !e.Redirect && rep != "" { 56 | rep = " required" 57 | } 58 | if e.Redirect { 59 | return "merge" + rep + " " + strings.Join(e.Path, ".") 60 | } 61 | return "merge" + rep 62 | } 63 | -------------------------------------------------------------------------------- /dynaml/merge_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | var _ = Describe("merges", func() { 11 | Context("when the equivalent node is found", func() { 12 | It("evaluates to the merged node", func() { 13 | referencedNode := IntegerExpr{42} 14 | 15 | expr := MergeExpr{ 16 | Path: []string{"foo", "bar"}, 17 | Redirect: false, 18 | Replace: false, 19 | Required: false, 20 | KeyName: "", 21 | } 22 | 23 | binding := FakeBinding{ 24 | FoundInStubs: map[string]yaml.Node{ 25 | "foo.bar": NewNode(referencedNode, nil), 26 | }, 27 | } 28 | 29 | Expect(expr).To(EvaluateAs(referencedNode, binding)) 30 | }) 31 | }) 32 | 33 | Context("when the equivalent node is NOT found", func() { 34 | It("fails", func() { 35 | referencedNode := IntegerExpr{42} 36 | 37 | expr := MergeExpr{ 38 | Path: []string{"foo", "bar", "baz"}, 39 | Redirect: false, 40 | Replace: false, 41 | Required: false, 42 | KeyName: "", 43 | } 44 | 45 | binding := FakeBinding{ 46 | FoundInStubs: map[string]yaml.Node{ 47 | "foo.bar": NewNode(referencedNode, nil), 48 | }, 49 | } 50 | 51 | Expect(expr).To(FailToEvaluate(binding)) 52 | }) 53 | }) 54 | }) 55 | -------------------------------------------------------------------------------- /dynaml/mkdir.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | func init() { 11 | RegisterFunction("mkdir", func_mkdir) 12 | } 13 | 14 | func func_mkdir(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 15 | var err error 16 | info := DefaultInfo() 17 | 18 | wopt := WriteOpts{ 19 | Permissions: 0755, 20 | } 21 | 22 | if !binding.GetState().FileAccessAllowed() { 23 | return info.DenyOSOperation("mkdir") 24 | } 25 | 26 | if len(arguments) < 1 || len(arguments) > 2 { 27 | return info.Error("mkdir requires one or two arguments") 28 | } 29 | path, _, err := getArg(0, arguments[0], wopt, false) 30 | if err != nil { 31 | comps, ok := arguments[0].([]yaml.Node) 32 | if !ok { 33 | return info.Error("path argument must be a non-empty string") 34 | } 35 | for i, a := range comps { 36 | comp, _, err := getArg(i, a.Value(), wopt, false) 37 | if err != nil || comp == "" { 38 | return info.Error("path component %d must be a non-empty string", i) 39 | } 40 | path = filepath.Join(path, comp) 41 | } 42 | } 43 | if path == "" { 44 | return info.Error("path argument must not be empty") 45 | } 46 | 47 | if len(arguments) == 2 { 48 | wopt, err = getWriteOptions(arguments[1], wopt, true) 49 | } 50 | if err == nil { 51 | err = binding.GetState().FileSystem().MkdirAll(path, os.FileMode(wopt.Permissions)) 52 | if err == nil { 53 | return path, info, true 54 | } 55 | } 56 | return info.Error("cannot create directory %q: %s", path, err) 57 | } 58 | -------------------------------------------------------------------------------- /dynaml/modulo.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type ModuloExpr struct { 8 | A Expression 9 | B Expression 10 | } 11 | 12 | func (e ModuloExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 13 | resolved := true 14 | 15 | aint, info, ok := ResolveIntegerExpressionOrPushEvaluation(&e.A, &resolved, nil, binding, false) 16 | if !ok { 17 | return nil, info, false 18 | } 19 | 20 | bint, info, ok := ResolveIntegerExpressionOrPushEvaluation(&e.B, &resolved, &info, binding, false) 21 | if !ok { 22 | return nil, info, false 23 | } 24 | 25 | if !resolved { 26 | return e, info, true 27 | } 28 | 29 | if bint == 0 { 30 | return info.Error("division by zero") 31 | } 32 | return aint % bint, info, true 33 | } 34 | 35 | func (e ModuloExpr) String() string { 36 | return fmt.Sprintf("%s %% %s", e.A, e.B) 37 | } 38 | -------------------------------------------------------------------------------- /dynaml/modulo_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("modulo", func() { 9 | It("multiplies both numbers", func() { 10 | expr := ModuloExpr{ 11 | IntegerExpr{16}, 12 | IntegerExpr{3}, 13 | } 14 | 15 | Expect(expr).To(EvaluateAs(1, FakeBinding{})) 16 | }) 17 | 18 | Context("when the left-hand side is not an integer", func() { 19 | It("fails", func() { 20 | expr := ModuloExpr{ 21 | StringExpr{"lol"}, 22 | IntegerExpr{2}, 23 | } 24 | 25 | Expect(expr).To(FailToEvaluate(FakeBinding{})) 26 | }) 27 | }) 28 | 29 | Context("when the right-hand side is not an integer", func() { 30 | It("fails", func() { 31 | expr := ModuloExpr{ 32 | IntegerExpr{2}, 33 | StringExpr{"lol"}, 34 | } 35 | 36 | Expect(expr).To(FailToEvaluate(FakeBinding{})) 37 | }) 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /dynaml/multiplication.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | type MultiplicationExpr struct { 9 | A Expression 10 | B Expression 11 | } 12 | 13 | func (e MultiplicationExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 14 | resolved := true 15 | 16 | a, info, ok := ResolveExpressionOrPushEvaluation(&e.A, &resolved, nil, binding, false) 17 | if !ok { 18 | return nil, info, false 19 | } 20 | 21 | b, info, ok := ResolveExpressionOrPushEvaluation(&e.B, &resolved, &info, binding, false) 22 | if !ok { 23 | return nil, info, false 24 | } 25 | 26 | if !resolved { 27 | return e, info, true 28 | } 29 | 30 | str, ok := a.(string) 31 | if ok { 32 | ip, cidr, err := net.ParseCIDR(str) 33 | if err != nil { 34 | return info.Error("first argument of multiplication must be CIDR or number: %s", err) 35 | } 36 | ones, _ := cidr.Mask.Size() 37 | size := int64(1 << (32 - uint32(ones))) 38 | 39 | bint, ok := b.(int64) 40 | if !ok { 41 | return info.Error("CIDR multiplication requires an integer argument") 42 | } 43 | 44 | ip = IPAdd(ip.Mask(cidr.Mask), size*bint) 45 | return (&net.IPNet{ip, cidr.Mask}).String(), info, true 46 | } 47 | 48 | a, b, err := NumberOperands(a, b) 49 | if err != nil { 50 | return info.Error("non-CIDR multiplication requires number arguments") 51 | } 52 | if _, ok := a.(int64); ok { 53 | return a.(int64) * b.(int64), info, true 54 | } 55 | return a.(float64) * b.(float64), info, true 56 | } 57 | 58 | func (e MultiplicationExpr) String() string { 59 | return fmt.Sprintf("%s * %s", e.A, e.B) 60 | } 61 | -------------------------------------------------------------------------------- /dynaml/nil.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | type NilExpr struct{} 4 | 5 | func (e NilExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 6 | return nil, DefaultInfo(), true 7 | } 8 | 9 | func (e NilExpr) String() string { 10 | return "nil" 11 | } 12 | -------------------------------------------------------------------------------- /dynaml/nil_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("nil", func() { 9 | It("evaluates to nil", func() { 10 | Expect(NilExpr{}).To(EvaluateAs(nil, FakeBinding{})) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /dynaml/node.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/mandelsoft/spiff/yaml" 7 | ) 8 | 9 | func value(node yaml.Node) interface{} { 10 | if node == nil { 11 | return nil 12 | } 13 | return node.Value() 14 | } 15 | 16 | func NewNode(val interface{}, src SourceProvider) yaml.Node { 17 | source := "" 18 | 19 | if src == nil || len(src.SourceName()) == 0 { 20 | source = "dynaml" 21 | } else { 22 | if !strings.HasPrefix(src.SourceName(), "dynaml@") { 23 | source = "dynaml@" + src.SourceName() 24 | } else { 25 | source = src.SourceName() 26 | } 27 | } 28 | 29 | return yaml.NewNode(val, source) 30 | } 31 | 32 | func IssueNode(env Binding, preservePath bool, node yaml.Node, error bool, failed bool, issue yaml.Issue) yaml.Node { 33 | if node.Issue().OrigPath != nil { 34 | issue.OrigPath = node.Issue().OrigPath 35 | } else { 36 | if env != nil && preservePath { 37 | issue.OrigPath = env.Path() 38 | } 39 | } 40 | return yaml.IssueNode(node, error, failed, issue) 41 | } 42 | -------------------------------------------------------------------------------- /dynaml/not.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/mandelsoft/spiff/debug" 7 | ) 8 | 9 | type NotExpr struct { 10 | Expr Expression 11 | } 12 | 13 | func (e NotExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 14 | resolved := true 15 | v, info, ok := ResolveExpressionOrPushEvaluation(&e.Expr, &resolved, nil, binding, false) 16 | if !ok { 17 | return nil, info, false 18 | } 19 | if !resolved { 20 | return e, info, true 21 | } 22 | 23 | debug.Debug("NOT: %#v\n", v) 24 | return !toBool(v), info, true 25 | } 26 | 27 | func (e NotExpr) String() string { 28 | return fmt.Sprintf("!(%s)", e.Expr) 29 | } 30 | -------------------------------------------------------------------------------- /dynaml/not_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("logical and", func() { 9 | Context("on boolean", func() { 10 | It("not true = false", func() { 11 | expr := NotExpr{ 12 | BooleanExpr{true}, 13 | } 14 | 15 | Expect(expr).To(EvaluateAs(false, FakeBinding{})) 16 | }) 17 | 18 | It("not false = true", func() { 19 | expr := NotExpr{ 20 | BooleanExpr{false}, 21 | } 22 | 23 | Expect(expr).To(EvaluateAs(true, FakeBinding{})) 24 | }) 25 | }) 26 | 27 | Context("on integer", func() { 28 | It("!=0 returns false", func() { 29 | expr := NotExpr{ 30 | IntegerExpr{6}, 31 | } 32 | 33 | Expect(expr).To(EvaluateAs(false, FakeBinding{})) 34 | }) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /dynaml/or.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type OrExpr struct { 9 | A Expression 10 | B Expression 11 | } 12 | 13 | func (e OrExpr) IncludesDirectMerge() bool { 14 | return IncludesDirectMerge(e.A) || IncludesDirectMerge(e.B) 15 | } 16 | 17 | func (e OrExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 18 | a, infoa, ok := e.A.Evaluate(binding, false) 19 | if ok && !infoa.Undefined { 20 | if reflect.DeepEqual(a, e.A) { 21 | // fmt.Printf("================== %s\n", e.A) 22 | return e, infoa, true 23 | } 24 | // fmt.Printf("++++++++++++++++++ %s\n", e.A) 25 | if IsExpression(a) { 26 | return e, infoa, true 27 | } 28 | return a, infoa, true 29 | } 30 | // fmt.Printf("------------------ %t %t %s\n", ok, infoa.Undefined, e.A) 31 | b, infob, ok := e.B.Evaluate(binding, false) 32 | info := infoa.CleanError().Join(infob) 33 | info.Undefined = infob.Undefined 34 | return b, info, ok 35 | } 36 | 37 | func (e OrExpr) String() string { 38 | return fmt.Sprintf("%s || %s", e.A, e.B) 39 | } 40 | -------------------------------------------------------------------------------- /dynaml/prefer.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type PreferExpr struct { 8 | expression Expression 9 | } 10 | 11 | func (e PreferExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 12 | 13 | val, info, ok := e.expression.Evaluate(binding, locally) 14 | info.Preferred = true 15 | return val, info, ok 16 | } 17 | 18 | func (e PreferExpr) String() string { 19 | return fmt.Sprintf("prefer %s", e.expression) 20 | } 21 | -------------------------------------------------------------------------------- /dynaml/qualified_expression.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/mandelsoft/spiff/debug" 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | type QualifiedExpr struct { 11 | Expression Expression 12 | Reference ReferenceExpr 13 | } 14 | 15 | func (e QualifiedExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 16 | root, info, ok := e.Expression.Evaluate(binding, locally) 17 | if !ok { 18 | debug.Debug("base of qualified expression failed: %s\n", info.Issue.Issue) 19 | return nil, info, false 20 | } 21 | locally = locally || info.Raw 22 | if !isLocallyResolvedValue(root, binding) { 23 | debug.Debug("not locally resolved: %v\n", root) 24 | if root != nil { 25 | if ex, ok := root.(Expression); ok { 26 | return QualifiedExpr{ex, e.Reference}, info, true 27 | } 28 | } 29 | return e, info, true 30 | } 31 | if !locally && !isResolvedValue(root, binding) { 32 | debug.Debug("not resoved: %v\n", root) 33 | return e, info, true 34 | } 35 | 36 | debug.Debug("qualified reference (%t): %v\n", locally, e.Reference.Path) 37 | return e.Reference.find(func(end int, path []string) (yaml.Node, bool) { 38 | return yaml.Find(NewNode(root, nil), binding.GetFeatures(), path[:end+1]...) 39 | }, binding, locally) 40 | } 41 | 42 | func (e QualifiedExpr) String() string { 43 | return fmt.Sprintf("(%s).%s", e.Expression, e.Reference) 44 | } 45 | -------------------------------------------------------------------------------- /dynaml/range_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | var _ = Describe("integer range", func() { 11 | It("evaluates an increasing range", func() { 12 | expr := RangeExpr{ 13 | IntegerExpr{1}, 14 | IntegerExpr{3}, 15 | } 16 | 17 | Expect(expr).To(EvaluateAs([]yaml.Node{NewNode(1, nil), NewNode(2, nil), NewNode(3, nil)}, FakeBinding{})) 18 | }) 19 | 20 | It("evaluates a decreasing range", func() { 21 | expr := RangeExpr{ 22 | IntegerExpr{1}, 23 | IntegerExpr{-1}, 24 | } 25 | 26 | Expect(expr).To(EvaluateAs([]yaml.Node{NewNode(1, nil), NewNode(0, nil), NewNode(-1, nil)}, FakeBinding{})) 27 | }) 28 | 29 | It("evaluates a single element range", func() { 30 | expr := RangeExpr{ 31 | IntegerExpr{1}, 32 | IntegerExpr{1}, 33 | } 34 | 35 | Expect(expr).To(EvaluateAs([]yaml.Node{NewNode(1, nil)}, FakeBinding{})) 36 | }) 37 | 38 | It("evaluates to failure", func() { 39 | expr := RangeExpr{ 40 | StringExpr{"foo"}, 41 | IntegerExpr{1}, 42 | } 43 | 44 | Expect(expr).To(FailToEvaluate(FakeBinding{})) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /dynaml/reference_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | var _ = Describe("references", func() { 11 | Context("when the reference is found", func() { 12 | It("evaluates to the referenced node", func() { 13 | expr := ReferenceExpr{Path: []string{"foo", "bar"}} 14 | 15 | binding := FakeBinding{ 16 | FoundReferences: map[string]yaml.Node{ 17 | "foo": NewNode(nil, nil), 18 | "foo.bar": NewNode(42, nil), 19 | }, 20 | } 21 | 22 | Expect(expr).To(EvaluateAs(42, binding)) 23 | }) 24 | 25 | Context("and it refers to another expression", func() { 26 | It("returns itself so the referred node can evaluate first", func() { 27 | expr := ReferenceExpr{Path: []string{"foo", "bar"}} 28 | 29 | binding := FakeBinding{ 30 | FoundReferences: map[string]yaml.Node{ 31 | "foo": NewNode(MergeExpr{}, nil), 32 | }, 33 | } 34 | 35 | Expect(expr).To(EvaluateAs(expr, binding)) 36 | }) 37 | }) 38 | }) 39 | 40 | Context("when the reference is NOT found", func() { 41 | It("fails", func() { 42 | expr := ReferenceExpr{Path: []string{"foo", "bar", "baz"}} 43 | 44 | binding := FakeBinding{} 45 | 46 | Expect(expr).To(FailToEvaluate(binding)) 47 | }) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /dynaml/registry.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | type Registry interface { 4 | LookupFunction(name string) Function 5 | 6 | LookupControl(name string) (*Control, bool) 7 | IsTemplateControlOption(name string) bool 8 | 9 | WithFunctions(Functions) Registry 10 | WithControls(Controls) Registry 11 | } 12 | 13 | type registry struct { 14 | functions Functions 15 | controls Controls 16 | } 17 | 18 | func (r *registry) WithFunctions(f Functions) Registry { 19 | if r == nil { 20 | return ®istry{functions: f} 21 | } 22 | return ®istry{ 23 | functions: f, 24 | controls: r.controls, 25 | } 26 | } 27 | 28 | func (r *registry) WithControls(c Controls) Registry { 29 | if r == nil { 30 | return ®istry{controls: c} 31 | } 32 | return ®istry{ 33 | functions: r.functions, 34 | controls: c, 35 | } 36 | } 37 | 38 | func (r *registry) LookupFunction(name string) Function { 39 | if r == nil || r.functions == nil { 40 | return function_registry.LookupFunction(name) 41 | } 42 | return r.functions.LookupFunction(name) 43 | } 44 | 45 | func (r *registry) LookupControl(name string) (*Control, bool) { 46 | if r == nil || r.controls == nil { 47 | return control_registry.LookupControl(name) 48 | } 49 | return r.controls.LookupControl(name) 50 | } 51 | 52 | func (r *registry) IsTemplateControlOption(name string) bool { 53 | if r == nil || r.controls == nil { 54 | return control_registry.IsTemplateControlOption(name) 55 | } 56 | return r.controls.IsTemplateControlOption(name) 57 | } 58 | 59 | func DefaultRegistry() Registry { 60 | var r *registry 61 | return r // standard behaviour support on nil pointer inter 62 | } 63 | -------------------------------------------------------------------------------- /dynaml/require.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | func (e CallExpr) require(binding Binding) (interface{}, EvaluationInfo, bool) { 4 | info := DefaultInfo() 5 | if len(e.Arguments) != 1 { 6 | return info.Error("one argument expected for 'require'") 7 | } 8 | pushed := e.Arguments[0] 9 | ok := true 10 | resolved := true 11 | 12 | val, _, ok := ResolveExpressionOrPushEvaluation(&pushed, &resolved, nil, binding, true) 13 | if !resolved { 14 | return e, info, true 15 | } 16 | 17 | if !ok || val == nil { 18 | return info.Error("required expression %q undefined", e.Arguments[0]) 19 | } 20 | 21 | return val, info, val != nil 22 | } 23 | -------------------------------------------------------------------------------- /dynaml/reverse.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | ) 6 | 7 | func init() { 8 | RegisterFunction("reverse", func_reverse) 9 | } 10 | 11 | func func_reverse(arguments []interface{}, binding Binding) (result interface{}, info EvaluationInfo, ok bool) { 12 | info = DefaultInfo() 13 | 14 | if len(arguments) != 1 { 15 | return info.Error("reverse takes one list argument") 16 | } 17 | 18 | list, ok := arguments[0].([]yaml.Node) 19 | if !ok { 20 | return info.Error("argument for reverse must be a list") 21 | } 22 | 23 | max := len(list) - 1 24 | 25 | for i := 0; i < (max+1)/2; i++ { 26 | list[i], list[max-i] = list[max-i], list[i] 27 | } 28 | return list, info, true 29 | } 30 | -------------------------------------------------------------------------------- /dynaml/scope.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | "github.com/mandelsoft/spiff/debug" 6 | "github.com/mandelsoft/spiff/yaml" 7 | ) 8 | 9 | type ScopeExpr struct { 10 | CreateMapExpr 11 | E Expression 12 | } 13 | 14 | func (e ScopeExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 15 | local, info, ok := e.CreateMapExpr.Evaluate(binding, locally) 16 | 17 | if !ok { 18 | return local, info, ok 19 | } 20 | 21 | if _, ok := local.(Expression); ok { 22 | return e, info, ok 23 | } 24 | b := local.(map[string]yaml.Node) 25 | binding = binding.WithLocalScope(b) 26 | debug.Debug("SCOPE: %s\n", binding) 27 | local, info, ok = e.E.Evaluate(binding, locally) 28 | if ok && IsExpression(local) { 29 | return e, info, ok 30 | } 31 | return local, info, ok 32 | } 33 | 34 | func (e ScopeExpr) String() string { 35 | result := "(" 36 | sep := "" 37 | for _, a := range e.Assignments { 38 | result += sep + a.String() 39 | sep = ", " 40 | } 41 | return fmt.Sprintf("%s ) %s", result, e.E) 42 | } 43 | -------------------------------------------------------------------------------- /dynaml/semver/compare.go: -------------------------------------------------------------------------------- 1 | package semver 2 | 3 | import ( 4 | . "github.com/mandelsoft/spiff/dynaml" 5 | ) 6 | 7 | const F_Compare = "semvercmp" 8 | 9 | func init() { 10 | RegisterFunction(F_Compare, func_compare) 11 | } 12 | 13 | func func_compare(args []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 14 | info := DefaultInfo() 15 | if len(args) != 2 { 16 | return info.Error("%s requires two arguments", F_Compare) 17 | } 18 | v1, info := parse(F_Compare, args[0]) 19 | if v1 == nil { 20 | return nil, info, false 21 | } 22 | v2, info := parse(F_Compare, args[1]) 23 | if v2 == nil { 24 | return nil, info, false 25 | } 26 | return v1.Compare(v2), info, true 27 | } 28 | -------------------------------------------------------------------------------- /dynaml/semver/match.go: -------------------------------------------------------------------------------- 1 | package semver 2 | 3 | import ( 4 | . "github.com/mandelsoft/spiff/dynaml" 5 | ) 6 | 7 | const F_Match = "semvermatch" 8 | 9 | func init() { 10 | RegisterFunction(F_Match, func_match) 11 | } 12 | 13 | func func_match(args []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 14 | info := DefaultInfo() 15 | 16 | if len(args) == 0 { 17 | return info.Error("%s requires at least one arguments", F_Match) 18 | } 19 | 20 | ok, _, err, _ := validate(F_Match, false, args[0], binding, args[1:]...) 21 | if err != nil { 22 | return info.Error("%s", err) 23 | } 24 | return ok, info, true 25 | } 26 | -------------------------------------------------------------------------------- /dynaml/semver/mod.go: -------------------------------------------------------------------------------- 1 | package semver 2 | 3 | import ( 4 | "github.com/Masterminds/semver/v3" 5 | 6 | . "github.com/mandelsoft/spiff/dynaml" 7 | ) 8 | 9 | const F_IncMajor = "semverincmajor" 10 | const F_IncMinor = "semverincminor" 11 | const F_IncPatch = "semverincpatch" 12 | 13 | func init() { 14 | RegisterFunction(F_IncMajor, func_incmajor) 15 | RegisterFunction(F_IncMinor, func_incminor) 16 | RegisterFunction(F_IncPatch, func_incpatch) 17 | } 18 | 19 | func func_incmajor(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 20 | return func_semver(F_IncMajor, func(v *semver.Version) interface{} { r := v.IncMajor(); return r.Original() }, arguments, binding) 21 | } 22 | 23 | func func_incminor(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 24 | return func_semver(F_IncMinor, func(v *semver.Version) interface{} { r := v.IncMinor(); return r.Original() }, arguments, binding) 25 | } 26 | 27 | func func_incpatch(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 28 | return func_semver(F_IncPatch, func(v *semver.Version) interface{} { r := v.IncPatch(); return r.Original() }, arguments, binding) 29 | } 30 | -------------------------------------------------------------------------------- /dynaml/semver/sort.go: -------------------------------------------------------------------------------- 1 | package semver 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/Masterminds/semver/v3" 7 | 8 | . "github.com/mandelsoft/spiff/dynaml" 9 | "github.com/mandelsoft/spiff/yaml" 10 | ) 11 | 12 | const F_Sort = "semversort" 13 | 14 | func init() { 15 | RegisterFunction(F_Sort, func_sort) 16 | } 17 | 18 | func func_sort(args []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 19 | info := DefaultInfo() 20 | 21 | if len(args) == 1 { 22 | if a, ok := args[0].([]yaml.Node); ok { 23 | args = make([]interface{}, len(a)) 24 | for i, v := range a { 25 | args[i] = v.Value() 26 | } 27 | } 28 | } 29 | versions := make([]*semver.Version, len(args)) 30 | 31 | for i, a := range args { 32 | v, info := parse(F_Compare, a) 33 | if v == nil { 34 | return nil, info, false 35 | } 36 | versions[i] = v 37 | } 38 | sort.Sort(semver.Collection(versions)) 39 | 40 | val := make([]yaml.Node, len(args)) 41 | for i, v := range versions { 42 | val[i] = yaml.NewNode(v.Original(), binding.SourceName()) 43 | } 44 | return val, info, true 45 | } 46 | -------------------------------------------------------------------------------- /dynaml/string.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type StringExpr struct { 8 | Value string 9 | } 10 | 11 | func (e StringExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 12 | return e.Value, DefaultInfo(), true 13 | } 14 | 15 | func (e StringExpr) String() string { 16 | return fmt.Sprintf("%q", e.Value) 17 | } 18 | -------------------------------------------------------------------------------- /dynaml/string_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("integers", func() { 9 | It("evaluates to an int", func() { 10 | Expect(StringExpr{"foo"}).To(EvaluateAs("foo", FakeBinding{})) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /dynaml/stub.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/mandelsoft/spiff/yaml" 7 | ) 8 | 9 | func (e CallExpr) stub(binding Binding) (interface{}, EvaluationInfo, bool) { 10 | info := DefaultInfo() 11 | var arg []string 12 | 13 | switch len(e.Arguments) { 14 | case 0: 15 | arg = binding.Path() 16 | case 1: 17 | pushed := e.Arguments[0] 18 | ref, ok := pushed.(ReferenceExpr) 19 | if !ok { 20 | resolved := true 21 | val, info, ok := ResolveExpressionOrPushEvaluation(&pushed, &resolved, nil, binding, true) 22 | if !resolved { 23 | return e, info, true 24 | } 25 | 26 | if !ok { 27 | return val, info, ok 28 | } else { 29 | switch v := val.(type) { 30 | case string: 31 | arg = PathComponents(v, true) 32 | case []yaml.Node: 33 | for _, n := range v { 34 | str, ok := n.Value().(string) 35 | if !ok { 36 | return info.Error("stub() requires a string entries in list") 37 | } 38 | arg = append(arg, str) 39 | } 40 | default: 41 | if !info.Undefined { 42 | return info.Error("stub() requires a string or reference argument") 43 | } 44 | arg = binding.Path() 45 | } 46 | } 47 | } else { 48 | arg = ref.Path 49 | } 50 | 51 | default: 52 | return info.Error("a maximum of one argument expected for 'stub'") 53 | } 54 | 55 | stub, ok := binding.FindInStubs(arg) 56 | if !ok { 57 | return info.Error("'%s' not found in any stub", strings.Join(arg, ".")) 58 | } 59 | return stub.Value(), info, ok 60 | } 61 | -------------------------------------------------------------------------------- /dynaml/substr.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import () 4 | 5 | func func_substr(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 6 | info := DefaultInfo() 7 | 8 | if len(arguments) > 3 || len(arguments) < 2 { 9 | return info.Error("substr takes two to three arguments") 10 | } 11 | 12 | str, ok := arguments[0].(string) 13 | if !ok { 14 | return info.Error("first argument for substr must be a string") 15 | } 16 | start, ok := arguments[1].(int64) 17 | if !ok { 18 | return info.Error("second argument for substr must be an integer") 19 | } 20 | if start < 0 { 21 | start = int64(len(str)) + start 22 | } 23 | var end int64 = int64(len(str)) 24 | if len(arguments) >= 3 { 25 | end, ok = arguments[2].(int64) 26 | if !ok { 27 | return info.Error("third argument for substr must be an integer") 28 | } 29 | if end < 0 { 30 | end = int64(len(str)) + end 31 | } 32 | } 33 | 34 | if int64(len(str)) < end { 35 | return info.Error("substr effective end index (%d) exceeds string length (%d)", end, len(str)) 36 | } 37 | if start < 0 { 38 | return info.Error("negative substr effective start index (%d)", start) 39 | } 40 | if start > end { 41 | return info.Error("substr start index (%d) aftsre end index (%d) ", start, end) 42 | } 43 | 44 | return str[start:end], info, true 45 | } 46 | -------------------------------------------------------------------------------- /dynaml/sum_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | ) 7 | 8 | var _ = Describe("sum expressions", func() { 9 | It("prints sum expression", func() { 10 | desc := SumExpr{ 11 | ReferenceExpr{Path: []string{"list"}}, 12 | IntegerExpr{0}, 13 | ConcatenationExpr{ 14 | ReferenceExpr{Path: []string{"x"}}, 15 | StringExpr{".*"}, 16 | }, 17 | }.String() 18 | Expect(desc).To(Equal("sum[list|0|x \".*\"]")) 19 | }) 20 | 21 | It("simplifies lambda sum expression", func() { 22 | desc := SumExpr{ 23 | ReferenceExpr{Path: []string{"list"}}, 24 | IntegerExpr{0}, 25 | LambdaExpr{ 26 | []Parameter{Parameter{Name: "x"}}, 27 | false, 28 | ConcatenationExpr{ 29 | ReferenceExpr{Path: []string{"x"}}, 30 | StringExpr{".*"}, 31 | }, 32 | }, 33 | }.String() 34 | Expect(desc).To(Equal("sum[list|0|x|->x \".*\"]")) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /dynaml/tagdef.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | const F_TagDef = "tagdef" 11 | 12 | func init() { 13 | RegisterFunction(F_TagDef, func_tagdef) 14 | } 15 | 16 | func func_tagdef(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 17 | info := DefaultInfo() 18 | ttype := "local" 19 | 20 | if len(arguments) < 2 || len(arguments) > 3 { 21 | return info.Error("two or three arguments expected for %s", F_TagDef) 22 | } 23 | name, ok := arguments[0].(string) 24 | if !ok { 25 | return info.Error("name argument for %s must be a string", F_TagDef) 26 | } 27 | if strings.HasPrefix(name, "*") && len(arguments) == 2 { 28 | name = name[1:] 29 | ttype = "global" 30 | } 31 | if err := CheckTagName(name); err != nil { 32 | return info.Error("invalid tag name %q for %s: %s", name, F_TagDef, err) 33 | } 34 | value := yaml.NewNode(arguments[1], fmt.Sprintf("tagdef(%s)", binding.Path())) 35 | if len(arguments) == 3 { 36 | str, ok := arguments[2].(string) 37 | if !ok { 38 | return info.Error("type argument for %s must be a string (local or global)", F_TagDef) 39 | } 40 | ttype = str 41 | } 42 | var scope TagScope 43 | switch ttype { 44 | case "local": 45 | scope = TAG_SCOPE_STREAM 46 | case "global": 47 | scope = TAG_SCOPE_GLOBAL 48 | default: 49 | return info.Error("invalid scope argument %q for %s (must be local or global)", ttype, F_TagDef) 50 | } 51 | scope |= TAG_LOCAL 52 | err := binding.GetState().SetTag(name, value, binding.Path(), scope) 53 | if err != nil { 54 | return info.Error("%s", err) 55 | } 56 | return arguments[1], info, true 57 | } 58 | -------------------------------------------------------------------------------- /dynaml/tempfile.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func func_tempfile(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 8 | var err error 9 | 10 | info := DefaultInfo() 11 | 12 | if !binding.GetState().FileAccessAllowed() { 13 | return info.DenyOSOperation("tempfile") 14 | } 15 | 16 | if len(arguments) < 1 || len(arguments) > 2 { 17 | return info.Error("temp_file requires exactly one or two arguments") 18 | } 19 | 20 | wopt := WriteOpts{ 21 | Permissions: 0644, 22 | } 23 | if len(arguments) == 2 { 24 | wopt, err = getWriteOptions(arguments[1], wopt, false) 25 | } 26 | if err != nil { 27 | return info.Error("cannot create temporary file: %s", err) 28 | } 29 | _, _, data, _ := getData("", wopt, 0, arguments[0], true) 30 | 31 | name, err := binding.GetTempName(data) 32 | if err != nil { 33 | return info.Error("cannot create temporary file: %s", err) 34 | } 35 | 36 | err = binding.GetState().FileSystem().WriteFile(name, []byte(data), os.FileMode(wopt.Permissions)) 37 | if err != nil { 38 | return info.Error("cannot write file: %s", err) 39 | } 40 | 41 | return name, info, true 42 | } 43 | -------------------------------------------------------------------------------- /dynaml/trim.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/mandelsoft/spiff/yaml" 7 | ) 8 | 9 | func func_trim(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 10 | info := DefaultInfo() 11 | ok := true 12 | 13 | if len(arguments) > 2 { 14 | return info.Error("split takes one or two arguments") 15 | } 16 | 17 | cutset := " \t" 18 | if len(arguments) == 2 { 19 | cutset, ok = arguments[1].(string) 20 | if !ok { 21 | return info.Error("second argument of split must be a string") 22 | } 23 | } 24 | var result interface{} 25 | switch v := arguments[0].(type) { 26 | case string: 27 | result = strings.Trim(v, cutset) 28 | case []yaml.Node: 29 | list := make([]yaml.Node, len(v)) 30 | for i, e := range v { 31 | t, ok := e.Value().(string) 32 | if !ok { 33 | return info.Error("list elements must be strings to be trimmed") 34 | } 35 | t = strings.Trim(t, cutset) 36 | list[i] = NewNode(t, binding) 37 | } 38 | result = list 39 | default: 40 | return info.Error("trim accepts only a string or list") 41 | } 42 | 43 | return result, info, true 44 | } 45 | -------------------------------------------------------------------------------- /dynaml/type.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | ) 6 | 7 | func func_type(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 8 | info := DefaultInfo() 9 | 10 | if len(arguments) != 1 { 11 | info.Error("exactly one argument required for function 'type'") 12 | } 13 | 14 | tn := ExpressionType(arguments[0]) 15 | if tn == "" { 16 | return info.Error("unknown type for %v", arguments[0]) 17 | } else { 18 | return tn, info, true 19 | } 20 | } 21 | 22 | func ExpressionType(elem interface{}) string { 23 | node, ok := elem.(yaml.Node) 24 | if ok { 25 | if node.Undefined() { 26 | return "undef" 27 | } 28 | elem = node.Value() 29 | } 30 | switch elem.(type) { 31 | case string: 32 | return "string" 33 | case int64: 34 | return "int" 35 | case float64: 36 | return "float" 37 | case bool: 38 | return "bool" 39 | case []yaml.Node: 40 | return "list" 41 | case map[string]yaml.Node: 42 | return "map" 43 | case TemplateValue: 44 | return "template" 45 | case LambdaValue: 46 | return "lambda" 47 | case nil: 48 | return "nil" 49 | default: 50 | return "" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /dynaml/undefined.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | type UndefinedExpr struct{} 4 | 5 | func (e UndefinedExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 6 | info := DefaultInfo() 7 | info.Undefined = true 8 | return nil, info, true 9 | } 10 | 11 | func (e UndefinedExpr) String() string { 12 | return "~~" 13 | } 14 | -------------------------------------------------------------------------------- /dynaml/uniq.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/yaml" 5 | ) 6 | 7 | func func_uniq(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 8 | info := DefaultInfo() 9 | 10 | if len(arguments) != 1 { 11 | return info.Error("uniq takes exactly 1 argument") 12 | } 13 | 14 | list, ok := arguments[0].([]yaml.Node) 15 | if !ok { 16 | return info.Error("invalid type for function uniq") 17 | } 18 | 19 | var newList []yaml.Node 20 | 21 | for _, v := range list { 22 | found := false 23 | for _, n := range newList { 24 | r, _, _ := compareEquals(v.Value(), n.Value()) 25 | if r { 26 | found = true 27 | break 28 | } 29 | } 30 | if !found { 31 | newList = append(newList, v) 32 | } 33 | } 34 | return newList, info, true 35 | } 36 | -------------------------------------------------------------------------------- /dynaml/unresolved_check_test.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "github.com/mandelsoft/spiff/yaml" 8 | ) 9 | 10 | var _ = Describe("Reporting unresolved nodes", func() { 11 | It("formats a message listing the nodes", func() { 12 | err := UnresolvedNodes{ 13 | Nodes: []UnresolvedNode{ 14 | { 15 | Node: yaml.NewNode( 16 | AutoExpr{}, 17 | "some-file.yml", 18 | ), 19 | Context: []string{"foo", "bar"}, 20 | Path: []string{"foo", "bar"}, 21 | }, 22 | { 23 | Node: yaml.NewNode( 24 | MergeExpr{}, 25 | "some-other-file.yml", 26 | ), 27 | Context: []string{"fizz", "[2]", "buzz"}, 28 | Path: []string{"fizz", "fizzbuzz", "buzz"}, 29 | }, 30 | }, 31 | } 32 | 33 | Expect(err.Error()).To(Equal( 34 | `unresolved nodes: 35 | (( auto )) in some-file.yml foo.bar (foo.bar) 36 | (( merge )) in some-other-file.yml fizz.[2].buzz (fizz.fizzbuzz.buzz)`)) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /dynaml/validor.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | type ValidOrExpr struct { 9 | A Expression 10 | B Expression 11 | } 12 | 13 | func (e ValidOrExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 14 | a, infoa, ok := e.A.Evaluate(binding, false) 15 | if ok { 16 | if reflect.DeepEqual(a, e.A) { 17 | return nil, infoa, false 18 | } 19 | if IsExpression(a) { 20 | return e, infoa, true 21 | } 22 | if a != nil { 23 | return a, infoa, true 24 | } 25 | } 26 | 27 | b, infob, ok := e.B.Evaluate(binding, false) 28 | info := infoa.Join(infob) 29 | info.Undefined = infob.Undefined 30 | return b, info, ok 31 | } 32 | 33 | func (e ValidOrExpr) String() string { 34 | return fmt.Sprintf("%s // %s", e.A, e.B) 35 | } 36 | -------------------------------------------------------------------------------- /dynaml/value.go: -------------------------------------------------------------------------------- 1 | package dynaml 2 | 3 | import ( 4 | "fmt" 5 | "github.com/mandelsoft/spiff/yaml" 6 | "strconv" 7 | ) 8 | 9 | type ValueExpr struct { 10 | Value interface{} 11 | } 12 | 13 | func (e ValueExpr) String() string { 14 | return ValueAsString(e.Value, true) 15 | } 16 | 17 | func (e ValueExpr) Evaluate(binding Binding, locally bool) (interface{}, EvaluationInfo, bool) { 18 | return e.Value, DefaultInfo(), true 19 | } 20 | 21 | func ValueAsString(val interface{}, all bool) string { 22 | switch v := val.(type) { 23 | case []yaml.Node: 24 | s := "[" 25 | sep := "" 26 | for _, e := range v { 27 | s = fmt.Sprintf("%s%s%s", s, sep, ValueAsString(e.Value(), all)) 28 | sep = ", " 29 | } 30 | return s + "]" 31 | case map[string]yaml.Node: 32 | s := "{" 33 | sep := "" 34 | for _, k := range getSortedKeys(v) { 35 | if all || k != "_" { 36 | s = fmt.Sprintf("%s%s\"%s\"=%s", s, sep, k, ValueAsString(v[k].Value(), all)) 37 | sep = ", " 38 | } 39 | } 40 | return s + "}" 41 | case string: 42 | return fmt.Sprintf("\"%s\"", v) 43 | case int64: 44 | return strconv.FormatInt(v, 10) 45 | case float64: 46 | return strconv.FormatFloat(v, 'g', -1, 64) 47 | case bool: 48 | return strconv.FormatBool(v) 49 | default: 50 | return fmt.Sprintf("%s", v) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /dynaml/wireguard/genkey.go: -------------------------------------------------------------------------------- 1 | package wireguard 2 | 3 | import ( 4 | . "github.com/mandelsoft/spiff/dynaml" 5 | ) 6 | 7 | const F_GenKey = "wggenkey" 8 | 9 | func init() { 10 | RegisterFunction(F_GenKey, func_genkey) 11 | } 12 | 13 | func func_genkey(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 14 | info := DefaultInfo() 15 | 16 | if len(arguments) > 1 { 17 | return info.Error("a maximum of one argument expected for %s", F_GenKey) 18 | } 19 | ktype := "private" 20 | if len(arguments) == 1 { 21 | str, ok := arguments[0].(string) 22 | if !ok { 23 | return info.Error("argument for %s must be a string (private or preshared)", F_GenKey) 24 | } 25 | ktype = str 26 | } 27 | var key Key 28 | var err error 29 | switch ktype { 30 | case "private": 31 | key, err = GeneratePrivateKey() 32 | case "preshared": 33 | key, err = GenerateKey() 34 | default: 35 | return info.Error("invalid key type %q, use private or preshared", ktype) 36 | } 37 | if err != nil { 38 | return info.Error("error generating key: %s", err) 39 | } 40 | return key.String(), info, true 41 | } 42 | -------------------------------------------------------------------------------- /dynaml/wireguard/pubkey.go: -------------------------------------------------------------------------------- 1 | package wireguard 2 | 3 | import ( 4 | . "github.com/mandelsoft/spiff/dynaml" 5 | ) 6 | 7 | const F_PubKey = "wgpublickey" 8 | 9 | func init() { 10 | RegisterFunction(F_PubKey, func_pubkey) 11 | } 12 | 13 | func func_pubkey(arguments []interface{}, binding Binding) (interface{}, EvaluationInfo, bool) { 14 | info := DefaultInfo() 15 | 16 | if len(arguments) != 1 { 17 | return info.Error("one argument required for %q", F_PubKey) 18 | } 19 | 20 | str, ok := arguments[0].(string) 21 | if !ok { 22 | return info.Error("argument for %s must be a provate wireguard key (string)", F_PubKey) 23 | } 24 | key, err := ParseKey(str) 25 | if err != nil { 26 | return info.Error("error parsing key %q: %s", str, err) 27 | } 28 | return key.PublicKey().String(), info, true 29 | } 30 | -------------------------------------------------------------------------------- /examples/graph/closures.yaml: -------------------------------------------------------------------------------- 1 | 2 | utilities: 3 | graph: 4 | 5 | 6 | closures: (( utilities.graph.evaluate(graph) )) 7 | cycles: (( utilities.graph.cycles(closures) )) 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/graph/graph.yaml: -------------------------------------------------------------------------------- 1 | 2 | graph: 3 | d: 4 | - b 5 | c: 6 | - b 7 | - a 8 | b: [] 9 | a: 10 | - b 11 | - c 12 | e: 13 | - d 14 | - b 15 | -------------------------------------------------------------------------------- /examples/graph/utilities.yaml: -------------------------------------------------------------------------------- 1 | 2 | utilities: 3 | <<: (( &temporary )) 4 | graph: 5 | <<: (( &temporary )) 6 | _dep: (( |model,comp,closure|->contains(closure,comp) ? { $deps=[], $err=closure [comp]} :($deps=_._deps(model,comp,closure [comp]))($err=sum[deps|[]|s,e|-> length(s) >= length(e.err) ? s :e.err]) { $deps=_.join(map[deps|e|->e.deps]), $err=err} )) 7 | _deps: (( |model,comp,closure|->map[model.[comp]|dep|->($deps=_._dep(model,dep,closure)) { $deps=[dep] deps.deps, $err=deps.err }] )) 8 | join: (( |lists|->sum[lists|[]|s,e|-> s e] )) 9 | min: (( |list|->sum[list|~|s,e|-> s ? e < s ? e :s :e] )) 10 | 11 | normcycle: (( |cycle|->($min=_.min(cycle)) min ? sum[cycle|cycle|s,e|->s.[0] == min ? s :(s.[1..] [s.[1]])] :cycle )) 12 | cycle: (( |list|->list ? ($elem=list.[length(list) - 1]) _.normcycle(sum[list|[]|s,e|->s ? s [e] :e == elem ? [e] :s]) :list )) 13 | norm: (( |deps|->{ $deps=_.reverse(uniq(_.reverse(deps.deps))), $err=_.cycle(deps.err) } )) 14 | reverse: (( |list|->sum[list|[]|s,e|->[e] s] )) 15 | 16 | evaluate: (( |model|->sum[model|{}|s,k,v|->s { k=_.norm(_._dep(model,k,[]))}] )) 17 | cycles: (( |result|->uniq(sum[result|[]|s,k,v|-> v.err ? s [v.err] :s]) )) 18 | 19 | -------------------------------------------------------------------------------- /examples/tutorial.md: -------------------------------------------------------------------------------- 1 | ### `spiff merge a.yml b.yml c.yml` 2 | 3 | #### `a.yml` 4 | 5 | ```yaml 6 | hello: world 7 | happy: (( merge )) 8 | ``` 9 | 10 | #### `b.yml` 11 | 12 | ```yaml 13 | foo: bar 14 | ``` 15 | 16 | #### `c.yml` 17 | 18 | ```yaml 19 | happy: birthday 20 | ``` 21 | 22 | #### output 23 | 24 | ```yaml 25 | happy: birthday 26 | hello: world 27 | ``` 28 | 29 | Note: happy will reach all the way down to c to grab “birthday” 30 | 31 | --------------------------------------- 32 | 33 | ### `spiff merge a.yml b.yml` 34 | 35 | #### `a.yml` 36 | 37 | ```yaml 38 | hello: world 39 | happy: ~ 40 | ``` 41 | 42 | #### `b.yml` 43 | 44 | ```yaml 45 | foo: bar 46 | happy: new_year 47 | ``` 48 | 49 | #### output 50 | 51 | ```yaml 52 | happy: new_year 53 | hello: world 54 | ``` 55 | 56 | Note: When the "happy" key appears in two places, the right most value wins out. 57 | There is an implicit merge. 58 | 59 | --------------------------------------- 60 | 61 | ### `spiff merge a.yml b.yml` 62 | 63 | #### `a.yml` 64 | 65 | ```yaml 66 | happy: 67 | - name: holidays 68 | date: yesterday 69 | - name: new_year 70 | date: tomorrow 71 | ``` 72 | 73 | #### `b.yml` 74 | 75 | ```yaml 76 | happy: 77 | - name: birthday 78 | date: today 79 | - name: new_year 80 | date: "12/31" 81 | ``` 82 | 83 | #### output 84 | 85 | ```yaml 86 | happy: 87 | - date: yesterday 88 | name: holidays 89 | - date: 12/31 90 | name: new_year 91 | ``` 92 | 93 | Arrays are a special exception. "name" is a special key, which 94 | identifies a member of an array for possible merging. If there is overlap of 95 | name during a merge, the rightmost file wins, for that element. 96 | -------------------------------------------------------------------------------- /features/features.go: -------------------------------------------------------------------------------- 1 | package features 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | ) 8 | 9 | const INTERPOLATION = "interpolation" 10 | const CONTROL = "control" 11 | 12 | type FeatureFlags map[string]struct{} 13 | 14 | func (this FeatureFlags) Enabled(name string) bool { 15 | if this == nil { 16 | return false 17 | } 18 | _, ok := this[name] 19 | return ok 20 | } 21 | 22 | func (this FeatureFlags) Set(name string, active bool) error { 23 | no := strings.HasPrefix(name, "no") 24 | if no { 25 | name = name[2:] 26 | } 27 | switch name { 28 | case INTERPOLATION: 29 | case CONTROL: 30 | default: 31 | return fmt.Errorf("unknown feature flag %q", name) 32 | } 33 | if active != no { 34 | this[name] = struct{}{} 35 | } else { 36 | delete(this, name) 37 | } 38 | return nil 39 | } 40 | 41 | func (this FeatureFlags) Size() int { 42 | return len(this) 43 | } 44 | 45 | func (this FeatureFlags) InterpolationEnabled() bool { 46 | return this.Enabled(INTERPOLATION) 47 | } 48 | func (this FeatureFlags) SetInterpolation(active bool) { 49 | this.Set(INTERPOLATION, active) 50 | } 51 | 52 | func (this FeatureFlags) ControlEnabled() bool { 53 | return this.Enabled(CONTROL) 54 | } 55 | func (this FeatureFlags) SetControl(active bool) { 56 | this.Set(CONTROL, active) 57 | } 58 | 59 | func Features() FeatureFlags { 60 | features := FeatureFlags{} 61 | // setup defaults 62 | setting := os.Getenv("SPIFF_FEATURES") 63 | for _, f := range strings.Split(setting, ",") { 64 | features.Set(strings.TrimSpace(f), true) 65 | } 66 | return features 67 | } 68 | 69 | func EncryptionKey() string { 70 | return os.Getenv("SPIFF_ENCRYPTION_KEY") 71 | } 72 | -------------------------------------------------------------------------------- /flow/cascade_as_helper_test.go: -------------------------------------------------------------------------------- 1 | package flow 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/mandelsoft/spiff/yaml" 7 | ) 8 | 9 | func CascadeAs(expected yaml.Node, stubs ...yaml.Node) *CascadeAsMatcher { 10 | matcher := &CascadeAsMatcher{} 11 | support := &MatcherSupport{OmegaMatcher: matcher, Expected: expected, Stubs: stubs} 12 | matcher.MatcherSupport = support 13 | return matcher 14 | } 15 | 16 | type CascadeAsMatcher struct { 17 | *MatcherSupport 18 | } 19 | 20 | func (matcher *CascadeAsMatcher) WithFeatures(features ...string) *CascadeAsMatcher { 21 | matcher.features = features 22 | return matcher 23 | } 24 | 25 | func (matcher *CascadeAsMatcher) Match(source interface{}) (success bool, err error) { 26 | if source == nil && matcher.Expected == nil { 27 | return false, fmt.Errorf("Refusing to compare to .") 28 | } 29 | env := matcher.createEnv() 30 | matcher.actual, err = Cascade(env, source.(yaml.Node), Options{}, matcher.Stubs...) 31 | if err != nil { 32 | return false, err 33 | } 34 | 35 | if matcher.actual.EquivalentToNode(matcher.Expected) { 36 | return true, nil 37 | } else { 38 | return false, nil 39 | } 40 | 41 | return 42 | } 43 | 44 | func (matcher *CascadeAsMatcher) FailureMessage(actual interface{}) (message string) { 45 | return formatMessage(matcher.actual, "to flow as", matcher.Expected) 46 | } 47 | 48 | func (matcher *CascadeAsMatcher) NegatedFailureMessage(actual interface{}) (message string) { 49 | return formatMessage(matcher.actual, "not to flow as", matcher.Expected) 50 | } 51 | -------------------------------------------------------------------------------- /flow/flow_control.go: -------------------------------------------------------------------------------- 1 | package flow 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/dynaml" 5 | "github.com/mandelsoft/spiff/yaml" 6 | 7 | _ "github.com/mandelsoft/spiff/dynaml/control" 8 | ) 9 | 10 | func flowControl(node yaml.Node, undef map[string]yaml.Node, env dynaml.Binding) (yaml.Node, bool, bool) { 11 | flags := node.GetAnnotation().Flags() 12 | resolved := false 13 | is := false 14 | ctx, err := dynaml.GetControl(node, undef, env) 15 | if err != nil { 16 | node, resolved = dynaml.ControlIssue(ctx, err.Error()) 17 | } else if ctx != nil { 18 | if err == nil { 19 | is = true 20 | node, resolved = ctx.Function() 21 | } 22 | } 23 | if resolved { 24 | if flags != 0 { 25 | node = yaml.AddFlags(node, flags) 26 | } 27 | } 28 | return node, is, resolved 29 | } 30 | -------------------------------------------------------------------------------- /flow/init_test.go: -------------------------------------------------------------------------------- 1 | package flow 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | 9 | "github.com/mandelsoft/spiff/yaml" 10 | ) 11 | 12 | func Test(t *testing.T) { 13 | RegisterFailHandler(Fail) 14 | RunSpecs(t, "Flowing") 15 | } 16 | 17 | func parseYAML(source string, file ...string) yaml.Node { 18 | if len(file) == 0 { 19 | file = []string{"test"} 20 | } 21 | parsed, err := yaml.Parse(file[0], []byte(source)) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | return parsed 27 | } 28 | -------------------------------------------------------------------------------- /flow/version.go: -------------------------------------------------------------------------------- 1 | package flow 2 | 3 | var VERSION = "v1.7.0-beta-7" 4 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/mandelsoft/spiff 2 | 3 | go 1.22.5 4 | 5 | require ( 6 | github.com/Masterminds/semver/v3 v3.2.0 7 | github.com/cloudfoundry-incubator/candiedyaml v0.0.0-20170901234223-a41693b7b7af 8 | github.com/mandelsoft/vfs v0.4.4 9 | github.com/onsi/ginkgo v1.16.5 10 | github.com/onsi/gomega v1.24.2 11 | github.com/pointlander/peg v0.0.0-20160608205303-1d0268dfff9b 12 | github.com/spf13/cobra v1.6.1 13 | github.com/spf13/viper v1.14.0 14 | golang.org/x/crypto v0.10.0 15 | ) 16 | 17 | require ( 18 | github.com/fsnotify/fsnotify v1.6.0 // indirect 19 | github.com/google/go-cmp v0.5.9 // indirect 20 | github.com/hashicorp/hcl v1.0.0 // indirect 21 | github.com/inconshreveable/mousetrap v1.0.1 // indirect 22 | github.com/magiconair/properties v1.8.6 // indirect 23 | github.com/mandelsoft/filepath v0.0.0-20240223090642-3e2777258aa3 // indirect 24 | github.com/mitchellh/mapstructure v1.5.0 // indirect 25 | github.com/modern-go/reflect2 v1.0.2 // indirect 26 | github.com/nxadm/tail v1.4.8 // indirect 27 | github.com/pelletier/go-toml v1.9.5 // indirect 28 | github.com/pelletier/go-toml/v2 v2.0.5 // indirect 29 | github.com/pointlander/compress v1.1.0 // indirect 30 | github.com/pointlander/jetset v1.0.0 // indirect 31 | github.com/spf13/afero v1.9.2 // indirect 32 | github.com/spf13/cast v1.5.0 // indirect 33 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 34 | github.com/spf13/pflag v1.0.5 // indirect 35 | github.com/subosito/gotenv v1.4.1 // indirect 36 | golang.org/x/net v0.11.0 // indirect 37 | golang.org/x/sys v0.9.0 // indirect 38 | golang.org/x/text v0.10.0 // indirect 39 | gopkg.in/ini.v1 v1.67.0 // indirect 40 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect 41 | gopkg.in/yaml.v2 v2.4.0 // indirect 42 | gopkg.in/yaml.v3 v3.0.1 // indirect 43 | ) 44 | -------------------------------------------------------------------------------- /hack/peg: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | current_dir="$(pwd)" 4 | if ! readlink -f . &>/dev/null; then 5 | echo "you're probably on OSX. Please install gnu readlink -- otherwise you're missing the most useful readlink flag." 6 | exit 1 7 | fi 8 | tool_dir="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" 9 | cd "${tool_dir}/../vendor/github.com/pointlander/peg" 10 | echo go run -v . "${current_dir}/$1" 11 | go run -v . "${current_dir}/$1" 12 | -------------------------------------------------------------------------------- /hack/tools.go: -------------------------------------------------------------------------------- 1 | // +build tools 2 | 3 | package tools 4 | 5 | import ( 6 | _ "github.com/pointlander/peg" 7 | ) 8 | -------------------------------------------------------------------------------- /init_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | "github.com/onsi/gomega/gexec" 9 | 10 | "github.com/mandelsoft/spiff/yaml" 11 | ) 12 | 13 | var spiff string 14 | 15 | func Test(t *testing.T) { 16 | BeforeSuite(func() { 17 | var err error 18 | spiff, err = gexec.Build("github.com/mandelsoft/spiff") 19 | Ω(err).ShouldNot(HaveOccurred()) 20 | }) 21 | 22 | AfterSuite(func() { 23 | gexec.CleanupBuildArtifacts() 24 | }) 25 | 26 | RegisterFailHandler(Fail) 27 | RunSpecs(t, "Executable") 28 | } 29 | 30 | func parseYAML(source string) yaml.Node { 31 | parsed, err := yaml.Parse("test", []byte(source)) 32 | if err != nil { 33 | panic(err) 34 | } 35 | 36 | return parsed 37 | } 38 | -------------------------------------------------------------------------------- /legacy/.gitignore: -------------------------------------------------------------------------------- 1 | *.coverprofile 2 | -------------------------------------------------------------------------------- /legacy/candiedyaml/.gitignore: -------------------------------------------------------------------------------- 1 | *.coverprofile 2 | -------------------------------------------------------------------------------- /legacy/candiedyaml/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-Present CloudFoundry.org Foundation, Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | This project may include a number of subcomponents with separate 16 | copyright notices and license terms. Your use of these subcomponents 17 | is subject to the terms and conditions of each subcomponent's license, 18 | as noted in the LICENSE file. 19 | -------------------------------------------------------------------------------- /legacy/candiedyaml/candiedyaml_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | package candiedyaml 16 | 17 | import ( 18 | . "github.com/onsi/ginkgo" 19 | . "github.com/onsi/gomega" 20 | 21 | "testing" 22 | ) 23 | 24 | func TestCandiedyaml(t *testing.T) { 25 | RegisterFailHandler(Fail) 26 | RunSpecs(t, "Candiedyaml Suite") 27 | } 28 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_1.yaml: -------------------------------------------------------------------------------- 1 | - Mark McGwire 2 | - Sammy Sosa 3 | - Ken Griffey 4 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_10.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | hr: 3 | - Mark McGwire 4 | # Following node labeled SS 5 | - &SS Sammy Sosa 6 | rbi: 7 | - *SS # Subsequent occurrence 8 | - Ken Griffey 9 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_11.yaml: -------------------------------------------------------------------------------- 1 | ? - Detroit Tigers 2 | - Chicago cubs 3 | : 4 | - 2001-07-23 5 | 6 | ? [ New York Yankees, 7 | Atlanta Braves ] 8 | : [ 2001-07-02, 2001-08-12, 9 | 2001-08-14 ] 10 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_12.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # products purchased 3 | - item : Super Hoop 4 | quantity: 1 5 | - item : Basketball 6 | quantity: 4 7 | - item : Big Shoes 8 | quantity: 1 9 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_13.yaml: -------------------------------------------------------------------------------- 1 | # ASCII Art 2 | --- | 3 | \//||\/|| 4 | // || ||__ 5 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_14.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | Mark McGwire's 3 | year was crippled 4 | by a knee injury. 5 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_15.yaml: -------------------------------------------------------------------------------- 1 | > 2 | Sammy Sosa completed another 3 | fine season with great stats. 4 | 5 | 63 Home Runs 6 | 0.288 Batting Average 7 | 8 | What a year! 9 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_15_dumped.yaml: -------------------------------------------------------------------------------- 1 | > 2 | Sammy Sosa completed another fine season with great stats. 3 | 4 | 63 Home Runs 5 | 0.288 Batting Average 6 | 7 | What a year! -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_16.yaml: -------------------------------------------------------------------------------- 1 | name: Mark McGwire 2 | accomplishment: > 3 | Mark set a major league 4 | home run record in 1998. 5 | stats: | 6 | 65 Home Runs 7 | 0.278 Batting Average 8 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_17.yaml: -------------------------------------------------------------------------------- 1 | unicode: "Sosa did fine.\u263A" 2 | control: "\b1998\t1999\t2000\n" 3 | hexesc: "\x0D\x0A is \r\n" 4 | 5 | single: '"Howdy!" he cried.' 6 | quoted: ' # not a ''comment''.' 7 | tie-fighter: '|\-*-/|' 8 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_17_control.yaml: -------------------------------------------------------------------------------- 1 | control: "\b1998\t1999\t2000\n" 2 | 3 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_17_hexesc.yaml: -------------------------------------------------------------------------------- 1 | hexesc: "\x0D\x0A is \r\n" 2 | 3 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_17_quoted.yaml: -------------------------------------------------------------------------------- 1 | quoted: ' # not a ''comment''.' 2 | 3 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_17_single.yaml: -------------------------------------------------------------------------------- 1 | single: '"Howdy!" he cried.' 2 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_17_tie_fighter.yaml: -------------------------------------------------------------------------------- 1 | tie-fighter: '|\-*-/|' 2 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_17_unicode.yaml: -------------------------------------------------------------------------------- 1 | unicode: "Sosa did fine.\u263A" 2 | 3 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_18.yaml: -------------------------------------------------------------------------------- 1 | plain: 2 | This unquoted scalar 3 | spans many lines. 4 | 5 | quoted: "So does this 6 | quoted scalar.\n" 7 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_19.yaml: -------------------------------------------------------------------------------- 1 | canonical: 12345 2 | decimal: +12_345 3 | octal: 014 4 | hexadecimal: 0xC 5 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_2.yaml: -------------------------------------------------------------------------------- 1 | hr: 65 # Home runs 2 | avg: 0.278 # Batting average 3 | rbi: 147 # Runs Batted In 4 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_20.yaml: -------------------------------------------------------------------------------- 1 | canonical: 1.23015e+3 2 | exponential: 12.3015e+02 3 | fixed: 1_230.15 4 | negative infinity: -.inf 5 | not a number: .NaN 6 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_21.yaml: -------------------------------------------------------------------------------- 1 | null: ~ 2 | true: yes 3 | false: no 4 | string: '12345' 5 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_22.yaml: -------------------------------------------------------------------------------- 1 | canonical: 2001-12-15T02:59:43.1Z 2 | iso8601: 2001-12-14t21:59:43.10-05:00 3 | spaced: 2001-12-14 21:59:43.10 -5 4 | date: 2002-12-14 5 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_23.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | not-date: !!str 2002-04-28 3 | 4 | picture: !!binary "\ 5 | R0lGODlhDAAMAIQAAP//9/X\ 6 | 17unp5WZmZgAAAOfn515eXv\ 7 | Pz7Y6OjuDg4J+fn5OTk6enp\ 8 | 56enmleECcgggoBADs=" 9 | 10 | application specific tag: !something | 11 | The semantics of the tag 12 | above may be different for 13 | different documents. 14 | 15 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_23_application.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | application specific tag: !something | 3 | The semantics of the tag 4 | above may be different for 5 | different documents. 6 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_23_non_date.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | not-date: !!str 2002-04-28 3 | 4 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_23_picture.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | picture: !!binary "\ 3 | R0lGODlhDAAMAIQAAP//9/X\ 4 | 17unp5WZmZgAAAOfn515eXv\ 5 | Pz7Y6OjuDg4J+fn5OTk6enp\ 6 | 56enmleECcgggoBADs=" 7 | 8 | 9 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_24.yaml: -------------------------------------------------------------------------------- 1 | %TAG ! tag:clarkevans.com,2002: 2 | --- !shape 3 | # Use the ! handle for presenting 4 | # tag:clarkevans.com,2002:circle 5 | - !circle 6 | center: &ORIGIN {x: 73, y: 129} 7 | radius: 7 8 | - !line 9 | start: *ORIGIN 10 | finish: { x: 89, y: 102 } 11 | - !label 12 | start: *ORIGIN 13 | color: 0xFFEEBB 14 | text: Pretty vector drawing. 15 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_24_dumped.yaml: -------------------------------------------------------------------------------- 1 | !shape 2 | - !circle 3 | center: &id001 {x: 73, y: 129} 4 | radius: 7 5 | - !line 6 | finish: {x: 89, y: 102} 7 | start: *id001 8 | - !label 9 | color: 0xFFEEBB 10 | start: *id001 11 | text: Pretty vector drawing. -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_25.yaml: -------------------------------------------------------------------------------- 1 | # sets are represented as a 2 | # mapping where each key is 3 | # associated with the empty string 4 | --- !!set 5 | ? Mark McGwire 6 | ? Sammy Sosa 7 | ? Ken Griff 8 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_26.yaml: -------------------------------------------------------------------------------- 1 | # ordered maps are represented as 2 | # a sequence of mappings, with 3 | # each mapping having one key 4 | --- !!omap 5 | - Mark McGwire: 65 6 | - Sammy Sosa: 63 7 | - Ken Griffy: 58 8 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_27.yaml: -------------------------------------------------------------------------------- 1 | --- ! 2 | invoice: 34843 3 | date : 2001-01-23 4 | billTo: &id001 5 | given : Chris 6 | family : Dumars 7 | address: 8 | lines: | 9 | 458 Walkman Dr. 10 | Suite #292 11 | city : Royal Oak 12 | state : MI 13 | postal : 48046 14 | shipTo: *id001 15 | product: 16 | - sku : BL394D 17 | quantity : 4 18 | description : Basketball 19 | price : 450.00 20 | - sku : BL4438H 21 | quantity : 1 22 | description : Super Hoop 23 | price : 2392.00 24 | tax : 251.42 25 | total: 4443.52 26 | comments: 27 | Late afternoon is best. 28 | Backup contact is Nancy 29 | Billsmer @ 338-4338. 30 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_27_dumped.yaml: -------------------------------------------------------------------------------- 1 | !!org.yaml.snakeyaml.Invoice 2 | billTo: &id001 3 | address: 4 | city: Royal Oak 5 | lines: | 6 | 458 Walkman Dr. 7 | Suite #292 8 | postal: '48046' 9 | state: MI 10 | family: Dumars 11 | given: Chris 12 | comments: Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338. 13 | date: '2001-01-23' 14 | invoice: 34843 15 | product: 16 | - {description: Basketball, price: 450.0, quantity: 4, sku: BL394D} 17 | - {description: Super Hoop, price: 2392.0, quantity: 1, sku: BL4438H} 18 | shipTo: *id001 19 | tax: 251.42 20 | total: 4443.52 -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_28.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | Time: 2001-11-23 15:01:42 -5 3 | User: ed 4 | Warning: 5 | This is an error message 6 | for the log file 7 | --- 8 | Time: 2001-11-23 15:02:31 -5 9 | User: ed 10 | Warning: 11 | A slightly different error 12 | message. 13 | --- 14 | Date: 2001-11-23 15:03:17 -5 15 | User: ed 16 | Fatal: 17 | Unknown variable "bar" 18 | Stack: 19 | - file: TopClass.py 20 | line: 23 21 | code: | 22 | x = MoreObject("345\n") 23 | - file: MoreClass.py 24 | line: 58 25 | code: |- 26 | foo = bar 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_3.yaml: -------------------------------------------------------------------------------- 1 | american: 2 | - Boston Red Sox 3 | - Detroit Tigers 4 | - New York Yankees 5 | national: 6 | - New York Mets 7 | - Chicago Cubs 8 | - Atlanta Braves -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_4.yaml: -------------------------------------------------------------------------------- 1 | - 2 | name: Mark McGwire 3 | hr: 65 4 | avg: 0.278 5 | - 6 | name: Sammy Sosa 7 | hr: 63 8 | avg: 0.288 9 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_5.yaml: -------------------------------------------------------------------------------- 1 | - [name , hr, avg ] 2 | - [Mark McGwire, 65, 0.278] 3 | - [Sammy Sosa , 63, 0.288] 4 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_6.yaml: -------------------------------------------------------------------------------- 1 | Mark McGwire: {hr: 65, avg: 0.278} 2 | Sammy Sosa: { 3 | hr: 63, 4 | avg: 0.288 5 | } 6 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_7.yaml: -------------------------------------------------------------------------------- 1 | # Ranking of 1998 home runs 2 | --- 3 | - Mark McGwire 4 | - Sammy Sosa 5 | - Ken Griffey 6 | 7 | # Team ranking 8 | --- 9 | - Chicago Cubs 10 | - St Louis Cardinals 11 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_8.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | time: 20:03:20 3 | player: Sammy Sosa 4 | action: strike (miss) 5 | ... 6 | --- 7 | time: 20:03:47 8 | player: Sammy Sosa 9 | action: grand slam 10 | ... 11 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example2_9.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | hr: # 1998 hr ranking 3 | - Mark McGwire 4 | - Sammy Sosa 5 | rbi: 6 | # 1998 rbi ranking 7 | - Sammy Sosa 8 | - Ken Griffey 9 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/example_empty.yaml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandelsoft/spiff/c5d636ea32344f72e76ed5098c4f77b6c92df80b/legacy/candiedyaml/fixtures/specification/example_empty.yaml -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/types/map.yaml: -------------------------------------------------------------------------------- 1 | # Unordered set of key: value pairs. 2 | Block style: !!map 3 | Clark : Evans 4 | Brian : Ingerson 5 | Oren : Ben-Kiki 6 | Flow style: !!map { Clark: Evans, Brian: Ingerson, Oren: Ben-Kiki } 7 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/types/map_mixed_tags.yaml: -------------------------------------------------------------------------------- 1 | # Unordered set of key: value pairs. 2 | Block style: ! 3 | Clark : Evans 4 | Brian : Ingerson 5 | Oren : Ben-Kiki 6 | Flow style: { Clark: Evans, Brian: Ingerson, Oren: Ben-Kiki } 7 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/types/merge.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - &CENTER { x: 1, y: 2 } 3 | - &LEFT { x: 0, y: 2 } 4 | - &BIG { r: 10 } 5 | - &SMALL { r: 1 } 6 | 7 | # All the following maps are equal: 8 | 9 | - # Explicit keys 10 | x: 1 11 | y: 2 12 | r: 10 13 | label: center/big 14 | 15 | - # Merge one map 16 | << : *CENTER 17 | r: 10 18 | label: center/big 19 | 20 | - # Merge multiple maps 21 | << : [ *CENTER, *BIG ] 22 | label: center/big 23 | 24 | - # Override 25 | << : [ *BIG, *LEFT, *SMALL ] 26 | x: 1 27 | label: center/big 28 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/types/omap.yaml: -------------------------------------------------------------------------------- 1 | # Explicitly typed ordered map (dictionary). 2 | Bestiary: !!omap 3 | - aardvark: African pig-like ant eater. Ugly. 4 | - anteater: South-American ant eater. Two species. 5 | - anaconda: South-American constrictor snake. Scaly. 6 | # Etc. 7 | # Flow style 8 | Numbers: !!omap [ one: 1, two: 2, three : 3 ] 9 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/types/pairs.yaml: -------------------------------------------------------------------------------- 1 | # Explicitly typed pairs. 2 | Block tasks: !!pairs 3 | - meeting: with team. 4 | - meeting: with boss. 5 | - break: lunch. 6 | - meeting: with client. 7 | Flow tasks: !!pairs [ meeting: with team, meeting: with boss ] 8 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/types/seq.yaml: -------------------------------------------------------------------------------- 1 | # Ordered sequence of nodes 2 | Block style: !!seq 3 | - Mercury # Rotates - no light/dark sides. 4 | - Venus # Deadliest. Aptly named. 5 | - Earth # Mostly dirt. 6 | - Mars # Seems empty. 7 | - Jupiter # The king. 8 | - Saturn # Pretty. 9 | - Uranus # Where the sun hardly shines. 10 | - Neptune # Boring. No rings. 11 | - Pluto # You call this a planet? 12 | Flow style: !!seq [ Mercury, Venus, Earth, Mars, # Rocks 13 | Jupiter, Saturn, Uranus, Neptune, # Gas 14 | Pluto ] # Overrated 15 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/types/set.yaml: -------------------------------------------------------------------------------- 1 | # Explicitly typed set. 2 | baseball players: !!set 3 | ? Mark McGwire 4 | ? Sammy Sosa 5 | ? Ken Griffey 6 | # Flow style 7 | baseball teams: !!set { Boston Red Sox, Detroit Tigers, New York Yankees } 8 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/types/v.yaml: -------------------------------------------------------------------------------- 1 | --- # New schema 2 | link with: 3 | - = : library1.dll 4 | version: 1.2 5 | -------------------------------------------------------------------------------- /legacy/candiedyaml/fixtures/specification/types/value.yaml: -------------------------------------------------------------------------------- 1 | --- # Old schema 2 | link with: 3 | - library1.dll 4 | - library2.dll 5 | --- # New schema 6 | link with: 7 | - = : library1.dll 8 | version: 1.2 9 | - = : library2.dll 10 | version: 2.3 11 | -------------------------------------------------------------------------------- /legacy/candiedyaml/libyaml-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006 Kirill Simonov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /legacy/candiedyaml/run_parser.go: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | package candiedyaml 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | ) 21 | 22 | func Run_parser(cmd string, args []string) { 23 | for i := 0; i < len(args); i++ { 24 | fmt.Printf("[%d] Scanning '%s'", i, args[i]) 25 | file, err := os.Open(args[i]) 26 | if err != nil { 27 | panic(fmt.Sprintf("Invalid file '%s': %s", args[i], err.Error())) 28 | } 29 | 30 | parser := yaml_parser_t{} 31 | yaml_parser_initialize(&parser) 32 | yaml_parser_set_input_reader(&parser, file) 33 | 34 | failed := false 35 | token := yaml_token_t{} 36 | count := 0 37 | for { 38 | if !yaml_parser_scan(&parser, &token) { 39 | failed = true 40 | break 41 | } 42 | 43 | if token.token_type == yaml_STREAM_END_TOKEN { 44 | break 45 | } 46 | count++ 47 | } 48 | 49 | file.Close() 50 | 51 | msg := "SUCCESS" 52 | if failed { 53 | msg = "FAILED" 54 | if parser.error != yaml_NO_ERROR { 55 | m := parser.problem_mark 56 | fmt.Printf("ERROR: (%s) %s @ line: %d col: %d\n", 57 | parser.context, parser.problem, m.line, m.column) 58 | } 59 | } 60 | fmt.Printf("%s (%d tokens)\n", msg, count) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /legacy/candiedyaml/yaml_definesh.go: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | package candiedyaml 16 | 17 | const ( 18 | yaml_VERSION_MAJOR = 0 19 | yaml_VERSION_MINOR = 1 20 | yaml_VERSION_PATCH = 6 21 | yaml_VERSION_STRING = "0.1.6" 22 | ) 23 | -------------------------------------------------------------------------------- /libraries/graph/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Directed Graph Analysis 3 | 4 | This graph library offers some useful lambda expression to work 5 | with directed graphs. 6 | 7 | A graph is given by a dicated yaml map hosting the nodes represented by 8 | their names. Each name sub-node represents the edges by holding a list 9 | of node names 10 | 11 | The package name is `utilities.graph`. 12 | 13 | ## Invert a Directed Graph 14 | 15 | ``` 16 | invert() 17 | ``` 18 | 19 | ## Evaluate a directed Graph 20 | 21 | ``` 22 | evaluate() 23 | ``` 24 | 25 | The result a a graph evaluation map with entries for every node 26 | providing the evaluated node info: 27 | - `deps` The dependency closure 28 | - `err`: Cyclic dependencies for this node 29 | - `missing`: dangling edges (without a node in the graph) 30 | 31 | ## List all Cycles 32 | 33 | ``` 34 | cycles() -> list of lists 35 | ``` 36 | 37 | List all normalized dependencies cycles in a graph using its 38 | evaluated model. 39 | 40 | ## Provide a global Execution Order 41 | 42 | ``` 43 | order() -> list 44 | ``` 45 | 46 | Uses the evaluated model to determine a global execution order 47 | for nodes included i the graph. 48 | 49 | ## Reverse a List 50 | 51 | ``` 52 | reverse() -> list 53 | ``` 54 | 55 | Reverse the order of elements in a list. -------------------------------------------------------------------------------- /libraries/graph/graph.yaml: -------------------------------------------------------------------------------- 1 | 2 | utilities: 3 | <<: (( &temporary(merge || ~) )) 4 | 5 | graph: 6 | <<: (( &temporary )) 7 | _dep: (( |model,comp,closure|->contains(closure,comp) ? { $deps=[], $err=closure [comp]} :($deps=_._deps(model,comp,closure [comp]))($err=sum[deps|[]|s,e|-> length(s) >= length(e.err) ? s :e.err]) { $deps=_.join(map[deps|e|->e.deps]), $err=err} )) 8 | _deps: (( |model,comp,closure|->map[model.[comp]|dep|->($deps=_._dep(model,dep,closure)) { $deps=[dep] deps.deps, $err=deps.err }] || [{$deps=[], $err=[]}] )) 9 | missing: (( |model,comp|->sum[model.[comp]|[]|s,v|-> defined(model.[v]) ? s :s v] )) 10 | join: (( |lists|->sum[lists|[]|s,e|-> s e] )) 11 | min: (( |list|->sum[list|~|s,e|-> s ? e < s ? e :s :e] )) 12 | 13 | normcycle: (( |cycle|->($min=_.min(cycle)) min ? sum[cycle|cycle|s,e|->s.[0] == min ? s :(s.[1..] [s.[1]])] :cycle )) 14 | cycle: (( |list|->list ? ($elem=list.[length(list) - 1]) _.normcycle(sum[list|[]|s,e|->s ? s [e] :e == elem ? [e] :s]) :list )) 15 | norm: (( |model,comp,deps|->($d= _.reverse(uniq(_.reverse(deps.deps)))) { $deps=d, $err=_.cycle(deps.err), $order=_.reverse([comp] d), $missing=_.missing(model,comp) } )) 16 | reverse: (( |list|->sum[list|[]|s,e|->[e] s] )) 17 | 18 | invert: (( |graph|->map{graph|c,l|->sum[graph|[]|s,k,v|->contains(v,c) ? s k :s ]} )) 19 | 20 | evaluate: (( |model|->sum[model|{}|s,k,v|->s { k=_.norm(model,k,_._dep(model,k,[]))}] )) 21 | cycles: (( |result|->uniq(sum[result|[]|s,k,v|-> v.err ? s [v.err] :s]) )) 22 | order: (( |result|->uniq(_.reverse(sum[result|[]|s,k,v|->s [k] v.deps])) )) 23 | 24 | -------------------------------------------------------------------------------- /libraries/state/gen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # evaluate the template with a dedicated 5 | # stub file holding the state from a previous call 6 | # and generate a new version for this state after 7 | # successful merging. 8 | # 9 | 10 | set -e 11 | 12 | if [ -f "state.yaml" ]; then 13 | V="$(spiff merge template.yaml state.yaml)" 14 | else 15 | V="$(spiff merge template.yaml)" 16 | fi 17 | 18 | spiff merge --select state - <<<"$V" >state.yaml 19 | 20 | echo "$V" -------------------------------------------------------------------------------- /scripts/build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # vim: set ft=sh 3 | 4 | set -e -x -u 5 | cd $(dirname ${BASH_SOURCE[0]})/.. 6 | export GOPATH=$PWD/Godeps/_workspace:$GOPATH 7 | 8 | FIRST_GOPATH=`echo $GOPATH | cut -d':' -f1` 9 | 10 | mkdir -p $FIRST_GOPATH/bin 11 | export PATH=$FIRST_GOPATH/bin:$PATH 12 | 13 | go build spiff.go 14 | -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # vim: set ft=sh 3 | 4 | set -e -x -u 5 | 6 | #uses the local version of cover, if it is already checked out 7 | go get golang.org/x/tools/cmd/cover 8 | 9 | export GOPATH=$PWD/Godeps/_workspace:$GOPATH 10 | 11 | FIRST_GOPATH=`echo $GOPATH | cut -d':' -f1` 12 | 13 | mkdir -p $FIRST_GOPATH/bin 14 | export PATH=$FIRST_GOPATH/bin:$PATH 15 | 16 | go install github.com/onsi/ginkgo/ginkgo 17 | 18 | ginkgo -cover -r -failOnPending -randomizeAllSpecs -race "$@" 19 | -------------------------------------------------------------------------------- /spiff++.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/mandelsoft/spiff/cmd" 5 | ) 6 | 7 | func main() { 8 | cmd.Execute() 9 | } 10 | -------------------------------------------------------------------------------- /spiffing/examples_test.go: -------------------------------------------------------------------------------- 1 | package spiffing 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func ExampleEvaluateDynamlExpression() { 8 | ctx, _ := New().WithValues(map[string]interface{}{ 9 | "values": map[string]interface{}{ 10 | "alice": 25, 11 | "bob": 26, 12 | }, 13 | }) 14 | result, _ := EvaluateDynamlExpression(ctx, "values.alice + values.bob") 15 | fmt.Printf("%s", result) 16 | // Output: 51 17 | } 18 | 19 | func ExampleEvaluateDynamlExpression_complex_data() { 20 | ctx, _ := New().WithValues(map[string]interface{}{ 21 | "values": map[string]interface{}{ 22 | "alice": 25, 23 | "bob": 26, 24 | }, 25 | }) 26 | result, _ := EvaluateDynamlExpression(ctx, "[values.alice, values.bob]") 27 | fmt.Printf("%s", result) 28 | // Output: - 25 29 | //- 26 30 | } 31 | 32 | func ExampleSpiff_WithInterpolation() { 33 | ctx := New().WithInterpolation(true) 34 | 35 | template, _ := ctx.Unmarshal("template", []byte(` 36 | host: example.com 37 | port: 8080 38 | url: http://(( host )):(( port )) 39 | `)) 40 | 41 | result, _ := ctx.Cascade(template, nil) 42 | out, _ := ctx.Marshal(result) 43 | fmt.Printf("%s", out) 44 | // Output: host: example.com 45 | //port: 8080 46 | //url: http://example.com:8080 47 | } 48 | -------------------------------------------------------------------------------- /spiffing/init_test.go: -------------------------------------------------------------------------------- 1 | package spiffing 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func Test(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Spiffing") 13 | } 14 | -------------------------------------------------------------------------------- /travis.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | P="$(pwd)" 3 | O="mandelsoft/spiff" 4 | if [ ! -d "../../$O" ]; then 5 | echo "preparing original path" 6 | cd ../.. 7 | mkdir -p "$(dirname "$O")" 8 | mv "$P" "$O" 9 | cd "$O" 10 | echo "now in $(pwd)" 11 | fi 12 | -------------------------------------------------------------------------------- /vendor/github.com/Masterminds/semver/v3/.gitignore: -------------------------------------------------------------------------------- 1 | _fuzz/ -------------------------------------------------------------------------------- /vendor/github.com/Masterminds/semver/v3/.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | deadline: 2m 3 | 4 | linters: 5 | disable-all: true 6 | enable: 7 | - misspell 8 | - structcheck 9 | - govet 10 | - staticcheck 11 | - deadcode 12 | - errcheck 13 | - varcheck 14 | - unparam 15 | - ineffassign 16 | - nakedret 17 | - gocyclo 18 | - dupl 19 | - goimports 20 | - revive 21 | - gosec 22 | - gosimple 23 | - typecheck 24 | - unused 25 | 26 | linters-settings: 27 | gofmt: 28 | simplify: true 29 | dupl: 30 | threshold: 600 31 | -------------------------------------------------------------------------------- /vendor/github.com/Masterminds/semver/v3/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (C) 2014-2019, Matt Butcher and Matt Farina 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/Masterminds/semver/v3/Makefile: -------------------------------------------------------------------------------- 1 | GOPATH=$(shell go env GOPATH) 2 | GOLANGCI_LINT=$(GOPATH)/bin/golangci-lint 3 | GOFUZZBUILD = $(GOPATH)/bin/go-fuzz-build 4 | GOFUZZ = $(GOPATH)/bin/go-fuzz 5 | 6 | .PHONY: lint 7 | lint: $(GOLANGCI_LINT) 8 | @echo "==> Linting codebase" 9 | @$(GOLANGCI_LINT) run 10 | 11 | .PHONY: test 12 | test: 13 | @echo "==> Running tests" 14 | GO111MODULE=on go test -v 15 | 16 | .PHONY: test-cover 17 | test-cover: 18 | @echo "==> Running Tests with coverage" 19 | GO111MODULE=on go test -cover . 20 | 21 | .PHONY: fuzz 22 | fuzz: $(GOFUZZBUILD) $(GOFUZZ) 23 | @echo "==> Fuzz testing" 24 | $(GOFUZZBUILD) 25 | $(GOFUZZ) -workdir=_fuzz 26 | 27 | $(GOLANGCI_LINT): 28 | # Install golangci-lint. The configuration for it is in the .golangci.yml 29 | # file in the root of the repository 30 | echo ${GOPATH} 31 | curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.17.1 32 | 33 | $(GOFUZZBUILD): 34 | cd / && go get -u github.com/dvyukov/go-fuzz/go-fuzz-build 35 | 36 | $(GOFUZZ): 37 | cd / && go get -u github.com/dvyukov/go-fuzz/go-fuzz github.com/dvyukov/go-fuzz/go-fuzz-dep -------------------------------------------------------------------------------- /vendor/github.com/Masterminds/semver/v3/collection.go: -------------------------------------------------------------------------------- 1 | package semver 2 | 3 | // Collection is a collection of Version instances and implements the sort 4 | // interface. See the sort package for more details. 5 | // https://golang.org/pkg/sort/ 6 | type Collection []*Version 7 | 8 | // Len returns the length of a collection. The number of Version instances 9 | // on the slice. 10 | func (c Collection) Len() int { 11 | return len(c) 12 | } 13 | 14 | // Less is needed for the sort interface to compare two Version objects on the 15 | // slice. If checks if one is less than the other. 16 | func (c Collection) Less(i, j int) bool { 17 | return c[i].LessThan(c[j]) 18 | } 19 | 20 | // Swap is needed for the sort interface to replace the Version objects 21 | // at two different positions in the slice. 22 | func (c Collection) Swap(i, j int) { 23 | c[i], c[j] = c[j], c[i] 24 | } 25 | -------------------------------------------------------------------------------- /vendor/github.com/Masterminds/semver/v3/fuzz.go: -------------------------------------------------------------------------------- 1 | // +build gofuzz 2 | 3 | package semver 4 | 5 | func Fuzz(data []byte) int { 6 | d := string(data) 7 | 8 | // Test NewVersion 9 | _, _ = NewVersion(d) 10 | 11 | // Test StrictNewVersion 12 | _, _ = StrictNewVersion(d) 13 | 14 | // Test NewConstraint 15 | _, _ = NewConstraint(d) 16 | 17 | // The return value should be 0 normally, 1 if the priority in future tests 18 | // should be increased, and -1 if future tests should skip passing in that 19 | // data. We do not have a reason to change priority so 0 is always returned. 20 | // There are example tests that do this. 21 | return 0 22 | } 23 | -------------------------------------------------------------------------------- /vendor/github.com/cloudfoundry-incubator/candiedyaml/.gitignore: -------------------------------------------------------------------------------- 1 | *.coverprofile 2 | -------------------------------------------------------------------------------- /vendor/github.com/cloudfoundry-incubator/candiedyaml/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-Present CloudFoundry.org Foundation, Inc. All Rights Reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | This project may include a number of subcomponents with separate 16 | copyright notices and license terms. Your use of these subcomponents 17 | is subject to the terms and conditions of each subcomponent's license, 18 | as noted in the LICENSE file. 19 | -------------------------------------------------------------------------------- /vendor/github.com/cloudfoundry-incubator/candiedyaml/libyaml-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006 Kirill Simonov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /vendor/github.com/cloudfoundry-incubator/candiedyaml/run_parser.go: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | package candiedyaml 16 | 17 | import ( 18 | "fmt" 19 | "os" 20 | ) 21 | 22 | func Run_parser(cmd string, args []string) { 23 | for i := 0; i < len(args); i++ { 24 | fmt.Printf("[%d] Scanning '%s'", i, args[i]) 25 | file, err := os.Open(args[i]) 26 | if err != nil { 27 | panic(fmt.Sprintf("Invalid file '%s': %s", args[i], err.Error())) 28 | } 29 | 30 | parser := yaml_parser_t{} 31 | yaml_parser_initialize(&parser) 32 | yaml_parser_set_input_reader(&parser, file) 33 | 34 | failed := false 35 | token := yaml_token_t{} 36 | count := 0 37 | for { 38 | if !yaml_parser_scan(&parser, &token) { 39 | failed = true 40 | break 41 | } 42 | 43 | if token.token_type == yaml_STREAM_END_TOKEN { 44 | break 45 | } 46 | count++ 47 | } 48 | 49 | file.Close() 50 | 51 | msg := "SUCCESS" 52 | if failed { 53 | msg = "FAILED" 54 | if parser.error != yaml_NO_ERROR { 55 | m := parser.problem_mark 56 | fmt.Printf("ERROR: (%s) %s @ line: %d col: %d\n", 57 | parser.context, parser.problem, m.line, m.column) 58 | } 59 | } 60 | fmt.Printf("%s (%d tokens)\n", msg, count) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /vendor/github.com/cloudfoundry-incubator/candiedyaml/yaml_definesh.go: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | package candiedyaml 16 | 17 | const ( 18 | yaml_VERSION_MAJOR = 0 19 | yaml_VERSION_MINOR = 1 20 | yaml_VERSION_PATCH = 6 21 | yaml_VERSION_STRING = "0.1.6" 22 | ) 23 | -------------------------------------------------------------------------------- /vendor/github.com/google/go-cmp/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 The Go Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/google/go-cmp/cmp/export_panic.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017, The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build purego 6 | // +build purego 7 | 8 | package cmp 9 | 10 | import "reflect" 11 | 12 | const supportExporters = false 13 | 14 | func retrieveUnexportedField(reflect.Value, reflect.StructField, bool) reflect.Value { 15 | panic("no support for forcibly accessing unexported fields") 16 | } 17 | -------------------------------------------------------------------------------- /vendor/github.com/google/go-cmp/cmp/export_unsafe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017, The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !purego 6 | // +build !purego 7 | 8 | package cmp 9 | 10 | import ( 11 | "reflect" 12 | "unsafe" 13 | ) 14 | 15 | const supportExporters = true 16 | 17 | // retrieveUnexportedField uses unsafe to forcibly retrieve any field from 18 | // a struct such that the value has read-write permissions. 19 | // 20 | // The parent struct, v, must be addressable, while f must be a StructField 21 | // describing the field to retrieve. If addr is false, 22 | // then the returned value will be shallowed copied to be non-addressable. 23 | func retrieveUnexportedField(v reflect.Value, f reflect.StructField, addr bool) reflect.Value { 24 | ve := reflect.NewAt(f.Type, unsafe.Pointer(uintptr(unsafe.Pointer(v.UnsafeAddr()))+f.Offset)).Elem() 25 | if !addr { 26 | // A field is addressable if and only if the struct is addressable. 27 | // If the original parent value was not addressable, shallow copy the 28 | // value to make it non-addressable to avoid leaking an implementation 29 | // detail of how forcibly exporting a field works. 30 | if ve.Kind() == reflect.Interface && ve.IsNil() { 31 | return reflect.Zero(f.Type) 32 | } 33 | return reflect.ValueOf(ve.Interface()).Convert(f.Type) 34 | } 35 | return ve 36 | } 37 | -------------------------------------------------------------------------------- /vendor/github.com/google/go-cmp/cmp/internal/diff/debug_disable.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017, The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !cmp_debug 6 | // +build !cmp_debug 7 | 8 | package diff 9 | 10 | var debug debugger 11 | 12 | type debugger struct{} 13 | 14 | func (debugger) Begin(_, _ int, f EqualFunc, _, _ *EditScript) EqualFunc { 15 | return f 16 | } 17 | func (debugger) Update() {} 18 | func (debugger) Finish() {} 19 | -------------------------------------------------------------------------------- /vendor/github.com/google/go-cmp/cmp/internal/flags/flags.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019, The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package flags 6 | 7 | // Deterministic controls whether the output of Diff should be deterministic. 8 | // This is only used for testing. 9 | var Deterministic bool 10 | -------------------------------------------------------------------------------- /vendor/github.com/google/go-cmp/cmp/internal/value/pointer_purego.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build purego 6 | // +build purego 7 | 8 | package value 9 | 10 | import "reflect" 11 | 12 | // Pointer is an opaque typed pointer and is guaranteed to be comparable. 13 | type Pointer struct { 14 | p uintptr 15 | t reflect.Type 16 | } 17 | 18 | // PointerOf returns a Pointer from v, which must be a 19 | // reflect.Ptr, reflect.Slice, or reflect.Map. 20 | func PointerOf(v reflect.Value) Pointer { 21 | // NOTE: Storing a pointer as an uintptr is technically incorrect as it 22 | // assumes that the GC implementation does not use a moving collector. 23 | return Pointer{v.Pointer(), v.Type()} 24 | } 25 | 26 | // IsNil reports whether the pointer is nil. 27 | func (p Pointer) IsNil() bool { 28 | return p.p == 0 29 | } 30 | 31 | // Uintptr returns the pointer as a uintptr. 32 | func (p Pointer) Uintptr() uintptr { 33 | return p.p 34 | } 35 | -------------------------------------------------------------------------------- /vendor/github.com/google/go-cmp/cmp/internal/value/pointer_unsafe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018, The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !purego 6 | // +build !purego 7 | 8 | package value 9 | 10 | import ( 11 | "reflect" 12 | "unsafe" 13 | ) 14 | 15 | // Pointer is an opaque typed pointer and is guaranteed to be comparable. 16 | type Pointer struct { 17 | p unsafe.Pointer 18 | t reflect.Type 19 | } 20 | 21 | // PointerOf returns a Pointer from v, which must be a 22 | // reflect.Ptr, reflect.Slice, or reflect.Map. 23 | func PointerOf(v reflect.Value) Pointer { 24 | // The proper representation of a pointer is unsafe.Pointer, 25 | // which is necessary if the GC ever uses a moving collector. 26 | return Pointer{unsafe.Pointer(v.Pointer()), v.Type()} 27 | } 28 | 29 | // IsNil reports whether the pointer is nil. 30 | func (p Pointer) IsNil() bool { 31 | return p.p == nil 32 | } 33 | 34 | // Uintptr returns the pointer as a uintptr. 35 | func (p Pointer) Uintptr() uintptr { 36 | return uintptr(p.p) 37 | } 38 | -------------------------------------------------------------------------------- /vendor/github.com/google/go-cmp/cmp/report.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017, The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cmp 6 | 7 | // defaultReporter implements the reporter interface. 8 | // 9 | // As Equal serially calls the PushStep, Report, and PopStep methods, the 10 | // defaultReporter constructs a tree-based representation of the compared value 11 | // and the result of each comparison (see valueNode). 12 | // 13 | // When the String method is called, the FormatDiff method transforms the 14 | // valueNode tree into a textNode tree, which is a tree-based representation 15 | // of the textual output (see textNode). 16 | // 17 | // Lastly, the textNode.String method produces the final report as a string. 18 | type defaultReporter struct { 19 | root *valueNode 20 | curr *valueNode 21 | } 22 | 23 | func (r *defaultReporter) PushStep(ps PathStep) { 24 | r.curr = r.curr.PushStep(ps) 25 | if r.root == nil { 26 | r.root = r.curr 27 | } 28 | } 29 | func (r *defaultReporter) Report(rs Result) { 30 | r.curr.Report(rs) 31 | } 32 | func (r *defaultReporter) PopStep() { 33 | r.curr = r.curr.PopStep() 34 | } 35 | 36 | // String provides a full report of the differences detected as a structured 37 | // literal in pseudo-Go syntax. String may only be called after the entire tree 38 | // has been traversed. 39 | func (r *defaultReporter) String() string { 40 | assert(r.root != nil && r.curr == nil) 41 | if r.root.NumDiff == 0 { 42 | return "" 43 | } 44 | ptrs := new(pointerReferences) 45 | text := formatOptions{}.FormatDiff(r.root, ptrs) 46 | resolveReferences(text) 47 | return text.String() 48 | } 49 | 50 | func assert(ok bool) { 51 | if !ok { 52 | panic("assertion failure") 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/filepath/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 by mandelsoft. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/osfs/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Mandelsoft. All rights reserved. 3 | * This file is licensed under the Apache Software License, v. 2 except as noted 4 | * otherwise in the LICENSE file 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | // Package osfs maps the operating system filesystem to a virtual filesystem. 20 | package osfs 21 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/osfs/osfs_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | /* 4 | * Copyright 2022 Mandelsoft. All rights reserved. 5 | * This file is licensed under the Apache Software License, v. 2 except as noted 6 | * otherwise in the LICENSE file 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | package osfs 22 | 23 | func mapPath(path string) string { 24 | return path 25 | } 26 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/osfs/osfs_windows.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Mandelsoft. All rights reserved. 3 | * This file is licensed under the Apache Software License, v. 2 except as noted 4 | * otherwise in the LICENSE file 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package osfs 20 | 21 | import ( 22 | "os" 23 | 24 | "github.com/mandelsoft/vfs/pkg/vfs" 25 | ) 26 | 27 | func mapPath(p string) string { 28 | mapped := "" 29 | for _, c := range p { 30 | if os.PathSeparator == c { 31 | mapped = mapped + string(vfs.PathSeparatorChar) 32 | } else { 33 | mapped = mapped + string(c) 34 | } 35 | } 36 | return mapped 37 | } 38 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/osfs/tempfs.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Mandelsoft. All rights reserved. 3 | * This file is licensed under the Apache Software License, v. 2 except as noted 4 | * otherwise in the LICENSE file 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package osfs 20 | 21 | import ( 22 | "io/ioutil" 23 | "os" 24 | 25 | "github.com/mandelsoft/vfs/pkg/projectionfs" 26 | "github.com/mandelsoft/vfs/pkg/vfs" 27 | ) 28 | 29 | type tempfs struct { 30 | vfs.FileSystem 31 | dir string 32 | } 33 | 34 | var _ vfs.FileSystemCleanup = (*tempfs)(nil) 35 | 36 | func NewTempFileSystem() (vfs.FileSystem, error) { 37 | dir, err := ioutil.TempDir("", "VFS-") 38 | if err != nil { 39 | return nil, err 40 | } 41 | fs, err := projectionfs.New(New(), dir) 42 | if err != nil { 43 | os.Remove(dir) 44 | } 45 | return &tempfs{fs, dir}, err 46 | } 47 | 48 | func (t *tempfs) Cleanup() error { 49 | return os.RemoveAll(t.dir) 50 | } 51 | 52 | func (t *tempfs) Root() string { 53 | return t.dir 54 | } 55 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/projectionfs/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Mandelsoft. All rights reserved. 3 | * This file is licensed under the Apache Software License, v. 2 except as noted 4 | * otherwise in the LICENSE file 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | // Package projectionfs implements virtual filesystems based on a 20 | // dedicated directory of a base filesystem. 21 | package projectionfs 22 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/utils/base.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Mandelsoft. All rights reserved. 3 | * This file is licensed under the Apache Software License, v. 2 except as noted 4 | * otherwise in the LICENSE file 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package utils 20 | 21 | import ( 22 | "github.com/mandelsoft/vfs/pkg/vfs" 23 | ) 24 | 25 | type FileSystemBase struct{} 26 | 27 | func (FileSystemBase) VolumeName(name string) string { 28 | return "" 29 | } 30 | 31 | func (FileSystemBase) FSTempDir() string { 32 | return "/" 33 | } 34 | 35 | func (FileSystemBase) Normalize(path string) string { 36 | return path 37 | } 38 | 39 | func (FileSystemBase) Getwd() (string, error) { 40 | return vfs.PathSeparatorString, nil 41 | } 42 | 43 | func (FileSystemBase) Cleanup() error { 44 | return nil 45 | } 46 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/utils/sorter.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Mandelsoft. All rights reserved. 3 | * This file is licensed under the Apache Software License, v. 2 except as noted 4 | * otherwise in the LICENSE file 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package utils 20 | 21 | import ( 22 | "os" 23 | ) 24 | 25 | type FilesSorter []os.FileInfo 26 | 27 | func (s FilesSorter) Len() int { return len(s) } 28 | func (s FilesSorter) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 29 | func (s FilesSorter) Less(i, j int) bool { return s[i].Name() < s[j].Name() } 30 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/utils/utils.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Mandelsoft. All rights reserved. 3 | * This file is licensed under the Apache Software License, v. 2 except as noted 4 | * otherwise in the LICENSE file 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package utils 20 | 21 | import ( 22 | "github.com/mandelsoft/vfs/pkg/vfs" 23 | ) 24 | 25 | type RenamedFile struct { 26 | vfs.File 27 | name string 28 | } 29 | 30 | func NewRenamedFile(name string, file vfs.File) vfs.File { 31 | return &RenamedFile{file, name} 32 | } 33 | 34 | func (r *RenamedFile) Name() string { 35 | return r.name 36 | } 37 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/vfs/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Mandelsoft. All rights reserved. 3 | * This file is licensed under the Apache Software License, v. 2 except as noted 4 | * otherwise in the LICENSE file 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | // Package vfs provides a virtual filesystem for GO. It is the main package 20 | // providing the abstract interfaces and implementation-agnostic 21 | // utility functions wrapped together with an implementation into a 22 | // comprehensive user interface VFS. 23 | // (see https://pkg.go.dev/github.com/mandelsoft/vfs) 24 | package vfs 25 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/vfs/errors_unix.go: -------------------------------------------------------------------------------- 1 | // +build !windows 2 | 3 | /* 4 | * Copyright 2022 Mandelsoft. All rights reserved. 5 | * This file is licensed under the Apache Software License, v. 2 except as noted 6 | * otherwise in the LICENSE file 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | */ 20 | 21 | package vfs 22 | 23 | import ( 24 | "syscall" 25 | ) 26 | 27 | func isUnderlyingErrNotDir(err error) bool { 28 | return err == syscall.ENOTDIR 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/vfs/errors_windows.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2022 Mandelsoft. All rights reserved. 3 | * This file is licensed under the Apache Software License, v. 2 except as noted 4 | * otherwise in the LICENSE file 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package vfs 20 | 21 | import ( 22 | "syscall" 23 | ) 24 | 25 | // TODO 26 | 27 | func isUnderlyingErrNotDir(err error) bool { 28 | return err == syscall.ENOTDIR 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/vfs/iofs.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Mandelsoft. All rights reserved. 3 | * This file is licensed under the Apache Software License, v. 2 except as noted 4 | * otherwise in the LICENSE file 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package vfs 20 | 21 | import ( 22 | "io/fs" 23 | "sort" 24 | ) 25 | 26 | type iofs struct { 27 | FileSystem 28 | } 29 | 30 | var ( 31 | _ fs.File = File(nil) 32 | _ fs.ReadDirFile = File(nil) 33 | ) 34 | 35 | func (i *iofs) Open(name string) (fs.File, error) { 36 | return i.FileSystem.Open(name) 37 | } 38 | 39 | func (i *iofs) ReadDir(name string) ([]fs.DirEntry, error) { 40 | f, err := i.FileSystem.Open(name) 41 | if err != nil { 42 | return nil, err 43 | } 44 | defer f.Close() 45 | dirs, err := f.ReadDir(-1) 46 | sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() }) 47 | return dirs, err 48 | } 49 | 50 | // AsIoFS maps a virtual filesystem 51 | func AsIoFS(fs FileSystem) fs.ReadDirFS { 52 | return &iofs{fs} 53 | } 54 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/vfs/utils_regular.go: -------------------------------------------------------------------------------- 1 | //go:build !windows 2 | // +build !windows 3 | 4 | /* 5 | * Copyright 2024 Mandelsoft. All rights reserved. 6 | * This file is licensed under the Apache Software License, v. 2 except as noted 7 | * otherwise in the LICENSE file 8 | * 9 | * Licensed under the Apache License, Version 2.0 (the "License"); 10 | * you may not use this file except in compliance with the License. 11 | * You may obtain a copy of the License at 12 | * 13 | * http://www.apache.org/licenses/LICENSE-2.0 14 | * 15 | * Unless required by applicable law or agreed to in writing, software 16 | * distributed under the License is distributed on an "AS IS" BASIS, 17 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | * See the License for the specific language governing permissions and 19 | * limitations under the License. 20 | */ 21 | package vfs 22 | 23 | func isSlash(c uint8) bool { 24 | return c == '/' 25 | } 26 | -------------------------------------------------------------------------------- /vendor/github.com/mandelsoft/vfs/pkg/vfs/utils_windows.go: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2024 Mandelsoft. All rights reserved. 3 | * This file is licensed under the Apache Software License, v. 2 except as noted 4 | * otherwise in the LICENSE file 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package vfs 20 | 21 | func isSlash(c uint8) bool { 22 | return c == '\\' || c == '/' 23 | } 24 | -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /coverage.txt 3 | -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.9.x 5 | - 1.x 6 | 7 | before_install: 8 | - go get -t -v ./... 9 | - go get -t -v github.com/modern-go/reflect2-tests/... 10 | 11 | script: 12 | - ./test.sh 13 | 14 | after_success: 15 | - bash <(curl -s https://codecov.io/bash) 16 | -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [solve-meta] 5 | analyzer-name = "dep" 6 | analyzer-version = 1 7 | input-imports = [] 8 | solver-name = "gps-cdcl" 9 | solver-version = 1 10 | -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/Gopkg.toml: -------------------------------------------------------------------------------- 1 | # Gopkg.toml example 2 | # 3 | # Refer to https://golang.github.io/dep/docs/Gopkg.toml.html 4 | # for detailed Gopkg.toml documentation. 5 | # 6 | # required = ["github.com/user/thing/cmd/thing"] 7 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 8 | # 9 | # [[constraint]] 10 | # name = "github.com/user/project" 11 | # version = "1.0.0" 12 | # 13 | # [[constraint]] 14 | # name = "github.com/user/project2" 15 | # branch = "dev" 16 | # source = "github.com/myfork/project2" 17 | # 18 | # [[override]] 19 | # name = "github.com/x/y" 20 | # version = "2.4.0" 21 | # 22 | # [prune] 23 | # non-go = false 24 | # go-tests = true 25 | # unused-packages = true 26 | 27 | ignored = [] 28 | 29 | [prune] 30 | go-tests = true 31 | unused-packages = true 32 | -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/go_above_118.go: -------------------------------------------------------------------------------- 1 | //+build go1.18 2 | 3 | package reflect2 4 | 5 | import ( 6 | "unsafe" 7 | ) 8 | 9 | // m escapes into the return value, but the caller of mapiterinit 10 | // doesn't let the return value escape. 11 | //go:noescape 12 | //go:linkname mapiterinit reflect.mapiterinit 13 | func mapiterinit(rtype unsafe.Pointer, m unsafe.Pointer, it *hiter) 14 | 15 | func (type2 *UnsafeMapType) UnsafeIterate(obj unsafe.Pointer) MapIterator { 16 | var it hiter 17 | mapiterinit(type2.rtype, *(*unsafe.Pointer)(obj), &it) 18 | return &UnsafeMapIterator{ 19 | hiter: &it, 20 | pKeyRType: type2.pKeyRType, 21 | pElemRType: type2.pElemRType, 22 | } 23 | } -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/go_above_19.go: -------------------------------------------------------------------------------- 1 | //+build go1.9 2 | 3 | package reflect2 4 | 5 | import ( 6 | "unsafe" 7 | ) 8 | 9 | //go:linkname resolveTypeOff reflect.resolveTypeOff 10 | func resolveTypeOff(rtype unsafe.Pointer, off int32) unsafe.Pointer 11 | 12 | //go:linkname makemap reflect.makemap 13 | func makemap(rtype unsafe.Pointer, cap int) (m unsafe.Pointer) 14 | 15 | func makeMapWithSize(rtype unsafe.Pointer, cap int) unsafe.Pointer { 16 | return makemap(rtype, cap) 17 | } 18 | -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/go_below_118.go: -------------------------------------------------------------------------------- 1 | //+build !go1.18 2 | 3 | package reflect2 4 | 5 | import ( 6 | "unsafe" 7 | ) 8 | 9 | // m escapes into the return value, but the caller of mapiterinit 10 | // doesn't let the return value escape. 11 | //go:noescape 12 | //go:linkname mapiterinit reflect.mapiterinit 13 | func mapiterinit(rtype unsafe.Pointer, m unsafe.Pointer) (val *hiter) 14 | 15 | func (type2 *UnsafeMapType) UnsafeIterate(obj unsafe.Pointer) MapIterator { 16 | return &UnsafeMapIterator{ 17 | hiter: mapiterinit(type2.rtype, *(*unsafe.Pointer)(obj)), 18 | pKeyRType: type2.pKeyRType, 19 | pElemRType: type2.pElemRType, 20 | } 21 | } -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/reflect2_amd64.s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandelsoft/spiff/c5d636ea32344f72e76ed5098c4f77b6c92df80b/vendor/github.com/modern-go/reflect2/reflect2_amd64.s -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/reflect2_kind.go: -------------------------------------------------------------------------------- 1 | package reflect2 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | // DefaultTypeOfKind return the non aliased default type for the kind 9 | func DefaultTypeOfKind(kind reflect.Kind) Type { 10 | return kindTypes[kind] 11 | } 12 | 13 | var kindTypes = map[reflect.Kind]Type{ 14 | reflect.Bool: TypeOf(true), 15 | reflect.Uint8: TypeOf(uint8(0)), 16 | reflect.Int8: TypeOf(int8(0)), 17 | reflect.Uint16: TypeOf(uint16(0)), 18 | reflect.Int16: TypeOf(int16(0)), 19 | reflect.Uint32: TypeOf(uint32(0)), 20 | reflect.Int32: TypeOf(int32(0)), 21 | reflect.Uint64: TypeOf(uint64(0)), 22 | reflect.Int64: TypeOf(int64(0)), 23 | reflect.Uint: TypeOf(uint(0)), 24 | reflect.Int: TypeOf(int(0)), 25 | reflect.Float32: TypeOf(float32(0)), 26 | reflect.Float64: TypeOf(float64(0)), 27 | reflect.Uintptr: TypeOf(uintptr(0)), 28 | reflect.String: TypeOf(""), 29 | reflect.UnsafePointer: TypeOf(unsafe.Pointer(nil)), 30 | } 31 | -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/relfect2_386.s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandelsoft/spiff/c5d636ea32344f72e76ed5098c4f77b6c92df80b/vendor/github.com/modern-go/reflect2/relfect2_386.s -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/relfect2_amd64p32.s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandelsoft/spiff/c5d636ea32344f72e76ed5098c4f77b6c92df80b/vendor/github.com/modern-go/reflect2/relfect2_amd64p32.s -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/relfect2_arm.s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandelsoft/spiff/c5d636ea32344f72e76ed5098c4f77b6c92df80b/vendor/github.com/modern-go/reflect2/relfect2_arm.s -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/relfect2_arm64.s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandelsoft/spiff/c5d636ea32344f72e76ed5098c4f77b6c92df80b/vendor/github.com/modern-go/reflect2/relfect2_arm64.s -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/relfect2_mips64x.s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandelsoft/spiff/c5d636ea32344f72e76ed5098c4f77b6c92df80b/vendor/github.com/modern-go/reflect2/relfect2_mips64x.s -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/relfect2_mipsx.s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandelsoft/spiff/c5d636ea32344f72e76ed5098c4f77b6c92df80b/vendor/github.com/modern-go/reflect2/relfect2_mipsx.s -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/relfect2_ppc64x.s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandelsoft/spiff/c5d636ea32344f72e76ed5098c4f77b6c92df80b/vendor/github.com/modern-go/reflect2/relfect2_ppc64x.s -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/relfect2_s390x.s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mandelsoft/spiff/c5d636ea32344f72e76ed5098c4f77b6c92df80b/vendor/github.com/modern-go/reflect2/relfect2_s390x.s -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/safe_field.go: -------------------------------------------------------------------------------- 1 | package reflect2 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | type safeField struct { 9 | reflect.StructField 10 | } 11 | 12 | func (field *safeField) Offset() uintptr { 13 | return field.StructField.Offset 14 | } 15 | 16 | func (field *safeField) Name() string { 17 | return field.StructField.Name 18 | } 19 | 20 | func (field *safeField) PkgPath() string { 21 | return field.StructField.PkgPath 22 | } 23 | 24 | func (field *safeField) Type() Type { 25 | panic("not implemented") 26 | } 27 | 28 | func (field *safeField) Tag() reflect.StructTag { 29 | return field.StructField.Tag 30 | } 31 | 32 | func (field *safeField) Index() []int { 33 | return field.StructField.Index 34 | } 35 | 36 | func (field *safeField) Anonymous() bool { 37 | return field.StructField.Anonymous 38 | } 39 | 40 | func (field *safeField) Set(obj interface{}, value interface{}) { 41 | val := reflect.ValueOf(obj).Elem() 42 | val.FieldByIndex(field.Index()).Set(reflect.ValueOf(value).Elem()) 43 | } 44 | 45 | func (field *safeField) UnsafeSet(obj unsafe.Pointer, value unsafe.Pointer) { 46 | panic("unsafe operation is not supported") 47 | } 48 | 49 | func (field *safeField) Get(obj interface{}) interface{} { 50 | val := reflect.ValueOf(obj).Elem().FieldByIndex(field.Index()) 51 | ptr := reflect.New(val.Type()) 52 | ptr.Elem().Set(val) 53 | return ptr.Interface() 54 | } 55 | 56 | func (field *safeField) UnsafeGet(obj unsafe.Pointer) unsafe.Pointer { 57 | panic("does not support unsafe operation") 58 | } 59 | -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/safe_struct.go: -------------------------------------------------------------------------------- 1 | package reflect2 2 | 3 | type safeStructType struct { 4 | safeType 5 | } 6 | 7 | func (type2 *safeStructType) FieldByName(name string) StructField { 8 | field, found := type2.Type.FieldByName(name) 9 | if !found { 10 | panic("field " + name + " not found") 11 | } 12 | return &safeField{StructField: field} 13 | } 14 | 15 | func (type2 *safeStructType) Field(i int) StructField { 16 | return &safeField{StructField: type2.Type.Field(i)} 17 | } 18 | 19 | func (type2 *safeStructType) FieldByIndex(index []int) StructField { 20 | return &safeField{StructField: type2.Type.FieldByIndex(index)} 21 | } 22 | 23 | func (type2 *safeStructType) FieldByNameFunc(match func(string) bool) StructField { 24 | field, found := type2.Type.FieldByNameFunc(match) 25 | if !found { 26 | panic("field match condition not found in " + type2.Type.String()) 27 | } 28 | return &safeField{StructField: field} 29 | } 30 | -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/unsafe_eface.go: -------------------------------------------------------------------------------- 1 | package reflect2 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | type eface struct { 9 | rtype unsafe.Pointer 10 | data unsafe.Pointer 11 | } 12 | 13 | func unpackEFace(obj interface{}) *eface { 14 | return (*eface)(unsafe.Pointer(&obj)) 15 | } 16 | 17 | func packEFace(rtype unsafe.Pointer, data unsafe.Pointer) interface{} { 18 | var i interface{} 19 | e := (*eface)(unsafe.Pointer(&i)) 20 | e.rtype = rtype 21 | e.data = data 22 | return i 23 | } 24 | 25 | type UnsafeEFaceType struct { 26 | unsafeType 27 | } 28 | 29 | func newUnsafeEFaceType(cfg *frozenConfig, type1 reflect.Type) *UnsafeEFaceType { 30 | return &UnsafeEFaceType{ 31 | unsafeType: *newUnsafeType(cfg, type1), 32 | } 33 | } 34 | 35 | func (type2 *UnsafeEFaceType) IsNil(obj interface{}) bool { 36 | if obj == nil { 37 | return true 38 | } 39 | objEFace := unpackEFace(obj) 40 | assertType("Type.IsNil argument 1", type2.ptrRType, objEFace.rtype) 41 | return type2.UnsafeIsNil(objEFace.data) 42 | } 43 | 44 | func (type2 *UnsafeEFaceType) UnsafeIsNil(ptr unsafe.Pointer) bool { 45 | if ptr == nil { 46 | return true 47 | } 48 | return unpackEFace(*(*interface{})(ptr)).data == nil 49 | } 50 | 51 | func (type2 *UnsafeEFaceType) Indirect(obj interface{}) interface{} { 52 | objEFace := unpackEFace(obj) 53 | assertType("Type.Indirect argument 1", type2.ptrRType, objEFace.rtype) 54 | return type2.UnsafeIndirect(objEFace.data) 55 | } 56 | 57 | func (type2 *UnsafeEFaceType) UnsafeIndirect(ptr unsafe.Pointer) interface{} { 58 | return *(*interface{})(ptr) 59 | } 60 | -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/unsafe_iface.go: -------------------------------------------------------------------------------- 1 | package reflect2 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | type iface struct { 9 | itab *itab 10 | data unsafe.Pointer 11 | } 12 | 13 | type itab struct { 14 | ignore unsafe.Pointer 15 | rtype unsafe.Pointer 16 | } 17 | 18 | func IFaceToEFace(ptr unsafe.Pointer) interface{} { 19 | iface := (*iface)(ptr) 20 | if iface.itab == nil { 21 | return nil 22 | } 23 | return packEFace(iface.itab.rtype, iface.data) 24 | } 25 | 26 | type UnsafeIFaceType struct { 27 | unsafeType 28 | } 29 | 30 | func newUnsafeIFaceType(cfg *frozenConfig, type1 reflect.Type) *UnsafeIFaceType { 31 | return &UnsafeIFaceType{ 32 | unsafeType: *newUnsafeType(cfg, type1), 33 | } 34 | } 35 | 36 | func (type2 *UnsafeIFaceType) Indirect(obj interface{}) interface{} { 37 | objEFace := unpackEFace(obj) 38 | assertType("Type.Indirect argument 1", type2.ptrRType, objEFace.rtype) 39 | return type2.UnsafeIndirect(objEFace.data) 40 | } 41 | 42 | func (type2 *UnsafeIFaceType) UnsafeIndirect(ptr unsafe.Pointer) interface{} { 43 | return IFaceToEFace(ptr) 44 | } 45 | 46 | func (type2 *UnsafeIFaceType) IsNil(obj interface{}) bool { 47 | if obj == nil { 48 | return true 49 | } 50 | objEFace := unpackEFace(obj) 51 | assertType("Type.IsNil argument 1", type2.ptrRType, objEFace.rtype) 52 | return type2.UnsafeIsNil(objEFace.data) 53 | } 54 | 55 | func (type2 *UnsafeIFaceType) UnsafeIsNil(ptr unsafe.Pointer) bool { 56 | if ptr == nil { 57 | return true 58 | } 59 | iface := (*iface)(ptr) 60 | if iface.itab == nil { 61 | return true 62 | } 63 | return false 64 | } 65 | -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/unsafe_ptr.go: -------------------------------------------------------------------------------- 1 | package reflect2 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | type UnsafePtrType struct { 9 | unsafeType 10 | } 11 | 12 | func newUnsafePtrType(cfg *frozenConfig, type1 reflect.Type) *UnsafePtrType { 13 | return &UnsafePtrType{ 14 | unsafeType: *newUnsafeType(cfg, type1), 15 | } 16 | } 17 | 18 | func (type2 *UnsafePtrType) IsNil(obj interface{}) bool { 19 | if obj == nil { 20 | return true 21 | } 22 | objEFace := unpackEFace(obj) 23 | assertType("Type.IsNil argument 1", type2.ptrRType, objEFace.rtype) 24 | return type2.UnsafeIsNil(objEFace.data) 25 | } 26 | 27 | func (type2 *UnsafePtrType) UnsafeIsNil(ptr unsafe.Pointer) bool { 28 | if ptr == nil { 29 | return true 30 | } 31 | return *(*unsafe.Pointer)(ptr) == nil 32 | } 33 | 34 | func (type2 *UnsafePtrType) LikePtr() bool { 35 | return true 36 | } 37 | 38 | func (type2 *UnsafePtrType) Indirect(obj interface{}) interface{} { 39 | objEFace := unpackEFace(obj) 40 | assertType("Type.Indirect argument 1", type2.ptrRType, objEFace.rtype) 41 | return type2.UnsafeIndirect(objEFace.data) 42 | } 43 | 44 | func (type2 *UnsafePtrType) UnsafeIndirect(ptr unsafe.Pointer) interface{} { 45 | return packEFace(type2.rtype, *(*unsafe.Pointer)(ptr)) 46 | } 47 | -------------------------------------------------------------------------------- /vendor/github.com/modern-go/reflect2/unsafe_struct.go: -------------------------------------------------------------------------------- 1 | package reflect2 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | type UnsafeStructType struct { 9 | unsafeType 10 | likePtr bool 11 | } 12 | 13 | func newUnsafeStructType(cfg *frozenConfig, type1 reflect.Type) *UnsafeStructType { 14 | return &UnsafeStructType{ 15 | unsafeType: *newUnsafeType(cfg, type1), 16 | likePtr: likePtrType(type1), 17 | } 18 | } 19 | 20 | func (type2 *UnsafeStructType) LikePtr() bool { 21 | return type2.likePtr 22 | } 23 | 24 | func (type2 *UnsafeStructType) Indirect(obj interface{}) interface{} { 25 | objEFace := unpackEFace(obj) 26 | assertType("Type.Indirect argument 1", type2.ptrRType, objEFace.rtype) 27 | return type2.UnsafeIndirect(objEFace.data) 28 | } 29 | 30 | func (type2 *UnsafeStructType) UnsafeIndirect(ptr unsafe.Pointer) interface{} { 31 | if type2.likePtr { 32 | return packEFace(type2.rtype, *(*unsafe.Pointer)(ptr)) 33 | } 34 | return packEFace(type2.rtype, ptr) 35 | } 36 | 37 | func (type2 *UnsafeStructType) FieldByName(name string) StructField { 38 | structField, found := type2.Type.FieldByName(name) 39 | if !found { 40 | return nil 41 | } 42 | return newUnsafeStructField(type2, structField) 43 | } 44 | 45 | func (type2 *UnsafeStructType) Field(i int) StructField { 46 | return newUnsafeStructField(type2, type2.Type.Field(i)) 47 | } 48 | 49 | func (type2 *UnsafeStructType) FieldByIndex(index []int) StructField { 50 | return newUnsafeStructField(type2, type2.Type.FieldByIndex(index)) 51 | } 52 | 53 | func (type2 *UnsafeStructType) FieldByNameFunc(match func(string) bool) StructField { 54 | structField, found := type2.Type.FieldByNameFunc(match) 55 | if !found { 56 | panic("field match condition not found in " + type2.Type.String()) 57 | } 58 | return newUnsafeStructField(type2, structField) 59 | } 60 | -------------------------------------------------------------------------------- /vendor/github.com/nxadm/tail/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .test/ 3 | examples/_* -------------------------------------------------------------------------------- /vendor/github.com/nxadm/tail/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang 2 | 3 | RUN mkdir -p $GOPATH/src/github.com/nxadm/tail/ 4 | ADD . $GOPATH/src/github.com/nxadm/tail/ 5 | 6 | # expecting to fetch dependencies successfully. 7 | RUN go get -v github.com/nxadm/tail 8 | 9 | # expecting to run the test successfully. 10 | RUN go test -v github.com/nxadm/tail 11 | 12 | # expecting to install successfully 13 | RUN go install -v github.com/nxadm/tail 14 | RUN go install -v github.com/nxadm/tail/cmd/gotail 15 | 16 | RUN $GOPATH/bin/gotail -h || true 17 | 18 | ENV PATH $GOPATH/bin:$PATH 19 | CMD ["gotail"] 20 | -------------------------------------------------------------------------------- /vendor/github.com/nxadm/tail/LICENSE: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | # © Copyright 2015 Hewlett Packard Enterprise Development LP 4 | Copyright (c) 2014 ActiveState 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/nxadm/tail/README.md: -------------------------------------------------------------------------------- 1 | ![ci](https://github.com/nxadm/tail/workflows/ci/badge.svg)[![Go Reference](https://pkg.go.dev/badge/github.com/nxadm/tail.svg)](https://pkg.go.dev/github.com/nxadm/tail) 2 | 3 | # tail functionality in Go 4 | 5 | nxadm/tail provides a Go library that emulates the features of the BSD `tail` 6 | program. The library comes with full support for truncation/move detection as 7 | it is designed to work with log rotation tools. The library works on all 8 | operating systems supported by Go, including POSIX systems like Linux and 9 | *BSD, and MS Windows. Go 1.9 is the oldest compiler release supported. 10 | 11 | A simple example: 12 | 13 | ```Go 14 | // Create a tail 15 | t, err := tail.TailFile( 16 | "/var/log/nginx.log", tail.Config{Follow: true, ReOpen: true}) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | // Print the text of each received line 22 | for line := range t.Lines { 23 | fmt.Println(line.Text) 24 | } 25 | ``` 26 | 27 | See [API documentation](https://pkg.go.dev/github.com/nxadm/tail). 28 | 29 | ## Installing 30 | 31 | go get github.com/nxadm/tail/... 32 | 33 | ## History 34 | 35 | This project is an active, drop-in replacement for the 36 | [abandoned](https://en.wikipedia.org/wiki/HPE_Helion) Go tail library at 37 | [hpcloud](https://github.com/hpcloud/tail). Next to 38 | [addressing open issues/PRs of the original project](https://github.com/nxadm/tail/issues/6), 39 | nxadm/tail continues the development by keeping up to date with the Go toolchain 40 | (e.g. go modules) and dependencies, completing the documentation, adding features 41 | and fixing bugs. 42 | 43 | ## Examples 44 | Examples, e.g. used to debug an issue, are kept in the [examples directory](/examples). -------------------------------------------------------------------------------- /vendor/github.com/nxadm/tail/ratelimiter/Licence: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 99designs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /vendor/github.com/nxadm/tail/ratelimiter/memory.go: -------------------------------------------------------------------------------- 1 | package ratelimiter 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | ) 7 | 8 | const ( 9 | GC_SIZE int = 100 10 | GC_PERIOD time.Duration = 60 * time.Second 11 | ) 12 | 13 | type Memory struct { 14 | store map[string]LeakyBucket 15 | lastGCCollected time.Time 16 | } 17 | 18 | func NewMemory() *Memory { 19 | m := new(Memory) 20 | m.store = make(map[string]LeakyBucket) 21 | m.lastGCCollected = time.Now() 22 | return m 23 | } 24 | 25 | func (m *Memory) GetBucketFor(key string) (*LeakyBucket, error) { 26 | 27 | bucket, ok := m.store[key] 28 | if !ok { 29 | return nil, errors.New("miss") 30 | } 31 | 32 | return &bucket, nil 33 | } 34 | 35 | func (m *Memory) SetBucketFor(key string, bucket LeakyBucket) error { 36 | 37 | if len(m.store) > GC_SIZE { 38 | m.GarbageCollect() 39 | } 40 | 41 | m.store[key] = bucket 42 | 43 | return nil 44 | } 45 | 46 | func (m *Memory) GarbageCollect() { 47 | now := time.Now() 48 | 49 | // rate limit GC to once per minute 50 | if now.Unix() >= m.lastGCCollected.Add(GC_PERIOD).Unix() { 51 | for key, bucket := range m.store { 52 | // if the bucket is drained, then GC 53 | if bucket.DrainedAt().Unix() < now.Unix() { 54 | delete(m.store, key) 55 | } 56 | } 57 | 58 | m.lastGCCollected = now 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /vendor/github.com/nxadm/tail/ratelimiter/storage.go: -------------------------------------------------------------------------------- 1 | package ratelimiter 2 | 3 | type Storage interface { 4 | GetBucketFor(string) (*LeakyBucket, error) 5 | SetBucketFor(string, LeakyBucket) error 6 | } 7 | -------------------------------------------------------------------------------- /vendor/github.com/nxadm/tail/tail_posix.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail 2 | // +build !windows 3 | 4 | package tail 5 | 6 | import ( 7 | "os" 8 | ) 9 | 10 | // Deprecated: this function is only useful internally and, as such, 11 | // it will be removed from the API in a future major release. 12 | // 13 | // OpenFile proxies a os.Open call for a file so it can be correctly tailed 14 | // on POSIX and non-POSIX OSes like MS Windows. 15 | func OpenFile(name string) (file *os.File, err error) { 16 | return os.Open(name) 17 | } 18 | -------------------------------------------------------------------------------- /vendor/github.com/nxadm/tail/tail_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail 2 | // +build windows 3 | 4 | package tail 5 | 6 | import ( 7 | "os" 8 | 9 | "github.com/nxadm/tail/winfile" 10 | ) 11 | 12 | // Deprecated: this function is only useful internally and, as such, 13 | // it will be removed from the API in a future major release. 14 | // 15 | // OpenFile proxies a os.Open call for a file so it can be correctly tailed 16 | // on POSIX and non-POSIX OSes like MS Windows. 17 | func OpenFile(name string) (file *os.File, err error) { 18 | return winfile.OpenFile(name, os.O_RDONLY, 0) 19 | } 20 | -------------------------------------------------------------------------------- /vendor/github.com/nxadm/tail/util/util.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail 2 | // Copyright (c) 2015 HPE Software Inc. All rights reserved. 3 | // Copyright (c) 2013 ActiveState Software Inc. All rights reserved. 4 | 5 | package util 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "os" 11 | "runtime/debug" 12 | ) 13 | 14 | type Logger struct { 15 | *log.Logger 16 | } 17 | 18 | var LOGGER = &Logger{log.New(os.Stderr, "", log.LstdFlags)} 19 | 20 | // fatal is like panic except it displays only the current goroutine's stack. 21 | func Fatal(format string, v ...interface{}) { 22 | // https://github.com/nxadm/log/blob/master/log.go#L45 23 | LOGGER.Output(2, fmt.Sprintf("FATAL -- "+format, v...)+"\n"+string(debug.Stack())) 24 | os.Exit(1) 25 | } 26 | 27 | // partitionString partitions the string into chunks of given size, 28 | // with the last chunk of variable size. 29 | func PartitionString(s string, chunkSize int) []string { 30 | if chunkSize <= 0 { 31 | panic("invalid chunkSize") 32 | } 33 | length := len(s) 34 | chunks := 1 + length/chunkSize 35 | start := 0 36 | end := chunkSize 37 | parts := make([]string, 0, chunks) 38 | for { 39 | if end > length { 40 | end = length 41 | } 42 | parts = append(parts, s[start:end]) 43 | if end == length { 44 | break 45 | } 46 | start, end = end, end+chunkSize 47 | } 48 | return parts 49 | } 50 | -------------------------------------------------------------------------------- /vendor/github.com/nxadm/tail/watch/filechanges.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail 2 | package watch 3 | 4 | type FileChanges struct { 5 | Modified chan bool // Channel to get notified of modifications 6 | Truncated chan bool // Channel to get notified of truncations 7 | Deleted chan bool // Channel to get notified of deletions/renames 8 | } 9 | 10 | func NewFileChanges() *FileChanges { 11 | return &FileChanges{ 12 | make(chan bool, 1), make(chan bool, 1), make(chan bool, 1)} 13 | } 14 | 15 | func (fc *FileChanges) NotifyModified() { 16 | sendOnlyIfEmpty(fc.Modified) 17 | } 18 | 19 | func (fc *FileChanges) NotifyTruncated() { 20 | sendOnlyIfEmpty(fc.Truncated) 21 | } 22 | 23 | func (fc *FileChanges) NotifyDeleted() { 24 | sendOnlyIfEmpty(fc.Deleted) 25 | } 26 | 27 | // sendOnlyIfEmpty sends on a bool channel only if the channel has no 28 | // backlog to be read by other goroutines. This concurrency pattern 29 | // can be used to notify other goroutines if and only if they are 30 | // looking for it (i.e., subsequent notifications can be compressed 31 | // into one). 32 | func sendOnlyIfEmpty(ch chan bool) { 33 | select { 34 | case ch <- true: 35 | default: 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /vendor/github.com/nxadm/tail/watch/watch.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail 2 | // Copyright (c) 2015 HPE Software Inc. All rights reserved. 3 | // Copyright (c) 2013 ActiveState Software Inc. All rights reserved. 4 | 5 | package watch 6 | 7 | import "gopkg.in/tomb.v1" 8 | 9 | // FileWatcher monitors file-level events. 10 | type FileWatcher interface { 11 | // BlockUntilExists blocks until the file comes into existence. 12 | BlockUntilExists(*tomb.Tomb) error 13 | 14 | // ChangeEvents reports on changes to a file, be it modification, 15 | // deletion, renames or truncations. Returned FileChanges group of 16 | // channels will be closed, thus become unusable, after a deletion 17 | // or truncation event. 18 | // In order to properly report truncations, ChangeEvents requires 19 | // the caller to pass their current offset in the file. 20 | ChangeEvents(*tomb.Tomb, int64) (*FileChanges, error) 21 | } 22 | -------------------------------------------------------------------------------- /vendor/github.com/subosito/gotenv/.env: -------------------------------------------------------------------------------- 1 | HELLO=world 2 | -------------------------------------------------------------------------------- /vendor/github.com/subosito/gotenv/.env.invalid: -------------------------------------------------------------------------------- 1 | lol$wut 2 | -------------------------------------------------------------------------------- /vendor/github.com/subosito/gotenv/.gitignore: -------------------------------------------------------------------------------- 1 | *.test 2 | *.out 3 | annotate.json 4 | profile.cov 5 | -------------------------------------------------------------------------------- /vendor/github.com/subosito/gotenv/.golangci.yaml: -------------------------------------------------------------------------------- 1 | # Options for analysis running. 2 | run: 3 | timeout: 1m 4 | 5 | linters-settings: 6 | gofmt: 7 | simplify: true 8 | -------------------------------------------------------------------------------- /vendor/github.com/subosito/gotenv/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.4.0] - 2022-06-02 4 | 5 | ### Added 6 | 7 | - Add `Marshal` and `Unmarshal` helpers 8 | 9 | ### Changed 10 | 11 | - The CI will now run a linter and the tests on PRs. 12 | 13 | ## [1.3.0] - 2022-05-23 14 | 15 | ### Added 16 | 17 | - Support = within double-quoted strings 18 | - Add support for multiline values 19 | 20 | ### Changed 21 | 22 | - `OverLoad` prefer environment variables over local variables 23 | 24 | ## [1.2.0] - 2019-08-03 25 | 26 | ### Added 27 | 28 | - Add `Must` helper to raise an error as panic. It can be used with `Load` and `OverLoad`. 29 | - Add more tests to be 100% coverage. 30 | - Add CHANGELOG 31 | - Add more OS for the test: OSX and Windows 32 | 33 | ### Changed 34 | 35 | - Reduce complexity and improve source code for having `A+` score in [goreportcard](https://goreportcard.com/report/github.com/subosito/gotenv). 36 | - Updated README with mentions to all available functions 37 | 38 | ### Removed 39 | 40 | - Remove `ErrFormat` 41 | - Remove `MustLoad` and `MustOverload`, replaced with `Must` helper. 42 | 43 | ## [1.1.1] - 2018-06-05 44 | 45 | ### Changed 46 | 47 | - Replace `os.Getenv` with `os.LookupEnv` to ensure that the environment variable is not set, by [radding](https://github.com/radding) 48 | 49 | ## [1.1.0] - 2017-03-20 50 | 51 | ### Added 52 | 53 | - Supports carriage return in env 54 | - Handle files with UTF-8 BOM 55 | 56 | ### Changed 57 | 58 | - Whitespace handling 59 | 60 | ### Fixed 61 | 62 | - Incorrect variable expansion 63 | - Handling escaped '$' characters 64 | 65 | ## [1.0.0] - 2014-10-05 66 | 67 | First stable release. 68 | 69 | -------------------------------------------------------------------------------- /vendor/github.com/subosito/gotenv/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Alif Rachmawadi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /yaml/equals.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | ) 7 | 8 | func Equals(a, b Node, path []string) (bool, string) { 9 | 10 | if reflect.TypeOf(a.Value()) != reflect.TypeOf(b.Value()) { 11 | return false, fmt.Sprintf("non matching type %T!=%T at %+v", a.Value(), b.Value(), path) 12 | 13 | } 14 | if a.Value() == nil && b.Value() == nil { 15 | return true, "" 16 | } 17 | if a.Value() == nil || b.Value() == nil { 18 | return false, fmt.Sprintf("found non-matching nil at %+v", path) 19 | } 20 | 21 | if !reflect.DeepEqual(a.GetAnnotation(), b.GetAnnotation()) { 22 | return false, fmt.Sprintf("annotation diff at %+v: %v", path, a.GetAnnotation()) 23 | } 24 | switch va := a.Value().(type) { 25 | case []Node: 26 | vb := b.Value().([]Node) 27 | if len(va) != len(vb) { 28 | return false, fmt.Sprintf("list length mismatch %d!=%d at %+v", len(va), len(vb), path) 29 | } 30 | for i, v := range va { 31 | if b, r := Equals(v, vb[i], append(path, fmt.Sprintf("[%d]", i))); !b { 32 | return b, r 33 | } 34 | } 35 | case map[string]Node: 36 | vb := b.Value().(map[string]Node) 37 | 38 | for k, v := range va { 39 | _, ok := vb[k] 40 | if !ok { 41 | return false, fmt.Sprintf("key %q missing in b at %+v", k, path) 42 | } 43 | if b, r := Equals(v, vb[k], append(path, k)); !b { 44 | return b, r 45 | } 46 | } 47 | for k := range vb { 48 | _, ok := va[k] 49 | if !ok { 50 | return false, fmt.Sprintf("additional key %q in b at %+v", k, path) 51 | } 52 | } 53 | default: 54 | e := reflect.DeepEqual(a.Value(), b.Value()) 55 | if !e { 56 | return false, fmt.Sprintf("diff at %+v: %v!=%v", path, a.Value(), b.Value()) 57 | } 58 | 59 | } 60 | return true, "" 61 | } 62 | -------------------------------------------------------------------------------- /yaml/init_test.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func Test(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "YAML parsing") 13 | } 14 | 15 | func parseYAML(source string) Node { 16 | parsed, err := Parse("test", []byte(source)) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | return parsed 22 | } 23 | 24 | func node(val interface{}) Node { 25 | return NewNode(val, "test") 26 | } 27 | -------------------------------------------------------------------------------- /yaml/value.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "sort" 5 | ) 6 | 7 | type ComparableValue interface { 8 | EquivalentTo(interface{}) bool 9 | } 10 | 11 | func GetSortedKeys(unsortedMap map[string]Node) []string { 12 | keys := make([]string, len(unsortedMap)) 13 | i := 0 14 | for k, _ := range unsortedMap { 15 | keys[i] = k 16 | i++ 17 | } 18 | sort.Strings(keys) 19 | return keys 20 | } 21 | --------------------------------------------------------------------------------