mirror of
				https://github.com/kardolus/chatgpt-cli.git
				synced 2024-09-08 23:15:00 +03:00 
			
		
		
		
	Switch to viper
This commit is contained in:
		| @@ -29,10 +29,10 @@ building the application: | ||||
| ./bin/chatgpt what type of dog is a Jack Russel? | ||||
| ``` | ||||
|  | ||||
| ## Up Next | ||||
|  | ||||
| * Add a command line flag for non-streaming | ||||
| * Use Viper for command line parsing | ||||
| For more options see: | ||||
| ```shell | ||||
| ./bin/chatgpt --help | ||||
| ``` | ||||
|  | ||||
| ## Links | ||||
|  | ||||
|   | ||||
| @@ -2,40 +2,58 @@ package main | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"github.com/kardolus/chatgpt-poc/client" | ||||
| 	"github.com/kardolus/chatgpt-poc/http" | ||||
| 	"log" | ||||
| 	"github.com/spf13/cobra" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| const secretEnv = "OPENAI_API_KEY" | ||||
|  | ||||
| var queryMode bool | ||||
|  | ||||
| func main() { | ||||
| 	exit(run()) | ||||
| } | ||||
|  | ||||
| func exit(err error) { | ||||
| 	if err == nil { | ||||
| 		os.Exit(0) | ||||
| 	var rootCmd = &cobra.Command{ | ||||
| 		Use:   "chatgpt", | ||||
| 		Short: "ChatGPT Proof of Concept", | ||||
| 		Long:  "A Proof of Concept for building ChatGPT clients.", | ||||
| 		RunE:  run, | ||||
| 	} | ||||
|  | ||||
| 	rootCmd.PersistentFlags().BoolVarP(&queryMode, "query", "q", false, "Use query mode instead of stream mode") | ||||
|  | ||||
| 	viper.AutomaticEnv() | ||||
|  | ||||
| 	if err := rootCmd.Execute(); err != nil { | ||||
| 		fmt.Println(err) | ||||
| 		os.Exit(1) | ||||
| 	} | ||||
| 	log.Printf("Error: %s\n", err) | ||||
| 	os.Exit(1) | ||||
| } | ||||
|  | ||||
| func run() error { | ||||
| 	if len(os.Args) <= 1 { | ||||
| func run(cmd *cobra.Command, args []string) error { | ||||
| 	if len(args) == 0 { | ||||
| 		return errors.New("you must specify your query") | ||||
| 	} | ||||
|  | ||||
| 	secret := os.Getenv(secretEnv) | ||||
| 	secret := viper.GetString(secretEnv) | ||||
| 	if secret == "" { | ||||
| 		return errors.New("missing environment variable: " + secretEnv) | ||||
| 	} | ||||
| 	client := client.New(http.New().WithSecret(secret)) | ||||
|  | ||||
| 	if err := client.Stream(strings.Join(os.Args[1:], " ")); err != nil { | ||||
| 		return err | ||||
| 	if queryMode { | ||||
| 		result, err := client.Query(strings.Join(args, " ")) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		fmt.Println(result) | ||||
| 	} else { | ||||
| 		if err := client.Stream(strings.Join(args, " ")); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
|   | ||||
							
								
								
									
										15
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,11 +3,26 @@ module github.com/kardolus/chatgpt-poc | ||||
| go 1.18 | ||||
|  | ||||
| require ( | ||||
| 	github.com/fsnotify/fsnotify v1.6.0 // indirect | ||||
| 	github.com/golang/mock v1.6.0 // indirect | ||||
| 	github.com/google/go-cmp v0.5.9 // indirect | ||||
| 	github.com/hashicorp/hcl v1.0.0 // indirect | ||||
| 	github.com/inconshreveable/mousetrap v1.1.0 // indirect | ||||
| 	github.com/magiconair/properties v1.8.7 // indirect | ||||
| 	github.com/mitchellh/mapstructure v1.5.0 // indirect | ||||
| 	github.com/onsi/gomega v1.27.6 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.0.6 // indirect | ||||
| 	github.com/sclevine/spec v1.4.0 // indirect | ||||
| 	github.com/spf13/afero v1.9.3 // indirect | ||||
| 	github.com/spf13/cast v1.5.0 // indirect | ||||
| 	github.com/spf13/cobra v1.7.0 // indirect | ||||
| 	github.com/spf13/jwalterweatherman v1.1.0 // indirect | ||||
| 	github.com/spf13/pflag v1.0.5 // indirect | ||||
| 	github.com/spf13/viper v1.15.0 // indirect | ||||
| 	github.com/subosito/gotenv v1.4.2 // indirect | ||||
| 	golang.org/x/net v0.8.0 // indirect | ||||
| 	golang.org/x/sys v0.6.0 // indirect | ||||
| 	golang.org/x/text v0.8.0 // indirect | ||||
| 	gopkg.in/ini.v1 v1.67.0 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
| ) | ||||
|   | ||||
							
								
								
									
										453
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										453
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,38 +1,491 @@ | ||||
| cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= | ||||
| cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= | ||||
| cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= | ||||
| cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= | ||||
| cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= | ||||
| cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= | ||||
| cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= | ||||
| cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= | ||||
| cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= | ||||
| cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= | ||||
| cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= | ||||
| cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= | ||||
| cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= | ||||
| cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= | ||||
| cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= | ||||
| cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= | ||||
| cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= | ||||
| cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= | ||||
| cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= | ||||
| cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= | ||||
| cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= | ||||
| cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= | ||||
| cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= | ||||
| cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= | ||||
| cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= | ||||
| cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= | ||||
| cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= | ||||
| cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= | ||||
| cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= | ||||
| cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= | ||||
| cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= | ||||
| cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= | ||||
| cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= | ||||
| cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= | ||||
| cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= | ||||
| dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | ||||
| github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | ||||
| github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | ||||
| github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | ||||
| github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= | ||||
| github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= | ||||
| github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= | ||||
| github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= | ||||
| github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= | ||||
| github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | ||||
| github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | ||||
| github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= | ||||
| github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= | ||||
| github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= | ||||
| github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | ||||
| github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= | ||||
| github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= | ||||
| github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= | ||||
| github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= | ||||
| github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= | ||||
| github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | ||||
| github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||
| github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||
| github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||
| github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | ||||
| github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | ||||
| github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= | ||||
| github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= | ||||
| github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= | ||||
| github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= | ||||
| github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= | ||||
| github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= | ||||
| github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= | ||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= | ||||
| github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= | ||||
| github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= | ||||
| github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | ||||
| github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | ||||
| github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | ||||
| github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | ||||
| github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | ||||
| github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= | ||||
| github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||
| github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||
| github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||
| github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||||
| github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||
| github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | ||||
| github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= | ||||
| github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= | ||||
| github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
| github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
| github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= | ||||
| github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= | ||||
| github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | ||||
| github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= | ||||
| github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | ||||
| github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | ||||
| github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= | ||||
| github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= | ||||
| github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= | ||||
| github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= | ||||
| github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= | ||||
| github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= | ||||
| github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= | ||||
| github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= | ||||
| github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | ||||
| github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= | ||||
| github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||||
| github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
| github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= | ||||
| github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= | ||||
| github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= | ||||
| github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | ||||
| github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= | ||||
| github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= | ||||
| github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= | ||||
| github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= | ||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||||
| github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | ||||
| github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= | ||||
| github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= | ||||
| github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= | ||||
| github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= | ||||
| github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= | ||||
| github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= | ||||
| github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= | ||||
| github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= | ||||
| github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= | ||||
| github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= | ||||
| github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= | ||||
| github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||||
| github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||||
| github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= | ||||
| github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||||
| github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||
| github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||||
| github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||||
| github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= | ||||
| github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= | ||||
| github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | ||||
| go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= | ||||
| go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= | ||||
| go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= | ||||
| golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | ||||
| golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= | ||||
| golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= | ||||
| golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= | ||||
| golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= | ||||
| golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= | ||||
| golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= | ||||
| golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= | ||||
| golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | ||||
| golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | ||||
| golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= | ||||
| golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= | ||||
| golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | ||||
| golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | ||||
| golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | ||||
| golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= | ||||
| golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= | ||||
| golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= | ||||
| golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= | ||||
| golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | ||||
| golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | ||||
| golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | ||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
| golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
| golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
| golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
| golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
| golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||
| golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||
| golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= | ||||
| golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= | ||||
| golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= | ||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
| golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= | ||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= | ||||
| golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | ||||
| golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= | ||||
| golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | ||||
| golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | ||||
| golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | ||||
| golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||
| golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||
| golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||
| golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= | ||||
| golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= | ||||
| golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= | ||||
| golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||
| golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||
| golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||
| golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||
| golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= | ||||
| golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= | ||||
| golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= | ||||
| golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= | ||||
| golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= | ||||
| golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= | ||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= | ||||
| google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= | ||||
| google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | ||||
| google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | ||||
| google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= | ||||
| google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= | ||||
| google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= | ||||
| google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | ||||
| google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | ||||
| google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | ||||
| google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | ||||
| google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | ||||
| google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= | ||||
| google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= | ||||
| google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= | ||||
| google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= | ||||
| google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= | ||||
| google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= | ||||
| google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= | ||||
| google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||
| google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | ||||
| google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||
| google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||
| google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||
| google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||||
| google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | ||||
| google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | ||||
| google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= | ||||
| google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= | ||||
| google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= | ||||
| google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= | ||||
| google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= | ||||
| google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||
| google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= | ||||
| google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= | ||||
| google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= | ||||
| google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= | ||||
| google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||
| google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||
| google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||
| google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= | ||||
| google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= | ||||
| google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= | ||||
| google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= | ||||
| google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= | ||||
| google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= | ||||
| google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= | ||||
| google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= | ||||
| google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | ||||
| google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | ||||
| google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | ||||
| google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | ||||
| google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | ||||
| google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= | ||||
| google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | ||||
| gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= | ||||
| gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | ||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= | ||||
| honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= | ||||
| honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= | ||||
| rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= | ||||
| rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= | ||||
| rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= | ||||
|   | ||||
							
								
								
									
										12
									
								
								vendor/github.com/fsnotify/fsnotify/.editorconfig
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								vendor/github.com/fsnotify/fsnotify/.editorconfig
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| root = true | ||||
|  | ||||
| [*.go] | ||||
| indent_style = tab | ||||
| indent_size = 4 | ||||
| insert_final_newline = true | ||||
|  | ||||
| [*.{yml,yaml}] | ||||
| indent_style = space | ||||
| indent_size = 2 | ||||
| insert_final_newline = true | ||||
| trim_trailing_whitespace = true | ||||
							
								
								
									
										1
									
								
								vendor/github.com/fsnotify/fsnotify/.gitattributes
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/fsnotify/fsnotify/.gitattributes
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| go.sum linguist-generated | ||||
							
								
								
									
										6
									
								
								vendor/github.com/fsnotify/fsnotify/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/fsnotify/fsnotify/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| # go test -c output | ||||
| *.test | ||||
| *.test.exe | ||||
|  | ||||
| # Output of go build ./cmd/fsnotify | ||||
| /fsnotify | ||||
							
								
								
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/.mailmap
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/fsnotify/fsnotify/.mailmap
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| Chris Howey <howeyc@gmail.com> <chris@howey.me> | ||||
| Nathan Youngman <git@nathany.com> <4566+nathany@users.noreply.github.com> | ||||
							
								
								
									
										470
									
								
								vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										470
									
								
								vendor/github.com/fsnotify/fsnotify/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,470 @@ | ||||
| # Changelog | ||||
|  | ||||
| All notable changes to this project will be documented in this file. | ||||
|  | ||||
| The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), | ||||
| and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | ||||
|  | ||||
| ## [Unreleased] | ||||
|  | ||||
| Nothing yet. | ||||
|  | ||||
| ## [1.6.0] - 2022-10-13 | ||||
|  | ||||
| This version of fsnotify needs Go 1.16 (this was already the case since 1.5.1, | ||||
| but not documented). It also increases the minimum Linux version to 2.6.32. | ||||
|  | ||||
| ### Additions | ||||
|  | ||||
| - all: add `Event.Has()` and `Op.Has()` ([#477]) | ||||
|  | ||||
|   This makes checking events a lot easier; for example: | ||||
|  | ||||
| 	    if event.Op&Write == Write && !(event.Op&Remove == Remove) { | ||||
| 	    } | ||||
|  | ||||
| 	Becomes: | ||||
|  | ||||
| 	    if event.Has(Write) && !event.Has(Remove) { | ||||
| 	    } | ||||
|  | ||||
| - all: add cmd/fsnotify ([#463]) | ||||
|  | ||||
|   A command-line utility for testing and some examples. | ||||
|  | ||||
| ### Changes and fixes | ||||
|  | ||||
| - inotify: don't ignore events for files that don't exist ([#260], [#470]) | ||||
|  | ||||
|   Previously the inotify watcher would call `os.Lstat()` to check if a file | ||||
|   still exists before emitting events. | ||||
|  | ||||
|   This was inconsistent with other platforms and resulted in inconsistent event | ||||
|   reporting (e.g. when a file is quickly removed and re-created), and generally | ||||
|   a source of confusion. It was added in 2013 to fix a memory leak that no | ||||
|   longer exists. | ||||
|  | ||||
| - all: return `ErrNonExistentWatch` when `Remove()` is called on a path that's | ||||
|   not watched ([#460]) | ||||
|  | ||||
| - inotify: replace epoll() with non-blocking inotify ([#434]) | ||||
|  | ||||
|   Non-blocking inotify was not generally available at the time this library was | ||||
|   written in 2014, but now it is. As a result, the minimum Linux version is | ||||
|   bumped from 2.6.27 to 2.6.32. This hugely simplifies the code and is faster. | ||||
|  | ||||
| - kqueue: don't check for events every 100ms ([#480]) | ||||
|  | ||||
|   The watcher would wake up every 100ms, even when there was nothing to do. Now | ||||
|   it waits until there is something to do. | ||||
|  | ||||
| - macos: retry opening files on EINTR ([#475]) | ||||
|  | ||||
| - kqueue: skip unreadable files ([#479]) | ||||
|  | ||||
|   kqueue requires a file descriptor for every file in a directory; this would | ||||
|   fail if a file was unreadable by the current user. Now these files are simply | ||||
|   skipped. | ||||
|  | ||||
| - windows: fix renaming a watched directory if the parent is also watched ([#370]) | ||||
|  | ||||
| - windows: increase buffer size from 4K to 64K ([#485]) | ||||
|  | ||||
| - windows: close file handle on Remove() ([#288]) | ||||
|  | ||||
| - kqueue: put pathname in the error if watching a file fails ([#471]) | ||||
|  | ||||
| - inotify, windows: calling Close() more than once could race ([#465]) | ||||
|  | ||||
| - kqueue: improve Close() performance ([#233]) | ||||
|  | ||||
| - all: various documentation additions and clarifications. | ||||
|  | ||||
| [#233]: https://github.com/fsnotify/fsnotify/pull/233 | ||||
| [#260]: https://github.com/fsnotify/fsnotify/pull/260 | ||||
| [#288]: https://github.com/fsnotify/fsnotify/pull/288 | ||||
| [#370]: https://github.com/fsnotify/fsnotify/pull/370 | ||||
| [#434]: https://github.com/fsnotify/fsnotify/pull/434 | ||||
| [#460]: https://github.com/fsnotify/fsnotify/pull/460 | ||||
| [#463]: https://github.com/fsnotify/fsnotify/pull/463 | ||||
| [#465]: https://github.com/fsnotify/fsnotify/pull/465 | ||||
| [#470]: https://github.com/fsnotify/fsnotify/pull/470 | ||||
| [#471]: https://github.com/fsnotify/fsnotify/pull/471 | ||||
| [#475]: https://github.com/fsnotify/fsnotify/pull/475 | ||||
| [#477]: https://github.com/fsnotify/fsnotify/pull/477 | ||||
| [#479]: https://github.com/fsnotify/fsnotify/pull/479 | ||||
| [#480]: https://github.com/fsnotify/fsnotify/pull/480 | ||||
| [#485]: https://github.com/fsnotify/fsnotify/pull/485 | ||||
|  | ||||
| ## [1.5.4] - 2022-04-25 | ||||
|  | ||||
| * Windows: add missing defer to `Watcher.WatchList` [#447](https://github.com/fsnotify/fsnotify/pull/447) | ||||
| * go.mod: use latest x/sys [#444](https://github.com/fsnotify/fsnotify/pull/444) | ||||
| * Fix compilation for OpenBSD [#443](https://github.com/fsnotify/fsnotify/pull/443) | ||||
|  | ||||
| ## [1.5.3] - 2022-04-22 | ||||
|  | ||||
| * This version is retracted. An incorrect branch is published accidentally [#445](https://github.com/fsnotify/fsnotify/issues/445) | ||||
|  | ||||
| ## [1.5.2] - 2022-04-21 | ||||
|  | ||||
| * Add a feature to return the directories and files that are being monitored [#374](https://github.com/fsnotify/fsnotify/pull/374) | ||||
| * Fix potential crash on windows if `raw.FileNameLength` exceeds `syscall.MAX_PATH` [#361](https://github.com/fsnotify/fsnotify/pull/361) | ||||
| * Allow build on unsupported GOOS [#424](https://github.com/fsnotify/fsnotify/pull/424) | ||||
| * Don't set `poller.fd` twice in `newFdPoller` [#406](https://github.com/fsnotify/fsnotify/pull/406) | ||||
| * fix go vet warnings: call to `(*T).Fatalf` from a non-test goroutine [#416](https://github.com/fsnotify/fsnotify/pull/416) | ||||
|  | ||||
| ## [1.5.1] - 2021-08-24 | ||||
|  | ||||
| * Revert Add AddRaw to not follow symlinks [#394](https://github.com/fsnotify/fsnotify/pull/394) | ||||
|  | ||||
| ## [1.5.0] - 2021-08-20 | ||||
|  | ||||
| * Go: Increase minimum required version to Go 1.12 [#381](https://github.com/fsnotify/fsnotify/pull/381) | ||||
| * Feature: Add AddRaw method which does not follow symlinks when adding a watch [#289](https://github.com/fsnotify/fsnotify/pull/298) | ||||
| * Windows: Follow symlinks by default like on all other systems [#289](https://github.com/fsnotify/fsnotify/pull/289) | ||||
| * CI: Use GitHub Actions for CI and cover go 1.12-1.17 | ||||
|    [#378](https://github.com/fsnotify/fsnotify/pull/378) | ||||
|    [#381](https://github.com/fsnotify/fsnotify/pull/381) | ||||
|    [#385](https://github.com/fsnotify/fsnotify/pull/385) | ||||
| * Go 1.14+: Fix unsafe pointer conversion [#325](https://github.com/fsnotify/fsnotify/pull/325) | ||||
|  | ||||
| ## [1.4.9] - 2020-03-11 | ||||
|  | ||||
| * Move example usage to the readme #329. This may resolve #328. | ||||
|  | ||||
| ## [1.4.8] - 2020-03-10 | ||||
|  | ||||
| * CI: test more go versions (@nathany 1d13583d846ea9d66dcabbfefbfb9d8e6fb05216) | ||||
| * Tests: Queued inotify events could have been read by the test before max_queued_events was hit (@matthias-stone #265) | ||||
| * Tests:  t.Fatalf -> t.Errorf in go routines (@gdey #266) | ||||
| * CI: Less verbosity (@nathany #267) | ||||
| * Tests: Darwin: Exchangedata is deprecated on 10.13 (@nathany #267) | ||||
| * Tests: Check if channels are closed in the example (@alexeykazakov #244) | ||||
| * CI: Only run golint on latest version of go and fix issues (@cpuguy83 #284) | ||||
| * CI: Add windows to travis matrix (@cpuguy83 #284) | ||||
| * Docs: Remover appveyor badge (@nathany 11844c0959f6fff69ba325d097fce35bd85a8e93) | ||||
| * Linux: create epoll and pipe fds with close-on-exec (@JohannesEbke #219) | ||||
| * Linux: open files with close-on-exec (@linxiulei #273) | ||||
| * Docs: Plan to support fanotify (@nathany ab058b44498e8b7566a799372a39d150d9ea0119 ) | ||||
| * Project: Add go.mod (@nathany #309) | ||||
| * Project: Revise editor config (@nathany #309) | ||||
| * Project: Update copyright for 2019 (@nathany #309) | ||||
| * CI: Drop go1.8 from CI matrix (@nathany #309) | ||||
| * Docs: Updating the FAQ section for supportability with NFS & FUSE filesystems (@Pratik32 4bf2d1fec78374803a39307bfb8d340688f4f28e ) | ||||
|  | ||||
| ## [1.4.7] - 2018-01-09 | ||||
|  | ||||
| * BSD/macOS: Fix possible deadlock on closing the watcher on kqueue (thanks @nhooyr and @glycerine) | ||||
| * Tests: Fix missing verb on format string (thanks @rchiossi) | ||||
| * Linux: Fix deadlock in Remove (thanks @aarondl) | ||||
| * Linux: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (thanks @twpayne) | ||||
| * Docs: Moved FAQ into the README (thanks @vahe) | ||||
| * Linux: Properly handle inotify's IN_Q_OVERFLOW event (thanks @zeldovich) | ||||
| * Docs: replace references to OS X with macOS | ||||
|  | ||||
| ## [1.4.2] - 2016-10-10 | ||||
|  | ||||
| * Linux: use InotifyInit1 with IN_CLOEXEC to stop leaking a file descriptor to a child process when using fork/exec [#178](https://github.com/fsnotify/fsnotify/pull/178) (thanks @pattyshack) | ||||
|  | ||||
| ## [1.4.1] - 2016-10-04 | ||||
|  | ||||
| * Fix flaky inotify stress test on Linux [#177](https://github.com/fsnotify/fsnotify/pull/177) (thanks @pattyshack) | ||||
|  | ||||
| ## [1.4.0] - 2016-10-01 | ||||
|  | ||||
| * add a String() method to Event.Op [#165](https://github.com/fsnotify/fsnotify/pull/165) (thanks @oozie) | ||||
|  | ||||
| ## [1.3.1] - 2016-06-28 | ||||
|  | ||||
| * Windows: fix for double backslash when watching the root of a drive [#151](https://github.com/fsnotify/fsnotify/issues/151) (thanks @brunoqc) | ||||
|  | ||||
| ## [1.3.0] - 2016-04-19 | ||||
|  | ||||
| * Support linux/arm64 by [patching](https://go-review.googlesource.com/#/c/21971/) x/sys/unix and switching to to it from syscall (thanks @suihkulokki) [#135](https://github.com/fsnotify/fsnotify/pull/135) | ||||
|  | ||||
| ## [1.2.10] - 2016-03-02 | ||||
|  | ||||
| * Fix golint errors in windows.go [#121](https://github.com/fsnotify/fsnotify/pull/121) (thanks @tiffanyfj) | ||||
|  | ||||
| ## [1.2.9] - 2016-01-13 | ||||
|  | ||||
| kqueue: Fix logic for CREATE after REMOVE [#111](https://github.com/fsnotify/fsnotify/pull/111) (thanks @bep) | ||||
|  | ||||
| ## [1.2.8] - 2015-12-17 | ||||
|  | ||||
| * kqueue: fix race condition in Close [#105](https://github.com/fsnotify/fsnotify/pull/105) (thanks @djui for reporting the issue and @ppknap for writing a failing test) | ||||
| * inotify: fix race in test | ||||
| * enable race detection for continuous integration (Linux, Mac, Windows) | ||||
|  | ||||
| ## [1.2.5] - 2015-10-17 | ||||
|  | ||||
| * inotify: use epoll_create1 for arm64 support (requires Linux 2.6.27 or later) [#100](https://github.com/fsnotify/fsnotify/pull/100) (thanks @suihkulokki) | ||||
| * inotify: fix path leaks [#73](https://github.com/fsnotify/fsnotify/pull/73) (thanks @chamaken) | ||||
| * kqueue: watch for rename events on subdirectories [#83](https://github.com/fsnotify/fsnotify/pull/83) (thanks @guotie) | ||||
| * kqueue: avoid infinite loops from symlinks cycles [#101](https://github.com/fsnotify/fsnotify/pull/101) (thanks @illicitonion) | ||||
|  | ||||
| ## [1.2.1] - 2015-10-14 | ||||
|  | ||||
| * kqueue: don't watch named pipes [#98](https://github.com/fsnotify/fsnotify/pull/98) (thanks @evanphx) | ||||
|  | ||||
| ## [1.2.0] - 2015-02-08 | ||||
|  | ||||
| * inotify: use epoll to wake up readEvents [#66](https://github.com/fsnotify/fsnotify/pull/66) (thanks @PieterD) | ||||
| * inotify: closing watcher should now always shut down goroutine [#63](https://github.com/fsnotify/fsnotify/pull/63) (thanks @PieterD) | ||||
| * kqueue: close kqueue after removing watches, fixes [#59](https://github.com/fsnotify/fsnotify/issues/59) | ||||
|  | ||||
| ## [1.1.1] - 2015-02-05 | ||||
|  | ||||
| * inotify: Retry read on EINTR [#61](https://github.com/fsnotify/fsnotify/issues/61) (thanks @PieterD) | ||||
|  | ||||
| ## [1.1.0] - 2014-12-12 | ||||
|  | ||||
| * kqueue: rework internals [#43](https://github.com/fsnotify/fsnotify/pull/43) | ||||
|     * add low-level functions | ||||
|     * only need to store flags on directories | ||||
|     * less mutexes [#13](https://github.com/fsnotify/fsnotify/issues/13) | ||||
|     * done can be an unbuffered channel | ||||
|     * remove calls to os.NewSyscallError | ||||
| * More efficient string concatenation for Event.String() [#52](https://github.com/fsnotify/fsnotify/pull/52) (thanks @mdlayher) | ||||
| * kqueue: fix regression in  rework causing subdirectories to be watched [#48](https://github.com/fsnotify/fsnotify/issues/48) | ||||
| * kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51) | ||||
|  | ||||
| ## [1.0.4] - 2014-09-07 | ||||
|  | ||||
| * kqueue: add dragonfly to the build tags. | ||||
| * Rename source code files, rearrange code so exported APIs are at the top. | ||||
| * Add done channel to example code. [#37](https://github.com/fsnotify/fsnotify/pull/37) (thanks @chenyukang) | ||||
|  | ||||
| ## [1.0.3] - 2014-08-19 | ||||
|  | ||||
| * [Fix] Windows MOVED_TO now translates to Create like on BSD and Linux. [#36](https://github.com/fsnotify/fsnotify/issues/36) | ||||
|  | ||||
| ## [1.0.2] - 2014-08-17 | ||||
|  | ||||
| * [Fix] Missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso) | ||||
| * [Fix] Make ./path and path equivalent. (thanks @zhsso) | ||||
|  | ||||
| ## [1.0.0] - 2014-08-15 | ||||
|  | ||||
| * [API] Remove AddWatch on Windows, use Add. | ||||
| * Improve documentation for exported identifiers. [#30](https://github.com/fsnotify/fsnotify/issues/30) | ||||
| * Minor updates based on feedback from golint. | ||||
|  | ||||
| ## dev / 2014-07-09 | ||||
|  | ||||
| * Moved to [github.com/fsnotify/fsnotify](https://github.com/fsnotify/fsnotify). | ||||
| * Use os.NewSyscallError instead of returning errno (thanks @hariharan-uno) | ||||
|  | ||||
| ## dev / 2014-07-04 | ||||
|  | ||||
| * kqueue: fix incorrect mutex used in Close() | ||||
| * Update example to demonstrate usage of Op. | ||||
|  | ||||
| ## dev / 2014-06-28 | ||||
|  | ||||
| * [API] Don't set the Write Op for attribute notifications [#4](https://github.com/fsnotify/fsnotify/issues/4) | ||||
| * Fix for String() method on Event (thanks Alex Brainman) | ||||
| * Don't build on Plan 9 or Solaris (thanks @4ad) | ||||
|  | ||||
| ## dev / 2014-06-21 | ||||
|  | ||||
| * Events channel of type Event rather than *Event. | ||||
| * [internal] use syscall constants directly for inotify and kqueue. | ||||
| * [internal] kqueue: rename events to kevents and fileEvent to event. | ||||
|  | ||||
| ## dev / 2014-06-19 | ||||
|  | ||||
| * Go 1.3+ required on Windows (uses syscall.ERROR_MORE_DATA internally). | ||||
| * [internal] remove cookie from Event struct (unused). | ||||
| * [internal] Event struct has the same definition across every OS. | ||||
| * [internal] remove internal watch and removeWatch methods. | ||||
|  | ||||
| ## dev / 2014-06-12 | ||||
|  | ||||
| * [API] Renamed Watch() to Add() and RemoveWatch() to Remove(). | ||||
| * [API] Pluralized channel names: Events and Errors. | ||||
| * [API] Renamed FileEvent struct to Event. | ||||
| * [API] Op constants replace methods like IsCreate(). | ||||
|  | ||||
| ## dev / 2014-06-12 | ||||
|  | ||||
| * Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98) | ||||
|  | ||||
| ## dev / 2014-05-23 | ||||
|  | ||||
| * [API] Remove current implementation of WatchFlags. | ||||
|     * current implementation doesn't take advantage of OS for efficiency | ||||
|     * provides little benefit over filtering events as they are received, but has  extra bookkeeping and mutexes | ||||
|     * no tests for the current implementation | ||||
|     * not fully implemented on Windows [#93](https://github.com/howeyc/fsnotify/issues/93#issuecomment-39285195) | ||||
|  | ||||
| ## [0.9.3] - 2014-12-31 | ||||
|  | ||||
| * kqueue: cleanup internal watch before sending remove event [#51](https://github.com/fsnotify/fsnotify/issues/51) | ||||
|  | ||||
| ## [0.9.2] - 2014-08-17 | ||||
|  | ||||
| * [Backport] Fix missing create events on macOS. [#14](https://github.com/fsnotify/fsnotify/issues/14) (thanks @zhsso) | ||||
|  | ||||
| ## [0.9.1] - 2014-06-12 | ||||
|  | ||||
| * Fix data race on kevent buffer (thanks @tilaks) [#98](https://github.com/howeyc/fsnotify/pull/98) | ||||
|  | ||||
| ## [0.9.0] - 2014-01-17 | ||||
|  | ||||
| * IsAttrib() for events that only concern a file's metadata [#79][] (thanks @abustany) | ||||
| * [Fix] kqueue: fix deadlock [#77][] (thanks @cespare) | ||||
| * [NOTICE] Development has moved to `code.google.com/p/go.exp/fsnotify` in preparation for inclusion in the Go standard library. | ||||
|  | ||||
| ## [0.8.12] - 2013-11-13 | ||||
|  | ||||
| * [API] Remove FD_SET and friends from Linux adapter | ||||
|  | ||||
| ## [0.8.11] - 2013-11-02 | ||||
|  | ||||
| * [Doc] Add Changelog [#72][] (thanks @nathany) | ||||
| * [Doc] Spotlight and double modify events on macOS [#62][] (reported by @paulhammond) | ||||
|  | ||||
| ## [0.8.10] - 2013-10-19 | ||||
|  | ||||
| * [Fix] kqueue: remove file watches when parent directory is removed [#71][] (reported by @mdwhatcott) | ||||
| * [Fix] kqueue: race between Close and readEvents [#70][] (reported by @bernerdschaefer) | ||||
| * [Doc] specify OS-specific limits in README (thanks @debrando) | ||||
|  | ||||
| ## [0.8.9] - 2013-09-08 | ||||
|  | ||||
| * [Doc] Contributing (thanks @nathany) | ||||
| * [Doc] update package path in example code [#63][] (thanks @paulhammond) | ||||
| * [Doc] GoCI badge in README (Linux only) [#60][] | ||||
| * [Doc] Cross-platform testing with Vagrant  [#59][] (thanks @nathany) | ||||
|  | ||||
| ## [0.8.8] - 2013-06-17 | ||||
|  | ||||
| * [Fix] Windows: handle `ERROR_MORE_DATA` on Windows [#49][] (thanks @jbowtie) | ||||
|  | ||||
| ## [0.8.7] - 2013-06-03 | ||||
|  | ||||
| * [API] Make syscall flags internal | ||||
| * [Fix] inotify: ignore event changes | ||||
| * [Fix] race in symlink test [#45][] (reported by @srid) | ||||
| * [Fix] tests on Windows | ||||
| * lower case error messages | ||||
|  | ||||
| ## [0.8.6] - 2013-05-23 | ||||
|  | ||||
| * kqueue: Use EVT_ONLY flag on Darwin | ||||
| * [Doc] Update README with full example | ||||
|  | ||||
| ## [0.8.5] - 2013-05-09 | ||||
|  | ||||
| * [Fix] inotify: allow monitoring of "broken" symlinks (thanks @tsg) | ||||
|  | ||||
| ## [0.8.4] - 2013-04-07 | ||||
|  | ||||
| * [Fix] kqueue: watch all file events [#40][] (thanks @ChrisBuchholz) | ||||
|  | ||||
| ## [0.8.3] - 2013-03-13 | ||||
|  | ||||
| * [Fix] inoitfy/kqueue memory leak [#36][] (reported by @nbkolchin) | ||||
| * [Fix] kqueue: use fsnFlags for watching a directory [#33][] (reported by @nbkolchin) | ||||
|  | ||||
| ## [0.8.2] - 2013-02-07 | ||||
|  | ||||
| * [Doc] add Authors | ||||
| * [Fix] fix data races for map access [#29][] (thanks @fsouza) | ||||
|  | ||||
| ## [0.8.1] - 2013-01-09 | ||||
|  | ||||
| * [Fix] Windows path separators | ||||
| * [Doc] BSD License | ||||
|  | ||||
| ## [0.8.0] - 2012-11-09 | ||||
|  | ||||
| * kqueue: directory watching improvements (thanks @vmirage) | ||||
| * inotify: add `IN_MOVED_TO` [#25][] (requested by @cpisto) | ||||
| * [Fix] kqueue: deleting watched directory [#24][] (reported by @jakerr) | ||||
|  | ||||
| ## [0.7.4] - 2012-10-09 | ||||
|  | ||||
| * [Fix] inotify: fixes from https://codereview.appspot.com/5418045/ (ugorji) | ||||
| * [Fix] kqueue: preserve watch flags when watching for delete [#21][] (reported by @robfig) | ||||
| * [Fix] kqueue: watch the directory even if it isn't a new watch (thanks @robfig) | ||||
| * [Fix] kqueue: modify after recreation of file | ||||
|  | ||||
| ## [0.7.3] - 2012-09-27 | ||||
|  | ||||
| * [Fix] kqueue: watch with an existing folder inside the watched folder (thanks @vmirage) | ||||
| * [Fix] kqueue: no longer get duplicate CREATE events | ||||
|  | ||||
| ## [0.7.2] - 2012-09-01 | ||||
|  | ||||
| * kqueue: events for created directories | ||||
|  | ||||
| ## [0.7.1] - 2012-07-14 | ||||
|  | ||||
| * [Fix] for renaming files | ||||
|  | ||||
| ## [0.7.0] - 2012-07-02 | ||||
|  | ||||
| * [Feature] FSNotify flags | ||||
| * [Fix] inotify: Added file name back to event path | ||||
|  | ||||
| ## [0.6.0] - 2012-06-06 | ||||
|  | ||||
| * kqueue: watch files after directory created (thanks @tmc) | ||||
|  | ||||
| ## [0.5.1] - 2012-05-22 | ||||
|  | ||||
| * [Fix] inotify: remove all watches before Close() | ||||
|  | ||||
| ## [0.5.0] - 2012-05-03 | ||||
|  | ||||
| * [API] kqueue: return errors during watch instead of sending over channel | ||||
| * kqueue: match symlink behavior on Linux | ||||
| * inotify: add `DELETE_SELF` (requested by @taralx) | ||||
| * [Fix] kqueue: handle EINTR (reported by @robfig) | ||||
| * [Doc] Godoc example [#1][] (thanks @davecheney) | ||||
|  | ||||
| ## [0.4.0] - 2012-03-30 | ||||
|  | ||||
| * Go 1 released: build with go tool | ||||
| * [Feature] Windows support using winfsnotify | ||||
| * Windows does not have attribute change notifications | ||||
| * Roll attribute notifications into IsModify | ||||
|  | ||||
| ## [0.3.0] - 2012-02-19 | ||||
|  | ||||
| * kqueue: add files when watch directory | ||||
|  | ||||
| ## [0.2.0] - 2011-12-30 | ||||
|  | ||||
| * update to latest Go weekly code | ||||
|  | ||||
| ## [0.1.0] - 2011-10-19 | ||||
|  | ||||
| * kqueue: add watch on file creation to match inotify | ||||
| * kqueue: create file event | ||||
| * inotify: ignore `IN_IGNORED` events | ||||
| * event String() | ||||
| * linux: common FileEvent functions | ||||
| * initial commit | ||||
|  | ||||
| [#79]: https://github.com/howeyc/fsnotify/pull/79 | ||||
| [#77]: https://github.com/howeyc/fsnotify/pull/77 | ||||
| [#72]: https://github.com/howeyc/fsnotify/issues/72 | ||||
| [#71]: https://github.com/howeyc/fsnotify/issues/71 | ||||
| [#70]: https://github.com/howeyc/fsnotify/issues/70 | ||||
| [#63]: https://github.com/howeyc/fsnotify/issues/63 | ||||
| [#62]: https://github.com/howeyc/fsnotify/issues/62 | ||||
| [#60]: https://github.com/howeyc/fsnotify/issues/60 | ||||
| [#59]: https://github.com/howeyc/fsnotify/issues/59 | ||||
| [#49]: https://github.com/howeyc/fsnotify/issues/49 | ||||
| [#45]: https://github.com/howeyc/fsnotify/issues/45 | ||||
| [#40]: https://github.com/howeyc/fsnotify/issues/40 | ||||
| [#36]: https://github.com/howeyc/fsnotify/issues/36 | ||||
| [#33]: https://github.com/howeyc/fsnotify/issues/33 | ||||
| [#29]: https://github.com/howeyc/fsnotify/issues/29 | ||||
| [#25]: https://github.com/howeyc/fsnotify/issues/25 | ||||
| [#24]: https://github.com/howeyc/fsnotify/issues/24 | ||||
| [#21]: https://github.com/howeyc/fsnotify/issues/21 | ||||
							
								
								
									
										26
									
								
								vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/fsnotify/fsnotify/CONTRIBUTING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| Thank you for your interest in contributing to fsnotify! We try to review and | ||||
| merge PRs in a reasonable timeframe, but please be aware that: | ||||
|  | ||||
| - To avoid "wasted" work, please discus changes on the issue tracker first. You | ||||
|   can just send PRs, but they may end up being rejected for one reason or the | ||||
|   other. | ||||
|  | ||||
| - fsnotify is a cross-platform library, and changes must work reasonably well on | ||||
|   all supported platforms. | ||||
|  | ||||
| - Changes will need to be compatible; old code should still compile, and the | ||||
|   runtime behaviour can't change in ways that are likely to lead to problems for | ||||
|   users. | ||||
|  | ||||
| Testing | ||||
| ------- | ||||
| Just `go test ./...` runs all the tests; the CI runs this on all supported | ||||
| platforms. Testing different platforms locally can be done with something like | ||||
| [goon] or [Vagrant], but this isn't super-easy to set up at the moment. | ||||
|  | ||||
| Use the `-short` flag to make the "stress test" run faster. | ||||
|  | ||||
|  | ||||
| [goon]: https://github.com/arp242/goon | ||||
| [Vagrant]: https://www.vagrantup.com/ | ||||
| [integration_test.go]: /integration_test.go | ||||
							
								
								
									
										25
									
								
								vendor/github.com/fsnotify/fsnotify/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								vendor/github.com/fsnotify/fsnotify/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| Copyright © 2012 The Go Authors. All rights reserved. | ||||
| Copyright © fsnotify Authors. All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without modification, | ||||
| are permitted provided that the following conditions are met: | ||||
|  | ||||
| * Redistributions of source code must retain the above copyright notice, this | ||||
|   list of conditions and the following disclaimer. | ||||
| * Redistributions in binary form must reproduce the above copyright notice, this | ||||
|   list of conditions and the following disclaimer in the documentation and/or | ||||
|   other materials provided with the distribution. | ||||
| * Neither the name of Google Inc. nor the names of its contributors may be used | ||||
|   to endorse or promote products derived from this software without specific | ||||
|   prior written permission. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||||
| ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON | ||||
| ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										161
									
								
								vendor/github.com/fsnotify/fsnotify/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								vendor/github.com/fsnotify/fsnotify/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,161 @@ | ||||
| fsnotify is a Go library to provide cross-platform filesystem notifications on | ||||
| Windows, Linux, macOS, and BSD systems. | ||||
|  | ||||
| Go 1.16 or newer is required; the full documentation is at | ||||
| https://pkg.go.dev/github.com/fsnotify/fsnotify | ||||
|  | ||||
| **It's best to read the documentation at pkg.go.dev, as it's pinned to the last | ||||
| released version, whereas this README is for the last development version which | ||||
| may include additions/changes.** | ||||
|  | ||||
| --- | ||||
|  | ||||
| Platform support: | ||||
|  | ||||
| | Adapter               | OS             | Status                                                       | | ||||
| | --------------------- | ---------------| -------------------------------------------------------------| | ||||
| | inotify               | Linux 2.6.32+  | Supported                                                    | | ||||
| | kqueue                | BSD, macOS     | Supported                                                    | | ||||
| | ReadDirectoryChangesW | Windows        | Supported                                                    | | ||||
| | FSEvents              | macOS          | [Planned](https://github.com/fsnotify/fsnotify/issues/11)    | | ||||
| | FEN                   | Solaris 11     | [In Progress](https://github.com/fsnotify/fsnotify/pull/371) | | ||||
| | fanotify              | Linux 5.9+     | [Maybe](https://github.com/fsnotify/fsnotify/issues/114)     | | ||||
| | USN Journals          | Windows        | [Maybe](https://github.com/fsnotify/fsnotify/issues/53)      | | ||||
| | Polling               | *All*          | [Maybe](https://github.com/fsnotify/fsnotify/issues/9)       | | ||||
|  | ||||
| Linux and macOS should include Android and iOS, but these are currently untested. | ||||
|  | ||||
| Usage | ||||
| ----- | ||||
| A basic example: | ||||
|  | ||||
| ```go | ||||
| package main | ||||
|  | ||||
| import ( | ||||
|     "log" | ||||
|  | ||||
|     "github.com/fsnotify/fsnotify" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
|     // Create new watcher. | ||||
|     watcher, err := fsnotify.NewWatcher() | ||||
|     if err != nil { | ||||
|         log.Fatal(err) | ||||
|     } | ||||
|     defer watcher.Close() | ||||
|  | ||||
|     // Start listening for events. | ||||
|     go func() { | ||||
|         for { | ||||
|             select { | ||||
|             case event, ok := <-watcher.Events: | ||||
|                 if !ok { | ||||
|                     return | ||||
|                 } | ||||
|                 log.Println("event:", event) | ||||
|                 if event.Has(fsnotify.Write) { | ||||
|                     log.Println("modified file:", event.Name) | ||||
|                 } | ||||
|             case err, ok := <-watcher.Errors: | ||||
|                 if !ok { | ||||
|                     return | ||||
|                 } | ||||
|                 log.Println("error:", err) | ||||
|             } | ||||
|         } | ||||
|     }() | ||||
|  | ||||
|     // Add a path. | ||||
|     err = watcher.Add("/tmp") | ||||
|     if err != nil { | ||||
|         log.Fatal(err) | ||||
|     } | ||||
|  | ||||
|     // Block main goroutine forever. | ||||
|     <-make(chan struct{}) | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Some more examples can be found in [cmd/fsnotify](cmd/fsnotify), which can be | ||||
| run with: | ||||
|  | ||||
|     % go run ./cmd/fsnotify | ||||
|  | ||||
| FAQ | ||||
| --- | ||||
| ### Will a file still be watched when it's moved to another directory? | ||||
| No, not unless you are watching the location it was moved to. | ||||
|  | ||||
| ### Are subdirectories watched too? | ||||
| No, you must add watches for any directory you want to watch (a recursive | ||||
| watcher is on the roadmap: [#18]). | ||||
|  | ||||
| [#18]: https://github.com/fsnotify/fsnotify/issues/18 | ||||
|  | ||||
| ### Do I have to watch the Error and Event channels in a goroutine? | ||||
| As of now, yes (you can read both channels in the same goroutine using `select`, | ||||
| you don't need a separate goroutine for both channels; see the example). | ||||
|  | ||||
| ### Why don't notifications work with NFS, SMB, FUSE, /proc, or /sys? | ||||
| fsnotify requires support from underlying OS to work. The current NFS and SMB | ||||
| protocols does not provide network level support for file notifications, and | ||||
| neither do the /proc and /sys virtual filesystems. | ||||
|  | ||||
| This could be fixed with a polling watcher ([#9]), but it's not yet implemented. | ||||
|  | ||||
| [#9]: https://github.com/fsnotify/fsnotify/issues/9 | ||||
|  | ||||
| Platform-specific notes | ||||
| ----------------------- | ||||
| ### Linux | ||||
| When a file is removed a REMOVE event won't be emitted until all file | ||||
| descriptors are closed; it will emit a CHMOD instead: | ||||
|  | ||||
|     fp := os.Open("file") | ||||
|     os.Remove("file")        // CHMOD | ||||
|     fp.Close()               // REMOVE | ||||
|  | ||||
| This is the event that inotify sends, so not much can be changed about this. | ||||
|  | ||||
| The `fs.inotify.max_user_watches` sysctl variable specifies the upper limit for | ||||
| the number of watches per user, and `fs.inotify.max_user_instances` specifies | ||||
| the maximum number of inotify instances per user. Every Watcher you create is an | ||||
| "instance", and every path you add is a "watch". | ||||
|  | ||||
| These are also exposed in `/proc` as `/proc/sys/fs/inotify/max_user_watches` and | ||||
| `/proc/sys/fs/inotify/max_user_instances` | ||||
|  | ||||
| To increase them you can use `sysctl` or write the value to proc file: | ||||
|  | ||||
|     # The default values on Linux 5.18 | ||||
|     sysctl fs.inotify.max_user_watches=124983 | ||||
|     sysctl fs.inotify.max_user_instances=128 | ||||
|  | ||||
| To make the changes persist on reboot edit `/etc/sysctl.conf` or | ||||
| `/usr/lib/sysctl.d/50-default.conf` (details differ per Linux distro; check your | ||||
| distro's documentation): | ||||
|  | ||||
|     fs.inotify.max_user_watches=124983 | ||||
|     fs.inotify.max_user_instances=128 | ||||
|  | ||||
| Reaching the limit will result in a "no space left on device" or "too many open | ||||
| files" error. | ||||
|  | ||||
| ### kqueue (macOS, all BSD systems) | ||||
| kqueue requires opening a file descriptor for every file that's being watched; | ||||
| so if you're watching a directory with five files then that's six file | ||||
| descriptors. You will run in to your system's "max open files" limit faster on | ||||
| these platforms. | ||||
|  | ||||
| The sysctl variables `kern.maxfiles` and `kern.maxfilesperproc` can be used to | ||||
| control the maximum number of open files. | ||||
|  | ||||
| ### macOS | ||||
| Spotlight indexing on macOS can result in multiple events (see [#15]). A temporary | ||||
| workaround is to add your folder(s) to the *Spotlight Privacy settings* until we | ||||
| have a native FSEvents implementation (see [#11]). | ||||
|  | ||||
| [#11]: https://github.com/fsnotify/fsnotify/issues/11 | ||||
| [#15]: https://github.com/fsnotify/fsnotify/issues/15 | ||||
							
								
								
									
										162
									
								
								vendor/github.com/fsnotify/fsnotify/backend_fen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								vendor/github.com/fsnotify/fsnotify/backend_fen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,162 @@ | ||||
| //go:build solaris | ||||
| // +build solaris | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of paths, delivering events on a channel. | ||||
| // | ||||
| // A watcher should not be copied (e.g. pass it by pointer, rather than by | ||||
| // value). | ||||
| // | ||||
| // # Linux notes | ||||
| // | ||||
| // When a file is removed a Remove event won't be emitted until all file | ||||
| // descriptors are closed, and deletes will always emit a Chmod. For example: | ||||
| // | ||||
| //     fp := os.Open("file") | ||||
| //     os.Remove("file")        // Triggers Chmod | ||||
| //     fp.Close()               // Triggers Remove | ||||
| // | ||||
| // This is the event that inotify sends, so not much can be changed about this. | ||||
| // | ||||
| // The fs.inotify.max_user_watches sysctl variable specifies the upper limit | ||||
| // for the number of watches per user, and fs.inotify.max_user_instances | ||||
| // specifies the maximum number of inotify instances per user. Every Watcher you | ||||
| // create is an "instance", and every path you add is a "watch". | ||||
| // | ||||
| // These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and | ||||
| // /proc/sys/fs/inotify/max_user_instances | ||||
| // | ||||
| // To increase them you can use sysctl or write the value to the /proc file: | ||||
| // | ||||
| //     # Default values on Linux 5.18 | ||||
| //     sysctl fs.inotify.max_user_watches=124983 | ||||
| //     sysctl fs.inotify.max_user_instances=128 | ||||
| // | ||||
| // To make the changes persist on reboot edit /etc/sysctl.conf or | ||||
| // /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check | ||||
| // your distro's documentation): | ||||
| // | ||||
| //     fs.inotify.max_user_watches=124983 | ||||
| //     fs.inotify.max_user_instances=128 | ||||
| // | ||||
| // Reaching the limit will result in a "no space left on device" or "too many open | ||||
| // files" error. | ||||
| // | ||||
| // # kqueue notes (macOS, BSD) | ||||
| // | ||||
| // kqueue requires opening a file descriptor for every file that's being watched; | ||||
| // so if you're watching a directory with five files then that's six file | ||||
| // descriptors. You will run in to your system's "max open files" limit faster on | ||||
| // these platforms. | ||||
| // | ||||
| // The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to | ||||
| // control the maximum number of open files, as well as /etc/login.conf on BSD | ||||
| // systems. | ||||
| // | ||||
| // # macOS notes | ||||
| // | ||||
| // Spotlight indexing on macOS can result in multiple events (see [#15]). A | ||||
| // temporary workaround is to add your folder(s) to the "Spotlight Privacy | ||||
| // Settings" until we have a native FSEvents implementation (see [#11]). | ||||
| // | ||||
| // [#11]: https://github.com/fsnotify/fsnotify/issues/11 | ||||
| // [#15]: https://github.com/fsnotify/fsnotify/issues/15 | ||||
| type Watcher struct { | ||||
| 	// Events sends the filesystem change events. | ||||
| 	// | ||||
| 	// fsnotify can send the following events; a "path" here can refer to a | ||||
| 	// file, directory, symbolic link, or special file like a FIFO. | ||||
| 	// | ||||
| 	//   fsnotify.Create    A new path was created; this may be followed by one | ||||
| 	//                      or more Write events if data also gets written to a | ||||
| 	//                      file. | ||||
| 	// | ||||
| 	//   fsnotify.Remove    A path was removed. | ||||
| 	// | ||||
| 	//   fsnotify.Rename    A path was renamed. A rename is always sent with the | ||||
| 	//                      old path as Event.Name, and a Create event will be | ||||
| 	//                      sent with the new name. Renames are only sent for | ||||
| 	//                      paths that are currently watched; e.g. moving an | ||||
| 	//                      unmonitored file into a monitored directory will | ||||
| 	//                      show up as just a Create. Similarly, renaming a file | ||||
| 	//                      to outside a monitored directory will show up as | ||||
| 	//                      only a Rename. | ||||
| 	// | ||||
| 	//   fsnotify.Write     A file or named pipe was written to. A Truncate will | ||||
| 	//                      also trigger a Write. A single "write action" | ||||
| 	//                      initiated by the user may show up as one or multiple | ||||
| 	//                      writes, depending on when the system syncs things to | ||||
| 	//                      disk. For example when compiling a large Go program | ||||
| 	//                      you may get hundreds of Write events, so you | ||||
| 	//                      probably want to wait until you've stopped receiving | ||||
| 	//                      them (see the dedup example in cmd/fsnotify). | ||||
| 	// | ||||
| 	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent | ||||
| 	//                      when a file is removed (or more accurately, when a | ||||
| 	//                      link to an inode is removed). On kqueue it's sent | ||||
| 	//                      and on kqueue when a file is truncated. On Windows | ||||
| 	//                      it's never sent. | ||||
| 	Events chan Event | ||||
|  | ||||
| 	// Errors sends any errors. | ||||
| 	Errors chan error | ||||
| } | ||||
|  | ||||
| // NewWatcher creates a new Watcher. | ||||
| func NewWatcher() (*Watcher, error) { | ||||
| 	return nil, errors.New("FEN based watcher not yet supported for fsnotify\n") | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| func (w *Watcher) Close() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Add starts monitoring the path for changes. | ||||
| // | ||||
| // A path can only be watched once; attempting to watch it more than once will | ||||
| // return an error. Paths that do not yet exist on the filesystem cannot be | ||||
| // added. A watch will be automatically removed if the path is deleted. | ||||
| // | ||||
| // A path will remain watched if it gets renamed to somewhere else on the same | ||||
| // filesystem, but the monitor will get removed if the path gets deleted and | ||||
| // re-created, or if it's moved to a different filesystem. | ||||
| // | ||||
| // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special | ||||
| // filesystems (/proc, /sys, etc.) generally don't work. | ||||
| // | ||||
| // # Watching directories | ||||
| // | ||||
| // All files in a directory are monitored, including new files that are created | ||||
| // after the watcher is started. Subdirectories are not watched (i.e. it's | ||||
| // non-recursive). | ||||
| // | ||||
| // # Watching files | ||||
| // | ||||
| // Watching individual files (rather than directories) is generally not | ||||
| // recommended as many tools update files atomically. Instead of "just" writing | ||||
| // to the file a temporary file will be written to first, and if successful the | ||||
| // temporary file is moved to to destination removing the original, or some | ||||
| // variant thereof. The watcher on the original file is now lost, as it no | ||||
| // longer exists. | ||||
| // | ||||
| // Instead, watch the parent directory and use Event.Name to filter out files | ||||
| // you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Remove stops monitoring the path for changes. | ||||
| // | ||||
| // Directories are always removed non-recursively. For example, if you added | ||||
| // /tmp/dir and /tmp/dir/subdir then you will need to remove both. | ||||
| // | ||||
| // Removing a path that has not yet been added returns [ErrNonExistentWatch]. | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										459
									
								
								vendor/github.com/fsnotify/fsnotify/backend_inotify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										459
									
								
								vendor/github.com/fsnotify/fsnotify/backend_inotify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,459 @@ | ||||
| //go:build linux | ||||
| // +build linux | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"unsafe" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of paths, delivering events on a channel. | ||||
| // | ||||
| // A watcher should not be copied (e.g. pass it by pointer, rather than by | ||||
| // value). | ||||
| // | ||||
| // # Linux notes | ||||
| // | ||||
| // When a file is removed a Remove event won't be emitted until all file | ||||
| // descriptors are closed, and deletes will always emit a Chmod. For example: | ||||
| // | ||||
| //     fp := os.Open("file") | ||||
| //     os.Remove("file")        // Triggers Chmod | ||||
| //     fp.Close()               // Triggers Remove | ||||
| // | ||||
| // This is the event that inotify sends, so not much can be changed about this. | ||||
| // | ||||
| // The fs.inotify.max_user_watches sysctl variable specifies the upper limit | ||||
| // for the number of watches per user, and fs.inotify.max_user_instances | ||||
| // specifies the maximum number of inotify instances per user. Every Watcher you | ||||
| // create is an "instance", and every path you add is a "watch". | ||||
| // | ||||
| // These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and | ||||
| // /proc/sys/fs/inotify/max_user_instances | ||||
| // | ||||
| // To increase them you can use sysctl or write the value to the /proc file: | ||||
| // | ||||
| //     # Default values on Linux 5.18 | ||||
| //     sysctl fs.inotify.max_user_watches=124983 | ||||
| //     sysctl fs.inotify.max_user_instances=128 | ||||
| // | ||||
| // To make the changes persist on reboot edit /etc/sysctl.conf or | ||||
| // /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check | ||||
| // your distro's documentation): | ||||
| // | ||||
| //     fs.inotify.max_user_watches=124983 | ||||
| //     fs.inotify.max_user_instances=128 | ||||
| // | ||||
| // Reaching the limit will result in a "no space left on device" or "too many open | ||||
| // files" error. | ||||
| // | ||||
| // # kqueue notes (macOS, BSD) | ||||
| // | ||||
| // kqueue requires opening a file descriptor for every file that's being watched; | ||||
| // so if you're watching a directory with five files then that's six file | ||||
| // descriptors. You will run in to your system's "max open files" limit faster on | ||||
| // these platforms. | ||||
| // | ||||
| // The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to | ||||
| // control the maximum number of open files, as well as /etc/login.conf on BSD | ||||
| // systems. | ||||
| // | ||||
| // # macOS notes | ||||
| // | ||||
| // Spotlight indexing on macOS can result in multiple events (see [#15]). A | ||||
| // temporary workaround is to add your folder(s) to the "Spotlight Privacy | ||||
| // Settings" until we have a native FSEvents implementation (see [#11]). | ||||
| // | ||||
| // [#11]: https://github.com/fsnotify/fsnotify/issues/11 | ||||
| // [#15]: https://github.com/fsnotify/fsnotify/issues/15 | ||||
| type Watcher struct { | ||||
| 	// Events sends the filesystem change events. | ||||
| 	// | ||||
| 	// fsnotify can send the following events; a "path" here can refer to a | ||||
| 	// file, directory, symbolic link, or special file like a FIFO. | ||||
| 	// | ||||
| 	//   fsnotify.Create    A new path was created; this may be followed by one | ||||
| 	//                      or more Write events if data also gets written to a | ||||
| 	//                      file. | ||||
| 	// | ||||
| 	//   fsnotify.Remove    A path was removed. | ||||
| 	// | ||||
| 	//   fsnotify.Rename    A path was renamed. A rename is always sent with the | ||||
| 	//                      old path as Event.Name, and a Create event will be | ||||
| 	//                      sent with the new name. Renames are only sent for | ||||
| 	//                      paths that are currently watched; e.g. moving an | ||||
| 	//                      unmonitored file into a monitored directory will | ||||
| 	//                      show up as just a Create. Similarly, renaming a file | ||||
| 	//                      to outside a monitored directory will show up as | ||||
| 	//                      only a Rename. | ||||
| 	// | ||||
| 	//   fsnotify.Write     A file or named pipe was written to. A Truncate will | ||||
| 	//                      also trigger a Write. A single "write action" | ||||
| 	//                      initiated by the user may show up as one or multiple | ||||
| 	//                      writes, depending on when the system syncs things to | ||||
| 	//                      disk. For example when compiling a large Go program | ||||
| 	//                      you may get hundreds of Write events, so you | ||||
| 	//                      probably want to wait until you've stopped receiving | ||||
| 	//                      them (see the dedup example in cmd/fsnotify). | ||||
| 	// | ||||
| 	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent | ||||
| 	//                      when a file is removed (or more accurately, when a | ||||
| 	//                      link to an inode is removed). On kqueue it's sent | ||||
| 	//                      and on kqueue when a file is truncated. On Windows | ||||
| 	//                      it's never sent. | ||||
| 	Events chan Event | ||||
|  | ||||
| 	// Errors sends any errors. | ||||
| 	Errors chan error | ||||
|  | ||||
| 	// Store fd here as os.File.Read() will no longer return on close after | ||||
| 	// calling Fd(). See: https://github.com/golang/go/issues/26439 | ||||
| 	fd          int | ||||
| 	mu          sync.Mutex // Map access | ||||
| 	inotifyFile *os.File | ||||
| 	watches     map[string]*watch // Map of inotify watches (key: path) | ||||
| 	paths       map[int]string    // Map of watched paths (key: watch descriptor) | ||||
| 	done        chan struct{}     // Channel for sending a "quit message" to the reader goroutine | ||||
| 	doneResp    chan struct{}     // Channel to respond to Close | ||||
| } | ||||
|  | ||||
| // NewWatcher creates a new Watcher. | ||||
| func NewWatcher() (*Watcher, error) { | ||||
| 	// Create inotify fd | ||||
| 	// Need to set the FD to nonblocking mode in order for SetDeadline methods to work | ||||
| 	// Otherwise, blocking i/o operations won't terminate on close | ||||
| 	fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK) | ||||
| 	if fd == -1 { | ||||
| 		return nil, errno | ||||
| 	} | ||||
|  | ||||
| 	w := &Watcher{ | ||||
| 		fd:          fd, | ||||
| 		inotifyFile: os.NewFile(uintptr(fd), ""), | ||||
| 		watches:     make(map[string]*watch), | ||||
| 		paths:       make(map[int]string), | ||||
| 		Events:      make(chan Event), | ||||
| 		Errors:      make(chan error), | ||||
| 		done:        make(chan struct{}), | ||||
| 		doneResp:    make(chan struct{}), | ||||
| 	} | ||||
|  | ||||
| 	go w.readEvents() | ||||
| 	return w, nil | ||||
| } | ||||
|  | ||||
| // Returns true if the event was sent, or false if watcher is closed. | ||||
| func (w *Watcher) sendEvent(e Event) bool { | ||||
| 	select { | ||||
| 	case w.Events <- e: | ||||
| 		return true | ||||
| 	case <-w.done: | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // Returns true if the error was sent, or false if watcher is closed. | ||||
| func (w *Watcher) sendError(err error) bool { | ||||
| 	select { | ||||
| 	case w.Errors <- err: | ||||
| 		return true | ||||
| 	case <-w.done: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *Watcher) isClosed() bool { | ||||
| 	select { | ||||
| 	case <-w.done: | ||||
| 		return true | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| func (w *Watcher) Close() error { | ||||
| 	w.mu.Lock() | ||||
| 	if w.isClosed() { | ||||
| 		w.mu.Unlock() | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	// Send 'close' signal to goroutine, and set the Watcher to closed. | ||||
| 	close(w.done) | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	// Causes any blocking reads to return with an error, provided the file | ||||
| 	// still supports deadline operations. | ||||
| 	err := w.inotifyFile.Close() | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// Wait for goroutine to close | ||||
| 	<-w.doneResp | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Add starts monitoring the path for changes. | ||||
| // | ||||
| // A path can only be watched once; attempting to watch it more than once will | ||||
| // return an error. Paths that do not yet exist on the filesystem cannot be | ||||
| // added. A watch will be automatically removed if the path is deleted. | ||||
| // | ||||
| // A path will remain watched if it gets renamed to somewhere else on the same | ||||
| // filesystem, but the monitor will get removed if the path gets deleted and | ||||
| // re-created, or if it's moved to a different filesystem. | ||||
| // | ||||
| // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special | ||||
| // filesystems (/proc, /sys, etc.) generally don't work. | ||||
| // | ||||
| // # Watching directories | ||||
| // | ||||
| // All files in a directory are monitored, including new files that are created | ||||
| // after the watcher is started. Subdirectories are not watched (i.e. it's | ||||
| // non-recursive). | ||||
| // | ||||
| // # Watching files | ||||
| // | ||||
| // Watching individual files (rather than directories) is generally not | ||||
| // recommended as many tools update files atomically. Instead of "just" writing | ||||
| // to the file a temporary file will be written to first, and if successful the | ||||
| // temporary file is moved to to destination removing the original, or some | ||||
| // variant thereof. The watcher on the original file is now lost, as it no | ||||
| // longer exists. | ||||
| // | ||||
| // Instead, watch the parent directory and use Event.Name to filter out files | ||||
| // you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	name = filepath.Clean(name) | ||||
| 	if w.isClosed() { | ||||
| 		return errors.New("inotify instance already closed") | ||||
| 	} | ||||
|  | ||||
| 	var flags uint32 = unix.IN_MOVED_TO | unix.IN_MOVED_FROM | | ||||
| 		unix.IN_CREATE | unix.IN_ATTRIB | unix.IN_MODIFY | | ||||
| 		unix.IN_MOVE_SELF | unix.IN_DELETE | unix.IN_DELETE_SELF | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	defer w.mu.Unlock() | ||||
| 	watchEntry := w.watches[name] | ||||
| 	if watchEntry != nil { | ||||
| 		flags |= watchEntry.flags | unix.IN_MASK_ADD | ||||
| 	} | ||||
| 	wd, errno := unix.InotifyAddWatch(w.fd, name, flags) | ||||
| 	if wd == -1 { | ||||
| 		return errno | ||||
| 	} | ||||
|  | ||||
| 	if watchEntry == nil { | ||||
| 		w.watches[name] = &watch{wd: uint32(wd), flags: flags} | ||||
| 		w.paths[wd] = name | ||||
| 	} else { | ||||
| 		watchEntry.wd = uint32(wd) | ||||
| 		watchEntry.flags = flags | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Remove stops monitoring the path for changes. | ||||
| // | ||||
| // Directories are always removed non-recursively. For example, if you added | ||||
| // /tmp/dir and /tmp/dir/subdir then you will need to remove both. | ||||
| // | ||||
| // Removing a path that has not yet been added returns [ErrNonExistentWatch]. | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	name = filepath.Clean(name) | ||||
|  | ||||
| 	// Fetch the watch. | ||||
| 	w.mu.Lock() | ||||
| 	defer w.mu.Unlock() | ||||
| 	watch, ok := w.watches[name] | ||||
|  | ||||
| 	// Remove it from inotify. | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("%w: %s", ErrNonExistentWatch, name) | ||||
| 	} | ||||
|  | ||||
| 	// We successfully removed the watch if InotifyRmWatch doesn't return an | ||||
| 	// error, we need to clean up our internal state to ensure it matches | ||||
| 	// inotify's kernel state. | ||||
| 	delete(w.paths, int(watch.wd)) | ||||
| 	delete(w.watches, name) | ||||
|  | ||||
| 	// inotify_rm_watch will return EINVAL if the file has been deleted; | ||||
| 	// the inotify will already have been removed. | ||||
| 	// watches and pathes are deleted in ignoreLinux() implicitly and asynchronously | ||||
| 	// by calling inotify_rm_watch() below. e.g. readEvents() goroutine receives IN_IGNORE | ||||
| 	// so that EINVAL means that the wd is being rm_watch()ed or its file removed | ||||
| 	// by another thread and we have not received IN_IGNORE event. | ||||
| 	success, errno := unix.InotifyRmWatch(w.fd, watch.wd) | ||||
| 	if success == -1 { | ||||
| 		// TODO: Perhaps it's not helpful to return an error here in every case; | ||||
| 		//       The only two possible errors are: | ||||
| 		// | ||||
| 		//       - EBADF, which happens when w.fd is not a valid file descriptor | ||||
| 		//         of any kind. | ||||
| 		//       - EINVAL, which is when fd is not an inotify descriptor or wd | ||||
| 		//         is not a valid watch descriptor. Watch descriptors are | ||||
| 		//         invalidated when they are removed explicitly or implicitly; | ||||
| 		//         explicitly by inotify_rm_watch, implicitly when the file they | ||||
| 		//         are watching is deleted. | ||||
| 		return errno | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // WatchList returns all paths added with [Add] (and are not yet removed). | ||||
| func (w *Watcher) WatchList() []string { | ||||
| 	w.mu.Lock() | ||||
| 	defer w.mu.Unlock() | ||||
|  | ||||
| 	entries := make([]string, 0, len(w.watches)) | ||||
| 	for pathname := range w.watches { | ||||
| 		entries = append(entries, pathname) | ||||
| 	} | ||||
|  | ||||
| 	return entries | ||||
| } | ||||
|  | ||||
| type watch struct { | ||||
| 	wd    uint32 // Watch descriptor (as returned by the inotify_add_watch() syscall) | ||||
| 	flags uint32 // inotify flags of this watch (see inotify(7) for the list of valid flags) | ||||
| } | ||||
|  | ||||
| // readEvents reads from the inotify file descriptor, converts the | ||||
| // received events into Event objects and sends them via the Events channel | ||||
| func (w *Watcher) readEvents() { | ||||
| 	defer func() { | ||||
| 		close(w.doneResp) | ||||
| 		close(w.Errors) | ||||
| 		close(w.Events) | ||||
| 	}() | ||||
|  | ||||
| 	var ( | ||||
| 		buf   [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events | ||||
| 		errno error                                // Syscall errno | ||||
| 	) | ||||
| 	for { | ||||
| 		// See if we have been closed. | ||||
| 		if w.isClosed() { | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		n, err := w.inotifyFile.Read(buf[:]) | ||||
| 		switch { | ||||
| 		case errors.Unwrap(err) == os.ErrClosed: | ||||
| 			return | ||||
| 		case err != nil: | ||||
| 			if !w.sendError(err) { | ||||
| 				return | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if n < unix.SizeofInotifyEvent { | ||||
| 			var err error | ||||
| 			if n == 0 { | ||||
| 				// If EOF is received. This should really never happen. | ||||
| 				err = io.EOF | ||||
| 			} else if n < 0 { | ||||
| 				// If an error occurred while reading. | ||||
| 				err = errno | ||||
| 			} else { | ||||
| 				// Read was too short. | ||||
| 				err = errors.New("notify: short read in readEvents()") | ||||
| 			} | ||||
| 			if !w.sendError(err) { | ||||
| 				return | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		var offset uint32 | ||||
| 		// We don't know how many events we just read into the buffer | ||||
| 		// While the offset points to at least one whole event... | ||||
| 		for offset <= uint32(n-unix.SizeofInotifyEvent) { | ||||
| 			var ( | ||||
| 				// Point "raw" to the event in the buffer | ||||
| 				raw     = (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset])) | ||||
| 				mask    = uint32(raw.Mask) | ||||
| 				nameLen = uint32(raw.Len) | ||||
| 			) | ||||
|  | ||||
| 			if mask&unix.IN_Q_OVERFLOW != 0 { | ||||
| 				if !w.sendError(ErrEventOverflow) { | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// If the event happened to the watched directory or the watched file, the kernel | ||||
| 			// doesn't append the filename to the event, but we would like to always fill the | ||||
| 			// the "Name" field with a valid filename. We retrieve the path of the watch from | ||||
| 			// the "paths" map. | ||||
| 			w.mu.Lock() | ||||
| 			name, ok := w.paths[int(raw.Wd)] | ||||
| 			// IN_DELETE_SELF occurs when the file/directory being watched is removed. | ||||
| 			// This is a sign to clean up the maps, otherwise we are no longer in sync | ||||
| 			// with the inotify kernel state which has already deleted the watch | ||||
| 			// automatically. | ||||
| 			if ok && mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF { | ||||
| 				delete(w.paths, int(raw.Wd)) | ||||
| 				delete(w.watches, name) | ||||
| 			} | ||||
| 			w.mu.Unlock() | ||||
|  | ||||
| 			if nameLen > 0 { | ||||
| 				// Point "bytes" at the first byte of the filename | ||||
| 				bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen] | ||||
| 				// The filename is padded with NULL bytes. TrimRight() gets rid of those. | ||||
| 				name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000") | ||||
| 			} | ||||
|  | ||||
| 			event := w.newEvent(name, mask) | ||||
|  | ||||
| 			// Send the events that are not ignored on the events channel | ||||
| 			if mask&unix.IN_IGNORED == 0 { | ||||
| 				if !w.sendEvent(event) { | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Move to the next event in the buffer | ||||
| 			offset += unix.SizeofInotifyEvent + nameLen | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // newEvent returns an platform-independent Event based on an inotify mask. | ||||
| func (w *Watcher) newEvent(name string, mask uint32) Event { | ||||
| 	e := Event{Name: name} | ||||
| 	if mask&unix.IN_CREATE == unix.IN_CREATE || mask&unix.IN_MOVED_TO == unix.IN_MOVED_TO { | ||||
| 		e.Op |= Create | ||||
| 	} | ||||
| 	if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF || mask&unix.IN_DELETE == unix.IN_DELETE { | ||||
| 		e.Op |= Remove | ||||
| 	} | ||||
| 	if mask&unix.IN_MODIFY == unix.IN_MODIFY { | ||||
| 		e.Op |= Write | ||||
| 	} | ||||
| 	if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF || mask&unix.IN_MOVED_FROM == unix.IN_MOVED_FROM { | ||||
| 		e.Op |= Rename | ||||
| 	} | ||||
| 	if mask&unix.IN_ATTRIB == unix.IN_ATTRIB { | ||||
| 		e.Op |= Chmod | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
							
								
								
									
										707
									
								
								vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										707
									
								
								vendor/github.com/fsnotify/fsnotify/backend_kqueue.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,707 @@ | ||||
| //go:build freebsd || openbsd || netbsd || dragonfly || darwin | ||||
| // +build freebsd openbsd netbsd dragonfly darwin | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"sync" | ||||
|  | ||||
| 	"golang.org/x/sys/unix" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of paths, delivering events on a channel. | ||||
| // | ||||
| // A watcher should not be copied (e.g. pass it by pointer, rather than by | ||||
| // value). | ||||
| // | ||||
| // # Linux notes | ||||
| // | ||||
| // When a file is removed a Remove event won't be emitted until all file | ||||
| // descriptors are closed, and deletes will always emit a Chmod. For example: | ||||
| // | ||||
| //     fp := os.Open("file") | ||||
| //     os.Remove("file")        // Triggers Chmod | ||||
| //     fp.Close()               // Triggers Remove | ||||
| // | ||||
| // This is the event that inotify sends, so not much can be changed about this. | ||||
| // | ||||
| // The fs.inotify.max_user_watches sysctl variable specifies the upper limit | ||||
| // for the number of watches per user, and fs.inotify.max_user_instances | ||||
| // specifies the maximum number of inotify instances per user. Every Watcher you | ||||
| // create is an "instance", and every path you add is a "watch". | ||||
| // | ||||
| // These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and | ||||
| // /proc/sys/fs/inotify/max_user_instances | ||||
| // | ||||
| // To increase them you can use sysctl or write the value to the /proc file: | ||||
| // | ||||
| //     # Default values on Linux 5.18 | ||||
| //     sysctl fs.inotify.max_user_watches=124983 | ||||
| //     sysctl fs.inotify.max_user_instances=128 | ||||
| // | ||||
| // To make the changes persist on reboot edit /etc/sysctl.conf or | ||||
| // /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check | ||||
| // your distro's documentation): | ||||
| // | ||||
| //     fs.inotify.max_user_watches=124983 | ||||
| //     fs.inotify.max_user_instances=128 | ||||
| // | ||||
| // Reaching the limit will result in a "no space left on device" or "too many open | ||||
| // files" error. | ||||
| // | ||||
| // # kqueue notes (macOS, BSD) | ||||
| // | ||||
| // kqueue requires opening a file descriptor for every file that's being watched; | ||||
| // so if you're watching a directory with five files then that's six file | ||||
| // descriptors. You will run in to your system's "max open files" limit faster on | ||||
| // these platforms. | ||||
| // | ||||
| // The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to | ||||
| // control the maximum number of open files, as well as /etc/login.conf on BSD | ||||
| // systems. | ||||
| // | ||||
| // # macOS notes | ||||
| // | ||||
| // Spotlight indexing on macOS can result in multiple events (see [#15]). A | ||||
| // temporary workaround is to add your folder(s) to the "Spotlight Privacy | ||||
| // Settings" until we have a native FSEvents implementation (see [#11]). | ||||
| // | ||||
| // [#11]: https://github.com/fsnotify/fsnotify/issues/11 | ||||
| // [#15]: https://github.com/fsnotify/fsnotify/issues/15 | ||||
| type Watcher struct { | ||||
| 	// Events sends the filesystem change events. | ||||
| 	// | ||||
| 	// fsnotify can send the following events; a "path" here can refer to a | ||||
| 	// file, directory, symbolic link, or special file like a FIFO. | ||||
| 	// | ||||
| 	//   fsnotify.Create    A new path was created; this may be followed by one | ||||
| 	//                      or more Write events if data also gets written to a | ||||
| 	//                      file. | ||||
| 	// | ||||
| 	//   fsnotify.Remove    A path was removed. | ||||
| 	// | ||||
| 	//   fsnotify.Rename    A path was renamed. A rename is always sent with the | ||||
| 	//                      old path as Event.Name, and a Create event will be | ||||
| 	//                      sent with the new name. Renames are only sent for | ||||
| 	//                      paths that are currently watched; e.g. moving an | ||||
| 	//                      unmonitored file into a monitored directory will | ||||
| 	//                      show up as just a Create. Similarly, renaming a file | ||||
| 	//                      to outside a monitored directory will show up as | ||||
| 	//                      only a Rename. | ||||
| 	// | ||||
| 	//   fsnotify.Write     A file or named pipe was written to. A Truncate will | ||||
| 	//                      also trigger a Write. A single "write action" | ||||
| 	//                      initiated by the user may show up as one or multiple | ||||
| 	//                      writes, depending on when the system syncs things to | ||||
| 	//                      disk. For example when compiling a large Go program | ||||
| 	//                      you may get hundreds of Write events, so you | ||||
| 	//                      probably want to wait until you've stopped receiving | ||||
| 	//                      them (see the dedup example in cmd/fsnotify). | ||||
| 	// | ||||
| 	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent | ||||
| 	//                      when a file is removed (or more accurately, when a | ||||
| 	//                      link to an inode is removed). On kqueue it's sent | ||||
| 	//                      and on kqueue when a file is truncated. On Windows | ||||
| 	//                      it's never sent. | ||||
| 	Events chan Event | ||||
|  | ||||
| 	// Errors sends any errors. | ||||
| 	Errors chan error | ||||
|  | ||||
| 	done         chan struct{} | ||||
| 	kq           int                         // File descriptor (as returned by the kqueue() syscall). | ||||
| 	closepipe    [2]int                      // Pipe used for closing. | ||||
| 	mu           sync.Mutex                  // Protects access to watcher data | ||||
| 	watches      map[string]int              // Watched file descriptors (key: path). | ||||
| 	watchesByDir map[string]map[int]struct{} // Watched file descriptors indexed by the parent directory (key: dirname(path)). | ||||
| 	userWatches  map[string]struct{}         // Watches added with Watcher.Add() | ||||
| 	dirFlags     map[string]uint32           // Watched directories to fflags used in kqueue. | ||||
| 	paths        map[int]pathInfo            // File descriptors to path names for processing kqueue events. | ||||
| 	fileExists   map[string]struct{}         // Keep track of if we know this file exists (to stop duplicate create events). | ||||
| 	isClosed     bool                        // Set to true when Close() is first called | ||||
| } | ||||
|  | ||||
| type pathInfo struct { | ||||
| 	name  string | ||||
| 	isDir bool | ||||
| } | ||||
|  | ||||
| // NewWatcher creates a new Watcher. | ||||
| func NewWatcher() (*Watcher, error) { | ||||
| 	kq, closepipe, err := newKqueue() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	w := &Watcher{ | ||||
| 		kq:           kq, | ||||
| 		closepipe:    closepipe, | ||||
| 		watches:      make(map[string]int), | ||||
| 		watchesByDir: make(map[string]map[int]struct{}), | ||||
| 		dirFlags:     make(map[string]uint32), | ||||
| 		paths:        make(map[int]pathInfo), | ||||
| 		fileExists:   make(map[string]struct{}), | ||||
| 		userWatches:  make(map[string]struct{}), | ||||
| 		Events:       make(chan Event), | ||||
| 		Errors:       make(chan error), | ||||
| 		done:         make(chan struct{}), | ||||
| 	} | ||||
|  | ||||
| 	go w.readEvents() | ||||
| 	return w, nil | ||||
| } | ||||
|  | ||||
| // newKqueue creates a new kernel event queue and returns a descriptor. | ||||
| // | ||||
| // This registers a new event on closepipe, which will trigger an event when | ||||
| // it's closed. This way we can use kevent() without timeout/polling; without | ||||
| // the closepipe, it would block forever and we wouldn't be able to stop it at | ||||
| // all. | ||||
| func newKqueue() (kq int, closepipe [2]int, err error) { | ||||
| 	kq, err = unix.Kqueue() | ||||
| 	if kq == -1 { | ||||
| 		return kq, closepipe, err | ||||
| 	} | ||||
|  | ||||
| 	// Register the close pipe. | ||||
| 	err = unix.Pipe(closepipe[:]) | ||||
| 	if err != nil { | ||||
| 		unix.Close(kq) | ||||
| 		return kq, closepipe, err | ||||
| 	} | ||||
|  | ||||
| 	// Register changes to listen on the closepipe. | ||||
| 	changes := make([]unix.Kevent_t, 1) | ||||
| 	// SetKevent converts int to the platform-specific types. | ||||
| 	unix.SetKevent(&changes[0], closepipe[0], unix.EVFILT_READ, | ||||
| 		unix.EV_ADD|unix.EV_ENABLE|unix.EV_ONESHOT) | ||||
|  | ||||
| 	ok, err := unix.Kevent(kq, changes, nil, nil) | ||||
| 	if ok == -1 { | ||||
| 		unix.Close(kq) | ||||
| 		unix.Close(closepipe[0]) | ||||
| 		unix.Close(closepipe[1]) | ||||
| 		return kq, closepipe, err | ||||
| 	} | ||||
| 	return kq, closepipe, nil | ||||
| } | ||||
|  | ||||
| // Returns true if the event was sent, or false if watcher is closed. | ||||
| func (w *Watcher) sendEvent(e Event) bool { | ||||
| 	select { | ||||
| 	case w.Events <- e: | ||||
| 		return true | ||||
| 	case <-w.done: | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // Returns true if the error was sent, or false if watcher is closed. | ||||
| func (w *Watcher) sendError(err error) bool { | ||||
| 	select { | ||||
| 	case w.Errors <- err: | ||||
| 		return true | ||||
| 	case <-w.done: | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| func (w *Watcher) Close() error { | ||||
| 	w.mu.Lock() | ||||
| 	if w.isClosed { | ||||
| 		w.mu.Unlock() | ||||
| 		return nil | ||||
| 	} | ||||
| 	w.isClosed = true | ||||
|  | ||||
| 	// copy paths to remove while locked | ||||
| 	pathsToRemove := make([]string, 0, len(w.watches)) | ||||
| 	for name := range w.watches { | ||||
| 		pathsToRemove = append(pathsToRemove, name) | ||||
| 	} | ||||
| 	w.mu.Unlock() // Unlock before calling Remove, which also locks | ||||
| 	for _, name := range pathsToRemove { | ||||
| 		w.Remove(name) | ||||
| 	} | ||||
|  | ||||
| 	// Send "quit" message to the reader goroutine. | ||||
| 	unix.Close(w.closepipe[1]) | ||||
| 	close(w.done) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Add starts monitoring the path for changes. | ||||
| // | ||||
| // A path can only be watched once; attempting to watch it more than once will | ||||
| // return an error. Paths that do not yet exist on the filesystem cannot be | ||||
| // added. A watch will be automatically removed if the path is deleted. | ||||
| // | ||||
| // A path will remain watched if it gets renamed to somewhere else on the same | ||||
| // filesystem, but the monitor will get removed if the path gets deleted and | ||||
| // re-created, or if it's moved to a different filesystem. | ||||
| // | ||||
| // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special | ||||
| // filesystems (/proc, /sys, etc.) generally don't work. | ||||
| // | ||||
| // # Watching directories | ||||
| // | ||||
| // All files in a directory are monitored, including new files that are created | ||||
| // after the watcher is started. Subdirectories are not watched (i.e. it's | ||||
| // non-recursive). | ||||
| // | ||||
| // # Watching files | ||||
| // | ||||
| // Watching individual files (rather than directories) is generally not | ||||
| // recommended as many tools update files atomically. Instead of "just" writing | ||||
| // to the file a temporary file will be written to first, and if successful the | ||||
| // temporary file is moved to to destination removing the original, or some | ||||
| // variant thereof. The watcher on the original file is now lost, as it no | ||||
| // longer exists. | ||||
| // | ||||
| // Instead, watch the parent directory and use Event.Name to filter out files | ||||
| // you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	w.mu.Lock() | ||||
| 	w.userWatches[name] = struct{}{} | ||||
| 	w.mu.Unlock() | ||||
| 	_, err := w.addWatch(name, noteAllEvents) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Remove stops monitoring the path for changes. | ||||
| // | ||||
| // Directories are always removed non-recursively. For example, if you added | ||||
| // /tmp/dir and /tmp/dir/subdir then you will need to remove both. | ||||
| // | ||||
| // Removing a path that has not yet been added returns [ErrNonExistentWatch]. | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	name = filepath.Clean(name) | ||||
| 	w.mu.Lock() | ||||
| 	watchfd, ok := w.watches[name] | ||||
| 	w.mu.Unlock() | ||||
| 	if !ok { | ||||
| 		return fmt.Errorf("%w: %s", ErrNonExistentWatch, name) | ||||
| 	} | ||||
|  | ||||
| 	err := w.register([]int{watchfd}, unix.EV_DELETE, 0) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	unix.Close(watchfd) | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	isDir := w.paths[watchfd].isDir | ||||
| 	delete(w.watches, name) | ||||
| 	delete(w.userWatches, name) | ||||
|  | ||||
| 	parentName := filepath.Dir(name) | ||||
| 	delete(w.watchesByDir[parentName], watchfd) | ||||
|  | ||||
| 	if len(w.watchesByDir[parentName]) == 0 { | ||||
| 		delete(w.watchesByDir, parentName) | ||||
| 	} | ||||
|  | ||||
| 	delete(w.paths, watchfd) | ||||
| 	delete(w.dirFlags, name) | ||||
| 	delete(w.fileExists, name) | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	// Find all watched paths that are in this directory that are not external. | ||||
| 	if isDir { | ||||
| 		var pathsToRemove []string | ||||
| 		w.mu.Lock() | ||||
| 		for fd := range w.watchesByDir[name] { | ||||
| 			path := w.paths[fd] | ||||
| 			if _, ok := w.userWatches[path.name]; !ok { | ||||
| 				pathsToRemove = append(pathsToRemove, path.name) | ||||
| 			} | ||||
| 		} | ||||
| 		w.mu.Unlock() | ||||
| 		for _, name := range pathsToRemove { | ||||
| 			// Since these are internal, not much sense in propagating error | ||||
| 			// to the user, as that will just confuse them with an error about | ||||
| 			// a path they did not explicitly watch themselves. | ||||
| 			w.Remove(name) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // WatchList returns all paths added with [Add] (and are not yet removed). | ||||
| func (w *Watcher) WatchList() []string { | ||||
| 	w.mu.Lock() | ||||
| 	defer w.mu.Unlock() | ||||
|  | ||||
| 	entries := make([]string, 0, len(w.userWatches)) | ||||
| 	for pathname := range w.userWatches { | ||||
| 		entries = append(entries, pathname) | ||||
| 	} | ||||
|  | ||||
| 	return entries | ||||
| } | ||||
|  | ||||
| // Watch all events (except NOTE_EXTEND, NOTE_LINK, NOTE_REVOKE) | ||||
| const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | unix.NOTE_RENAME | ||||
|  | ||||
| // addWatch adds name to the watched file set. | ||||
| // The flags are interpreted as described in kevent(2). | ||||
| // Returns the real path to the file which was added, if any, which may be different from the one passed in the case of symlinks. | ||||
| func (w *Watcher) addWatch(name string, flags uint32) (string, error) { | ||||
| 	var isDir bool | ||||
| 	// Make ./name and name equivalent | ||||
| 	name = filepath.Clean(name) | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	if w.isClosed { | ||||
| 		w.mu.Unlock() | ||||
| 		return "", errors.New("kevent instance already closed") | ||||
| 	} | ||||
| 	watchfd, alreadyWatching := w.watches[name] | ||||
| 	// We already have a watch, but we can still override flags. | ||||
| 	if alreadyWatching { | ||||
| 		isDir = w.paths[watchfd].isDir | ||||
| 	} | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	if !alreadyWatching { | ||||
| 		fi, err := os.Lstat(name) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		// Don't watch sockets or named pipes | ||||
| 		if (fi.Mode()&os.ModeSocket == os.ModeSocket) || (fi.Mode()&os.ModeNamedPipe == os.ModeNamedPipe) { | ||||
| 			return "", nil | ||||
| 		} | ||||
|  | ||||
| 		// Follow Symlinks | ||||
| 		// | ||||
| 		// Linux can add unresolvable symlinks to the watch list without issue, | ||||
| 		// and Windows can't do symlinks period. To maintain consistency, we | ||||
| 		// will act like everything is fine if the link can't be resolved. | ||||
| 		// There will simply be no file events for broken symlinks. Hence the | ||||
| 		// returns of nil on errors. | ||||
| 		if fi.Mode()&os.ModeSymlink == os.ModeSymlink { | ||||
| 			name, err = filepath.EvalSymlinks(name) | ||||
| 			if err != nil { | ||||
| 				return "", nil | ||||
| 			} | ||||
|  | ||||
| 			w.mu.Lock() | ||||
| 			_, alreadyWatching = w.watches[name] | ||||
| 			w.mu.Unlock() | ||||
|  | ||||
| 			if alreadyWatching { | ||||
| 				return name, nil | ||||
| 			} | ||||
|  | ||||
| 			fi, err = os.Lstat(name) | ||||
| 			if err != nil { | ||||
| 				return "", nil | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Retry on EINTR; open() can return EINTR in practice on macOS. | ||||
| 		// See #354, and go issues 11180 and 39237. | ||||
| 		for { | ||||
| 			watchfd, err = unix.Open(name, openMode, 0) | ||||
| 			if err == nil { | ||||
| 				break | ||||
| 			} | ||||
| 			if errors.Is(err, unix.EINTR) { | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			return "", err | ||||
| 		} | ||||
|  | ||||
| 		isDir = fi.IsDir() | ||||
| 	} | ||||
|  | ||||
| 	err := w.register([]int{watchfd}, unix.EV_ADD|unix.EV_CLEAR|unix.EV_ENABLE, flags) | ||||
| 	if err != nil { | ||||
| 		unix.Close(watchfd) | ||||
| 		return "", err | ||||
| 	} | ||||
|  | ||||
| 	if !alreadyWatching { | ||||
| 		w.mu.Lock() | ||||
| 		parentName := filepath.Dir(name) | ||||
| 		w.watches[name] = watchfd | ||||
|  | ||||
| 		watchesByDir, ok := w.watchesByDir[parentName] | ||||
| 		if !ok { | ||||
| 			watchesByDir = make(map[int]struct{}, 1) | ||||
| 			w.watchesByDir[parentName] = watchesByDir | ||||
| 		} | ||||
| 		watchesByDir[watchfd] = struct{}{} | ||||
|  | ||||
| 		w.paths[watchfd] = pathInfo{name: name, isDir: isDir} | ||||
| 		w.mu.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	if isDir { | ||||
| 		// Watch the directory if it has not been watched before, | ||||
| 		// or if it was watched before, but perhaps only a NOTE_DELETE (watchDirectoryFiles) | ||||
| 		w.mu.Lock() | ||||
|  | ||||
| 		watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE && | ||||
| 			(!alreadyWatching || (w.dirFlags[name]&unix.NOTE_WRITE) != unix.NOTE_WRITE) | ||||
| 		// Store flags so this watch can be updated later | ||||
| 		w.dirFlags[name] = flags | ||||
| 		w.mu.Unlock() | ||||
|  | ||||
| 		if watchDir { | ||||
| 			if err := w.watchDirectoryFiles(name); err != nil { | ||||
| 				return "", err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return name, nil | ||||
| } | ||||
|  | ||||
| // readEvents reads from kqueue and converts the received kevents into | ||||
| // Event values that it sends down the Events channel. | ||||
| func (w *Watcher) readEvents() { | ||||
| 	defer func() { | ||||
| 		err := unix.Close(w.kq) | ||||
| 		if err != nil { | ||||
| 			w.Errors <- err | ||||
| 		} | ||||
| 		unix.Close(w.closepipe[0]) | ||||
| 		close(w.Events) | ||||
| 		close(w.Errors) | ||||
| 	}() | ||||
|  | ||||
| 	eventBuffer := make([]unix.Kevent_t, 10) | ||||
| 	for closed := false; !closed; { | ||||
| 		kevents, err := w.read(eventBuffer) | ||||
| 		// EINTR is okay, the syscall was interrupted before timeout expired. | ||||
| 		if err != nil && err != unix.EINTR { | ||||
| 			if !w.sendError(fmt.Errorf("fsnotify.readEvents: %w", err)) { | ||||
| 				closed = true | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// Flush the events we received to the Events channel | ||||
| 		for _, kevent := range kevents { | ||||
| 			var ( | ||||
| 				watchfd = int(kevent.Ident) | ||||
| 				mask    = uint32(kevent.Fflags) | ||||
| 			) | ||||
|  | ||||
| 			// Shut down the loop when the pipe is closed, but only after all | ||||
| 			// other events have been processed. | ||||
| 			if watchfd == w.closepipe[0] { | ||||
| 				closed = true | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			w.mu.Lock() | ||||
| 			path := w.paths[watchfd] | ||||
| 			w.mu.Unlock() | ||||
|  | ||||
| 			event := w.newEvent(path.name, mask) | ||||
|  | ||||
| 			if path.isDir && !event.Has(Remove) { | ||||
| 				// Double check to make sure the directory exists. This can | ||||
| 				// happen when we do a rm -fr on a recursively watched folders | ||||
| 				// and we receive a modification event first but the folder has | ||||
| 				// been deleted and later receive the delete event. | ||||
| 				if _, err := os.Lstat(event.Name); os.IsNotExist(err) { | ||||
| 					event.Op |= Remove | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if event.Has(Rename) || event.Has(Remove) { | ||||
| 				w.Remove(event.Name) | ||||
| 				w.mu.Lock() | ||||
| 				delete(w.fileExists, event.Name) | ||||
| 				w.mu.Unlock() | ||||
| 			} | ||||
|  | ||||
| 			if path.isDir && event.Has(Write) && !event.Has(Remove) { | ||||
| 				w.sendDirectoryChangeEvents(event.Name) | ||||
| 			} else { | ||||
| 				if !w.sendEvent(event) { | ||||
| 					closed = true | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if event.Has(Remove) { | ||||
| 				// Look for a file that may have overwritten this. | ||||
| 				// For example, mv f1 f2 will delete f2, then create f2. | ||||
| 				if path.isDir { | ||||
| 					fileDir := filepath.Clean(event.Name) | ||||
| 					w.mu.Lock() | ||||
| 					_, found := w.watches[fileDir] | ||||
| 					w.mu.Unlock() | ||||
| 					if found { | ||||
| 						// make sure the directory exists before we watch for changes. When we | ||||
| 						// do a recursive watch and perform rm -fr, the parent directory might | ||||
| 						// have gone missing, ignore the missing directory and let the | ||||
| 						// upcoming delete event remove the watch from the parent directory. | ||||
| 						if _, err := os.Lstat(fileDir); err == nil { | ||||
| 							w.sendDirectoryChangeEvents(fileDir) | ||||
| 						} | ||||
| 					} | ||||
| 				} else { | ||||
| 					filePath := filepath.Clean(event.Name) | ||||
| 					if fileInfo, err := os.Lstat(filePath); err == nil { | ||||
| 						w.sendFileCreatedEventIfNew(filePath, fileInfo) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // newEvent returns an platform-independent Event based on kqueue Fflags. | ||||
| func (w *Watcher) newEvent(name string, mask uint32) Event { | ||||
| 	e := Event{Name: name} | ||||
| 	if mask&unix.NOTE_DELETE == unix.NOTE_DELETE { | ||||
| 		e.Op |= Remove | ||||
| 	} | ||||
| 	if mask&unix.NOTE_WRITE == unix.NOTE_WRITE { | ||||
| 		e.Op |= Write | ||||
| 	} | ||||
| 	if mask&unix.NOTE_RENAME == unix.NOTE_RENAME { | ||||
| 		e.Op |= Rename | ||||
| 	} | ||||
| 	if mask&unix.NOTE_ATTRIB == unix.NOTE_ATTRIB { | ||||
| 		e.Op |= Chmod | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| // watchDirectoryFiles to mimic inotify when adding a watch on a directory | ||||
| func (w *Watcher) watchDirectoryFiles(dirPath string) error { | ||||
| 	// Get all files | ||||
| 	files, err := ioutil.ReadDir(dirPath) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	for _, fileInfo := range files { | ||||
| 		path := filepath.Join(dirPath, fileInfo.Name()) | ||||
|  | ||||
| 		cleanPath, err := w.internalWatch(path, fileInfo) | ||||
| 		if err != nil { | ||||
| 			// No permission to read the file; that's not a problem: just skip. | ||||
| 			// But do add it to w.fileExists to prevent it from being picked up | ||||
| 			// as a "new" file later (it still shows up in the directory | ||||
| 			// listing). | ||||
| 			switch { | ||||
| 			case errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM): | ||||
| 				cleanPath = filepath.Clean(path) | ||||
| 			default: | ||||
| 				return fmt.Errorf("%q: %w", filepath.Join(dirPath, fileInfo.Name()), err) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		w.mu.Lock() | ||||
| 		w.fileExists[cleanPath] = struct{}{} | ||||
| 		w.mu.Unlock() | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Search the directory for new files and send an event for them. | ||||
| // | ||||
| // This functionality is to have the BSD watcher match the inotify, which sends | ||||
| // a create event for files created in a watched directory. | ||||
| func (w *Watcher) sendDirectoryChangeEvents(dir string) { | ||||
| 	// Get all files | ||||
| 	files, err := ioutil.ReadDir(dir) | ||||
| 	if err != nil { | ||||
| 		if !w.sendError(fmt.Errorf("fsnotify.sendDirectoryChangeEvents: %w", err)) { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Search for new files | ||||
| 	for _, fi := range files { | ||||
| 		err := w.sendFileCreatedEventIfNew(filepath.Join(dir, fi.Name()), fi) | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // sendFileCreatedEvent sends a create event if the file isn't already being tracked. | ||||
| func (w *Watcher) sendFileCreatedEventIfNew(filePath string, fileInfo os.FileInfo) (err error) { | ||||
| 	w.mu.Lock() | ||||
| 	_, doesExist := w.fileExists[filePath] | ||||
| 	w.mu.Unlock() | ||||
| 	if !doesExist { | ||||
| 		if !w.sendEvent(Event{Name: filePath, Op: Create}) { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// like watchDirectoryFiles (but without doing another ReadDir) | ||||
| 	filePath, err = w.internalWatch(filePath, fileInfo) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	w.fileExists[filePath] = struct{}{} | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (w *Watcher) internalWatch(name string, fileInfo os.FileInfo) (string, error) { | ||||
| 	if fileInfo.IsDir() { | ||||
| 		// mimic Linux providing delete events for subdirectories | ||||
| 		// but preserve the flags used if currently watching subdirectory | ||||
| 		w.mu.Lock() | ||||
| 		flags := w.dirFlags[name] | ||||
| 		w.mu.Unlock() | ||||
|  | ||||
| 		flags |= unix.NOTE_DELETE | unix.NOTE_RENAME | ||||
| 		return w.addWatch(name, flags) | ||||
| 	} | ||||
|  | ||||
| 	// watch file to mimic Linux inotify | ||||
| 	return w.addWatch(name, noteAllEvents) | ||||
| } | ||||
|  | ||||
| // Register events with the queue. | ||||
| func (w *Watcher) register(fds []int, flags int, fflags uint32) error { | ||||
| 	changes := make([]unix.Kevent_t, len(fds)) | ||||
| 	for i, fd := range fds { | ||||
| 		// SetKevent converts int to the platform-specific types. | ||||
| 		unix.SetKevent(&changes[i], fd, unix.EVFILT_VNODE, flags) | ||||
| 		changes[i].Fflags = fflags | ||||
| 	} | ||||
|  | ||||
| 	// Register the events. | ||||
| 	success, err := unix.Kevent(w.kq, changes, nil, nil) | ||||
| 	if success == -1 { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // read retrieves pending events, or waits until an event occurs. | ||||
| func (w *Watcher) read(events []unix.Kevent_t) ([]unix.Kevent_t, error) { | ||||
| 	n, err := unix.Kevent(w.kq, nil, events, nil) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return events[0:n], nil | ||||
| } | ||||
							
								
								
									
										66
									
								
								vendor/github.com/fsnotify/fsnotify/backend_other.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/fsnotify/fsnotify/backend_other.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| //go:build !darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows | ||||
| // +build !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"runtime" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of files, delivering events to a channel. | ||||
| type Watcher struct{} | ||||
|  | ||||
| // NewWatcher creates a new Watcher. | ||||
| func NewWatcher() (*Watcher, error) { | ||||
| 	return nil, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS) | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| func (w *Watcher) Close() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Add starts monitoring the path for changes. | ||||
| // | ||||
| // A path can only be watched once; attempting to watch it more than once will | ||||
| // return an error. Paths that do not yet exist on the filesystem cannot be | ||||
| // added. A watch will be automatically removed if the path is deleted. | ||||
| // | ||||
| // A path will remain watched if it gets renamed to somewhere else on the same | ||||
| // filesystem, but the monitor will get removed if the path gets deleted and | ||||
| // re-created, or if it's moved to a different filesystem. | ||||
| // | ||||
| // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special | ||||
| // filesystems (/proc, /sys, etc.) generally don't work. | ||||
| // | ||||
| // # Watching directories | ||||
| // | ||||
| // All files in a directory are monitored, including new files that are created | ||||
| // after the watcher is started. Subdirectories are not watched (i.e. it's | ||||
| // non-recursive). | ||||
| // | ||||
| // # Watching files | ||||
| // | ||||
| // Watching individual files (rather than directories) is generally not | ||||
| // recommended as many tools update files atomically. Instead of "just" writing | ||||
| // to the file a temporary file will be written to first, and if successful the | ||||
| // temporary file is moved to to destination removing the original, or some | ||||
| // variant thereof. The watcher on the original file is now lost, as it no | ||||
| // longer exists. | ||||
| // | ||||
| // Instead, watch the parent directory and use Event.Name to filter out files | ||||
| // you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Remove stops monitoring the path for changes. | ||||
| // | ||||
| // Directories are always removed non-recursively. For example, if you added | ||||
| // /tmp/dir and /tmp/dir/subdir then you will need to remove both. | ||||
| // | ||||
| // Removing a path that has not yet been added returns [ErrNonExistentWatch]. | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										746
									
								
								vendor/github.com/fsnotify/fsnotify/backend_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										746
									
								
								vendor/github.com/fsnotify/fsnotify/backend_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,746 @@ | ||||
| //go:build windows | ||||
| // +build windows | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"reflect" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"unsafe" | ||||
|  | ||||
| 	"golang.org/x/sys/windows" | ||||
| ) | ||||
|  | ||||
| // Watcher watches a set of paths, delivering events on a channel. | ||||
| // | ||||
| // A watcher should not be copied (e.g. pass it by pointer, rather than by | ||||
| // value). | ||||
| // | ||||
| // # Linux notes | ||||
| // | ||||
| // When a file is removed a Remove event won't be emitted until all file | ||||
| // descriptors are closed, and deletes will always emit a Chmod. For example: | ||||
| // | ||||
| //     fp := os.Open("file") | ||||
| //     os.Remove("file")        // Triggers Chmod | ||||
| //     fp.Close()               // Triggers Remove | ||||
| // | ||||
| // This is the event that inotify sends, so not much can be changed about this. | ||||
| // | ||||
| // The fs.inotify.max_user_watches sysctl variable specifies the upper limit | ||||
| // for the number of watches per user, and fs.inotify.max_user_instances | ||||
| // specifies the maximum number of inotify instances per user. Every Watcher you | ||||
| // create is an "instance", and every path you add is a "watch". | ||||
| // | ||||
| // These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and | ||||
| // /proc/sys/fs/inotify/max_user_instances | ||||
| // | ||||
| // To increase them you can use sysctl or write the value to the /proc file: | ||||
| // | ||||
| //     # Default values on Linux 5.18 | ||||
| //     sysctl fs.inotify.max_user_watches=124983 | ||||
| //     sysctl fs.inotify.max_user_instances=128 | ||||
| // | ||||
| // To make the changes persist on reboot edit /etc/sysctl.conf or | ||||
| // /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check | ||||
| // your distro's documentation): | ||||
| // | ||||
| //     fs.inotify.max_user_watches=124983 | ||||
| //     fs.inotify.max_user_instances=128 | ||||
| // | ||||
| // Reaching the limit will result in a "no space left on device" or "too many open | ||||
| // files" error. | ||||
| // | ||||
| // # kqueue notes (macOS, BSD) | ||||
| // | ||||
| // kqueue requires opening a file descriptor for every file that's being watched; | ||||
| // so if you're watching a directory with five files then that's six file | ||||
| // descriptors. You will run in to your system's "max open files" limit faster on | ||||
| // these platforms. | ||||
| // | ||||
| // The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to | ||||
| // control the maximum number of open files, as well as /etc/login.conf on BSD | ||||
| // systems. | ||||
| // | ||||
| // # macOS notes | ||||
| // | ||||
| // Spotlight indexing on macOS can result in multiple events (see [#15]). A | ||||
| // temporary workaround is to add your folder(s) to the "Spotlight Privacy | ||||
| // Settings" until we have a native FSEvents implementation (see [#11]). | ||||
| // | ||||
| // [#11]: https://github.com/fsnotify/fsnotify/issues/11 | ||||
| // [#15]: https://github.com/fsnotify/fsnotify/issues/15 | ||||
| type Watcher struct { | ||||
| 	// Events sends the filesystem change events. | ||||
| 	// | ||||
| 	// fsnotify can send the following events; a "path" here can refer to a | ||||
| 	// file, directory, symbolic link, or special file like a FIFO. | ||||
| 	// | ||||
| 	//   fsnotify.Create    A new path was created; this may be followed by one | ||||
| 	//                      or more Write events if data also gets written to a | ||||
| 	//                      file. | ||||
| 	// | ||||
| 	//   fsnotify.Remove    A path was removed. | ||||
| 	// | ||||
| 	//   fsnotify.Rename    A path was renamed. A rename is always sent with the | ||||
| 	//                      old path as Event.Name, and a Create event will be | ||||
| 	//                      sent with the new name. Renames are only sent for | ||||
| 	//                      paths that are currently watched; e.g. moving an | ||||
| 	//                      unmonitored file into a monitored directory will | ||||
| 	//                      show up as just a Create. Similarly, renaming a file | ||||
| 	//                      to outside a monitored directory will show up as | ||||
| 	//                      only a Rename. | ||||
| 	// | ||||
| 	//   fsnotify.Write     A file or named pipe was written to. A Truncate will | ||||
| 	//                      also trigger a Write. A single "write action" | ||||
| 	//                      initiated by the user may show up as one or multiple | ||||
| 	//                      writes, depending on when the system syncs things to | ||||
| 	//                      disk. For example when compiling a large Go program | ||||
| 	//                      you may get hundreds of Write events, so you | ||||
| 	//                      probably want to wait until you've stopped receiving | ||||
| 	//                      them (see the dedup example in cmd/fsnotify). | ||||
| 	// | ||||
| 	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent | ||||
| 	//                      when a file is removed (or more accurately, when a | ||||
| 	//                      link to an inode is removed). On kqueue it's sent | ||||
| 	//                      and on kqueue when a file is truncated. On Windows | ||||
| 	//                      it's never sent. | ||||
| 	Events chan Event | ||||
|  | ||||
| 	// Errors sends any errors. | ||||
| 	Errors chan error | ||||
|  | ||||
| 	port  windows.Handle // Handle to completion port | ||||
| 	input chan *input    // Inputs to the reader are sent on this channel | ||||
| 	quit  chan chan<- error | ||||
|  | ||||
| 	mu       sync.Mutex // Protects access to watches, isClosed | ||||
| 	watches  watchMap   // Map of watches (key: i-number) | ||||
| 	isClosed bool       // Set to true when Close() is first called | ||||
| } | ||||
|  | ||||
| // NewWatcher creates a new Watcher. | ||||
| func NewWatcher() (*Watcher, error) { | ||||
| 	port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0) | ||||
| 	if err != nil { | ||||
| 		return nil, os.NewSyscallError("CreateIoCompletionPort", err) | ||||
| 	} | ||||
| 	w := &Watcher{ | ||||
| 		port:    port, | ||||
| 		watches: make(watchMap), | ||||
| 		input:   make(chan *input, 1), | ||||
| 		Events:  make(chan Event, 50), | ||||
| 		Errors:  make(chan error), | ||||
| 		quit:    make(chan chan<- error, 1), | ||||
| 	} | ||||
| 	go w.readEvents() | ||||
| 	return w, nil | ||||
| } | ||||
|  | ||||
| func (w *Watcher) sendEvent(name string, mask uint64) bool { | ||||
| 	if mask == 0 { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	event := w.newEvent(name, uint32(mask)) | ||||
| 	select { | ||||
| 	case ch := <-w.quit: | ||||
| 		w.quit <- ch | ||||
| 	case w.Events <- event: | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // Returns true if the error was sent, or false if watcher is closed. | ||||
| func (w *Watcher) sendError(err error) bool { | ||||
| 	select { | ||||
| 	case w.Errors <- err: | ||||
| 		return true | ||||
| 	case <-w.quit: | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // Close removes all watches and closes the events channel. | ||||
| func (w *Watcher) Close() error { | ||||
| 	w.mu.Lock() | ||||
| 	if w.isClosed { | ||||
| 		w.mu.Unlock() | ||||
| 		return nil | ||||
| 	} | ||||
| 	w.isClosed = true | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	// Send "quit" message to the reader goroutine | ||||
| 	ch := make(chan error) | ||||
| 	w.quit <- ch | ||||
| 	if err := w.wakeupReader(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return <-ch | ||||
| } | ||||
|  | ||||
| // Add starts monitoring the path for changes. | ||||
| // | ||||
| // A path can only be watched once; attempting to watch it more than once will | ||||
| // return an error. Paths that do not yet exist on the filesystem cannot be | ||||
| // added. A watch will be automatically removed if the path is deleted. | ||||
| // | ||||
| // A path will remain watched if it gets renamed to somewhere else on the same | ||||
| // filesystem, but the monitor will get removed if the path gets deleted and | ||||
| // re-created, or if it's moved to a different filesystem. | ||||
| // | ||||
| // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special | ||||
| // filesystems (/proc, /sys, etc.) generally don't work. | ||||
| // | ||||
| // # Watching directories | ||||
| // | ||||
| // All files in a directory are monitored, including new files that are created | ||||
| // after the watcher is started. Subdirectories are not watched (i.e. it's | ||||
| // non-recursive). | ||||
| // | ||||
| // # Watching files | ||||
| // | ||||
| // Watching individual files (rather than directories) is generally not | ||||
| // recommended as many tools update files atomically. Instead of "just" writing | ||||
| // to the file a temporary file will be written to first, and if successful the | ||||
| // temporary file is moved to to destination removing the original, or some | ||||
| // variant thereof. The watcher on the original file is now lost, as it no | ||||
| // longer exists. | ||||
| // | ||||
| // Instead, watch the parent directory and use Event.Name to filter out files | ||||
| // you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. | ||||
| func (w *Watcher) Add(name string) error { | ||||
| 	w.mu.Lock() | ||||
| 	if w.isClosed { | ||||
| 		w.mu.Unlock() | ||||
| 		return errors.New("watcher already closed") | ||||
| 	} | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	in := &input{ | ||||
| 		op:    opAddWatch, | ||||
| 		path:  filepath.Clean(name), | ||||
| 		flags: sysFSALLEVENTS, | ||||
| 		reply: make(chan error), | ||||
| 	} | ||||
| 	w.input <- in | ||||
| 	if err := w.wakeupReader(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return <-in.reply | ||||
| } | ||||
|  | ||||
| // Remove stops monitoring the path for changes. | ||||
| // | ||||
| // Directories are always removed non-recursively. For example, if you added | ||||
| // /tmp/dir and /tmp/dir/subdir then you will need to remove both. | ||||
| // | ||||
| // Removing a path that has not yet been added returns [ErrNonExistentWatch]. | ||||
| func (w *Watcher) Remove(name string) error { | ||||
| 	in := &input{ | ||||
| 		op:    opRemoveWatch, | ||||
| 		path:  filepath.Clean(name), | ||||
| 		reply: make(chan error), | ||||
| 	} | ||||
| 	w.input <- in | ||||
| 	if err := w.wakeupReader(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return <-in.reply | ||||
| } | ||||
|  | ||||
| // WatchList returns all paths added with [Add] (and are not yet removed). | ||||
| func (w *Watcher) WatchList() []string { | ||||
| 	w.mu.Lock() | ||||
| 	defer w.mu.Unlock() | ||||
|  | ||||
| 	entries := make([]string, 0, len(w.watches)) | ||||
| 	for _, entry := range w.watches { | ||||
| 		for _, watchEntry := range entry { | ||||
| 			entries = append(entries, watchEntry.path) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return entries | ||||
| } | ||||
|  | ||||
| // These options are from the old golang.org/x/exp/winfsnotify, where you could | ||||
| // add various options to the watch. This has long since been removed. | ||||
| // | ||||
| // The "sys" in the name is misleading as they're not part of any "system". | ||||
| // | ||||
| // This should all be removed at some point, and just use windows.FILE_NOTIFY_* | ||||
| const ( | ||||
| 	sysFSALLEVENTS  = 0xfff | ||||
| 	sysFSATTRIB     = 0x4 | ||||
| 	sysFSCREATE     = 0x100 | ||||
| 	sysFSDELETE     = 0x200 | ||||
| 	sysFSDELETESELF = 0x400 | ||||
| 	sysFSMODIFY     = 0x2 | ||||
| 	sysFSMOVE       = 0xc0 | ||||
| 	sysFSMOVEDFROM  = 0x40 | ||||
| 	sysFSMOVEDTO    = 0x80 | ||||
| 	sysFSMOVESELF   = 0x800 | ||||
| 	sysFSIGNORED    = 0x8000 | ||||
| ) | ||||
|  | ||||
| func (w *Watcher) newEvent(name string, mask uint32) Event { | ||||
| 	e := Event{Name: name} | ||||
| 	if mask&sysFSCREATE == sysFSCREATE || mask&sysFSMOVEDTO == sysFSMOVEDTO { | ||||
| 		e.Op |= Create | ||||
| 	} | ||||
| 	if mask&sysFSDELETE == sysFSDELETE || mask&sysFSDELETESELF == sysFSDELETESELF { | ||||
| 		e.Op |= Remove | ||||
| 	} | ||||
| 	if mask&sysFSMODIFY == sysFSMODIFY { | ||||
| 		e.Op |= Write | ||||
| 	} | ||||
| 	if mask&sysFSMOVE == sysFSMOVE || mask&sysFSMOVESELF == sysFSMOVESELF || mask&sysFSMOVEDFROM == sysFSMOVEDFROM { | ||||
| 		e.Op |= Rename | ||||
| 	} | ||||
| 	if mask&sysFSATTRIB == sysFSATTRIB { | ||||
| 		e.Op |= Chmod | ||||
| 	} | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	opAddWatch = iota | ||||
| 	opRemoveWatch | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	provisional uint64 = 1 << (32 + iota) | ||||
| ) | ||||
|  | ||||
| type input struct { | ||||
| 	op    int | ||||
| 	path  string | ||||
| 	flags uint32 | ||||
| 	reply chan error | ||||
| } | ||||
|  | ||||
| type inode struct { | ||||
| 	handle windows.Handle | ||||
| 	volume uint32 | ||||
| 	index  uint64 | ||||
| } | ||||
|  | ||||
| type watch struct { | ||||
| 	ov     windows.Overlapped | ||||
| 	ino    *inode            // i-number | ||||
| 	path   string            // Directory path | ||||
| 	mask   uint64            // Directory itself is being watched with these notify flags | ||||
| 	names  map[string]uint64 // Map of names being watched and their notify flags | ||||
| 	rename string            // Remembers the old name while renaming a file | ||||
| 	buf    [65536]byte       // 64K buffer | ||||
| } | ||||
|  | ||||
| type ( | ||||
| 	indexMap map[uint64]*watch | ||||
| 	watchMap map[uint32]indexMap | ||||
| ) | ||||
|  | ||||
| func (w *Watcher) wakeupReader() error { | ||||
| 	err := windows.PostQueuedCompletionStatus(w.port, 0, 0, nil) | ||||
| 	if err != nil { | ||||
| 		return os.NewSyscallError("PostQueuedCompletionStatus", err) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (w *Watcher) getDir(pathname string) (dir string, err error) { | ||||
| 	attr, err := windows.GetFileAttributes(windows.StringToUTF16Ptr(pathname)) | ||||
| 	if err != nil { | ||||
| 		return "", os.NewSyscallError("GetFileAttributes", err) | ||||
| 	} | ||||
| 	if attr&windows.FILE_ATTRIBUTE_DIRECTORY != 0 { | ||||
| 		dir = pathname | ||||
| 	} else { | ||||
| 		dir, _ = filepath.Split(pathname) | ||||
| 		dir = filepath.Clean(dir) | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (w *Watcher) getIno(path string) (ino *inode, err error) { | ||||
| 	h, err := windows.CreateFile(windows.StringToUTF16Ptr(path), | ||||
| 		windows.FILE_LIST_DIRECTORY, | ||||
| 		windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, | ||||
| 		nil, windows.OPEN_EXISTING, | ||||
| 		windows.FILE_FLAG_BACKUP_SEMANTICS|windows.FILE_FLAG_OVERLAPPED, 0) | ||||
| 	if err != nil { | ||||
| 		return nil, os.NewSyscallError("CreateFile", err) | ||||
| 	} | ||||
|  | ||||
| 	var fi windows.ByHandleFileInformation | ||||
| 	err = windows.GetFileInformationByHandle(h, &fi) | ||||
| 	if err != nil { | ||||
| 		windows.CloseHandle(h) | ||||
| 		return nil, os.NewSyscallError("GetFileInformationByHandle", err) | ||||
| 	} | ||||
| 	ino = &inode{ | ||||
| 		handle: h, | ||||
| 		volume: fi.VolumeSerialNumber, | ||||
| 		index:  uint64(fi.FileIndexHigh)<<32 | uint64(fi.FileIndexLow), | ||||
| 	} | ||||
| 	return ino, nil | ||||
| } | ||||
|  | ||||
| // Must run within the I/O thread. | ||||
| func (m watchMap) get(ino *inode) *watch { | ||||
| 	if i := m[ino.volume]; i != nil { | ||||
| 		return i[ino.index] | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Must run within the I/O thread. | ||||
| func (m watchMap) set(ino *inode, watch *watch) { | ||||
| 	i := m[ino.volume] | ||||
| 	if i == nil { | ||||
| 		i = make(indexMap) | ||||
| 		m[ino.volume] = i | ||||
| 	} | ||||
| 	i[ino.index] = watch | ||||
| } | ||||
|  | ||||
| // Must run within the I/O thread. | ||||
| func (w *Watcher) addWatch(pathname string, flags uint64) error { | ||||
| 	dir, err := w.getDir(pathname) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	ino, err := w.getIno(dir) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	w.mu.Lock() | ||||
| 	watchEntry := w.watches.get(ino) | ||||
| 	w.mu.Unlock() | ||||
| 	if watchEntry == nil { | ||||
| 		_, err := windows.CreateIoCompletionPort(ino.handle, w.port, 0, 0) | ||||
| 		if err != nil { | ||||
| 			windows.CloseHandle(ino.handle) | ||||
| 			return os.NewSyscallError("CreateIoCompletionPort", err) | ||||
| 		} | ||||
| 		watchEntry = &watch{ | ||||
| 			ino:   ino, | ||||
| 			path:  dir, | ||||
| 			names: make(map[string]uint64), | ||||
| 		} | ||||
| 		w.mu.Lock() | ||||
| 		w.watches.set(ino, watchEntry) | ||||
| 		w.mu.Unlock() | ||||
| 		flags |= provisional | ||||
| 	} else { | ||||
| 		windows.CloseHandle(ino.handle) | ||||
| 	} | ||||
| 	if pathname == dir { | ||||
| 		watchEntry.mask |= flags | ||||
| 	} else { | ||||
| 		watchEntry.names[filepath.Base(pathname)] |= flags | ||||
| 	} | ||||
|  | ||||
| 	err = w.startRead(watchEntry) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if pathname == dir { | ||||
| 		watchEntry.mask &= ^provisional | ||||
| 	} else { | ||||
| 		watchEntry.names[filepath.Base(pathname)] &= ^provisional | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Must run within the I/O thread. | ||||
| func (w *Watcher) remWatch(pathname string) error { | ||||
| 	dir, err := w.getDir(pathname) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	ino, err := w.getIno(dir) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	w.mu.Lock() | ||||
| 	watch := w.watches.get(ino) | ||||
| 	w.mu.Unlock() | ||||
|  | ||||
| 	err = windows.CloseHandle(ino.handle) | ||||
| 	if err != nil { | ||||
| 		w.sendError(os.NewSyscallError("CloseHandle", err)) | ||||
| 	} | ||||
| 	if watch == nil { | ||||
| 		return fmt.Errorf("%w: %s", ErrNonExistentWatch, pathname) | ||||
| 	} | ||||
| 	if pathname == dir { | ||||
| 		w.sendEvent(watch.path, watch.mask&sysFSIGNORED) | ||||
| 		watch.mask = 0 | ||||
| 	} else { | ||||
| 		name := filepath.Base(pathname) | ||||
| 		w.sendEvent(filepath.Join(watch.path, name), watch.names[name]&sysFSIGNORED) | ||||
| 		delete(watch.names, name) | ||||
| 	} | ||||
|  | ||||
| 	return w.startRead(watch) | ||||
| } | ||||
|  | ||||
| // Must run within the I/O thread. | ||||
| func (w *Watcher) deleteWatch(watch *watch) { | ||||
| 	for name, mask := range watch.names { | ||||
| 		if mask&provisional == 0 { | ||||
| 			w.sendEvent(filepath.Join(watch.path, name), mask&sysFSIGNORED) | ||||
| 		} | ||||
| 		delete(watch.names, name) | ||||
| 	} | ||||
| 	if watch.mask != 0 { | ||||
| 		if watch.mask&provisional == 0 { | ||||
| 			w.sendEvent(watch.path, watch.mask&sysFSIGNORED) | ||||
| 		} | ||||
| 		watch.mask = 0 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Must run within the I/O thread. | ||||
| func (w *Watcher) startRead(watch *watch) error { | ||||
| 	err := windows.CancelIo(watch.ino.handle) | ||||
| 	if err != nil { | ||||
| 		w.sendError(os.NewSyscallError("CancelIo", err)) | ||||
| 		w.deleteWatch(watch) | ||||
| 	} | ||||
| 	mask := w.toWindowsFlags(watch.mask) | ||||
| 	for _, m := range watch.names { | ||||
| 		mask |= w.toWindowsFlags(m) | ||||
| 	} | ||||
| 	if mask == 0 { | ||||
| 		err := windows.CloseHandle(watch.ino.handle) | ||||
| 		if err != nil { | ||||
| 			w.sendError(os.NewSyscallError("CloseHandle", err)) | ||||
| 		} | ||||
| 		w.mu.Lock() | ||||
| 		delete(w.watches[watch.ino.volume], watch.ino.index) | ||||
| 		w.mu.Unlock() | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	rdErr := windows.ReadDirectoryChanges(watch.ino.handle, &watch.buf[0], | ||||
| 		uint32(unsafe.Sizeof(watch.buf)), false, mask, nil, &watch.ov, 0) | ||||
| 	if rdErr != nil { | ||||
| 		err := os.NewSyscallError("ReadDirectoryChanges", rdErr) | ||||
| 		if rdErr == windows.ERROR_ACCESS_DENIED && watch.mask&provisional == 0 { | ||||
| 			// Watched directory was probably removed | ||||
| 			w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) | ||||
| 			err = nil | ||||
| 		} | ||||
| 		w.deleteWatch(watch) | ||||
| 		w.startRead(watch) | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // readEvents reads from the I/O completion port, converts the | ||||
| // received events into Event objects and sends them via the Events channel. | ||||
| // Entry point to the I/O thread. | ||||
| func (w *Watcher) readEvents() { | ||||
| 	var ( | ||||
| 		n   uint32 | ||||
| 		key uintptr | ||||
| 		ov  *windows.Overlapped | ||||
| 	) | ||||
| 	runtime.LockOSThread() | ||||
|  | ||||
| 	for { | ||||
| 		qErr := windows.GetQueuedCompletionStatus(w.port, &n, &key, &ov, windows.INFINITE) | ||||
| 		// This error is handled after the watch == nil check below. NOTE: this | ||||
| 		// seems odd, note sure if it's correct. | ||||
|  | ||||
| 		watch := (*watch)(unsafe.Pointer(ov)) | ||||
| 		if watch == nil { | ||||
| 			select { | ||||
| 			case ch := <-w.quit: | ||||
| 				w.mu.Lock() | ||||
| 				var indexes []indexMap | ||||
| 				for _, index := range w.watches { | ||||
| 					indexes = append(indexes, index) | ||||
| 				} | ||||
| 				w.mu.Unlock() | ||||
| 				for _, index := range indexes { | ||||
| 					for _, watch := range index { | ||||
| 						w.deleteWatch(watch) | ||||
| 						w.startRead(watch) | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				err := windows.CloseHandle(w.port) | ||||
| 				if err != nil { | ||||
| 					err = os.NewSyscallError("CloseHandle", err) | ||||
| 				} | ||||
| 				close(w.Events) | ||||
| 				close(w.Errors) | ||||
| 				ch <- err | ||||
| 				return | ||||
| 			case in := <-w.input: | ||||
| 				switch in.op { | ||||
| 				case opAddWatch: | ||||
| 					in.reply <- w.addWatch(in.path, uint64(in.flags)) | ||||
| 				case opRemoveWatch: | ||||
| 					in.reply <- w.remWatch(in.path) | ||||
| 				} | ||||
| 			default: | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		switch qErr { | ||||
| 		case windows.ERROR_MORE_DATA: | ||||
| 			if watch == nil { | ||||
| 				w.sendError(errors.New("ERROR_MORE_DATA has unexpectedly null lpOverlapped buffer")) | ||||
| 			} else { | ||||
| 				// The i/o succeeded but the buffer is full. | ||||
| 				// In theory we should be building up a full packet. | ||||
| 				// In practice we can get away with just carrying on. | ||||
| 				n = uint32(unsafe.Sizeof(watch.buf)) | ||||
| 			} | ||||
| 		case windows.ERROR_ACCESS_DENIED: | ||||
| 			// Watched directory was probably removed | ||||
| 			w.sendEvent(watch.path, watch.mask&sysFSDELETESELF) | ||||
| 			w.deleteWatch(watch) | ||||
| 			w.startRead(watch) | ||||
| 			continue | ||||
| 		case windows.ERROR_OPERATION_ABORTED: | ||||
| 			// CancelIo was called on this handle | ||||
| 			continue | ||||
| 		default: | ||||
| 			w.sendError(os.NewSyscallError("GetQueuedCompletionPort", qErr)) | ||||
| 			continue | ||||
| 		case nil: | ||||
| 		} | ||||
|  | ||||
| 		var offset uint32 | ||||
| 		for { | ||||
| 			if n == 0 { | ||||
| 				w.sendError(errors.New("short read in readEvents()")) | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			// Point "raw" to the event in the buffer | ||||
| 			raw := (*windows.FileNotifyInformation)(unsafe.Pointer(&watch.buf[offset])) | ||||
|  | ||||
| 			// Create a buf that is the size of the path name | ||||
| 			size := int(raw.FileNameLength / 2) | ||||
| 			var buf []uint16 | ||||
| 			// TODO: Use unsafe.Slice in Go 1.17; https://stackoverflow.com/questions/51187973 | ||||
| 			sh := (*reflect.SliceHeader)(unsafe.Pointer(&buf)) | ||||
| 			sh.Data = uintptr(unsafe.Pointer(&raw.FileName)) | ||||
| 			sh.Len = size | ||||
| 			sh.Cap = size | ||||
| 			name := windows.UTF16ToString(buf) | ||||
| 			fullname := filepath.Join(watch.path, name) | ||||
|  | ||||
| 			var mask uint64 | ||||
| 			switch raw.Action { | ||||
| 			case windows.FILE_ACTION_REMOVED: | ||||
| 				mask = sysFSDELETESELF | ||||
| 			case windows.FILE_ACTION_MODIFIED: | ||||
| 				mask = sysFSMODIFY | ||||
| 			case windows.FILE_ACTION_RENAMED_OLD_NAME: | ||||
| 				watch.rename = name | ||||
| 			case windows.FILE_ACTION_RENAMED_NEW_NAME: | ||||
| 				// Update saved path of all sub-watches. | ||||
| 				old := filepath.Join(watch.path, watch.rename) | ||||
| 				w.mu.Lock() | ||||
| 				for _, watchMap := range w.watches { | ||||
| 					for _, ww := range watchMap { | ||||
| 						if strings.HasPrefix(ww.path, old) { | ||||
| 							ww.path = filepath.Join(fullname, strings.TrimPrefix(ww.path, old)) | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 				w.mu.Unlock() | ||||
|  | ||||
| 				if watch.names[watch.rename] != 0 { | ||||
| 					watch.names[name] |= watch.names[watch.rename] | ||||
| 					delete(watch.names, watch.rename) | ||||
| 					mask = sysFSMOVESELF | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			sendNameEvent := func() { | ||||
| 				w.sendEvent(fullname, watch.names[name]&mask) | ||||
| 			} | ||||
| 			if raw.Action != windows.FILE_ACTION_RENAMED_NEW_NAME { | ||||
| 				sendNameEvent() | ||||
| 			} | ||||
| 			if raw.Action == windows.FILE_ACTION_REMOVED { | ||||
| 				w.sendEvent(fullname, watch.names[name]&sysFSIGNORED) | ||||
| 				delete(watch.names, name) | ||||
| 			} | ||||
|  | ||||
| 			w.sendEvent(fullname, watch.mask&w.toFSnotifyFlags(raw.Action)) | ||||
| 			if raw.Action == windows.FILE_ACTION_RENAMED_NEW_NAME { | ||||
| 				fullname = filepath.Join(watch.path, watch.rename) | ||||
| 				sendNameEvent() | ||||
| 			} | ||||
|  | ||||
| 			// Move to the next event in the buffer | ||||
| 			if raw.NextEntryOffset == 0 { | ||||
| 				break | ||||
| 			} | ||||
| 			offset += raw.NextEntryOffset | ||||
|  | ||||
| 			// Error! | ||||
| 			if offset >= n { | ||||
| 				w.sendError(errors.New( | ||||
| 					"Windows system assumed buffer larger than it is, events have likely been missed.")) | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if err := w.startRead(watch); err != nil { | ||||
| 			w.sendError(err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (w *Watcher) toWindowsFlags(mask uint64) uint32 { | ||||
| 	var m uint32 | ||||
| 	if mask&sysFSMODIFY != 0 { | ||||
| 		m |= windows.FILE_NOTIFY_CHANGE_LAST_WRITE | ||||
| 	} | ||||
| 	if mask&sysFSATTRIB != 0 { | ||||
| 		m |= windows.FILE_NOTIFY_CHANGE_ATTRIBUTES | ||||
| 	} | ||||
| 	if mask&(sysFSMOVE|sysFSCREATE|sysFSDELETE) != 0 { | ||||
| 		m |= windows.FILE_NOTIFY_CHANGE_FILE_NAME | windows.FILE_NOTIFY_CHANGE_DIR_NAME | ||||
| 	} | ||||
| 	return m | ||||
| } | ||||
|  | ||||
| func (w *Watcher) toFSnotifyFlags(action uint32) uint64 { | ||||
| 	switch action { | ||||
| 	case windows.FILE_ACTION_ADDED: | ||||
| 		return sysFSCREATE | ||||
| 	case windows.FILE_ACTION_REMOVED: | ||||
| 		return sysFSDELETE | ||||
| 	case windows.FILE_ACTION_MODIFIED: | ||||
| 		return sysFSMODIFY | ||||
| 	case windows.FILE_ACTION_RENAMED_OLD_NAME: | ||||
| 		return sysFSMOVEDFROM | ||||
| 	case windows.FILE_ACTION_RENAMED_NEW_NAME: | ||||
| 		return sysFSMOVEDTO | ||||
| 	} | ||||
| 	return 0 | ||||
| } | ||||
							
								
								
									
										81
									
								
								vendor/github.com/fsnotify/fsnotify/fsnotify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								vendor/github.com/fsnotify/fsnotify/fsnotify.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| //go:build !plan9 | ||||
| // +build !plan9 | ||||
|  | ||||
| // Package fsnotify provides a cross-platform interface for file system | ||||
| // notifications. | ||||
| package fsnotify | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // Event represents a file system notification. | ||||
| type Event struct { | ||||
| 	// Path to the file or directory. | ||||
| 	// | ||||
| 	// Paths are relative to the input; for example with Add("dir") the Name | ||||
| 	// will be set to "dir/file" if you create that file, but if you use | ||||
| 	// Add("/path/to/dir") it will be "/path/to/dir/file". | ||||
| 	Name string | ||||
|  | ||||
| 	// File operation that triggered the event. | ||||
| 	// | ||||
| 	// This is a bitmask and some systems may send multiple operations at once. | ||||
| 	// Use the Event.Has() method instead of comparing with ==. | ||||
| 	Op Op | ||||
| } | ||||
|  | ||||
| // Op describes a set of file operations. | ||||
| type Op uint32 | ||||
|  | ||||
| // The operations fsnotify can trigger; see the documentation on [Watcher] for a | ||||
| // full description, and check them with [Event.Has]. | ||||
| const ( | ||||
| 	Create Op = 1 << iota | ||||
| 	Write | ||||
| 	Remove | ||||
| 	Rename | ||||
| 	Chmod | ||||
| ) | ||||
|  | ||||
| // Common errors that can be reported by a watcher | ||||
| var ( | ||||
| 	ErrNonExistentWatch = errors.New("can't remove non-existent watcher") | ||||
| 	ErrEventOverflow    = errors.New("fsnotify queue overflow") | ||||
| ) | ||||
|  | ||||
| func (op Op) String() string { | ||||
| 	var b strings.Builder | ||||
| 	if op.Has(Create) { | ||||
| 		b.WriteString("|CREATE") | ||||
| 	} | ||||
| 	if op.Has(Remove) { | ||||
| 		b.WriteString("|REMOVE") | ||||
| 	} | ||||
| 	if op.Has(Write) { | ||||
| 		b.WriteString("|WRITE") | ||||
| 	} | ||||
| 	if op.Has(Rename) { | ||||
| 		b.WriteString("|RENAME") | ||||
| 	} | ||||
| 	if op.Has(Chmod) { | ||||
| 		b.WriteString("|CHMOD") | ||||
| 	} | ||||
| 	if b.Len() == 0 { | ||||
| 		return "[no events]" | ||||
| 	} | ||||
| 	return b.String()[1:] | ||||
| } | ||||
|  | ||||
| // Has reports if this operation has the given operation. | ||||
| func (o Op) Has(h Op) bool { return o&h == h } | ||||
|  | ||||
| // Has reports if this event has the given operation. | ||||
| func (e Event) Has(op Op) bool { return e.Op.Has(op) } | ||||
|  | ||||
| // String returns a string representation of the event with their path. | ||||
| func (e Event) String() string { | ||||
| 	return fmt.Sprintf("%-13s %q", e.Op.String(), e.Name) | ||||
| } | ||||
							
								
								
									
										208
									
								
								vendor/github.com/fsnotify/fsnotify/mkdoc.zsh
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								vendor/github.com/fsnotify/fsnotify/mkdoc.zsh
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,208 @@ | ||||
| #!/usr/bin/env zsh | ||||
| [ "${ZSH_VERSION:-}" = "" ] && echo >&2 "Only works with zsh" && exit 1 | ||||
| setopt err_exit no_unset pipefail extended_glob | ||||
|  | ||||
| # Simple script to update the godoc comments on all watchers. Probably took me | ||||
| # more time to write this than doing it manually, but ah well 🙃 | ||||
|  | ||||
| watcher=$(<<EOF | ||||
| // Watcher watches a set of paths, delivering events on a channel. | ||||
| // | ||||
| // A watcher should not be copied (e.g. pass it by pointer, rather than by | ||||
| // value). | ||||
| // | ||||
| // # Linux notes | ||||
| // | ||||
| // When a file is removed a Remove event won't be emitted until all file | ||||
| // descriptors are closed, and deletes will always emit a Chmod. For example: | ||||
| // | ||||
| //     fp := os.Open("file") | ||||
| //     os.Remove("file")        // Triggers Chmod | ||||
| //     fp.Close()               // Triggers Remove | ||||
| // | ||||
| // This is the event that inotify sends, so not much can be changed about this. | ||||
| // | ||||
| // The fs.inotify.max_user_watches sysctl variable specifies the upper limit | ||||
| // for the number of watches per user, and fs.inotify.max_user_instances | ||||
| // specifies the maximum number of inotify instances per user. Every Watcher you | ||||
| // create is an "instance", and every path you add is a "watch". | ||||
| // | ||||
| // These are also exposed in /proc as /proc/sys/fs/inotify/max_user_watches and | ||||
| // /proc/sys/fs/inotify/max_user_instances | ||||
| // | ||||
| // To increase them you can use sysctl or write the value to the /proc file: | ||||
| // | ||||
| //     # Default values on Linux 5.18 | ||||
| //     sysctl fs.inotify.max_user_watches=124983 | ||||
| //     sysctl fs.inotify.max_user_instances=128 | ||||
| // | ||||
| // To make the changes persist on reboot edit /etc/sysctl.conf or | ||||
| // /usr/lib/sysctl.d/50-default.conf (details differ per Linux distro; check | ||||
| // your distro's documentation): | ||||
| // | ||||
| //     fs.inotify.max_user_watches=124983 | ||||
| //     fs.inotify.max_user_instances=128 | ||||
| // | ||||
| // Reaching the limit will result in a "no space left on device" or "too many open | ||||
| // files" error. | ||||
| // | ||||
| // # kqueue notes (macOS, BSD) | ||||
| // | ||||
| // kqueue requires opening a file descriptor for every file that's being watched; | ||||
| // so if you're watching a directory with five files then that's six file | ||||
| // descriptors. You will run in to your system's "max open files" limit faster on | ||||
| // these platforms. | ||||
| // | ||||
| // The sysctl variables kern.maxfiles and kern.maxfilesperproc can be used to | ||||
| // control the maximum number of open files, as well as /etc/login.conf on BSD | ||||
| // systems. | ||||
| // | ||||
| // # macOS notes | ||||
| // | ||||
| // Spotlight indexing on macOS can result in multiple events (see [#15]). A | ||||
| // temporary workaround is to add your folder(s) to the "Spotlight Privacy | ||||
| // Settings" until we have a native FSEvents implementation (see [#11]). | ||||
| // | ||||
| // [#11]: https://github.com/fsnotify/fsnotify/issues/11 | ||||
| // [#15]: https://github.com/fsnotify/fsnotify/issues/15 | ||||
| EOF | ||||
| ) | ||||
|  | ||||
| new=$(<<EOF | ||||
| // NewWatcher creates a new Watcher. | ||||
| EOF | ||||
| ) | ||||
|  | ||||
| add=$(<<EOF | ||||
| // Add starts monitoring the path for changes. | ||||
| // | ||||
| // A path can only be watched once; attempting to watch it more than once will | ||||
| // return an error. Paths that do not yet exist on the filesystem cannot be | ||||
| // added. A watch will be automatically removed if the path is deleted. | ||||
| // | ||||
| // A path will remain watched if it gets renamed to somewhere else on the same | ||||
| // filesystem, but the monitor will get removed if the path gets deleted and | ||||
| // re-created, or if it's moved to a different filesystem. | ||||
| // | ||||
| // Notifications on network filesystems (NFS, SMB, FUSE, etc.) or special | ||||
| // filesystems (/proc, /sys, etc.) generally don't work. | ||||
| // | ||||
| // # Watching directories | ||||
| // | ||||
| // All files in a directory are monitored, including new files that are created | ||||
| // after the watcher is started. Subdirectories are not watched (i.e. it's | ||||
| // non-recursive). | ||||
| // | ||||
| // # Watching files | ||||
| // | ||||
| // Watching individual files (rather than directories) is generally not | ||||
| // recommended as many tools update files atomically. Instead of "just" writing | ||||
| // to the file a temporary file will be written to first, and if successful the | ||||
| // temporary file is moved to to destination removing the original, or some | ||||
| // variant thereof. The watcher on the original file is now lost, as it no | ||||
| // longer exists. | ||||
| // | ||||
| // Instead, watch the parent directory and use Event.Name to filter out files | ||||
| // you're not interested in. There is an example of this in [cmd/fsnotify/file.go]. | ||||
| EOF | ||||
| ) | ||||
|  | ||||
| remove=$(<<EOF | ||||
| // Remove stops monitoring the path for changes. | ||||
| // | ||||
| // Directories are always removed non-recursively. For example, if you added | ||||
| // /tmp/dir and /tmp/dir/subdir then you will need to remove both. | ||||
| // | ||||
| // Removing a path that has not yet been added returns [ErrNonExistentWatch]. | ||||
| EOF | ||||
| ) | ||||
|  | ||||
| close=$(<<EOF | ||||
| // Close removes all watches and closes the events channel. | ||||
| EOF | ||||
| ) | ||||
|  | ||||
| watchlist=$(<<EOF | ||||
| // WatchList returns all paths added with [Add] (and are not yet removed). | ||||
| EOF | ||||
| ) | ||||
|  | ||||
| events=$(<<EOF | ||||
| 	// Events sends the filesystem change events. | ||||
| 	// | ||||
| 	// fsnotify can send the following events; a "path" here can refer to a | ||||
| 	// file, directory, symbolic link, or special file like a FIFO. | ||||
| 	// | ||||
| 	//   fsnotify.Create    A new path was created; this may be followed by one | ||||
| 	//                      or more Write events if data also gets written to a | ||||
| 	//                      file. | ||||
| 	// | ||||
| 	//   fsnotify.Remove    A path was removed. | ||||
| 	// | ||||
| 	//   fsnotify.Rename    A path was renamed. A rename is always sent with the | ||||
| 	//                      old path as Event.Name, and a Create event will be | ||||
| 	//                      sent with the new name. Renames are only sent for | ||||
| 	//                      paths that are currently watched; e.g. moving an | ||||
| 	//                      unmonitored file into a monitored directory will | ||||
| 	//                      show up as just a Create. Similarly, renaming a file | ||||
| 	//                      to outside a monitored directory will show up as | ||||
| 	//                      only a Rename. | ||||
| 	// | ||||
| 	//   fsnotify.Write     A file or named pipe was written to. A Truncate will | ||||
| 	//                      also trigger a Write. A single "write action" | ||||
| 	//                      initiated by the user may show up as one or multiple | ||||
| 	//                      writes, depending on when the system syncs things to | ||||
| 	//                      disk. For example when compiling a large Go program | ||||
| 	//                      you may get hundreds of Write events, so you | ||||
| 	//                      probably want to wait until you've stopped receiving | ||||
| 	//                      them (see the dedup example in cmd/fsnotify). | ||||
| 	// | ||||
| 	//   fsnotify.Chmod     Attributes were changed. On Linux this is also sent | ||||
| 	//                      when a file is removed (or more accurately, when a | ||||
| 	//                      link to an inode is removed). On kqueue it's sent | ||||
| 	//                      and on kqueue when a file is truncated. On Windows | ||||
| 	//                      it's never sent. | ||||
| EOF | ||||
| ) | ||||
|  | ||||
| errors=$(<<EOF | ||||
| 	// Errors sends any errors. | ||||
| EOF | ||||
| ) | ||||
|  | ||||
| set-cmt() { | ||||
| 	local pat=$1 | ||||
| 	local cmt=$2 | ||||
|  | ||||
| 	IFS=$'\n' local files=($(grep -n $pat backend_*~*_test.go)) | ||||
| 	for f in $files; do | ||||
| 		IFS=':' local fields=($=f) | ||||
| 		local file=$fields[1] | ||||
| 		local end=$(( $fields[2] - 1 )) | ||||
|  | ||||
| 		# Find start of comment. | ||||
| 		local start=0 | ||||
| 		IFS=$'\n' local lines=($(head -n$end $file)) | ||||
| 		for (( i = 1; i <= $#lines; i++ )); do | ||||
| 			local line=$lines[-$i] | ||||
| 			if ! grep -q '^[[:space:]]*//' <<<$line; then | ||||
| 				start=$(( end - (i - 2) )) | ||||
| 				break | ||||
| 			fi | ||||
| 		done | ||||
|  | ||||
| 		head -n $(( start - 1 )) $file  >/tmp/x | ||||
| 		print -r -- $cmt                >>/tmp/x | ||||
| 		tail -n+$(( end + 1 ))   $file  >>/tmp/x | ||||
| 		mv /tmp/x $file | ||||
| 	done | ||||
| } | ||||
|  | ||||
| set-cmt '^type Watcher struct '             $watcher | ||||
| set-cmt '^func NewWatcher('                 $new | ||||
| set-cmt '^func (w \*Watcher) Add('          $add | ||||
| set-cmt '^func (w \*Watcher) Remove('       $remove | ||||
| set-cmt '^func (w \*Watcher) Close('        $close | ||||
| set-cmt '^func (w \*Watcher) WatchList('    $watchlist | ||||
| set-cmt '^[[:space:]]*Events *chan Event$'  $events | ||||
| set-cmt '^[[:space:]]*Errors *chan error$'  $errors | ||||
							
								
								
									
										8
									
								
								vendor/github.com/fsnotify/fsnotify/system_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								vendor/github.com/fsnotify/fsnotify/system_bsd.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| //go:build freebsd || openbsd || netbsd || dragonfly | ||||
| // +build freebsd openbsd netbsd dragonfly | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import "golang.org/x/sys/unix" | ||||
|  | ||||
| const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC | ||||
							
								
								
									
										9
									
								
								vendor/github.com/fsnotify/fsnotify/system_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/fsnotify/fsnotify/system_darwin.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| //go:build darwin | ||||
| // +build darwin | ||||
|  | ||||
| package fsnotify | ||||
|  | ||||
| import "golang.org/x/sys/unix" | ||||
|  | ||||
| // note: this constant is not defined on BSD | ||||
| const openMode = unix.O_EVTONLY | unix.O_CLOEXEC | ||||
							
								
								
									
										9
									
								
								vendor/github.com/hashicorp/hcl/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								vendor/github.com/hashicorp/hcl/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| y.output | ||||
|  | ||||
| # ignore intellij files | ||||
| .idea | ||||
| *.iml | ||||
| *.ipr | ||||
| *.iws | ||||
|  | ||||
| *.test | ||||
							
								
								
									
										13
									
								
								vendor/github.com/hashicorp/hcl/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/hashicorp/hcl/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| sudo: false | ||||
|  | ||||
| language: go | ||||
|  | ||||
| go: | ||||
|   - 1.x | ||||
|   - tip | ||||
|  | ||||
| branches: | ||||
|   only: | ||||
|     - master | ||||
|  | ||||
| script: make test | ||||
							
								
								
									
										354
									
								
								vendor/github.com/hashicorp/hcl/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										354
									
								
								vendor/github.com/hashicorp/hcl/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,354 @@ | ||||
| Mozilla Public License, version 2.0 | ||||
|  | ||||
| 1. Definitions | ||||
|  | ||||
| 1.1. “Contributor” | ||||
|  | ||||
|      means each individual or legal entity that creates, contributes to the | ||||
|      creation of, or owns Covered Software. | ||||
|  | ||||
| 1.2. “Contributor Version” | ||||
|  | ||||
|      means the combination of the Contributions of others (if any) used by a | ||||
|      Contributor and that particular Contributor’s Contribution. | ||||
|  | ||||
| 1.3. “Contribution” | ||||
|  | ||||
|      means Covered Software of a particular Contributor. | ||||
|  | ||||
| 1.4. “Covered Software” | ||||
|  | ||||
|      means Source Code Form to which the initial Contributor has attached the | ||||
|      notice in Exhibit A, the Executable Form of such Source Code Form, and | ||||
|      Modifications of such Source Code Form, in each case including portions | ||||
|      thereof. | ||||
|  | ||||
| 1.5. “Incompatible With Secondary Licenses” | ||||
|      means | ||||
|  | ||||
|      a. that the initial Contributor has attached the notice described in | ||||
|         Exhibit B to the Covered Software; or | ||||
|  | ||||
|      b. that the Covered Software was made available under the terms of version | ||||
|         1.1 or earlier of the License, but not also under the terms of a | ||||
|         Secondary License. | ||||
|  | ||||
| 1.6. “Executable Form” | ||||
|  | ||||
|      means any form of the work other than Source Code Form. | ||||
|  | ||||
| 1.7. “Larger Work” | ||||
|  | ||||
|      means a work that combines Covered Software with other material, in a separate | ||||
|      file or files, that is not Covered Software. | ||||
|  | ||||
| 1.8. “License” | ||||
|  | ||||
|      means this document. | ||||
|  | ||||
| 1.9. “Licensable” | ||||
|  | ||||
|      means having the right to grant, to the maximum extent possible, whether at the | ||||
|      time of the initial grant or subsequently, any and all of the rights conveyed by | ||||
|      this License. | ||||
|  | ||||
| 1.10. “Modifications” | ||||
|  | ||||
|      means any of the following: | ||||
|  | ||||
|      a. any file in Source Code Form that results from an addition to, deletion | ||||
|         from, or modification of the contents of Covered Software; or | ||||
|  | ||||
|      b. any new file in Source Code Form that contains any Covered Software. | ||||
|  | ||||
| 1.11. “Patent Claims” of a Contributor | ||||
|  | ||||
|       means any patent claim(s), including without limitation, method, process, | ||||
|       and apparatus claims, in any patent Licensable by such Contributor that | ||||
|       would be infringed, but for the grant of the License, by the making, | ||||
|       using, selling, offering for sale, having made, import, or transfer of | ||||
|       either its Contributions or its Contributor Version. | ||||
|  | ||||
| 1.12. “Secondary License” | ||||
|  | ||||
|       means either the GNU General Public License, Version 2.0, the GNU Lesser | ||||
|       General Public License, Version 2.1, the GNU Affero General Public | ||||
|       License, Version 3.0, or any later versions of those licenses. | ||||
|  | ||||
| 1.13. “Source Code Form” | ||||
|  | ||||
|       means the form of the work preferred for making modifications. | ||||
|  | ||||
| 1.14. “You” (or “Your”) | ||||
|  | ||||
|       means an individual or a legal entity exercising rights under this | ||||
|       License. For legal entities, “You” includes any entity that controls, is | ||||
|       controlled by, or is under common control with You. For purposes of this | ||||
|       definition, “control” means (a) the power, direct or indirect, to cause | ||||
|       the direction or management of such entity, whether by contract or | ||||
|       otherwise, or (b) ownership of more than fifty percent (50%) of the | ||||
|       outstanding shares or beneficial ownership of such entity. | ||||
|  | ||||
|  | ||||
| 2. License Grants and Conditions | ||||
|  | ||||
| 2.1. Grants | ||||
|  | ||||
|      Each Contributor hereby grants You a world-wide, royalty-free, | ||||
|      non-exclusive license: | ||||
|  | ||||
|      a. under intellectual property rights (other than patent or trademark) | ||||
|         Licensable by such Contributor to use, reproduce, make available, | ||||
|         modify, display, perform, distribute, and otherwise exploit its | ||||
|         Contributions, either on an unmodified basis, with Modifications, or as | ||||
|         part of a Larger Work; and | ||||
|  | ||||
|      b. under Patent Claims of such Contributor to make, use, sell, offer for | ||||
|         sale, have made, import, and otherwise transfer either its Contributions | ||||
|         or its Contributor Version. | ||||
|  | ||||
| 2.2. Effective Date | ||||
|  | ||||
|      The licenses granted in Section 2.1 with respect to any Contribution become | ||||
|      effective for each Contribution on the date the Contributor first distributes | ||||
|      such Contribution. | ||||
|  | ||||
| 2.3. Limitations on Grant Scope | ||||
|  | ||||
|      The licenses granted in this Section 2 are the only rights granted under this | ||||
|      License. No additional rights or licenses will be implied from the distribution | ||||
|      or licensing of Covered Software under this License. Notwithstanding Section | ||||
|      2.1(b) above, no patent license is granted by a Contributor: | ||||
|  | ||||
|      a. for any code that a Contributor has removed from Covered Software; or | ||||
|  | ||||
|      b. for infringements caused by: (i) Your and any other third party’s | ||||
|         modifications of Covered Software, or (ii) the combination of its | ||||
|         Contributions with other software (except as part of its Contributor | ||||
|         Version); or | ||||
|  | ||||
|      c. under Patent Claims infringed by Covered Software in the absence of its | ||||
|         Contributions. | ||||
|  | ||||
|      This License does not grant any rights in the trademarks, service marks, or | ||||
|      logos of any Contributor (except as may be necessary to comply with the | ||||
|      notice requirements in Section 3.4). | ||||
|  | ||||
| 2.4. Subsequent Licenses | ||||
|  | ||||
|      No Contributor makes additional grants as a result of Your choice to | ||||
|      distribute the Covered Software under a subsequent version of this License | ||||
|      (see Section 10.2) or under the terms of a Secondary License (if permitted | ||||
|      under the terms of Section 3.3). | ||||
|  | ||||
| 2.5. Representation | ||||
|  | ||||
|      Each Contributor represents that the Contributor believes its Contributions | ||||
|      are its original creation(s) or it has sufficient rights to grant the | ||||
|      rights to its Contributions conveyed by this License. | ||||
|  | ||||
| 2.6. Fair Use | ||||
|  | ||||
|      This License is not intended to limit any rights You have under applicable | ||||
|      copyright doctrines of fair use, fair dealing, or other equivalents. | ||||
|  | ||||
| 2.7. Conditions | ||||
|  | ||||
|      Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in | ||||
|      Section 2.1. | ||||
|  | ||||
|  | ||||
| 3. Responsibilities | ||||
|  | ||||
| 3.1. Distribution of Source Form | ||||
|  | ||||
|      All distribution of Covered Software in Source Code Form, including any | ||||
|      Modifications that You create or to which You contribute, must be under the | ||||
|      terms of this License. You must inform recipients that the Source Code Form | ||||
|      of the Covered Software is governed by the terms of this License, and how | ||||
|      they can obtain a copy of this License. You may not attempt to alter or | ||||
|      restrict the recipients’ rights in the Source Code Form. | ||||
|  | ||||
| 3.2. Distribution of Executable Form | ||||
|  | ||||
|      If You distribute Covered Software in Executable Form then: | ||||
|  | ||||
|      a. such Covered Software must also be made available in Source Code Form, | ||||
|         as described in Section 3.1, and You must inform recipients of the | ||||
|         Executable Form how they can obtain a copy of such Source Code Form by | ||||
|         reasonable means in a timely manner, at a charge no more than the cost | ||||
|         of distribution to the recipient; and | ||||
|  | ||||
|      b. You may distribute such Executable Form under the terms of this License, | ||||
|         or sublicense it under different terms, provided that the license for | ||||
|         the Executable Form does not attempt to limit or alter the recipients’ | ||||
|         rights in the Source Code Form under this License. | ||||
|  | ||||
| 3.3. Distribution of a Larger Work | ||||
|  | ||||
|      You may create and distribute a Larger Work under terms of Your choice, | ||||
|      provided that You also comply with the requirements of this License for the | ||||
|      Covered Software. If the Larger Work is a combination of Covered Software | ||||
|      with a work governed by one or more Secondary Licenses, and the Covered | ||||
|      Software is not Incompatible With Secondary Licenses, this License permits | ||||
|      You to additionally distribute such Covered Software under the terms of | ||||
|      such Secondary License(s), so that the recipient of the Larger Work may, at | ||||
|      their option, further distribute the Covered Software under the terms of | ||||
|      either this License or such Secondary License(s). | ||||
|  | ||||
| 3.4. Notices | ||||
|  | ||||
|      You may not remove or alter the substance of any license notices (including | ||||
|      copyright notices, patent notices, disclaimers of warranty, or limitations | ||||
|      of liability) contained within the Source Code Form of the Covered | ||||
|      Software, except that You may alter any license notices to the extent | ||||
|      required to remedy known factual inaccuracies. | ||||
|  | ||||
| 3.5. Application of Additional Terms | ||||
|  | ||||
|      You may choose to offer, and to charge a fee for, warranty, support, | ||||
|      indemnity or liability obligations to one or more recipients of Covered | ||||
|      Software. However, You may do so only on Your own behalf, and not on behalf | ||||
|      of any Contributor. You must make it absolutely clear that any such | ||||
|      warranty, support, indemnity, or liability obligation is offered by You | ||||
|      alone, and You hereby agree to indemnify every Contributor for any | ||||
|      liability incurred by such Contributor as a result of warranty, support, | ||||
|      indemnity or liability terms You offer. You may include additional | ||||
|      disclaimers of warranty and limitations of liability specific to any | ||||
|      jurisdiction. | ||||
|  | ||||
| 4. Inability to Comply Due to Statute or Regulation | ||||
|  | ||||
|    If it is impossible for You to comply with any of the terms of this License | ||||
|    with respect to some or all of the Covered Software due to statute, judicial | ||||
|    order, or regulation then You must: (a) comply with the terms of this License | ||||
|    to the maximum extent possible; and (b) describe the limitations and the code | ||||
|    they affect. Such description must be placed in a text file included with all | ||||
|    distributions of the Covered Software under this License. Except to the | ||||
|    extent prohibited by statute or regulation, such description must be | ||||
|    sufficiently detailed for a recipient of ordinary skill to be able to | ||||
|    understand it. | ||||
|  | ||||
| 5. Termination | ||||
|  | ||||
| 5.1. The rights granted under this License will terminate automatically if You | ||||
|      fail to comply with any of its terms. However, if You become compliant, | ||||
|      then the rights granted under this License from a particular Contributor | ||||
|      are reinstated (a) provisionally, unless and until such Contributor | ||||
|      explicitly and finally terminates Your grants, and (b) on an ongoing basis, | ||||
|      if such Contributor fails to notify You of the non-compliance by some | ||||
|      reasonable means prior to 60 days after You have come back into compliance. | ||||
|      Moreover, Your grants from a particular Contributor are reinstated on an | ||||
|      ongoing basis if such Contributor notifies You of the non-compliance by | ||||
|      some reasonable means, this is the first time You have received notice of | ||||
|      non-compliance with this License from such Contributor, and You become | ||||
|      compliant prior to 30 days after Your receipt of the notice. | ||||
|  | ||||
| 5.2. If You initiate litigation against any entity by asserting a patent | ||||
|      infringement claim (excluding declaratory judgment actions, counter-claims, | ||||
|      and cross-claims) alleging that a Contributor Version directly or | ||||
|      indirectly infringes any patent, then the rights granted to You by any and | ||||
|      all Contributors for the Covered Software under Section 2.1 of this License | ||||
|      shall terminate. | ||||
|  | ||||
| 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user | ||||
|      license agreements (excluding distributors and resellers) which have been | ||||
|      validly granted by You or Your distributors under this License prior to | ||||
|      termination shall survive termination. | ||||
|  | ||||
| 6. Disclaimer of Warranty | ||||
|  | ||||
|    Covered Software is provided under this License on an “as is” basis, without | ||||
|    warranty of any kind, either expressed, implied, or statutory, including, | ||||
|    without limitation, warranties that the Covered Software is free of defects, | ||||
|    merchantable, fit for a particular purpose or non-infringing. The entire | ||||
|    risk as to the quality and performance of the Covered Software is with You. | ||||
|    Should any Covered Software prove defective in any respect, You (not any | ||||
|    Contributor) assume the cost of any necessary servicing, repair, or | ||||
|    correction. This disclaimer of warranty constitutes an essential part of this | ||||
|    License. No use of  any Covered Software is authorized under this License | ||||
|    except under this disclaimer. | ||||
|  | ||||
| 7. Limitation of Liability | ||||
|  | ||||
|    Under no circumstances and under no legal theory, whether tort (including | ||||
|    negligence), contract, or otherwise, shall any Contributor, or anyone who | ||||
|    distributes Covered Software as permitted above, be liable to You for any | ||||
|    direct, indirect, special, incidental, or consequential damages of any | ||||
|    character including, without limitation, damages for lost profits, loss of | ||||
|    goodwill, work stoppage, computer failure or malfunction, or any and all | ||||
|    other commercial damages or losses, even if such party shall have been | ||||
|    informed of the possibility of such damages. This limitation of liability | ||||
|    shall not apply to liability for death or personal injury resulting from such | ||||
|    party’s negligence to the extent applicable law prohibits such limitation. | ||||
|    Some jurisdictions do not allow the exclusion or limitation of incidental or | ||||
|    consequential damages, so this exclusion and limitation may not apply to You. | ||||
|  | ||||
| 8. Litigation | ||||
|  | ||||
|    Any litigation relating to this License may be brought only in the courts of | ||||
|    a jurisdiction where the defendant maintains its principal place of business | ||||
|    and such litigation shall be governed by laws of that jurisdiction, without | ||||
|    reference to its conflict-of-law provisions. Nothing in this Section shall | ||||
|    prevent a party’s ability to bring cross-claims or counter-claims. | ||||
|  | ||||
| 9. Miscellaneous | ||||
|  | ||||
|    This License represents the complete agreement concerning the subject matter | ||||
|    hereof. If any provision of this License is held to be unenforceable, such | ||||
|    provision shall be reformed only to the extent necessary to make it | ||||
|    enforceable. Any law or regulation which provides that the language of a | ||||
|    contract shall be construed against the drafter shall not be used to construe | ||||
|    this License against a Contributor. | ||||
|  | ||||
|  | ||||
| 10. Versions of the License | ||||
|  | ||||
| 10.1. New Versions | ||||
|  | ||||
|       Mozilla Foundation is the license steward. Except as provided in Section | ||||
|       10.3, no one other than the license steward has the right to modify or | ||||
|       publish new versions of this License. Each version will be given a | ||||
|       distinguishing version number. | ||||
|  | ||||
| 10.2. Effect of New Versions | ||||
|  | ||||
|       You may distribute the Covered Software under the terms of the version of | ||||
|       the License under which You originally received the Covered Software, or | ||||
|       under the terms of any subsequent version published by the license | ||||
|       steward. | ||||
|  | ||||
| 10.3. Modified Versions | ||||
|  | ||||
|       If you create software not governed by this License, and you want to | ||||
|       create a new license for such software, you may create and use a modified | ||||
|       version of this License if you rename the license and remove any | ||||
|       references to the name of the license steward (except to note that such | ||||
|       modified license differs from this License). | ||||
|  | ||||
| 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses | ||||
|       If You choose to distribute Source Code Form that is Incompatible With | ||||
|       Secondary Licenses under the terms of this version of the License, the | ||||
|       notice described in Exhibit B of this License must be attached. | ||||
|  | ||||
| Exhibit A - Source Code Form License Notice | ||||
|  | ||||
|       This Source Code Form is subject to the | ||||
|       terms of the Mozilla Public License, v. | ||||
|       2.0. If a copy of the MPL was not | ||||
|       distributed with this file, You can | ||||
|       obtain one at | ||||
|       http://mozilla.org/MPL/2.0/. | ||||
|  | ||||
| If it is not possible or desirable to put the notice in a particular file, then | ||||
| You may include the notice in a location (such as a LICENSE file in a relevant | ||||
| directory) where a recipient would be likely to look for such a notice. | ||||
|  | ||||
| You may add additional accurate notices of copyright ownership. | ||||
|  | ||||
| Exhibit B - “Incompatible With Secondary Licenses” Notice | ||||
|  | ||||
|       This Source Code Form is “Incompatible | ||||
|       With Secondary Licenses”, as defined by | ||||
|       the Mozilla Public License, v. 2.0. | ||||
|  | ||||
							
								
								
									
										18
									
								
								vendor/github.com/hashicorp/hcl/Makefile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								vendor/github.com/hashicorp/hcl/Makefile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| TEST?=./... | ||||
|  | ||||
| default: test | ||||
|  | ||||
| fmt: generate | ||||
| 	go fmt ./... | ||||
|  | ||||
| test: generate | ||||
| 	go get -t ./... | ||||
| 	go test $(TEST) $(TESTARGS) | ||||
|  | ||||
| generate: | ||||
| 	go generate ./... | ||||
|  | ||||
| updatedeps: | ||||
| 	go get -u golang.org/x/tools/cmd/stringer | ||||
|  | ||||
| .PHONY: default generate test updatedeps | ||||
							
								
								
									
										125
									
								
								vendor/github.com/hashicorp/hcl/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								vendor/github.com/hashicorp/hcl/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,125 @@ | ||||
| # HCL | ||||
|  | ||||
| [](https://godoc.org/github.com/hashicorp/hcl) [](https://travis-ci.org/hashicorp/hcl) | ||||
|  | ||||
| HCL (HashiCorp Configuration Language) is a configuration language built | ||||
| by HashiCorp. The goal of HCL is to build a structured configuration language | ||||
| that is both human and machine friendly for use with command-line tools, but | ||||
| specifically targeted towards DevOps tools, servers, etc. | ||||
|  | ||||
| HCL is also fully JSON compatible. That is, JSON can be used as completely | ||||
| valid input to a system expecting HCL. This helps makes systems | ||||
| interoperable with other systems. | ||||
|  | ||||
| HCL is heavily inspired by | ||||
| [libucl](https://github.com/vstakhov/libucl), | ||||
| nginx configuration, and others similar. | ||||
|  | ||||
| ## Why? | ||||
|  | ||||
| A common question when viewing HCL is to ask the question: why not | ||||
| JSON, YAML, etc.? | ||||
|  | ||||
| Prior to HCL, the tools we built at [HashiCorp](http://www.hashicorp.com) | ||||
| used a variety of configuration languages from full programming languages | ||||
| such as Ruby to complete data structure languages such as JSON. What we | ||||
| learned is that some people wanted human-friendly configuration languages | ||||
| and some people wanted machine-friendly languages. | ||||
|  | ||||
| JSON fits a nice balance in this, but is fairly verbose and most | ||||
| importantly doesn't support comments. With YAML, we found that beginners | ||||
| had a really hard time determining what the actual structure was, and | ||||
| ended up guessing more often than not whether to use a hyphen, colon, etc. | ||||
| in order to represent some configuration key. | ||||
|  | ||||
| Full programming languages such as Ruby enable complex behavior | ||||
| a configuration language shouldn't usually allow, and also forces | ||||
| people to learn some set of Ruby. | ||||
|  | ||||
| Because of this, we decided to create our own configuration language | ||||
| that is JSON-compatible. Our configuration language (HCL) is designed | ||||
| to be written and modified by humans. The API for HCL allows JSON | ||||
| as an input so that it is also machine-friendly (machines can generate | ||||
| JSON instead of trying to generate HCL). | ||||
|  | ||||
| Our goal with HCL is not to alienate other configuration languages. | ||||
| It is instead to provide HCL as a specialized language for our tools, | ||||
| and JSON as the interoperability layer. | ||||
|  | ||||
| ## Syntax | ||||
|  | ||||
| For a complete grammar, please see the parser itself. A high-level overview | ||||
| of the syntax and grammar is listed here. | ||||
|  | ||||
|   * Single line comments start with `#` or `//` | ||||
|  | ||||
|   * Multi-line comments are wrapped in `/*` and `*/`. Nested block comments | ||||
|     are not allowed. A multi-line comment (also known as a block comment) | ||||
|     terminates at the first `*/` found. | ||||
|  | ||||
|   * Values are assigned with the syntax `key = value` (whitespace doesn't | ||||
|     matter). The value can be any primitive: a string, number, boolean, | ||||
|     object, or list. | ||||
|  | ||||
|   * Strings are double-quoted and can contain any UTF-8 characters. | ||||
|     Example: `"Hello, World"` | ||||
|  | ||||
|   * Multi-line strings start with `<<EOF` at the end of a line, and end | ||||
|     with `EOF` on its own line ([here documents](https://en.wikipedia.org/wiki/Here_document)). | ||||
|     Any text may be used in place of `EOF`. Example: | ||||
| ``` | ||||
| <<FOO | ||||
| hello | ||||
| world | ||||
| FOO | ||||
| ``` | ||||
|  | ||||
|   * Numbers are assumed to be base 10. If you prefix a number with 0x, | ||||
|     it is treated as a hexadecimal. If it is prefixed with 0, it is | ||||
|     treated as an octal. Numbers can be in scientific notation: "1e10". | ||||
|  | ||||
|   * Boolean values: `true`, `false` | ||||
|  | ||||
|   * Arrays can be made by wrapping it in `[]`. Example: | ||||
|     `["foo", "bar", 42]`. Arrays can contain primitives, | ||||
|     other arrays, and objects. As an alternative, lists | ||||
|     of objects can be created with repeated blocks, using | ||||
|     this structure: | ||||
|  | ||||
|     ```hcl | ||||
|     service { | ||||
|         key = "value" | ||||
|     } | ||||
|  | ||||
|     service { | ||||
|         key = "value" | ||||
|     } | ||||
|     ``` | ||||
|  | ||||
| Objects and nested objects are created using the structure shown below: | ||||
|  | ||||
| ``` | ||||
| variable "ami" { | ||||
|     description = "the AMI to use" | ||||
| } | ||||
| ``` | ||||
| This would be equivalent to the following json: | ||||
| ``` json | ||||
| { | ||||
|   "variable": { | ||||
|       "ami": { | ||||
|           "description": "the AMI to use" | ||||
|         } | ||||
|     } | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ## Thanks | ||||
|  | ||||
| Thanks to: | ||||
|  | ||||
|   * [@vstakhov](https://github.com/vstakhov) - The original libucl parser | ||||
|     and syntax that HCL was based off of. | ||||
|  | ||||
|   * [@fatih](https://github.com/fatih) - The rewritten HCL parser | ||||
|     in pure Go (no goyacc) and support for a printer. | ||||
							
								
								
									
										19
									
								
								vendor/github.com/hashicorp/hcl/appveyor.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/hashicorp/hcl/appveyor.yml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| version: "build-{branch}-{build}" | ||||
| image: Visual Studio 2015 | ||||
| clone_folder: c:\gopath\src\github.com\hashicorp\hcl | ||||
| environment: | ||||
|   GOPATH: c:\gopath | ||||
| init: | ||||
|   - git config --global core.autocrlf false | ||||
| install: | ||||
| - cmd: >- | ||||
|     echo %Path% | ||||
|  | ||||
|     go version | ||||
|  | ||||
|     go env | ||||
|  | ||||
|     go get -t ./... | ||||
|  | ||||
| build_script: | ||||
| - cmd: go test -v ./... | ||||
							
								
								
									
										729
									
								
								vendor/github.com/hashicorp/hcl/decoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										729
									
								
								vendor/github.com/hashicorp/hcl/decoder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,729 @@ | ||||
| package hcl | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/hashicorp/hcl/hcl/ast" | ||||
| 	"github.com/hashicorp/hcl/hcl/parser" | ||||
| 	"github.com/hashicorp/hcl/hcl/token" | ||||
| ) | ||||
|  | ||||
| // This is the tag to use with structures to have settings for HCL | ||||
| const tagName = "hcl" | ||||
|  | ||||
| var ( | ||||
| 	// nodeType holds a reference to the type of ast.Node | ||||
| 	nodeType reflect.Type = findNodeType() | ||||
| ) | ||||
|  | ||||
| // Unmarshal accepts a byte slice as input and writes the | ||||
| // data to the value pointed to by v. | ||||
| func Unmarshal(bs []byte, v interface{}) error { | ||||
| 	root, err := parse(bs) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return DecodeObject(v, root) | ||||
| } | ||||
|  | ||||
| // Decode reads the given input and decodes it into the structure | ||||
| // given by `out`. | ||||
| func Decode(out interface{}, in string) error { | ||||
| 	obj, err := Parse(in) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return DecodeObject(out, obj) | ||||
| } | ||||
|  | ||||
| // DecodeObject is a lower-level version of Decode. It decodes a | ||||
| // raw Object into the given output. | ||||
| func DecodeObject(out interface{}, n ast.Node) error { | ||||
| 	val := reflect.ValueOf(out) | ||||
| 	if val.Kind() != reflect.Ptr { | ||||
| 		return errors.New("result must be a pointer") | ||||
| 	} | ||||
|  | ||||
| 	// If we have the file, we really decode the root node | ||||
| 	if f, ok := n.(*ast.File); ok { | ||||
| 		n = f.Node | ||||
| 	} | ||||
|  | ||||
| 	var d decoder | ||||
| 	return d.decode("root", n, val.Elem()) | ||||
| } | ||||
|  | ||||
| type decoder struct { | ||||
| 	stack []reflect.Kind | ||||
| } | ||||
|  | ||||
| func (d *decoder) decode(name string, node ast.Node, result reflect.Value) error { | ||||
| 	k := result | ||||
|  | ||||
| 	// If we have an interface with a valid value, we use that | ||||
| 	// for the check. | ||||
| 	if result.Kind() == reflect.Interface { | ||||
| 		elem := result.Elem() | ||||
| 		if elem.IsValid() { | ||||
| 			k = elem | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Push current onto stack unless it is an interface. | ||||
| 	if k.Kind() != reflect.Interface { | ||||
| 		d.stack = append(d.stack, k.Kind()) | ||||
|  | ||||
| 		// Schedule a pop | ||||
| 		defer func() { | ||||
| 			d.stack = d.stack[:len(d.stack)-1] | ||||
| 		}() | ||||
| 	} | ||||
|  | ||||
| 	switch k.Kind() { | ||||
| 	case reflect.Bool: | ||||
| 		return d.decodeBool(name, node, result) | ||||
| 	case reflect.Float32, reflect.Float64: | ||||
| 		return d.decodeFloat(name, node, result) | ||||
| 	case reflect.Int, reflect.Int32, reflect.Int64: | ||||
| 		return d.decodeInt(name, node, result) | ||||
| 	case reflect.Interface: | ||||
| 		// When we see an interface, we make our own thing | ||||
| 		return d.decodeInterface(name, node, result) | ||||
| 	case reflect.Map: | ||||
| 		return d.decodeMap(name, node, result) | ||||
| 	case reflect.Ptr: | ||||
| 		return d.decodePtr(name, node, result) | ||||
| 	case reflect.Slice: | ||||
| 		return d.decodeSlice(name, node, result) | ||||
| 	case reflect.String: | ||||
| 		return d.decodeString(name, node, result) | ||||
| 	case reflect.Struct: | ||||
| 		return d.decodeStruct(name, node, result) | ||||
| 	default: | ||||
| 		return &parser.PosError{ | ||||
| 			Pos: node.Pos(), | ||||
| 			Err: fmt.Errorf("%s: unknown kind to decode into: %s", name, k.Kind()), | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (d *decoder) decodeBool(name string, node ast.Node, result reflect.Value) error { | ||||
| 	switch n := node.(type) { | ||||
| 	case *ast.LiteralType: | ||||
| 		if n.Token.Type == token.BOOL { | ||||
| 			v, err := strconv.ParseBool(n.Token.Text) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			result.Set(reflect.ValueOf(v)) | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &parser.PosError{ | ||||
| 		Pos: node.Pos(), | ||||
| 		Err: fmt.Errorf("%s: unknown type %T", name, node), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (d *decoder) decodeFloat(name string, node ast.Node, result reflect.Value) error { | ||||
| 	switch n := node.(type) { | ||||
| 	case *ast.LiteralType: | ||||
| 		if n.Token.Type == token.FLOAT || n.Token.Type == token.NUMBER { | ||||
| 			v, err := strconv.ParseFloat(n.Token.Text, 64) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			result.Set(reflect.ValueOf(v).Convert(result.Type())) | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &parser.PosError{ | ||||
| 		Pos: node.Pos(), | ||||
| 		Err: fmt.Errorf("%s: unknown type %T", name, node), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (d *decoder) decodeInt(name string, node ast.Node, result reflect.Value) error { | ||||
| 	switch n := node.(type) { | ||||
| 	case *ast.LiteralType: | ||||
| 		switch n.Token.Type { | ||||
| 		case token.NUMBER: | ||||
| 			v, err := strconv.ParseInt(n.Token.Text, 0, 0) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			if result.Kind() == reflect.Interface { | ||||
| 				result.Set(reflect.ValueOf(int(v))) | ||||
| 			} else { | ||||
| 				result.SetInt(v) | ||||
| 			} | ||||
| 			return nil | ||||
| 		case token.STRING: | ||||
| 			v, err := strconv.ParseInt(n.Token.Value().(string), 0, 0) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
|  | ||||
| 			if result.Kind() == reflect.Interface { | ||||
| 				result.Set(reflect.ValueOf(int(v))) | ||||
| 			} else { | ||||
| 				result.SetInt(v) | ||||
| 			} | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &parser.PosError{ | ||||
| 		Pos: node.Pos(), | ||||
| 		Err: fmt.Errorf("%s: unknown type %T", name, node), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (d *decoder) decodeInterface(name string, node ast.Node, result reflect.Value) error { | ||||
| 	// When we see an ast.Node, we retain the value to enable deferred decoding. | ||||
| 	// Very useful in situations where we want to preserve ast.Node information | ||||
| 	// like Pos | ||||
| 	if result.Type() == nodeType && result.CanSet() { | ||||
| 		result.Set(reflect.ValueOf(node)) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	var set reflect.Value | ||||
| 	redecode := true | ||||
|  | ||||
| 	// For testing types, ObjectType should just be treated as a list. We | ||||
| 	// set this to a temporary var because we want to pass in the real node. | ||||
| 	testNode := node | ||||
| 	if ot, ok := node.(*ast.ObjectType); ok { | ||||
| 		testNode = ot.List | ||||
| 	} | ||||
|  | ||||
| 	switch n := testNode.(type) { | ||||
| 	case *ast.ObjectList: | ||||
| 		// If we're at the root or we're directly within a slice, then we | ||||
| 		// decode objects into map[string]interface{}, otherwise we decode | ||||
| 		// them into lists. | ||||
| 		if len(d.stack) == 0 || d.stack[len(d.stack)-1] == reflect.Slice { | ||||
| 			var temp map[string]interface{} | ||||
| 			tempVal := reflect.ValueOf(temp) | ||||
| 			result := reflect.MakeMap( | ||||
| 				reflect.MapOf( | ||||
| 					reflect.TypeOf(""), | ||||
| 					tempVal.Type().Elem())) | ||||
|  | ||||
| 			set = result | ||||
| 		} else { | ||||
| 			var temp []map[string]interface{} | ||||
| 			tempVal := reflect.ValueOf(temp) | ||||
| 			result := reflect.MakeSlice( | ||||
| 				reflect.SliceOf(tempVal.Type().Elem()), 0, len(n.Items)) | ||||
| 			set = result | ||||
| 		} | ||||
| 	case *ast.ObjectType: | ||||
| 		// If we're at the root or we're directly within a slice, then we | ||||
| 		// decode objects into map[string]interface{}, otherwise we decode | ||||
| 		// them into lists. | ||||
| 		if len(d.stack) == 0 || d.stack[len(d.stack)-1] == reflect.Slice { | ||||
| 			var temp map[string]interface{} | ||||
| 			tempVal := reflect.ValueOf(temp) | ||||
| 			result := reflect.MakeMap( | ||||
| 				reflect.MapOf( | ||||
| 					reflect.TypeOf(""), | ||||
| 					tempVal.Type().Elem())) | ||||
|  | ||||
| 			set = result | ||||
| 		} else { | ||||
| 			var temp []map[string]interface{} | ||||
| 			tempVal := reflect.ValueOf(temp) | ||||
| 			result := reflect.MakeSlice( | ||||
| 				reflect.SliceOf(tempVal.Type().Elem()), 0, 1) | ||||
| 			set = result | ||||
| 		} | ||||
| 	case *ast.ListType: | ||||
| 		var temp []interface{} | ||||
| 		tempVal := reflect.ValueOf(temp) | ||||
| 		result := reflect.MakeSlice( | ||||
| 			reflect.SliceOf(tempVal.Type().Elem()), 0, 0) | ||||
| 		set = result | ||||
| 	case *ast.LiteralType: | ||||
| 		switch n.Token.Type { | ||||
| 		case token.BOOL: | ||||
| 			var result bool | ||||
| 			set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) | ||||
| 		case token.FLOAT: | ||||
| 			var result float64 | ||||
| 			set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) | ||||
| 		case token.NUMBER: | ||||
| 			var result int | ||||
| 			set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) | ||||
| 		case token.STRING, token.HEREDOC: | ||||
| 			set = reflect.Indirect(reflect.New(reflect.TypeOf(""))) | ||||
| 		default: | ||||
| 			return &parser.PosError{ | ||||
| 				Pos: node.Pos(), | ||||
| 				Err: fmt.Errorf("%s: cannot decode into interface: %T", name, node), | ||||
| 			} | ||||
| 		} | ||||
| 	default: | ||||
| 		return fmt.Errorf( | ||||
| 			"%s: cannot decode into interface: %T", | ||||
| 			name, node) | ||||
| 	} | ||||
|  | ||||
| 	// Set the result to what its supposed to be, then reset | ||||
| 	// result so we don't reflect into this method anymore. | ||||
| 	result.Set(set) | ||||
|  | ||||
| 	if redecode { | ||||
| 		// Revisit the node so that we can use the newly instantiated | ||||
| 		// thing and populate it. | ||||
| 		if err := d.decode(name, node, result); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (d *decoder) decodeMap(name string, node ast.Node, result reflect.Value) error { | ||||
| 	if item, ok := node.(*ast.ObjectItem); ok { | ||||
| 		node = &ast.ObjectList{Items: []*ast.ObjectItem{item}} | ||||
| 	} | ||||
|  | ||||
| 	if ot, ok := node.(*ast.ObjectType); ok { | ||||
| 		node = ot.List | ||||
| 	} | ||||
|  | ||||
| 	n, ok := node.(*ast.ObjectList) | ||||
| 	if !ok { | ||||
| 		return &parser.PosError{ | ||||
| 			Pos: node.Pos(), | ||||
| 			Err: fmt.Errorf("%s: not an object type for map (%T)", name, node), | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// If we have an interface, then we can address the interface, | ||||
| 	// but not the slice itself, so get the element but set the interface | ||||
| 	set := result | ||||
| 	if result.Kind() == reflect.Interface { | ||||
| 		result = result.Elem() | ||||
| 	} | ||||
|  | ||||
| 	resultType := result.Type() | ||||
| 	resultElemType := resultType.Elem() | ||||
| 	resultKeyType := resultType.Key() | ||||
| 	if resultKeyType.Kind() != reflect.String { | ||||
| 		return &parser.PosError{ | ||||
| 			Pos: node.Pos(), | ||||
| 			Err: fmt.Errorf("%s: map must have string keys", name), | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Make a map if it is nil | ||||
| 	resultMap := result | ||||
| 	if result.IsNil() { | ||||
| 		resultMap = reflect.MakeMap( | ||||
| 			reflect.MapOf(resultKeyType, resultElemType)) | ||||
| 	} | ||||
|  | ||||
| 	// Go through each element and decode it. | ||||
| 	done := make(map[string]struct{}) | ||||
| 	for _, item := range n.Items { | ||||
| 		if item.Val == nil { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// github.com/hashicorp/terraform/issue/5740 | ||||
| 		if len(item.Keys) == 0 { | ||||
| 			return &parser.PosError{ | ||||
| 				Pos: node.Pos(), | ||||
| 				Err: fmt.Errorf("%s: map must have string keys", name), | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Get the key we're dealing with, which is the first item | ||||
| 		keyStr := item.Keys[0].Token.Value().(string) | ||||
|  | ||||
| 		// If we've already processed this key, then ignore it | ||||
| 		if _, ok := done[keyStr]; ok { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// Determine the value. If we have more than one key, then we | ||||
| 		// get the objectlist of only these keys. | ||||
| 		itemVal := item.Val | ||||
| 		if len(item.Keys) > 1 { | ||||
| 			itemVal = n.Filter(keyStr) | ||||
| 			done[keyStr] = struct{}{} | ||||
| 		} | ||||
|  | ||||
| 		// Make the field name | ||||
| 		fieldName := fmt.Sprintf("%s.%s", name, keyStr) | ||||
|  | ||||
| 		// Get the key/value as reflection values | ||||
| 		key := reflect.ValueOf(keyStr) | ||||
| 		val := reflect.Indirect(reflect.New(resultElemType)) | ||||
|  | ||||
| 		// If we have a pre-existing value in the map, use that | ||||
| 		oldVal := resultMap.MapIndex(key) | ||||
| 		if oldVal.IsValid() { | ||||
| 			val.Set(oldVal) | ||||
| 		} | ||||
|  | ||||
| 		// Decode! | ||||
| 		if err := d.decode(fieldName, itemVal, val); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		// Set the value on the map | ||||
| 		resultMap.SetMapIndex(key, val) | ||||
| 	} | ||||
|  | ||||
| 	// Set the final map if we can | ||||
| 	set.Set(resultMap) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (d *decoder) decodePtr(name string, node ast.Node, result reflect.Value) error { | ||||
| 	// Create an element of the concrete (non pointer) type and decode | ||||
| 	// into that. Then set the value of the pointer to this type. | ||||
| 	resultType := result.Type() | ||||
| 	resultElemType := resultType.Elem() | ||||
| 	val := reflect.New(resultElemType) | ||||
| 	if err := d.decode(name, node, reflect.Indirect(val)); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	result.Set(val) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (d *decoder) decodeSlice(name string, node ast.Node, result reflect.Value) error { | ||||
| 	// If we have an interface, then we can address the interface, | ||||
| 	// but not the slice itself, so get the element but set the interface | ||||
| 	set := result | ||||
| 	if result.Kind() == reflect.Interface { | ||||
| 		result = result.Elem() | ||||
| 	} | ||||
| 	// Create the slice if it isn't nil | ||||
| 	resultType := result.Type() | ||||
| 	resultElemType := resultType.Elem() | ||||
| 	if result.IsNil() { | ||||
| 		resultSliceType := reflect.SliceOf(resultElemType) | ||||
| 		result = reflect.MakeSlice( | ||||
| 			resultSliceType, 0, 0) | ||||
| 	} | ||||
|  | ||||
| 	// Figure out the items we'll be copying into the slice | ||||
| 	var items []ast.Node | ||||
| 	switch n := node.(type) { | ||||
| 	case *ast.ObjectList: | ||||
| 		items = make([]ast.Node, len(n.Items)) | ||||
| 		for i, item := range n.Items { | ||||
| 			items[i] = item | ||||
| 		} | ||||
| 	case *ast.ObjectType: | ||||
| 		items = []ast.Node{n} | ||||
| 	case *ast.ListType: | ||||
| 		items = n.List | ||||
| 	default: | ||||
| 		return &parser.PosError{ | ||||
| 			Pos: node.Pos(), | ||||
| 			Err: fmt.Errorf("unknown slice type: %T", node), | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for i, item := range items { | ||||
| 		fieldName := fmt.Sprintf("%s[%d]", name, i) | ||||
|  | ||||
| 		// Decode | ||||
| 		val := reflect.Indirect(reflect.New(resultElemType)) | ||||
|  | ||||
| 		// if item is an object that was decoded from ambiguous JSON and | ||||
| 		// flattened, make sure it's expanded if it needs to decode into a | ||||
| 		// defined structure. | ||||
| 		item := expandObject(item, val) | ||||
|  | ||||
| 		if err := d.decode(fieldName, item, val); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		// Append it onto the slice | ||||
| 		result = reflect.Append(result, val) | ||||
| 	} | ||||
|  | ||||
| 	set.Set(result) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // expandObject detects if an ambiguous JSON object was flattened to a List which | ||||
| // should be decoded into a struct, and expands the ast to properly deocode. | ||||
| func expandObject(node ast.Node, result reflect.Value) ast.Node { | ||||
| 	item, ok := node.(*ast.ObjectItem) | ||||
| 	if !ok { | ||||
| 		return node | ||||
| 	} | ||||
|  | ||||
| 	elemType := result.Type() | ||||
|  | ||||
| 	// our target type must be a struct | ||||
| 	switch elemType.Kind() { | ||||
| 	case reflect.Ptr: | ||||
| 		switch elemType.Elem().Kind() { | ||||
| 		case reflect.Struct: | ||||
| 			//OK | ||||
| 		default: | ||||
| 			return node | ||||
| 		} | ||||
| 	case reflect.Struct: | ||||
| 		//OK | ||||
| 	default: | ||||
| 		return node | ||||
| 	} | ||||
|  | ||||
| 	// A list value will have a key and field name. If it had more fields, | ||||
| 	// it wouldn't have been flattened. | ||||
| 	if len(item.Keys) != 2 { | ||||
| 		return node | ||||
| 	} | ||||
|  | ||||
| 	keyToken := item.Keys[0].Token | ||||
| 	item.Keys = item.Keys[1:] | ||||
|  | ||||
| 	// we need to un-flatten the ast enough to decode | ||||
| 	newNode := &ast.ObjectItem{ | ||||
| 		Keys: []*ast.ObjectKey{ | ||||
| 			&ast.ObjectKey{ | ||||
| 				Token: keyToken, | ||||
| 			}, | ||||
| 		}, | ||||
| 		Val: &ast.ObjectType{ | ||||
| 			List: &ast.ObjectList{ | ||||
| 				Items: []*ast.ObjectItem{item}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	return newNode | ||||
| } | ||||
|  | ||||
| func (d *decoder) decodeString(name string, node ast.Node, result reflect.Value) error { | ||||
| 	switch n := node.(type) { | ||||
| 	case *ast.LiteralType: | ||||
| 		switch n.Token.Type { | ||||
| 		case token.NUMBER: | ||||
| 			result.Set(reflect.ValueOf(n.Token.Text).Convert(result.Type())) | ||||
| 			return nil | ||||
| 		case token.STRING, token.HEREDOC: | ||||
| 			result.Set(reflect.ValueOf(n.Token.Value()).Convert(result.Type())) | ||||
| 			return nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &parser.PosError{ | ||||
| 		Pos: node.Pos(), | ||||
| 		Err: fmt.Errorf("%s: unknown type for string %T", name, node), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) error { | ||||
| 	var item *ast.ObjectItem | ||||
| 	if it, ok := node.(*ast.ObjectItem); ok { | ||||
| 		item = it | ||||
| 		node = it.Val | ||||
| 	} | ||||
|  | ||||
| 	if ot, ok := node.(*ast.ObjectType); ok { | ||||
| 		node = ot.List | ||||
| 	} | ||||
|  | ||||
| 	// Handle the special case where the object itself is a literal. Previously | ||||
| 	// the yacc parser would always ensure top-level elements were arrays. The new | ||||
| 	// parser does not make the same guarantees, thus we need to convert any | ||||
| 	// top-level literal elements into a list. | ||||
| 	if _, ok := node.(*ast.LiteralType); ok && item != nil { | ||||
| 		node = &ast.ObjectList{Items: []*ast.ObjectItem{item}} | ||||
| 	} | ||||
|  | ||||
| 	list, ok := node.(*ast.ObjectList) | ||||
| 	if !ok { | ||||
| 		return &parser.PosError{ | ||||
| 			Pos: node.Pos(), | ||||
| 			Err: fmt.Errorf("%s: not an object type for struct (%T)", name, node), | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// This slice will keep track of all the structs we'll be decoding. | ||||
| 	// There can be more than one struct if there are embedded structs | ||||
| 	// that are squashed. | ||||
| 	structs := make([]reflect.Value, 1, 5) | ||||
| 	structs[0] = result | ||||
|  | ||||
| 	// Compile the list of all the fields that we're going to be decoding | ||||
| 	// from all the structs. | ||||
| 	type field struct { | ||||
| 		field reflect.StructField | ||||
| 		val   reflect.Value | ||||
| 	} | ||||
| 	fields := []field{} | ||||
| 	for len(structs) > 0 { | ||||
| 		structVal := structs[0] | ||||
| 		structs = structs[1:] | ||||
|  | ||||
| 		structType := structVal.Type() | ||||
| 		for i := 0; i < structType.NumField(); i++ { | ||||
| 			fieldType := structType.Field(i) | ||||
| 			tagParts := strings.Split(fieldType.Tag.Get(tagName), ",") | ||||
|  | ||||
| 			// Ignore fields with tag name "-" | ||||
| 			if tagParts[0] == "-" { | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			if fieldType.Anonymous { | ||||
| 				fieldKind := fieldType.Type.Kind() | ||||
| 				if fieldKind != reflect.Struct { | ||||
| 					return &parser.PosError{ | ||||
| 						Pos: node.Pos(), | ||||
| 						Err: fmt.Errorf("%s: unsupported type to struct: %s", | ||||
| 							fieldType.Name, fieldKind), | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				// We have an embedded field. We "squash" the fields down | ||||
| 				// if specified in the tag. | ||||
| 				squash := false | ||||
| 				for _, tag := range tagParts[1:] { | ||||
| 					if tag == "squash" { | ||||
| 						squash = true | ||||
| 						break | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				if squash { | ||||
| 					structs = append( | ||||
| 						structs, result.FieldByName(fieldType.Name)) | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Normal struct field, store it away | ||||
| 			fields = append(fields, field{fieldType, structVal.Field(i)}) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	usedKeys := make(map[string]struct{}) | ||||
| 	decodedFields := make([]string, 0, len(fields)) | ||||
| 	decodedFieldsVal := make([]reflect.Value, 0) | ||||
| 	unusedKeysVal := make([]reflect.Value, 0) | ||||
| 	for _, f := range fields { | ||||
| 		field, fieldValue := f.field, f.val | ||||
| 		if !fieldValue.IsValid() { | ||||
| 			// This should never happen | ||||
| 			panic("field is not valid") | ||||
| 		} | ||||
|  | ||||
| 		// If we can't set the field, then it is unexported or something, | ||||
| 		// and we just continue onwards. | ||||
| 		if !fieldValue.CanSet() { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		fieldName := field.Name | ||||
|  | ||||
| 		tagValue := field.Tag.Get(tagName) | ||||
| 		tagParts := strings.SplitN(tagValue, ",", 2) | ||||
| 		if len(tagParts) >= 2 { | ||||
| 			switch tagParts[1] { | ||||
| 			case "decodedFields": | ||||
| 				decodedFieldsVal = append(decodedFieldsVal, fieldValue) | ||||
| 				continue | ||||
| 			case "key": | ||||
| 				if item == nil { | ||||
| 					return &parser.PosError{ | ||||
| 						Pos: node.Pos(), | ||||
| 						Err: fmt.Errorf("%s: %s asked for 'key', impossible", | ||||
| 							name, fieldName), | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				fieldValue.SetString(item.Keys[0].Token.Value().(string)) | ||||
| 				continue | ||||
| 			case "unusedKeys": | ||||
| 				unusedKeysVal = append(unusedKeysVal, fieldValue) | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if tagParts[0] != "" { | ||||
| 			fieldName = tagParts[0] | ||||
| 		} | ||||
|  | ||||
| 		// Determine the element we'll use to decode. If it is a single | ||||
| 		// match (only object with the field), then we decode it exactly. | ||||
| 		// If it is a prefix match, then we decode the matches. | ||||
| 		filter := list.Filter(fieldName) | ||||
|  | ||||
| 		prefixMatches := filter.Children() | ||||
| 		matches := filter.Elem() | ||||
| 		if len(matches.Items) == 0 && len(prefixMatches.Items) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// Track the used key | ||||
| 		usedKeys[fieldName] = struct{}{} | ||||
|  | ||||
| 		// Create the field name and decode. We range over the elements | ||||
| 		// because we actually want the value. | ||||
| 		fieldName = fmt.Sprintf("%s.%s", name, fieldName) | ||||
| 		if len(prefixMatches.Items) > 0 { | ||||
| 			if err := d.decode(fieldName, prefixMatches, fieldValue); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		for _, match := range matches.Items { | ||||
| 			var decodeNode ast.Node = match.Val | ||||
| 			if ot, ok := decodeNode.(*ast.ObjectType); ok { | ||||
| 				decodeNode = &ast.ObjectList{Items: ot.List.Items} | ||||
| 			} | ||||
|  | ||||
| 			if err := d.decode(fieldName, decodeNode, fieldValue); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		decodedFields = append(decodedFields, field.Name) | ||||
| 	} | ||||
|  | ||||
| 	if len(decodedFieldsVal) > 0 { | ||||
| 		// Sort it so that it is deterministic | ||||
| 		sort.Strings(decodedFields) | ||||
|  | ||||
| 		for _, v := range decodedFieldsVal { | ||||
| 			v.Set(reflect.ValueOf(decodedFields)) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // findNodeType returns the type of ast.Node | ||||
| func findNodeType() reflect.Type { | ||||
| 	var nodeContainer struct { | ||||
| 		Node ast.Node | ||||
| 	} | ||||
| 	value := reflect.ValueOf(nodeContainer).FieldByName("Node") | ||||
| 	return value.Type() | ||||
| } | ||||
							
								
								
									
										11
									
								
								vendor/github.com/hashicorp/hcl/hcl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								vendor/github.com/hashicorp/hcl/hcl.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| // Package hcl decodes HCL into usable Go structures. | ||||
| // | ||||
| // hcl input can come in either pure HCL format or JSON format. | ||||
| // It can be parsed into an AST, and then decoded into a structure, | ||||
| // or it can be decoded directly from a string into a structure. | ||||
| // | ||||
| // If you choose to parse HCL into a raw AST, the benefit is that you | ||||
| // can write custom visitor implementations to implement custom | ||||
| // semantic checks. By default, HCL does not perform any semantic | ||||
| // checks. | ||||
| package hcl | ||||
							
								
								
									
										219
									
								
								vendor/github.com/hashicorp/hcl/hcl/ast/ast.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								vendor/github.com/hashicorp/hcl/hcl/ast/ast.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,219 @@ | ||||
| // Package ast declares the types used to represent syntax trees for HCL | ||||
| // (HashiCorp Configuration Language) | ||||
| package ast | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/hashicorp/hcl/hcl/token" | ||||
| ) | ||||
|  | ||||
| // Node is an element in the abstract syntax tree. | ||||
| type Node interface { | ||||
| 	node() | ||||
| 	Pos() token.Pos | ||||
| } | ||||
|  | ||||
| func (File) node()         {} | ||||
| func (ObjectList) node()   {} | ||||
| func (ObjectKey) node()    {} | ||||
| func (ObjectItem) node()   {} | ||||
| func (Comment) node()      {} | ||||
| func (CommentGroup) node() {} | ||||
| func (ObjectType) node()   {} | ||||
| func (LiteralType) node()  {} | ||||
| func (ListType) node()     {} | ||||
|  | ||||
| // File represents a single HCL file | ||||
| type File struct { | ||||
| 	Node     Node            // usually a *ObjectList | ||||
| 	Comments []*CommentGroup // list of all comments in the source | ||||
| } | ||||
|  | ||||
| func (f *File) Pos() token.Pos { | ||||
| 	return f.Node.Pos() | ||||
| } | ||||
|  | ||||
| // ObjectList represents a list of ObjectItems. An HCL file itself is an | ||||
| // ObjectList. | ||||
| type ObjectList struct { | ||||
| 	Items []*ObjectItem | ||||
| } | ||||
|  | ||||
| func (o *ObjectList) Add(item *ObjectItem) { | ||||
| 	o.Items = append(o.Items, item) | ||||
| } | ||||
|  | ||||
| // Filter filters out the objects with the given key list as a prefix. | ||||
| // | ||||
| // The returned list of objects contain ObjectItems where the keys have | ||||
| // this prefix already stripped off. This might result in objects with | ||||
| // zero-length key lists if they have no children. | ||||
| // | ||||
| // If no matches are found, an empty ObjectList (non-nil) is returned. | ||||
| func (o *ObjectList) Filter(keys ...string) *ObjectList { | ||||
| 	var result ObjectList | ||||
| 	for _, item := range o.Items { | ||||
| 		// If there aren't enough keys, then ignore this | ||||
| 		if len(item.Keys) < len(keys) { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		match := true | ||||
| 		for i, key := range item.Keys[:len(keys)] { | ||||
| 			key := key.Token.Value().(string) | ||||
| 			if key != keys[i] && !strings.EqualFold(key, keys[i]) { | ||||
| 				match = false | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if !match { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// Strip off the prefix from the children | ||||
| 		newItem := *item | ||||
| 		newItem.Keys = newItem.Keys[len(keys):] | ||||
| 		result.Add(&newItem) | ||||
| 	} | ||||
|  | ||||
| 	return &result | ||||
| } | ||||
|  | ||||
| // Children returns further nested objects (key length > 0) within this | ||||
| // ObjectList. This should be used with Filter to get at child items. | ||||
| func (o *ObjectList) Children() *ObjectList { | ||||
| 	var result ObjectList | ||||
| 	for _, item := range o.Items { | ||||
| 		if len(item.Keys) > 0 { | ||||
| 			result.Add(item) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &result | ||||
| } | ||||
|  | ||||
| // Elem returns items in the list that are direct element assignments | ||||
| // (key length == 0). This should be used with Filter to get at elements. | ||||
| func (o *ObjectList) Elem() *ObjectList { | ||||
| 	var result ObjectList | ||||
| 	for _, item := range o.Items { | ||||
| 		if len(item.Keys) == 0 { | ||||
| 			result.Add(item) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &result | ||||
| } | ||||
|  | ||||
| func (o *ObjectList) Pos() token.Pos { | ||||
| 	// always returns the uninitiliazed position | ||||
| 	return o.Items[0].Pos() | ||||
| } | ||||
|  | ||||
| // ObjectItem represents a HCL Object Item. An item is represented with a key | ||||
| // (or keys). It can be an assignment or an object (both normal and nested) | ||||
| type ObjectItem struct { | ||||
| 	// keys is only one length long if it's of type assignment. If it's a | ||||
| 	// nested object it can be larger than one. In that case "assign" is | ||||
| 	// invalid as there is no assignments for a nested object. | ||||
| 	Keys []*ObjectKey | ||||
|  | ||||
| 	// assign contains the position of "=", if any | ||||
| 	Assign token.Pos | ||||
|  | ||||
| 	// val is the item itself. It can be an object,list, number, bool or a | ||||
| 	// string. If key length is larger than one, val can be only of type | ||||
| 	// Object. | ||||
| 	Val Node | ||||
|  | ||||
| 	LeadComment *CommentGroup // associated lead comment | ||||
| 	LineComment *CommentGroup // associated line comment | ||||
| } | ||||
|  | ||||
| func (o *ObjectItem) Pos() token.Pos { | ||||
| 	// I'm not entirely sure what causes this, but removing this causes | ||||
| 	// a test failure. We should investigate at some point. | ||||
| 	if len(o.Keys) == 0 { | ||||
| 		return token.Pos{} | ||||
| 	} | ||||
|  | ||||
| 	return o.Keys[0].Pos() | ||||
| } | ||||
|  | ||||
| // ObjectKeys are either an identifier or of type string. | ||||
| type ObjectKey struct { | ||||
| 	Token token.Token | ||||
| } | ||||
|  | ||||
| func (o *ObjectKey) Pos() token.Pos { | ||||
| 	return o.Token.Pos | ||||
| } | ||||
|  | ||||
| // LiteralType represents a literal of basic type. Valid types are: | ||||
| // token.NUMBER, token.FLOAT, token.BOOL and token.STRING | ||||
| type LiteralType struct { | ||||
| 	Token token.Token | ||||
|  | ||||
| 	// comment types, only used when in a list | ||||
| 	LeadComment *CommentGroup | ||||
| 	LineComment *CommentGroup | ||||
| } | ||||
|  | ||||
| func (l *LiteralType) Pos() token.Pos { | ||||
| 	return l.Token.Pos | ||||
| } | ||||
|  | ||||
| // ListStatement represents a HCL List type | ||||
| type ListType struct { | ||||
| 	Lbrack token.Pos // position of "[" | ||||
| 	Rbrack token.Pos // position of "]" | ||||
| 	List   []Node    // the elements in lexical order | ||||
| } | ||||
|  | ||||
| func (l *ListType) Pos() token.Pos { | ||||
| 	return l.Lbrack | ||||
| } | ||||
|  | ||||
| func (l *ListType) Add(node Node) { | ||||
| 	l.List = append(l.List, node) | ||||
| } | ||||
|  | ||||
| // ObjectType represents a HCL Object Type | ||||
| type ObjectType struct { | ||||
| 	Lbrace token.Pos   // position of "{" | ||||
| 	Rbrace token.Pos   // position of "}" | ||||
| 	List   *ObjectList // the nodes in lexical order | ||||
| } | ||||
|  | ||||
| func (o *ObjectType) Pos() token.Pos { | ||||
| 	return o.Lbrace | ||||
| } | ||||
|  | ||||
| // Comment node represents a single //, # style or /*- style commment | ||||
| type Comment struct { | ||||
| 	Start token.Pos // position of / or # | ||||
| 	Text  string | ||||
| } | ||||
|  | ||||
| func (c *Comment) Pos() token.Pos { | ||||
| 	return c.Start | ||||
| } | ||||
|  | ||||
| // CommentGroup node represents a sequence of comments with no other tokens and | ||||
| // no empty lines between. | ||||
| type CommentGroup struct { | ||||
| 	List []*Comment // len(List) > 0 | ||||
| } | ||||
|  | ||||
| func (c *CommentGroup) Pos() token.Pos { | ||||
| 	return c.List[0].Pos() | ||||
| } | ||||
|  | ||||
| //------------------------------------------------------------------- | ||||
| // GoStringer | ||||
| //------------------------------------------------------------------- | ||||
|  | ||||
| func (o *ObjectKey) GoString() string  { return fmt.Sprintf("*%#v", *o) } | ||||
| func (o *ObjectList) GoString() string { return fmt.Sprintf("*%#v", *o) } | ||||
							
								
								
									
										52
									
								
								vendor/github.com/hashicorp/hcl/hcl/ast/walk.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								vendor/github.com/hashicorp/hcl/hcl/ast/walk.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| package ast | ||||
|  | ||||
| import "fmt" | ||||
|  | ||||
| // WalkFunc describes a function to be called for each node during a Walk. The | ||||
| // returned node can be used to rewrite the AST. Walking stops the returned | ||||
| // bool is false. | ||||
| type WalkFunc func(Node) (Node, bool) | ||||
|  | ||||
| // Walk traverses an AST in depth-first order: It starts by calling fn(node); | ||||
| // node must not be nil. If fn returns true, Walk invokes fn recursively for | ||||
| // each of the non-nil children of node, followed by a call of fn(nil). The | ||||
| // returned node of fn can be used to rewrite the passed node to fn. | ||||
| func Walk(node Node, fn WalkFunc) Node { | ||||
| 	rewritten, ok := fn(node) | ||||
| 	if !ok { | ||||
| 		return rewritten | ||||
| 	} | ||||
|  | ||||
| 	switch n := node.(type) { | ||||
| 	case *File: | ||||
| 		n.Node = Walk(n.Node, fn) | ||||
| 	case *ObjectList: | ||||
| 		for i, item := range n.Items { | ||||
| 			n.Items[i] = Walk(item, fn).(*ObjectItem) | ||||
| 		} | ||||
| 	case *ObjectKey: | ||||
| 		// nothing to do | ||||
| 	case *ObjectItem: | ||||
| 		for i, k := range n.Keys { | ||||
| 			n.Keys[i] = Walk(k, fn).(*ObjectKey) | ||||
| 		} | ||||
|  | ||||
| 		if n.Val != nil { | ||||
| 			n.Val = Walk(n.Val, fn) | ||||
| 		} | ||||
| 	case *LiteralType: | ||||
| 		// nothing to do | ||||
| 	case *ListType: | ||||
| 		for i, l := range n.List { | ||||
| 			n.List[i] = Walk(l, fn) | ||||
| 		} | ||||
| 	case *ObjectType: | ||||
| 		n.List = Walk(n.List, fn).(*ObjectList) | ||||
| 	default: | ||||
| 		// should we panic here? | ||||
| 		fmt.Printf("unknown type: %T\n", n) | ||||
| 	} | ||||
|  | ||||
| 	fn(nil) | ||||
| 	return rewritten | ||||
| } | ||||
							
								
								
									
										17
									
								
								vendor/github.com/hashicorp/hcl/hcl/parser/error.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								vendor/github.com/hashicorp/hcl/hcl/parser/error.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| package parser | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/hashicorp/hcl/hcl/token" | ||||
| ) | ||||
|  | ||||
| // PosError is a parse error that contains a position. | ||||
| type PosError struct { | ||||
| 	Pos token.Pos | ||||
| 	Err error | ||||
| } | ||||
|  | ||||
| func (e *PosError) Error() string { | ||||
| 	return fmt.Sprintf("At %s: %s", e.Pos, e.Err) | ||||
| } | ||||
							
								
								
									
										532
									
								
								vendor/github.com/hashicorp/hcl/hcl/parser/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										532
									
								
								vendor/github.com/hashicorp/hcl/hcl/parser/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,532 @@ | ||||
| // Package parser implements a parser for HCL (HashiCorp Configuration | ||||
| // Language) | ||||
| package parser | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/hashicorp/hcl/hcl/ast" | ||||
| 	"github.com/hashicorp/hcl/hcl/scanner" | ||||
| 	"github.com/hashicorp/hcl/hcl/token" | ||||
| ) | ||||
|  | ||||
| type Parser struct { | ||||
| 	sc *scanner.Scanner | ||||
|  | ||||
| 	// Last read token | ||||
| 	tok       token.Token | ||||
| 	commaPrev token.Token | ||||
|  | ||||
| 	comments    []*ast.CommentGroup | ||||
| 	leadComment *ast.CommentGroup // last lead comment | ||||
| 	lineComment *ast.CommentGroup // last line comment | ||||
|  | ||||
| 	enableTrace bool | ||||
| 	indent      int | ||||
| 	n           int // buffer size (max = 1) | ||||
| } | ||||
|  | ||||
| func newParser(src []byte) *Parser { | ||||
| 	return &Parser{ | ||||
| 		sc: scanner.New(src), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Parse returns the fully parsed source and returns the abstract syntax tree. | ||||
| func Parse(src []byte) (*ast.File, error) { | ||||
| 	// normalize all line endings | ||||
| 	// since the scanner and output only work with "\n" line endings, we may | ||||
| 	// end up with dangling "\r" characters in the parsed data. | ||||
| 	src = bytes.Replace(src, []byte("\r\n"), []byte("\n"), -1) | ||||
|  | ||||
| 	p := newParser(src) | ||||
| 	return p.Parse() | ||||
| } | ||||
|  | ||||
| var errEofToken = errors.New("EOF token found") | ||||
|  | ||||
| // Parse returns the fully parsed source and returns the abstract syntax tree. | ||||
| func (p *Parser) Parse() (*ast.File, error) { | ||||
| 	f := &ast.File{} | ||||
| 	var err, scerr error | ||||
| 	p.sc.Error = func(pos token.Pos, msg string) { | ||||
| 		scerr = &PosError{Pos: pos, Err: errors.New(msg)} | ||||
| 	} | ||||
|  | ||||
| 	f.Node, err = p.objectList(false) | ||||
| 	if scerr != nil { | ||||
| 		return nil, scerr | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	f.Comments = p.comments | ||||
| 	return f, nil | ||||
| } | ||||
|  | ||||
| // objectList parses a list of items within an object (generally k/v pairs). | ||||
| // The parameter" obj" tells this whether to we are within an object (braces: | ||||
| // '{', '}') or just at the top level. If we're within an object, we end | ||||
| // at an RBRACE. | ||||
| func (p *Parser) objectList(obj bool) (*ast.ObjectList, error) { | ||||
| 	defer un(trace(p, "ParseObjectList")) | ||||
| 	node := &ast.ObjectList{} | ||||
|  | ||||
| 	for { | ||||
| 		if obj { | ||||
| 			tok := p.scan() | ||||
| 			p.unscan() | ||||
| 			if tok.Type == token.RBRACE { | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		n, err := p.objectItem() | ||||
| 		if err == errEofToken { | ||||
| 			break // we are finished | ||||
| 		} | ||||
|  | ||||
| 		// we don't return a nil node, because might want to use already | ||||
| 		// collected items. | ||||
| 		if err != nil { | ||||
| 			return node, err | ||||
| 		} | ||||
|  | ||||
| 		node.Add(n) | ||||
|  | ||||
| 		// object lists can be optionally comma-delimited e.g. when a list of maps | ||||
| 		// is being expressed, so a comma is allowed here - it's simply consumed | ||||
| 		tok := p.scan() | ||||
| 		if tok.Type != token.COMMA { | ||||
| 			p.unscan() | ||||
| 		} | ||||
| 	} | ||||
| 	return node, nil | ||||
| } | ||||
|  | ||||
| func (p *Parser) consumeComment() (comment *ast.Comment, endline int) { | ||||
| 	endline = p.tok.Pos.Line | ||||
|  | ||||
| 	// count the endline if it's multiline comment, ie starting with /* | ||||
| 	if len(p.tok.Text) > 1 && p.tok.Text[1] == '*' { | ||||
| 		// don't use range here - no need to decode Unicode code points | ||||
| 		for i := 0; i < len(p.tok.Text); i++ { | ||||
| 			if p.tok.Text[i] == '\n' { | ||||
| 				endline++ | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	comment = &ast.Comment{Start: p.tok.Pos, Text: p.tok.Text} | ||||
| 	p.tok = p.sc.Scan() | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func (p *Parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline int) { | ||||
| 	var list []*ast.Comment | ||||
| 	endline = p.tok.Pos.Line | ||||
|  | ||||
| 	for p.tok.Type == token.COMMENT && p.tok.Pos.Line <= endline+n { | ||||
| 		var comment *ast.Comment | ||||
| 		comment, endline = p.consumeComment() | ||||
| 		list = append(list, comment) | ||||
| 	} | ||||
|  | ||||
| 	// add comment group to the comments list | ||||
| 	comments = &ast.CommentGroup{List: list} | ||||
| 	p.comments = append(p.comments, comments) | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // objectItem parses a single object item | ||||
| func (p *Parser) objectItem() (*ast.ObjectItem, error) { | ||||
| 	defer un(trace(p, "ParseObjectItem")) | ||||
|  | ||||
| 	keys, err := p.objectKey() | ||||
| 	if len(keys) > 0 && err == errEofToken { | ||||
| 		// We ignore eof token here since it is an error if we didn't | ||||
| 		// receive a value (but we did receive a key) for the item. | ||||
| 		err = nil | ||||
| 	} | ||||
| 	if len(keys) > 0 && err != nil && p.tok.Type == token.RBRACE { | ||||
| 		// This is a strange boolean statement, but what it means is: | ||||
| 		// We have keys with no value, and we're likely in an object | ||||
| 		// (since RBrace ends an object). For this, we set err to nil so | ||||
| 		// we continue and get the error below of having the wrong value | ||||
| 		// type. | ||||
| 		err = nil | ||||
|  | ||||
| 		// Reset the token type so we don't think it completed fine. See | ||||
| 		// objectType which uses p.tok.Type to check if we're done with | ||||
| 		// the object. | ||||
| 		p.tok.Type = token.EOF | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	o := &ast.ObjectItem{ | ||||
| 		Keys: keys, | ||||
| 	} | ||||
|  | ||||
| 	if p.leadComment != nil { | ||||
| 		o.LeadComment = p.leadComment | ||||
| 		p.leadComment = nil | ||||
| 	} | ||||
|  | ||||
| 	switch p.tok.Type { | ||||
| 	case token.ASSIGN: | ||||
| 		o.Assign = p.tok.Pos | ||||
| 		o.Val, err = p.object() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	case token.LBRACE: | ||||
| 		o.Val, err = p.objectType() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	default: | ||||
| 		keyStr := make([]string, 0, len(keys)) | ||||
| 		for _, k := range keys { | ||||
| 			keyStr = append(keyStr, k.Token.Text) | ||||
| 		} | ||||
|  | ||||
| 		return nil, &PosError{ | ||||
| 			Pos: p.tok.Pos, | ||||
| 			Err: fmt.Errorf( | ||||
| 				"key '%s' expected start of object ('{') or assignment ('=')", | ||||
| 				strings.Join(keyStr, " ")), | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// key=#comment | ||||
| 	// val | ||||
| 	if p.lineComment != nil { | ||||
| 		o.LineComment, p.lineComment = p.lineComment, nil | ||||
| 	} | ||||
|  | ||||
| 	// do a look-ahead for line comment | ||||
| 	p.scan() | ||||
| 	if len(keys) > 0 && o.Val.Pos().Line == keys[0].Pos().Line && p.lineComment != nil { | ||||
| 		o.LineComment = p.lineComment | ||||
| 		p.lineComment = nil | ||||
| 	} | ||||
| 	p.unscan() | ||||
| 	return o, nil | ||||
| } | ||||
|  | ||||
| // objectKey parses an object key and returns a ObjectKey AST | ||||
| func (p *Parser) objectKey() ([]*ast.ObjectKey, error) { | ||||
| 	keyCount := 0 | ||||
| 	keys := make([]*ast.ObjectKey, 0) | ||||
|  | ||||
| 	for { | ||||
| 		tok := p.scan() | ||||
| 		switch tok.Type { | ||||
| 		case token.EOF: | ||||
| 			// It is very important to also return the keys here as well as | ||||
| 			// the error. This is because we need to be able to tell if we | ||||
| 			// did parse keys prior to finding the EOF, or if we just found | ||||
| 			// a bare EOF. | ||||
| 			return keys, errEofToken | ||||
| 		case token.ASSIGN: | ||||
| 			// assignment or object only, but not nested objects. this is not | ||||
| 			// allowed: `foo bar = {}` | ||||
| 			if keyCount > 1 { | ||||
| 				return nil, &PosError{ | ||||
| 					Pos: p.tok.Pos, | ||||
| 					Err: fmt.Errorf("nested object expected: LBRACE got: %s", p.tok.Type), | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if keyCount == 0 { | ||||
| 				return nil, &PosError{ | ||||
| 					Pos: p.tok.Pos, | ||||
| 					Err: errors.New("no object keys found!"), | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			return keys, nil | ||||
| 		case token.LBRACE: | ||||
| 			var err error | ||||
|  | ||||
| 			// If we have no keys, then it is a syntax error. i.e. {{}} is not | ||||
| 			// allowed. | ||||
| 			if len(keys) == 0 { | ||||
| 				err = &PosError{ | ||||
| 					Pos: p.tok.Pos, | ||||
| 					Err: fmt.Errorf("expected: IDENT | STRING got: %s", p.tok.Type), | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// object | ||||
| 			return keys, err | ||||
| 		case token.IDENT, token.STRING: | ||||
| 			keyCount++ | ||||
| 			keys = append(keys, &ast.ObjectKey{Token: p.tok}) | ||||
| 		case token.ILLEGAL: | ||||
| 			return keys, &PosError{ | ||||
| 				Pos: p.tok.Pos, | ||||
| 				Err: fmt.Errorf("illegal character"), | ||||
| 			} | ||||
| 		default: | ||||
| 			return keys, &PosError{ | ||||
| 				Pos: p.tok.Pos, | ||||
| 				Err: fmt.Errorf("expected: IDENT | STRING | ASSIGN | LBRACE got: %s", p.tok.Type), | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // object parses any type of object, such as number, bool, string, object or | ||||
| // list. | ||||
| func (p *Parser) object() (ast.Node, error) { | ||||
| 	defer un(trace(p, "ParseType")) | ||||
| 	tok := p.scan() | ||||
|  | ||||
| 	switch tok.Type { | ||||
| 	case token.NUMBER, token.FLOAT, token.BOOL, token.STRING, token.HEREDOC: | ||||
| 		return p.literalType() | ||||
| 	case token.LBRACE: | ||||
| 		return p.objectType() | ||||
| 	case token.LBRACK: | ||||
| 		return p.listType() | ||||
| 	case token.COMMENT: | ||||
| 		// implement comment | ||||
| 	case token.EOF: | ||||
| 		return nil, errEofToken | ||||
| 	} | ||||
|  | ||||
| 	return nil, &PosError{ | ||||
| 		Pos: tok.Pos, | ||||
| 		Err: fmt.Errorf("Unknown token: %+v", tok), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // objectType parses an object type and returns a ObjectType AST | ||||
| func (p *Parser) objectType() (*ast.ObjectType, error) { | ||||
| 	defer un(trace(p, "ParseObjectType")) | ||||
|  | ||||
| 	// we assume that the currently scanned token is a LBRACE | ||||
| 	o := &ast.ObjectType{ | ||||
| 		Lbrace: p.tok.Pos, | ||||
| 	} | ||||
|  | ||||
| 	l, err := p.objectList(true) | ||||
|  | ||||
| 	// if we hit RBRACE, we are good to go (means we parsed all Items), if it's | ||||
| 	// not a RBRACE, it's an syntax error and we just return it. | ||||
| 	if err != nil && p.tok.Type != token.RBRACE { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// No error, scan and expect the ending to be a brace | ||||
| 	if tok := p.scan(); tok.Type != token.RBRACE { | ||||
| 		return nil, &PosError{ | ||||
| 			Pos: tok.Pos, | ||||
| 			Err: fmt.Errorf("object expected closing RBRACE got: %s", tok.Type), | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	o.List = l | ||||
| 	o.Rbrace = p.tok.Pos // advanced via parseObjectList | ||||
| 	return o, nil | ||||
| } | ||||
|  | ||||
| // listType parses a list type and returns a ListType AST | ||||
| func (p *Parser) listType() (*ast.ListType, error) { | ||||
| 	defer un(trace(p, "ParseListType")) | ||||
|  | ||||
| 	// we assume that the currently scanned token is a LBRACK | ||||
| 	l := &ast.ListType{ | ||||
| 		Lbrack: p.tok.Pos, | ||||
| 	} | ||||
|  | ||||
| 	needComma := false | ||||
| 	for { | ||||
| 		tok := p.scan() | ||||
| 		if needComma { | ||||
| 			switch tok.Type { | ||||
| 			case token.COMMA, token.RBRACK: | ||||
| 			default: | ||||
| 				return nil, &PosError{ | ||||
| 					Pos: tok.Pos, | ||||
| 					Err: fmt.Errorf( | ||||
| 						"error parsing list, expected comma or list end, got: %s", | ||||
| 						tok.Type), | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		switch tok.Type { | ||||
| 		case token.BOOL, token.NUMBER, token.FLOAT, token.STRING, token.HEREDOC: | ||||
| 			node, err := p.literalType() | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			// If there is a lead comment, apply it | ||||
| 			if p.leadComment != nil { | ||||
| 				node.LeadComment = p.leadComment | ||||
| 				p.leadComment = nil | ||||
| 			} | ||||
|  | ||||
| 			l.Add(node) | ||||
| 			needComma = true | ||||
| 		case token.COMMA: | ||||
| 			// get next list item or we are at the end | ||||
| 			// do a look-ahead for line comment | ||||
| 			p.scan() | ||||
| 			if p.lineComment != nil && len(l.List) > 0 { | ||||
| 				lit, ok := l.List[len(l.List)-1].(*ast.LiteralType) | ||||
| 				if ok { | ||||
| 					lit.LineComment = p.lineComment | ||||
| 					l.List[len(l.List)-1] = lit | ||||
| 					p.lineComment = nil | ||||
| 				} | ||||
| 			} | ||||
| 			p.unscan() | ||||
|  | ||||
| 			needComma = false | ||||
| 			continue | ||||
| 		case token.LBRACE: | ||||
| 			// Looks like a nested object, so parse it out | ||||
| 			node, err := p.objectType() | ||||
| 			if err != nil { | ||||
| 				return nil, &PosError{ | ||||
| 					Pos: tok.Pos, | ||||
| 					Err: fmt.Errorf( | ||||
| 						"error while trying to parse object within list: %s", err), | ||||
| 				} | ||||
| 			} | ||||
| 			l.Add(node) | ||||
| 			needComma = true | ||||
| 		case token.LBRACK: | ||||
| 			node, err := p.listType() | ||||
| 			if err != nil { | ||||
| 				return nil, &PosError{ | ||||
| 					Pos: tok.Pos, | ||||
| 					Err: fmt.Errorf( | ||||
| 						"error while trying to parse list within list: %s", err), | ||||
| 				} | ||||
| 			} | ||||
| 			l.Add(node) | ||||
| 		case token.RBRACK: | ||||
| 			// finished | ||||
| 			l.Rbrack = p.tok.Pos | ||||
| 			return l, nil | ||||
| 		default: | ||||
| 			return nil, &PosError{ | ||||
| 				Pos: tok.Pos, | ||||
| 				Err: fmt.Errorf("unexpected token while parsing list: %s", tok.Type), | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // literalType parses a literal type and returns a LiteralType AST | ||||
| func (p *Parser) literalType() (*ast.LiteralType, error) { | ||||
| 	defer un(trace(p, "ParseLiteral")) | ||||
|  | ||||
| 	return &ast.LiteralType{ | ||||
| 		Token: p.tok, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // scan returns the next token from the underlying scanner. If a token has | ||||
| // been unscanned then read that instead. In the process, it collects any | ||||
| // comment groups encountered, and remembers the last lead and line comments. | ||||
| func (p *Parser) scan() token.Token { | ||||
| 	// If we have a token on the buffer, then return it. | ||||
| 	if p.n != 0 { | ||||
| 		p.n = 0 | ||||
| 		return p.tok | ||||
| 	} | ||||
|  | ||||
| 	// Otherwise read the next token from the scanner and Save it to the buffer | ||||
| 	// in case we unscan later. | ||||
| 	prev := p.tok | ||||
| 	p.tok = p.sc.Scan() | ||||
|  | ||||
| 	if p.tok.Type == token.COMMENT { | ||||
| 		var comment *ast.CommentGroup | ||||
| 		var endline int | ||||
|  | ||||
| 		// fmt.Printf("p.tok.Pos.Line = %+v prev: %d endline %d \n", | ||||
| 		// p.tok.Pos.Line, prev.Pos.Line, endline) | ||||
| 		if p.tok.Pos.Line == prev.Pos.Line { | ||||
| 			// The comment is on same line as the previous token; it | ||||
| 			// cannot be a lead comment but may be a line comment. | ||||
| 			comment, endline = p.consumeCommentGroup(0) | ||||
| 			if p.tok.Pos.Line != endline { | ||||
| 				// The next token is on a different line, thus | ||||
| 				// the last comment group is a line comment. | ||||
| 				p.lineComment = comment | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// consume successor comments, if any | ||||
| 		endline = -1 | ||||
| 		for p.tok.Type == token.COMMENT { | ||||
| 			comment, endline = p.consumeCommentGroup(1) | ||||
| 		} | ||||
|  | ||||
| 		if endline+1 == p.tok.Pos.Line && p.tok.Type != token.RBRACE { | ||||
| 			switch p.tok.Type { | ||||
| 			case token.RBRACE, token.RBRACK: | ||||
| 				// Do not count for these cases | ||||
| 			default: | ||||
| 				// The next token is following on the line immediately after the | ||||
| 				// comment group, thus the last comment group is a lead comment. | ||||
| 				p.leadComment = comment | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	return p.tok | ||||
| } | ||||
|  | ||||
| // unscan pushes the previously read token back onto the buffer. | ||||
| func (p *Parser) unscan() { | ||||
| 	p.n = 1 | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
| // Parsing support | ||||
|  | ||||
| func (p *Parser) printTrace(a ...interface{}) { | ||||
| 	if !p.enableTrace { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " | ||||
| 	const n = len(dots) | ||||
| 	fmt.Printf("%5d:%3d: ", p.tok.Pos.Line, p.tok.Pos.Column) | ||||
|  | ||||
| 	i := 2 * p.indent | ||||
| 	for i > n { | ||||
| 		fmt.Print(dots) | ||||
| 		i -= n | ||||
| 	} | ||||
| 	// i <= n | ||||
| 	fmt.Print(dots[0:i]) | ||||
| 	fmt.Println(a...) | ||||
| } | ||||
|  | ||||
| func trace(p *Parser, msg string) *Parser { | ||||
| 	p.printTrace(msg, "(") | ||||
| 	p.indent++ | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| // Usage pattern: defer un(trace(p, "...")) | ||||
| func un(p *Parser) { | ||||
| 	p.indent-- | ||||
| 	p.printTrace(")") | ||||
| } | ||||
							
								
								
									
										789
									
								
								vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										789
									
								
								vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,789 @@ | ||||
| package printer | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"sort" | ||||
|  | ||||
| 	"github.com/hashicorp/hcl/hcl/ast" | ||||
| 	"github.com/hashicorp/hcl/hcl/token" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	blank    = byte(' ') | ||||
| 	newline  = byte('\n') | ||||
| 	tab      = byte('\t') | ||||
| 	infinity = 1 << 30 // offset or line | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	unindent = []byte("\uE123") // in the private use space | ||||
| ) | ||||
|  | ||||
| type printer struct { | ||||
| 	cfg  Config | ||||
| 	prev token.Pos | ||||
|  | ||||
| 	comments           []*ast.CommentGroup // may be nil, contains all comments | ||||
| 	standaloneComments []*ast.CommentGroup // contains all standalone comments (not assigned to any node) | ||||
|  | ||||
| 	enableTrace bool | ||||
| 	indentTrace int | ||||
| } | ||||
|  | ||||
| type ByPosition []*ast.CommentGroup | ||||
|  | ||||
| func (b ByPosition) Len() int           { return len(b) } | ||||
| func (b ByPosition) Swap(i, j int)      { b[i], b[j] = b[j], b[i] } | ||||
| func (b ByPosition) Less(i, j int) bool { return b[i].Pos().Before(b[j].Pos()) } | ||||
|  | ||||
| // collectComments comments all standalone comments which are not lead or line | ||||
| // comment | ||||
| func (p *printer) collectComments(node ast.Node) { | ||||
| 	// first collect all comments. This is already stored in | ||||
| 	// ast.File.(comments) | ||||
| 	ast.Walk(node, func(nn ast.Node) (ast.Node, bool) { | ||||
| 		switch t := nn.(type) { | ||||
| 		case *ast.File: | ||||
| 			p.comments = t.Comments | ||||
| 			return nn, false | ||||
| 		} | ||||
| 		return nn, true | ||||
| 	}) | ||||
|  | ||||
| 	standaloneComments := make(map[token.Pos]*ast.CommentGroup, 0) | ||||
| 	for _, c := range p.comments { | ||||
| 		standaloneComments[c.Pos()] = c | ||||
| 	} | ||||
|  | ||||
| 	// next remove all lead and line comments from the overall comment map. | ||||
| 	// This will give us comments which are standalone, comments which are not | ||||
| 	// assigned to any kind of node. | ||||
| 	ast.Walk(node, func(nn ast.Node) (ast.Node, bool) { | ||||
| 		switch t := nn.(type) { | ||||
| 		case *ast.LiteralType: | ||||
| 			if t.LeadComment != nil { | ||||
| 				for _, comment := range t.LeadComment.List { | ||||
| 					if _, ok := standaloneComments[comment.Pos()]; ok { | ||||
| 						delete(standaloneComments, comment.Pos()) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if t.LineComment != nil { | ||||
| 				for _, comment := range t.LineComment.List { | ||||
| 					if _, ok := standaloneComments[comment.Pos()]; ok { | ||||
| 						delete(standaloneComments, comment.Pos()) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		case *ast.ObjectItem: | ||||
| 			if t.LeadComment != nil { | ||||
| 				for _, comment := range t.LeadComment.List { | ||||
| 					if _, ok := standaloneComments[comment.Pos()]; ok { | ||||
| 						delete(standaloneComments, comment.Pos()) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if t.LineComment != nil { | ||||
| 				for _, comment := range t.LineComment.List { | ||||
| 					if _, ok := standaloneComments[comment.Pos()]; ok { | ||||
| 						delete(standaloneComments, comment.Pos()) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return nn, true | ||||
| 	}) | ||||
|  | ||||
| 	for _, c := range standaloneComments { | ||||
| 		p.standaloneComments = append(p.standaloneComments, c) | ||||
| 	} | ||||
|  | ||||
| 	sort.Sort(ByPosition(p.standaloneComments)) | ||||
| } | ||||
|  | ||||
| // output prints creates b printable HCL output and returns it. | ||||
| func (p *printer) output(n interface{}) []byte { | ||||
| 	var buf bytes.Buffer | ||||
|  | ||||
| 	switch t := n.(type) { | ||||
| 	case *ast.File: | ||||
| 		// File doesn't trace so we add the tracing here | ||||
| 		defer un(trace(p, "File")) | ||||
| 		return p.output(t.Node) | ||||
| 	case *ast.ObjectList: | ||||
| 		defer un(trace(p, "ObjectList")) | ||||
|  | ||||
| 		var index int | ||||
| 		for { | ||||
| 			// Determine the location of the next actual non-comment | ||||
| 			// item. If we're at the end, the next item is at "infinity" | ||||
| 			var nextItem token.Pos | ||||
| 			if index != len(t.Items) { | ||||
| 				nextItem = t.Items[index].Pos() | ||||
| 			} else { | ||||
| 				nextItem = token.Pos{Offset: infinity, Line: infinity} | ||||
| 			} | ||||
|  | ||||
| 			// Go through the standalone comments in the file and print out | ||||
| 			// the comments that we should be for this object item. | ||||
| 			for _, c := range p.standaloneComments { | ||||
| 				// Go through all the comments in the group. The group | ||||
| 				// should be printed together, not separated by double newlines. | ||||
| 				printed := false | ||||
| 				newlinePrinted := false | ||||
| 				for _, comment := range c.List { | ||||
| 					// We only care about comments after the previous item | ||||
| 					// we've printed so that comments are printed in the | ||||
| 					// correct locations (between two objects for example). | ||||
| 					// And before the next item. | ||||
| 					if comment.Pos().After(p.prev) && comment.Pos().Before(nextItem) { | ||||
| 						// if we hit the end add newlines so we can print the comment | ||||
| 						// we don't do this if prev is invalid which means the | ||||
| 						// beginning of the file since the first comment should | ||||
| 						// be at the first line. | ||||
| 						if !newlinePrinted && p.prev.IsValid() && index == len(t.Items) { | ||||
| 							buf.Write([]byte{newline, newline}) | ||||
| 							newlinePrinted = true | ||||
| 						} | ||||
|  | ||||
| 						// Write the actual comment. | ||||
| 						buf.WriteString(comment.Text) | ||||
| 						buf.WriteByte(newline) | ||||
|  | ||||
| 						// Set printed to true to note that we printed something | ||||
| 						printed = true | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				// If we're not at the last item, write a new line so | ||||
| 				// that there is a newline separating this comment from | ||||
| 				// the next object. | ||||
| 				if printed && index != len(t.Items) { | ||||
| 					buf.WriteByte(newline) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if index == len(t.Items) { | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			buf.Write(p.output(t.Items[index])) | ||||
| 			if index != len(t.Items)-1 { | ||||
| 				// Always write a newline to separate us from the next item | ||||
| 				buf.WriteByte(newline) | ||||
|  | ||||
| 				// Need to determine if we're going to separate the next item | ||||
| 				// with a blank line. The logic here is simple, though there | ||||
| 				// are a few conditions: | ||||
| 				// | ||||
| 				//   1. The next object is more than one line away anyways, | ||||
| 				//      so we need an empty line. | ||||
| 				// | ||||
| 				//   2. The next object is not a "single line" object, so | ||||
| 				//      we need an empty line. | ||||
| 				// | ||||
| 				//   3. This current object is not a single line object, | ||||
| 				//      so we need an empty line. | ||||
| 				current := t.Items[index] | ||||
| 				next := t.Items[index+1] | ||||
| 				if next.Pos().Line != t.Items[index].Pos().Line+1 || | ||||
| 					!p.isSingleLineObject(next) || | ||||
| 					!p.isSingleLineObject(current) { | ||||
| 					buf.WriteByte(newline) | ||||
| 				} | ||||
| 			} | ||||
| 			index++ | ||||
| 		} | ||||
| 	case *ast.ObjectKey: | ||||
| 		buf.WriteString(t.Token.Text) | ||||
| 	case *ast.ObjectItem: | ||||
| 		p.prev = t.Pos() | ||||
| 		buf.Write(p.objectItem(t)) | ||||
| 	case *ast.LiteralType: | ||||
| 		buf.Write(p.literalType(t)) | ||||
| 	case *ast.ListType: | ||||
| 		buf.Write(p.list(t)) | ||||
| 	case *ast.ObjectType: | ||||
| 		buf.Write(p.objectType(t)) | ||||
| 	default: | ||||
| 		fmt.Printf(" unknown type: %T\n", n) | ||||
| 	} | ||||
|  | ||||
| 	return buf.Bytes() | ||||
| } | ||||
|  | ||||
| func (p *printer) literalType(lit *ast.LiteralType) []byte { | ||||
| 	result := []byte(lit.Token.Text) | ||||
| 	switch lit.Token.Type { | ||||
| 	case token.HEREDOC: | ||||
| 		// Clear the trailing newline from heredocs | ||||
| 		if result[len(result)-1] == '\n' { | ||||
| 			result = result[:len(result)-1] | ||||
| 		} | ||||
|  | ||||
| 		// Poison lines 2+ so that we don't indent them | ||||
| 		result = p.heredocIndent(result) | ||||
| 	case token.STRING: | ||||
| 		// If this is a multiline string, poison lines 2+ so we don't | ||||
| 		// indent them. | ||||
| 		if bytes.IndexRune(result, '\n') >= 0 { | ||||
| 			result = p.heredocIndent(result) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| // objectItem returns the printable HCL form of an object item. An object type | ||||
| // starts with one/multiple keys and has a value. The value might be of any | ||||
| // type. | ||||
| func (p *printer) objectItem(o *ast.ObjectItem) []byte { | ||||
| 	defer un(trace(p, fmt.Sprintf("ObjectItem: %s", o.Keys[0].Token.Text))) | ||||
| 	var buf bytes.Buffer | ||||
|  | ||||
| 	if o.LeadComment != nil { | ||||
| 		for _, comment := range o.LeadComment.List { | ||||
| 			buf.WriteString(comment.Text) | ||||
| 			buf.WriteByte(newline) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// If key and val are on different lines, treat line comments like lead comments. | ||||
| 	if o.LineComment != nil && o.Val.Pos().Line != o.Keys[0].Pos().Line { | ||||
| 		for _, comment := range o.LineComment.List { | ||||
| 			buf.WriteString(comment.Text) | ||||
| 			buf.WriteByte(newline) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for i, k := range o.Keys { | ||||
| 		buf.WriteString(k.Token.Text) | ||||
| 		buf.WriteByte(blank) | ||||
|  | ||||
| 		// reach end of key | ||||
| 		if o.Assign.IsValid() && i == len(o.Keys)-1 && len(o.Keys) == 1 { | ||||
| 			buf.WriteString("=") | ||||
| 			buf.WriteByte(blank) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	buf.Write(p.output(o.Val)) | ||||
|  | ||||
| 	if o.LineComment != nil && o.Val.Pos().Line == o.Keys[0].Pos().Line { | ||||
| 		buf.WriteByte(blank) | ||||
| 		for _, comment := range o.LineComment.List { | ||||
| 			buf.WriteString(comment.Text) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return buf.Bytes() | ||||
| } | ||||
|  | ||||
| // objectType returns the printable HCL form of an object type. An object type | ||||
| // begins with a brace and ends with a brace. | ||||
| func (p *printer) objectType(o *ast.ObjectType) []byte { | ||||
| 	defer un(trace(p, "ObjectType")) | ||||
| 	var buf bytes.Buffer | ||||
| 	buf.WriteString("{") | ||||
|  | ||||
| 	var index int | ||||
| 	var nextItem token.Pos | ||||
| 	var commented, newlinePrinted bool | ||||
| 	for { | ||||
| 		// Determine the location of the next actual non-comment | ||||
| 		// item. If we're at the end, the next item is the closing brace | ||||
| 		if index != len(o.List.Items) { | ||||
| 			nextItem = o.List.Items[index].Pos() | ||||
| 		} else { | ||||
| 			nextItem = o.Rbrace | ||||
| 		} | ||||
|  | ||||
| 		// Go through the standalone comments in the file and print out | ||||
| 		// the comments that we should be for this object item. | ||||
| 		for _, c := range p.standaloneComments { | ||||
| 			printed := false | ||||
| 			var lastCommentPos token.Pos | ||||
| 			for _, comment := range c.List { | ||||
| 				// We only care about comments after the previous item | ||||
| 				// we've printed so that comments are printed in the | ||||
| 				// correct locations (between two objects for example). | ||||
| 				// And before the next item. | ||||
| 				if comment.Pos().After(p.prev) && comment.Pos().Before(nextItem) { | ||||
| 					// If there are standalone comments and the initial newline has not | ||||
| 					// been printed yet, do it now. | ||||
| 					if !newlinePrinted { | ||||
| 						newlinePrinted = true | ||||
| 						buf.WriteByte(newline) | ||||
| 					} | ||||
|  | ||||
| 					// add newline if it's between other printed nodes | ||||
| 					if index > 0 { | ||||
| 						commented = true | ||||
| 						buf.WriteByte(newline) | ||||
| 					} | ||||
|  | ||||
| 					// Store this position | ||||
| 					lastCommentPos = comment.Pos() | ||||
|  | ||||
| 					// output the comment itself | ||||
| 					buf.Write(p.indent(p.heredocIndent([]byte(comment.Text)))) | ||||
|  | ||||
| 					// Set printed to true to note that we printed something | ||||
| 					printed = true | ||||
|  | ||||
| 					/* | ||||
| 						if index != len(o.List.Items) { | ||||
| 							buf.WriteByte(newline) // do not print on the end | ||||
| 						} | ||||
| 					*/ | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// Stuff to do if we had comments | ||||
| 			if printed { | ||||
| 				// Always write a newline | ||||
| 				buf.WriteByte(newline) | ||||
|  | ||||
| 				// If there is another item in the object and our comment | ||||
| 				// didn't hug it directly, then make sure there is a blank | ||||
| 				// line separating them. | ||||
| 				if nextItem != o.Rbrace && nextItem.Line != lastCommentPos.Line+1 { | ||||
| 					buf.WriteByte(newline) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if index == len(o.List.Items) { | ||||
| 			p.prev = o.Rbrace | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		// At this point we are sure that it's not a totally empty block: print | ||||
| 		// the initial newline if it hasn't been printed yet by the previous | ||||
| 		// block about standalone comments. | ||||
| 		if !newlinePrinted { | ||||
| 			buf.WriteByte(newline) | ||||
| 			newlinePrinted = true | ||||
| 		} | ||||
|  | ||||
| 		// check if we have adjacent one liner items. If yes we'll going to align | ||||
| 		// the comments. | ||||
| 		var aligned []*ast.ObjectItem | ||||
| 		for _, item := range o.List.Items[index:] { | ||||
| 			// we don't group one line lists | ||||
| 			if len(o.List.Items) == 1 { | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			// one means a oneliner with out any lead comment | ||||
| 			// two means a oneliner with lead comment | ||||
| 			// anything else might be something else | ||||
| 			cur := lines(string(p.objectItem(item))) | ||||
| 			if cur > 2 { | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			curPos := item.Pos() | ||||
|  | ||||
| 			nextPos := token.Pos{} | ||||
| 			if index != len(o.List.Items)-1 { | ||||
| 				nextPos = o.List.Items[index+1].Pos() | ||||
| 			} | ||||
|  | ||||
| 			prevPos := token.Pos{} | ||||
| 			if index != 0 { | ||||
| 				prevPos = o.List.Items[index-1].Pos() | ||||
| 			} | ||||
|  | ||||
| 			// fmt.Println("DEBUG ----------------") | ||||
| 			// fmt.Printf("prev = %+v prevPos: %s\n", prev, prevPos) | ||||
| 			// fmt.Printf("cur = %+v curPos: %s\n", cur, curPos) | ||||
| 			// fmt.Printf("next = %+v nextPos: %s\n", next, nextPos) | ||||
|  | ||||
| 			if curPos.Line+1 == nextPos.Line { | ||||
| 				aligned = append(aligned, item) | ||||
| 				index++ | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			if curPos.Line-1 == prevPos.Line { | ||||
| 				aligned = append(aligned, item) | ||||
| 				index++ | ||||
|  | ||||
| 				// finish if we have a new line or comment next. This happens | ||||
| 				// if the next item is not adjacent | ||||
| 				if curPos.Line+1 != nextPos.Line { | ||||
| 					break | ||||
| 				} | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		// put newlines if the items are between other non aligned items. | ||||
| 		// newlines are also added if there is a standalone comment already, so | ||||
| 		// check it too | ||||
| 		if !commented && index != len(aligned) { | ||||
| 			buf.WriteByte(newline) | ||||
| 		} | ||||
|  | ||||
| 		if len(aligned) >= 1 { | ||||
| 			p.prev = aligned[len(aligned)-1].Pos() | ||||
|  | ||||
| 			items := p.alignedItems(aligned) | ||||
| 			buf.Write(p.indent(items)) | ||||
| 		} else { | ||||
| 			p.prev = o.List.Items[index].Pos() | ||||
|  | ||||
| 			buf.Write(p.indent(p.objectItem(o.List.Items[index]))) | ||||
| 			index++ | ||||
| 		} | ||||
|  | ||||
| 		buf.WriteByte(newline) | ||||
| 	} | ||||
|  | ||||
| 	buf.WriteString("}") | ||||
| 	return buf.Bytes() | ||||
| } | ||||
|  | ||||
| func (p *printer) alignedItems(items []*ast.ObjectItem) []byte { | ||||
| 	var buf bytes.Buffer | ||||
|  | ||||
| 	// find the longest key and value length, needed for alignment | ||||
| 	var longestKeyLen int // longest key length | ||||
| 	var longestValLen int // longest value length | ||||
| 	for _, item := range items { | ||||
| 		key := len(item.Keys[0].Token.Text) | ||||
| 		val := len(p.output(item.Val)) | ||||
|  | ||||
| 		if key > longestKeyLen { | ||||
| 			longestKeyLen = key | ||||
| 		} | ||||
|  | ||||
| 		if val > longestValLen { | ||||
| 			longestValLen = val | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for i, item := range items { | ||||
| 		if item.LeadComment != nil { | ||||
| 			for _, comment := range item.LeadComment.List { | ||||
| 				buf.WriteString(comment.Text) | ||||
| 				buf.WriteByte(newline) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		for i, k := range item.Keys { | ||||
| 			keyLen := len(k.Token.Text) | ||||
| 			buf.WriteString(k.Token.Text) | ||||
| 			for i := 0; i < longestKeyLen-keyLen+1; i++ { | ||||
| 				buf.WriteByte(blank) | ||||
| 			} | ||||
|  | ||||
| 			// reach end of key | ||||
| 			if i == len(item.Keys)-1 && len(item.Keys) == 1 { | ||||
| 				buf.WriteString("=") | ||||
| 				buf.WriteByte(blank) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		val := p.output(item.Val) | ||||
| 		valLen := len(val) | ||||
| 		buf.Write(val) | ||||
|  | ||||
| 		if item.Val.Pos().Line == item.Keys[0].Pos().Line && item.LineComment != nil { | ||||
| 			for i := 0; i < longestValLen-valLen+1; i++ { | ||||
| 				buf.WriteByte(blank) | ||||
| 			} | ||||
|  | ||||
| 			for _, comment := range item.LineComment.List { | ||||
| 				buf.WriteString(comment.Text) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// do not print for the last item | ||||
| 		if i != len(items)-1 { | ||||
| 			buf.WriteByte(newline) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return buf.Bytes() | ||||
| } | ||||
|  | ||||
| // list returns the printable HCL form of an list type. | ||||
| func (p *printer) list(l *ast.ListType) []byte { | ||||
| 	if p.isSingleLineList(l) { | ||||
| 		return p.singleLineList(l) | ||||
| 	} | ||||
|  | ||||
| 	var buf bytes.Buffer | ||||
| 	buf.WriteString("[") | ||||
| 	buf.WriteByte(newline) | ||||
|  | ||||
| 	var longestLine int | ||||
| 	for _, item := range l.List { | ||||
| 		// for now we assume that the list only contains literal types | ||||
| 		if lit, ok := item.(*ast.LiteralType); ok { | ||||
| 			lineLen := len(lit.Token.Text) | ||||
| 			if lineLen > longestLine { | ||||
| 				longestLine = lineLen | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	haveEmptyLine := false | ||||
| 	for i, item := range l.List { | ||||
| 		// If we have a lead comment, then we want to write that first | ||||
| 		leadComment := false | ||||
| 		if lit, ok := item.(*ast.LiteralType); ok && lit.LeadComment != nil { | ||||
| 			leadComment = true | ||||
|  | ||||
| 			// Ensure an empty line before every element with a | ||||
| 			// lead comment (except the first item in a list). | ||||
| 			if !haveEmptyLine && i != 0 { | ||||
| 				buf.WriteByte(newline) | ||||
| 			} | ||||
|  | ||||
| 			for _, comment := range lit.LeadComment.List { | ||||
| 				buf.Write(p.indent([]byte(comment.Text))) | ||||
| 				buf.WriteByte(newline) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// also indent each line | ||||
| 		val := p.output(item) | ||||
| 		curLen := len(val) | ||||
| 		buf.Write(p.indent(val)) | ||||
|  | ||||
| 		// if this item is a heredoc, then we output the comma on | ||||
| 		// the next line. This is the only case this happens. | ||||
| 		comma := []byte{','} | ||||
| 		if lit, ok := item.(*ast.LiteralType); ok && lit.Token.Type == token.HEREDOC { | ||||
| 			buf.WriteByte(newline) | ||||
| 			comma = p.indent(comma) | ||||
| 		} | ||||
|  | ||||
| 		buf.Write(comma) | ||||
|  | ||||
| 		if lit, ok := item.(*ast.LiteralType); ok && lit.LineComment != nil { | ||||
| 			// if the next item doesn't have any comments, do not align | ||||
| 			buf.WriteByte(blank) // align one space | ||||
| 			for i := 0; i < longestLine-curLen; i++ { | ||||
| 				buf.WriteByte(blank) | ||||
| 			} | ||||
|  | ||||
| 			for _, comment := range lit.LineComment.List { | ||||
| 				buf.WriteString(comment.Text) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		buf.WriteByte(newline) | ||||
|  | ||||
| 		// Ensure an empty line after every element with a | ||||
| 		// lead comment (except the first item in a list). | ||||
| 		haveEmptyLine = leadComment && i != len(l.List)-1 | ||||
| 		if haveEmptyLine { | ||||
| 			buf.WriteByte(newline) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	buf.WriteString("]") | ||||
| 	return buf.Bytes() | ||||
| } | ||||
|  | ||||
| // isSingleLineList returns true if: | ||||
| // * they were previously formatted entirely on one line | ||||
| // * they consist entirely of literals | ||||
| // * there are either no heredoc strings or the list has exactly one element | ||||
| // * there are no line comments | ||||
| func (printer) isSingleLineList(l *ast.ListType) bool { | ||||
| 	for _, item := range l.List { | ||||
| 		if item.Pos().Line != l.Lbrack.Line { | ||||
| 			return false | ||||
| 		} | ||||
|  | ||||
| 		lit, ok := item.(*ast.LiteralType) | ||||
| 		if !ok { | ||||
| 			return false | ||||
| 		} | ||||
|  | ||||
| 		if lit.Token.Type == token.HEREDOC && len(l.List) != 1 { | ||||
| 			return false | ||||
| 		} | ||||
|  | ||||
| 		if lit.LineComment != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| // singleLineList prints a simple single line list. | ||||
| // For a definition of "simple", see isSingleLineList above. | ||||
| func (p *printer) singleLineList(l *ast.ListType) []byte { | ||||
| 	buf := &bytes.Buffer{} | ||||
|  | ||||
| 	buf.WriteString("[") | ||||
| 	for i, item := range l.List { | ||||
| 		if i != 0 { | ||||
| 			buf.WriteString(", ") | ||||
| 		} | ||||
|  | ||||
| 		// Output the item itself | ||||
| 		buf.Write(p.output(item)) | ||||
|  | ||||
| 		// The heredoc marker needs to be at the end of line. | ||||
| 		if lit, ok := item.(*ast.LiteralType); ok && lit.Token.Type == token.HEREDOC { | ||||
| 			buf.WriteByte(newline) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	buf.WriteString("]") | ||||
| 	return buf.Bytes() | ||||
| } | ||||
|  | ||||
| // indent indents the lines of the given buffer for each non-empty line | ||||
| func (p *printer) indent(buf []byte) []byte { | ||||
| 	var prefix []byte | ||||
| 	if p.cfg.SpacesWidth != 0 { | ||||
| 		for i := 0; i < p.cfg.SpacesWidth; i++ { | ||||
| 			prefix = append(prefix, blank) | ||||
| 		} | ||||
| 	} else { | ||||
| 		prefix = []byte{tab} | ||||
| 	} | ||||
|  | ||||
| 	var res []byte | ||||
| 	bol := true | ||||
| 	for _, c := range buf { | ||||
| 		if bol && c != '\n' { | ||||
| 			res = append(res, prefix...) | ||||
| 		} | ||||
|  | ||||
| 		res = append(res, c) | ||||
| 		bol = c == '\n' | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
|  | ||||
| // unindent removes all the indentation from the tombstoned lines | ||||
| func (p *printer) unindent(buf []byte) []byte { | ||||
| 	var res []byte | ||||
| 	for i := 0; i < len(buf); i++ { | ||||
| 		skip := len(buf)-i <= len(unindent) | ||||
| 		if !skip { | ||||
| 			skip = !bytes.Equal(unindent, buf[i:i+len(unindent)]) | ||||
| 		} | ||||
| 		if skip { | ||||
| 			res = append(res, buf[i]) | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// We have a marker. we have to backtrace here and clean out | ||||
| 		// any whitespace ahead of our tombstone up to a \n | ||||
| 		for j := len(res) - 1; j >= 0; j-- { | ||||
| 			if res[j] == '\n' { | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			res = res[:j] | ||||
| 		} | ||||
|  | ||||
| 		// Skip the entire unindent marker | ||||
| 		i += len(unindent) - 1 | ||||
| 	} | ||||
|  | ||||
| 	return res | ||||
| } | ||||
|  | ||||
| // heredocIndent marks all the 2nd and further lines as unindentable | ||||
| func (p *printer) heredocIndent(buf []byte) []byte { | ||||
| 	var res []byte | ||||
| 	bol := false | ||||
| 	for _, c := range buf { | ||||
| 		if bol && c != '\n' { | ||||
| 			res = append(res, unindent...) | ||||
| 		} | ||||
| 		res = append(res, c) | ||||
| 		bol = c == '\n' | ||||
| 	} | ||||
| 	return res | ||||
| } | ||||
|  | ||||
| // isSingleLineObject tells whether the given object item is a single | ||||
| // line object such as "obj {}". | ||||
| // | ||||
| // A single line object: | ||||
| // | ||||
| //   * has no lead comments (hence multi-line) | ||||
| //   * has no assignment | ||||
| //   * has no values in the stanza (within {}) | ||||
| // | ||||
| func (p *printer) isSingleLineObject(val *ast.ObjectItem) bool { | ||||
| 	// If there is a lead comment, can't be one line | ||||
| 	if val.LeadComment != nil { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	// If there is assignment, we always break by line | ||||
| 	if val.Assign.IsValid() { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	// If it isn't an object type, then its not a single line object | ||||
| 	ot, ok := val.Val.(*ast.ObjectType) | ||||
| 	if !ok { | ||||
| 		return false | ||||
| 	} | ||||
|  | ||||
| 	// If the object has no items, it is single line! | ||||
| 	return len(ot.List.Items) == 0 | ||||
| } | ||||
|  | ||||
| func lines(txt string) int { | ||||
| 	endline := 1 | ||||
| 	for i := 0; i < len(txt); i++ { | ||||
| 		if txt[i] == '\n' { | ||||
| 			endline++ | ||||
| 		} | ||||
| 	} | ||||
| 	return endline | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
| // Tracing support | ||||
|  | ||||
| func (p *printer) printTrace(a ...interface{}) { | ||||
| 	if !p.enableTrace { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " | ||||
| 	const n = len(dots) | ||||
| 	i := 2 * p.indentTrace | ||||
| 	for i > n { | ||||
| 		fmt.Print(dots) | ||||
| 		i -= n | ||||
| 	} | ||||
| 	// i <= n | ||||
| 	fmt.Print(dots[0:i]) | ||||
| 	fmt.Println(a...) | ||||
| } | ||||
|  | ||||
| func trace(p *printer, msg string) *printer { | ||||
| 	p.printTrace(msg, "(") | ||||
| 	p.indentTrace++ | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| // Usage pattern: defer un(trace(p, "...")) | ||||
| func un(p *printer) { | ||||
| 	p.indentTrace-- | ||||
| 	p.printTrace(")") | ||||
| } | ||||
							
								
								
									
										66
									
								
								vendor/github.com/hashicorp/hcl/hcl/printer/printer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/hashicorp/hcl/hcl/printer/printer.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| // Package printer implements printing of AST nodes to HCL format. | ||||
| package printer | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"io" | ||||
| 	"text/tabwriter" | ||||
|  | ||||
| 	"github.com/hashicorp/hcl/hcl/ast" | ||||
| 	"github.com/hashicorp/hcl/hcl/parser" | ||||
| ) | ||||
|  | ||||
| var DefaultConfig = Config{ | ||||
| 	SpacesWidth: 2, | ||||
| } | ||||
|  | ||||
| // A Config node controls the output of Fprint. | ||||
| type Config struct { | ||||
| 	SpacesWidth int // if set, it will use spaces instead of tabs for alignment | ||||
| } | ||||
|  | ||||
| func (c *Config) Fprint(output io.Writer, node ast.Node) error { | ||||
| 	p := &printer{ | ||||
| 		cfg:                *c, | ||||
| 		comments:           make([]*ast.CommentGroup, 0), | ||||
| 		standaloneComments: make([]*ast.CommentGroup, 0), | ||||
| 		// enableTrace:        true, | ||||
| 	} | ||||
|  | ||||
| 	p.collectComments(node) | ||||
|  | ||||
| 	if _, err := output.Write(p.unindent(p.output(node))); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	// flush tabwriter, if any | ||||
| 	var err error | ||||
| 	if tw, _ := output.(*tabwriter.Writer); tw != nil { | ||||
| 		err = tw.Flush() | ||||
| 	} | ||||
|  | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // Fprint "pretty-prints" an HCL node to output | ||||
| // It calls Config.Fprint with default settings. | ||||
| func Fprint(output io.Writer, node ast.Node) error { | ||||
| 	return DefaultConfig.Fprint(output, node) | ||||
| } | ||||
|  | ||||
| // Format formats src HCL and returns the result. | ||||
| func Format(src []byte) ([]byte, error) { | ||||
| 	node, err := parser.Parse(src) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var buf bytes.Buffer | ||||
| 	if err := DefaultConfig.Fprint(&buf, node); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// Add trailing newline to result | ||||
| 	buf.WriteString("\n") | ||||
| 	return buf.Bytes(), nil | ||||
| } | ||||
							
								
								
									
										652
									
								
								vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										652
									
								
								vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,652 @@ | ||||
| // Package scanner implements a scanner for HCL (HashiCorp Configuration | ||||
| // Language) source text. | ||||
| package scanner | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
| 	"unicode" | ||||
| 	"unicode/utf8" | ||||
|  | ||||
| 	"github.com/hashicorp/hcl/hcl/token" | ||||
| ) | ||||
|  | ||||
| // eof represents a marker rune for the end of the reader. | ||||
| const eof = rune(0) | ||||
|  | ||||
| // Scanner defines a lexical scanner | ||||
| type Scanner struct { | ||||
| 	buf *bytes.Buffer // Source buffer for advancing and scanning | ||||
| 	src []byte        // Source buffer for immutable access | ||||
|  | ||||
| 	// Source Position | ||||
| 	srcPos  token.Pos // current position | ||||
| 	prevPos token.Pos // previous position, used for peek() method | ||||
|  | ||||
| 	lastCharLen int // length of last character in bytes | ||||
| 	lastLineLen int // length of last line in characters (for correct column reporting) | ||||
|  | ||||
| 	tokStart int // token text start position | ||||
| 	tokEnd   int // token text end  position | ||||
|  | ||||
| 	// Error is called for each error encountered. If no Error | ||||
| 	// function is set, the error is reported to os.Stderr. | ||||
| 	Error func(pos token.Pos, msg string) | ||||
|  | ||||
| 	// ErrorCount is incremented by one for each error encountered. | ||||
| 	ErrorCount int | ||||
|  | ||||
| 	// tokPos is the start position of most recently scanned token; set by | ||||
| 	// Scan. The Filename field is always left untouched by the Scanner.  If | ||||
| 	// an error is reported (via Error) and Position is invalid, the scanner is | ||||
| 	// not inside a token. | ||||
| 	tokPos token.Pos | ||||
| } | ||||
|  | ||||
| // New creates and initializes a new instance of Scanner using src as | ||||
| // its source content. | ||||
| func New(src []byte) *Scanner { | ||||
| 	// even though we accept a src, we read from a io.Reader compatible type | ||||
| 	// (*bytes.Buffer). So in the future we might easily change it to streaming | ||||
| 	// read. | ||||
| 	b := bytes.NewBuffer(src) | ||||
| 	s := &Scanner{ | ||||
| 		buf: b, | ||||
| 		src: src, | ||||
| 	} | ||||
|  | ||||
| 	// srcPosition always starts with 1 | ||||
| 	s.srcPos.Line = 1 | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // next reads the next rune from the bufferred reader. Returns the rune(0) if | ||||
| // an error occurs (or io.EOF is returned). | ||||
| func (s *Scanner) next() rune { | ||||
| 	ch, size, err := s.buf.ReadRune() | ||||
| 	if err != nil { | ||||
| 		// advance for error reporting | ||||
| 		s.srcPos.Column++ | ||||
| 		s.srcPos.Offset += size | ||||
| 		s.lastCharLen = size | ||||
| 		return eof | ||||
| 	} | ||||
|  | ||||
| 	// remember last position | ||||
| 	s.prevPos = s.srcPos | ||||
|  | ||||
| 	s.srcPos.Column++ | ||||
| 	s.lastCharLen = size | ||||
| 	s.srcPos.Offset += size | ||||
|  | ||||
| 	if ch == utf8.RuneError && size == 1 { | ||||
| 		s.err("illegal UTF-8 encoding") | ||||
| 		return ch | ||||
| 	} | ||||
|  | ||||
| 	if ch == '\n' { | ||||
| 		s.srcPos.Line++ | ||||
| 		s.lastLineLen = s.srcPos.Column | ||||
| 		s.srcPos.Column = 0 | ||||
| 	} | ||||
|  | ||||
| 	if ch == '\x00' { | ||||
| 		s.err("unexpected null character (0x00)") | ||||
| 		return eof | ||||
| 	} | ||||
|  | ||||
| 	if ch == '\uE123' { | ||||
| 		s.err("unicode code point U+E123 reserved for internal use") | ||||
| 		return utf8.RuneError | ||||
| 	} | ||||
|  | ||||
| 	// debug | ||||
| 	// fmt.Printf("ch: %q, offset:column: %d:%d\n", ch, s.srcPos.Offset, s.srcPos.Column) | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // unread unreads the previous read Rune and updates the source position | ||||
| func (s *Scanner) unread() { | ||||
| 	if err := s.buf.UnreadRune(); err != nil { | ||||
| 		panic(err) // this is user fault, we should catch it | ||||
| 	} | ||||
| 	s.srcPos = s.prevPos // put back last position | ||||
| } | ||||
|  | ||||
| // peek returns the next rune without advancing the reader. | ||||
| func (s *Scanner) peek() rune { | ||||
| 	peek, _, err := s.buf.ReadRune() | ||||
| 	if err != nil { | ||||
| 		return eof | ||||
| 	} | ||||
|  | ||||
| 	s.buf.UnreadRune() | ||||
| 	return peek | ||||
| } | ||||
|  | ||||
| // Scan scans the next token and returns the token. | ||||
| func (s *Scanner) Scan() token.Token { | ||||
| 	ch := s.next() | ||||
|  | ||||
| 	// skip white space | ||||
| 	for isWhitespace(ch) { | ||||
| 		ch = s.next() | ||||
| 	} | ||||
|  | ||||
| 	var tok token.Type | ||||
|  | ||||
| 	// token text markings | ||||
| 	s.tokStart = s.srcPos.Offset - s.lastCharLen | ||||
|  | ||||
| 	// token position, initial next() is moving the offset by one(size of rune | ||||
| 	// actually), though we are interested with the starting point | ||||
| 	s.tokPos.Offset = s.srcPos.Offset - s.lastCharLen | ||||
| 	if s.srcPos.Column > 0 { | ||||
| 		// common case: last character was not a '\n' | ||||
| 		s.tokPos.Line = s.srcPos.Line | ||||
| 		s.tokPos.Column = s.srcPos.Column | ||||
| 	} else { | ||||
| 		// last character was a '\n' | ||||
| 		// (we cannot be at the beginning of the source | ||||
| 		// since we have called next() at least once) | ||||
| 		s.tokPos.Line = s.srcPos.Line - 1 | ||||
| 		s.tokPos.Column = s.lastLineLen | ||||
| 	} | ||||
|  | ||||
| 	switch { | ||||
| 	case isLetter(ch): | ||||
| 		tok = token.IDENT | ||||
| 		lit := s.scanIdentifier() | ||||
| 		if lit == "true" || lit == "false" { | ||||
| 			tok = token.BOOL | ||||
| 		} | ||||
| 	case isDecimal(ch): | ||||
| 		tok = s.scanNumber(ch) | ||||
| 	default: | ||||
| 		switch ch { | ||||
| 		case eof: | ||||
| 			tok = token.EOF | ||||
| 		case '"': | ||||
| 			tok = token.STRING | ||||
| 			s.scanString() | ||||
| 		case '#', '/': | ||||
| 			tok = token.COMMENT | ||||
| 			s.scanComment(ch) | ||||
| 		case '.': | ||||
| 			tok = token.PERIOD | ||||
| 			ch = s.peek() | ||||
| 			if isDecimal(ch) { | ||||
| 				tok = token.FLOAT | ||||
| 				ch = s.scanMantissa(ch) | ||||
| 				ch = s.scanExponent(ch) | ||||
| 			} | ||||
| 		case '<': | ||||
| 			tok = token.HEREDOC | ||||
| 			s.scanHeredoc() | ||||
| 		case '[': | ||||
| 			tok = token.LBRACK | ||||
| 		case ']': | ||||
| 			tok = token.RBRACK | ||||
| 		case '{': | ||||
| 			tok = token.LBRACE | ||||
| 		case '}': | ||||
| 			tok = token.RBRACE | ||||
| 		case ',': | ||||
| 			tok = token.COMMA | ||||
| 		case '=': | ||||
| 			tok = token.ASSIGN | ||||
| 		case '+': | ||||
| 			tok = token.ADD | ||||
| 		case '-': | ||||
| 			if isDecimal(s.peek()) { | ||||
| 				ch := s.next() | ||||
| 				tok = s.scanNumber(ch) | ||||
| 			} else { | ||||
| 				tok = token.SUB | ||||
| 			} | ||||
| 		default: | ||||
| 			s.err("illegal char") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// finish token ending | ||||
| 	s.tokEnd = s.srcPos.Offset | ||||
|  | ||||
| 	// create token literal | ||||
| 	var tokenText string | ||||
| 	if s.tokStart >= 0 { | ||||
| 		tokenText = string(s.src[s.tokStart:s.tokEnd]) | ||||
| 	} | ||||
| 	s.tokStart = s.tokEnd // ensure idempotency of tokenText() call | ||||
|  | ||||
| 	return token.Token{ | ||||
| 		Type: tok, | ||||
| 		Pos:  s.tokPos, | ||||
| 		Text: tokenText, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (s *Scanner) scanComment(ch rune) { | ||||
| 	// single line comments | ||||
| 	if ch == '#' || (ch == '/' && s.peek() != '*') { | ||||
| 		if ch == '/' && s.peek() != '/' { | ||||
| 			s.err("expected '/' for comment") | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		ch = s.next() | ||||
| 		for ch != '\n' && ch >= 0 && ch != eof { | ||||
| 			ch = s.next() | ||||
| 		} | ||||
| 		if ch != eof && ch >= 0 { | ||||
| 			s.unread() | ||||
| 		} | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// be sure we get the character after /* This allows us to find comment's | ||||
| 	// that are not erminated | ||||
| 	if ch == '/' { | ||||
| 		s.next() | ||||
| 		ch = s.next() // read character after "/*" | ||||
| 	} | ||||
|  | ||||
| 	// look for /* - style comments | ||||
| 	for { | ||||
| 		if ch < 0 || ch == eof { | ||||
| 			s.err("comment not terminated") | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		ch0 := ch | ||||
| 		ch = s.next() | ||||
| 		if ch0 == '*' && ch == '/' { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // scanNumber scans a HCL number definition starting with the given rune | ||||
| func (s *Scanner) scanNumber(ch rune) token.Type { | ||||
| 	if ch == '0' { | ||||
| 		// check for hexadecimal, octal or float | ||||
| 		ch = s.next() | ||||
| 		if ch == 'x' || ch == 'X' { | ||||
| 			// hexadecimal | ||||
| 			ch = s.next() | ||||
| 			found := false | ||||
| 			for isHexadecimal(ch) { | ||||
| 				ch = s.next() | ||||
| 				found = true | ||||
| 			} | ||||
|  | ||||
| 			if !found { | ||||
| 				s.err("illegal hexadecimal number") | ||||
| 			} | ||||
|  | ||||
| 			if ch != eof { | ||||
| 				s.unread() | ||||
| 			} | ||||
|  | ||||
| 			return token.NUMBER | ||||
| 		} | ||||
|  | ||||
| 		// now it's either something like: 0421(octal) or 0.1231(float) | ||||
| 		illegalOctal := false | ||||
| 		for isDecimal(ch) { | ||||
| 			ch = s.next() | ||||
| 			if ch == '8' || ch == '9' { | ||||
| 				// this is just a possibility. For example 0159 is illegal, but | ||||
| 				// 0159.23 is valid. So we mark a possible illegal octal. If | ||||
| 				// the next character is not a period, we'll print the error. | ||||
| 				illegalOctal = true | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if ch == 'e' || ch == 'E' { | ||||
| 			ch = s.scanExponent(ch) | ||||
| 			return token.FLOAT | ||||
| 		} | ||||
|  | ||||
| 		if ch == '.' { | ||||
| 			ch = s.scanFraction(ch) | ||||
|  | ||||
| 			if ch == 'e' || ch == 'E' { | ||||
| 				ch = s.next() | ||||
| 				ch = s.scanExponent(ch) | ||||
| 			} | ||||
| 			return token.FLOAT | ||||
| 		} | ||||
|  | ||||
| 		if illegalOctal { | ||||
| 			s.err("illegal octal number") | ||||
| 		} | ||||
|  | ||||
| 		if ch != eof { | ||||
| 			s.unread() | ||||
| 		} | ||||
| 		return token.NUMBER | ||||
| 	} | ||||
|  | ||||
| 	s.scanMantissa(ch) | ||||
| 	ch = s.next() // seek forward | ||||
| 	if ch == 'e' || ch == 'E' { | ||||
| 		ch = s.scanExponent(ch) | ||||
| 		return token.FLOAT | ||||
| 	} | ||||
|  | ||||
| 	if ch == '.' { | ||||
| 		ch = s.scanFraction(ch) | ||||
| 		if ch == 'e' || ch == 'E' { | ||||
| 			ch = s.next() | ||||
| 			ch = s.scanExponent(ch) | ||||
| 		} | ||||
| 		return token.FLOAT | ||||
| 	} | ||||
|  | ||||
| 	if ch != eof { | ||||
| 		s.unread() | ||||
| 	} | ||||
| 	return token.NUMBER | ||||
| } | ||||
|  | ||||
| // scanMantissa scans the mantissa beginning from the rune. It returns the next | ||||
| // non decimal rune. It's used to determine wheter it's a fraction or exponent. | ||||
| func (s *Scanner) scanMantissa(ch rune) rune { | ||||
| 	scanned := false | ||||
| 	for isDecimal(ch) { | ||||
| 		ch = s.next() | ||||
| 		scanned = true | ||||
| 	} | ||||
|  | ||||
| 	if scanned && ch != eof { | ||||
| 		s.unread() | ||||
| 	} | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // scanFraction scans the fraction after the '.' rune | ||||
| func (s *Scanner) scanFraction(ch rune) rune { | ||||
| 	if ch == '.' { | ||||
| 		ch = s.peek() // we peek just to see if we can move forward | ||||
| 		ch = s.scanMantissa(ch) | ||||
| 	} | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // scanExponent scans the remaining parts of an exponent after the 'e' or 'E' | ||||
| // rune. | ||||
| func (s *Scanner) scanExponent(ch rune) rune { | ||||
| 	if ch == 'e' || ch == 'E' { | ||||
| 		ch = s.next() | ||||
| 		if ch == '-' || ch == '+' { | ||||
| 			ch = s.next() | ||||
| 		} | ||||
| 		ch = s.scanMantissa(ch) | ||||
| 	} | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // scanHeredoc scans a heredoc string | ||||
| func (s *Scanner) scanHeredoc() { | ||||
| 	// Scan the second '<' in example: '<<EOF' | ||||
| 	if s.next() != '<' { | ||||
| 		s.err("heredoc expected second '<', didn't see it") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Get the original offset so we can read just the heredoc ident | ||||
| 	offs := s.srcPos.Offset | ||||
|  | ||||
| 	// Scan the identifier | ||||
| 	ch := s.next() | ||||
|  | ||||
| 	// Indented heredoc syntax | ||||
| 	if ch == '-' { | ||||
| 		ch = s.next() | ||||
| 	} | ||||
|  | ||||
| 	for isLetter(ch) || isDigit(ch) { | ||||
| 		ch = s.next() | ||||
| 	} | ||||
|  | ||||
| 	// If we reached an EOF then that is not good | ||||
| 	if ch == eof { | ||||
| 		s.err("heredoc not terminated") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Ignore the '\r' in Windows line endings | ||||
| 	if ch == '\r' { | ||||
| 		if s.peek() == '\n' { | ||||
| 			ch = s.next() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// If we didn't reach a newline then that is also not good | ||||
| 	if ch != '\n' { | ||||
| 		s.err("invalid characters in heredoc anchor") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Read the identifier | ||||
| 	identBytes := s.src[offs : s.srcPos.Offset-s.lastCharLen] | ||||
| 	if len(identBytes) == 0 || (len(identBytes) == 1 && identBytes[0] == '-') { | ||||
| 		s.err("zero-length heredoc anchor") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	var identRegexp *regexp.Regexp | ||||
| 	if identBytes[0] == '-' { | ||||
| 		identRegexp = regexp.MustCompile(fmt.Sprintf(`^[[:space:]]*%s\r*\z`, identBytes[1:])) | ||||
| 	} else { | ||||
| 		identRegexp = regexp.MustCompile(fmt.Sprintf(`^[[:space:]]*%s\r*\z`, identBytes)) | ||||
| 	} | ||||
|  | ||||
| 	// Read the actual string value | ||||
| 	lineStart := s.srcPos.Offset | ||||
| 	for { | ||||
| 		ch := s.next() | ||||
|  | ||||
| 		// Special newline handling. | ||||
| 		if ch == '\n' { | ||||
| 			// Math is fast, so we first compare the byte counts to see if we have a chance | ||||
| 			// of seeing the same identifier - if the length is less than the number of bytes | ||||
| 			// in the identifier, this cannot be a valid terminator. | ||||
| 			lineBytesLen := s.srcPos.Offset - s.lastCharLen - lineStart | ||||
| 			if lineBytesLen >= len(identBytes) && identRegexp.Match(s.src[lineStart:s.srcPos.Offset-s.lastCharLen]) { | ||||
| 				break | ||||
| 			} | ||||
|  | ||||
| 			// Not an anchor match, record the start of a new line | ||||
| 			lineStart = s.srcPos.Offset | ||||
| 		} | ||||
|  | ||||
| 		if ch == eof { | ||||
| 			s.err("heredoc not terminated") | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // scanString scans a quoted string | ||||
| func (s *Scanner) scanString() { | ||||
| 	braces := 0 | ||||
| 	for { | ||||
| 		// '"' opening already consumed | ||||
| 		// read character after quote | ||||
| 		ch := s.next() | ||||
|  | ||||
| 		if (ch == '\n' && braces == 0) || ch < 0 || ch == eof { | ||||
| 			s.err("literal not terminated") | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if ch == '"' && braces == 0 { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		// If we're going into a ${} then we can ignore quotes for awhile | ||||
| 		if braces == 0 && ch == '$' && s.peek() == '{' { | ||||
| 			braces++ | ||||
| 			s.next() | ||||
| 		} else if braces > 0 && ch == '{' { | ||||
| 			braces++ | ||||
| 		} | ||||
| 		if braces > 0 && ch == '}' { | ||||
| 			braces-- | ||||
| 		} | ||||
|  | ||||
| 		if ch == '\\' { | ||||
| 			s.scanEscape() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // scanEscape scans an escape sequence | ||||
| func (s *Scanner) scanEscape() rune { | ||||
| 	// http://en.cppreference.com/w/cpp/language/escape | ||||
| 	ch := s.next() // read character after '/' | ||||
| 	switch ch { | ||||
| 	case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"': | ||||
| 		// nothing to do | ||||
| 	case '0', '1', '2', '3', '4', '5', '6', '7': | ||||
| 		// octal notation | ||||
| 		ch = s.scanDigits(ch, 8, 3) | ||||
| 	case 'x': | ||||
| 		// hexademical notation | ||||
| 		ch = s.scanDigits(s.next(), 16, 2) | ||||
| 	case 'u': | ||||
| 		// universal character name | ||||
| 		ch = s.scanDigits(s.next(), 16, 4) | ||||
| 	case 'U': | ||||
| 		// universal character name | ||||
| 		ch = s.scanDigits(s.next(), 16, 8) | ||||
| 	default: | ||||
| 		s.err("illegal char escape") | ||||
| 	} | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // scanDigits scans a rune with the given base for n times. For example an | ||||
| // octal notation \184 would yield in scanDigits(ch, 8, 3) | ||||
| func (s *Scanner) scanDigits(ch rune, base, n int) rune { | ||||
| 	start := n | ||||
| 	for n > 0 && digitVal(ch) < base { | ||||
| 		ch = s.next() | ||||
| 		if ch == eof { | ||||
| 			// If we see an EOF, we halt any more scanning of digits | ||||
| 			// immediately. | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		n-- | ||||
| 	} | ||||
| 	if n > 0 { | ||||
| 		s.err("illegal char escape") | ||||
| 	} | ||||
|  | ||||
| 	if n != start && ch != eof { | ||||
| 		// we scanned all digits, put the last non digit char back, | ||||
| 		// only if we read anything at all | ||||
| 		s.unread() | ||||
| 	} | ||||
|  | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // scanIdentifier scans an identifier and returns the literal string | ||||
| func (s *Scanner) scanIdentifier() string { | ||||
| 	offs := s.srcPos.Offset - s.lastCharLen | ||||
| 	ch := s.next() | ||||
| 	for isLetter(ch) || isDigit(ch) || ch == '-' || ch == '.' { | ||||
| 		ch = s.next() | ||||
| 	} | ||||
|  | ||||
| 	if ch != eof { | ||||
| 		s.unread() // we got identifier, put back latest char | ||||
| 	} | ||||
|  | ||||
| 	return string(s.src[offs:s.srcPos.Offset]) | ||||
| } | ||||
|  | ||||
| // recentPosition returns the position of the character immediately after the | ||||
| // character or token returned by the last call to Scan. | ||||
| func (s *Scanner) recentPosition() (pos token.Pos) { | ||||
| 	pos.Offset = s.srcPos.Offset - s.lastCharLen | ||||
| 	switch { | ||||
| 	case s.srcPos.Column > 0: | ||||
| 		// common case: last character was not a '\n' | ||||
| 		pos.Line = s.srcPos.Line | ||||
| 		pos.Column = s.srcPos.Column | ||||
| 	case s.lastLineLen > 0: | ||||
| 		// last character was a '\n' | ||||
| 		// (we cannot be at the beginning of the source | ||||
| 		// since we have called next() at least once) | ||||
| 		pos.Line = s.srcPos.Line - 1 | ||||
| 		pos.Column = s.lastLineLen | ||||
| 	default: | ||||
| 		// at the beginning of the source | ||||
| 		pos.Line = 1 | ||||
| 		pos.Column = 1 | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // err prints the error of any scanning to s.Error function. If the function is | ||||
| // not defined, by default it prints them to os.Stderr | ||||
| func (s *Scanner) err(msg string) { | ||||
| 	s.ErrorCount++ | ||||
| 	pos := s.recentPosition() | ||||
|  | ||||
| 	if s.Error != nil { | ||||
| 		s.Error(pos, msg) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg) | ||||
| } | ||||
|  | ||||
| // isHexadecimal returns true if the given rune is a letter | ||||
| func isLetter(ch rune) bool { | ||||
| 	return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) | ||||
| } | ||||
|  | ||||
| // isDigit returns true if the given rune is a decimal digit | ||||
| func isDigit(ch rune) bool { | ||||
| 	return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) | ||||
| } | ||||
|  | ||||
| // isDecimal returns true if the given rune is a decimal number | ||||
| func isDecimal(ch rune) bool { | ||||
| 	return '0' <= ch && ch <= '9' | ||||
| } | ||||
|  | ||||
| // isHexadecimal returns true if the given rune is an hexadecimal number | ||||
| func isHexadecimal(ch rune) bool { | ||||
| 	return '0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'f' || 'A' <= ch && ch <= 'F' | ||||
| } | ||||
|  | ||||
| // isWhitespace returns true if the rune is a space, tab, newline or carriage return | ||||
| func isWhitespace(ch rune) bool { | ||||
| 	return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' | ||||
| } | ||||
|  | ||||
| // digitVal returns the integer value of a given octal,decimal or hexadecimal rune | ||||
| func digitVal(ch rune) int { | ||||
| 	switch { | ||||
| 	case '0' <= ch && ch <= '9': | ||||
| 		return int(ch - '0') | ||||
| 	case 'a' <= ch && ch <= 'f': | ||||
| 		return int(ch - 'a' + 10) | ||||
| 	case 'A' <= ch && ch <= 'F': | ||||
| 		return int(ch - 'A' + 10) | ||||
| 	} | ||||
| 	return 16 // larger than any legal digit val | ||||
| } | ||||
							
								
								
									
										241
									
								
								vendor/github.com/hashicorp/hcl/hcl/strconv/quote.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								vendor/github.com/hashicorp/hcl/hcl/strconv/quote.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,241 @@ | ||||
| package strconv | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| // ErrSyntax indicates that a value does not have the right syntax for the target type. | ||||
| var ErrSyntax = errors.New("invalid syntax") | ||||
|  | ||||
| // Unquote interprets s as a single-quoted, double-quoted, | ||||
| // or backquoted Go string literal, returning the string value | ||||
| // that s quotes.  (If s is single-quoted, it would be a Go | ||||
| // character literal; Unquote returns the corresponding | ||||
| // one-character string.) | ||||
| func Unquote(s string) (t string, err error) { | ||||
| 	n := len(s) | ||||
| 	if n < 2 { | ||||
| 		return "", ErrSyntax | ||||
| 	} | ||||
| 	quote := s[0] | ||||
| 	if quote != s[n-1] { | ||||
| 		return "", ErrSyntax | ||||
| 	} | ||||
| 	s = s[1 : n-1] | ||||
|  | ||||
| 	if quote != '"' { | ||||
| 		return "", ErrSyntax | ||||
| 	} | ||||
| 	if !contains(s, '$') && !contains(s, '{') && contains(s, '\n') { | ||||
| 		return "", ErrSyntax | ||||
| 	} | ||||
|  | ||||
| 	// Is it trivial?  Avoid allocation. | ||||
| 	if !contains(s, '\\') && !contains(s, quote) && !contains(s, '$') { | ||||
| 		switch quote { | ||||
| 		case '"': | ||||
| 			return s, nil | ||||
| 		case '\'': | ||||
| 			r, size := utf8.DecodeRuneInString(s) | ||||
| 			if size == len(s) && (r != utf8.RuneError || size != 1) { | ||||
| 				return s, nil | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	var runeTmp [utf8.UTFMax]byte | ||||
| 	buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations. | ||||
| 	for len(s) > 0 { | ||||
| 		// If we're starting a '${}' then let it through un-unquoted. | ||||
| 		// Specifically: we don't unquote any characters within the `${}` | ||||
| 		// section. | ||||
| 		if s[0] == '$' && len(s) > 1 && s[1] == '{' { | ||||
| 			buf = append(buf, '$', '{') | ||||
| 			s = s[2:] | ||||
|  | ||||
| 			// Continue reading until we find the closing brace, copying as-is | ||||
| 			braces := 1 | ||||
| 			for len(s) > 0 && braces > 0 { | ||||
| 				r, size := utf8.DecodeRuneInString(s) | ||||
| 				if r == utf8.RuneError { | ||||
| 					return "", ErrSyntax | ||||
| 				} | ||||
|  | ||||
| 				s = s[size:] | ||||
|  | ||||
| 				n := utf8.EncodeRune(runeTmp[:], r) | ||||
| 				buf = append(buf, runeTmp[:n]...) | ||||
|  | ||||
| 				switch r { | ||||
| 				case '{': | ||||
| 					braces++ | ||||
| 				case '}': | ||||
| 					braces-- | ||||
| 				} | ||||
| 			} | ||||
| 			if braces != 0 { | ||||
| 				return "", ErrSyntax | ||||
| 			} | ||||
| 			if len(s) == 0 { | ||||
| 				// If there's no string left, we're done! | ||||
| 				break | ||||
| 			} else { | ||||
| 				// If there's more left, we need to pop back up to the top of the loop | ||||
| 				// in case there's another interpolation in this string. | ||||
| 				continue | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if s[0] == '\n' { | ||||
| 			return "", ErrSyntax | ||||
| 		} | ||||
|  | ||||
| 		c, multibyte, ss, err := unquoteChar(s, quote) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		s = ss | ||||
| 		if c < utf8.RuneSelf || !multibyte { | ||||
| 			buf = append(buf, byte(c)) | ||||
| 		} else { | ||||
| 			n := utf8.EncodeRune(runeTmp[:], c) | ||||
| 			buf = append(buf, runeTmp[:n]...) | ||||
| 		} | ||||
| 		if quote == '\'' && len(s) != 0 { | ||||
| 			// single-quoted must be single character | ||||
| 			return "", ErrSyntax | ||||
| 		} | ||||
| 	} | ||||
| 	return string(buf), nil | ||||
| } | ||||
|  | ||||
| // contains reports whether the string contains the byte c. | ||||
| func contains(s string, c byte) bool { | ||||
| 	for i := 0; i < len(s); i++ { | ||||
| 		if s[i] == c { | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func unhex(b byte) (v rune, ok bool) { | ||||
| 	c := rune(b) | ||||
| 	switch { | ||||
| 	case '0' <= c && c <= '9': | ||||
| 		return c - '0', true | ||||
| 	case 'a' <= c && c <= 'f': | ||||
| 		return c - 'a' + 10, true | ||||
| 	case 'A' <= c && c <= 'F': | ||||
| 		return c - 'A' + 10, true | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| func unquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) { | ||||
| 	// easy cases | ||||
| 	switch c := s[0]; { | ||||
| 	case c == quote && (quote == '\'' || quote == '"'): | ||||
| 		err = ErrSyntax | ||||
| 		return | ||||
| 	case c >= utf8.RuneSelf: | ||||
| 		r, size := utf8.DecodeRuneInString(s) | ||||
| 		return r, true, s[size:], nil | ||||
| 	case c != '\\': | ||||
| 		return rune(s[0]), false, s[1:], nil | ||||
| 	} | ||||
|  | ||||
| 	// hard case: c is backslash | ||||
| 	if len(s) <= 1 { | ||||
| 		err = ErrSyntax | ||||
| 		return | ||||
| 	} | ||||
| 	c := s[1] | ||||
| 	s = s[2:] | ||||
|  | ||||
| 	switch c { | ||||
| 	case 'a': | ||||
| 		value = '\a' | ||||
| 	case 'b': | ||||
| 		value = '\b' | ||||
| 	case 'f': | ||||
| 		value = '\f' | ||||
| 	case 'n': | ||||
| 		value = '\n' | ||||
| 	case 'r': | ||||
| 		value = '\r' | ||||
| 	case 't': | ||||
| 		value = '\t' | ||||
| 	case 'v': | ||||
| 		value = '\v' | ||||
| 	case 'x', 'u', 'U': | ||||
| 		n := 0 | ||||
| 		switch c { | ||||
| 		case 'x': | ||||
| 			n = 2 | ||||
| 		case 'u': | ||||
| 			n = 4 | ||||
| 		case 'U': | ||||
| 			n = 8 | ||||
| 		} | ||||
| 		var v rune | ||||
| 		if len(s) < n { | ||||
| 			err = ErrSyntax | ||||
| 			return | ||||
| 		} | ||||
| 		for j := 0; j < n; j++ { | ||||
| 			x, ok := unhex(s[j]) | ||||
| 			if !ok { | ||||
| 				err = ErrSyntax | ||||
| 				return | ||||
| 			} | ||||
| 			v = v<<4 | x | ||||
| 		} | ||||
| 		s = s[n:] | ||||
| 		if c == 'x' { | ||||
| 			// single-byte string, possibly not UTF-8 | ||||
| 			value = v | ||||
| 			break | ||||
| 		} | ||||
| 		if v > utf8.MaxRune { | ||||
| 			err = ErrSyntax | ||||
| 			return | ||||
| 		} | ||||
| 		value = v | ||||
| 		multibyte = true | ||||
| 	case '0', '1', '2', '3', '4', '5', '6', '7': | ||||
| 		v := rune(c) - '0' | ||||
| 		if len(s) < 2 { | ||||
| 			err = ErrSyntax | ||||
| 			return | ||||
| 		} | ||||
| 		for j := 0; j < 2; j++ { // one digit already; two more | ||||
| 			x := rune(s[j]) - '0' | ||||
| 			if x < 0 || x > 7 { | ||||
| 				err = ErrSyntax | ||||
| 				return | ||||
| 			} | ||||
| 			v = (v << 3) | x | ||||
| 		} | ||||
| 		s = s[2:] | ||||
| 		if v > 255 { | ||||
| 			err = ErrSyntax | ||||
| 			return | ||||
| 		} | ||||
| 		value = v | ||||
| 	case '\\': | ||||
| 		value = '\\' | ||||
| 	case '\'', '"': | ||||
| 		if c != quote { | ||||
| 			err = ErrSyntax | ||||
| 			return | ||||
| 		} | ||||
| 		value = rune(c) | ||||
| 	default: | ||||
| 		err = ErrSyntax | ||||
| 		return | ||||
| 	} | ||||
| 	tail = s | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										46
									
								
								vendor/github.com/hashicorp/hcl/hcl/token/position.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/hashicorp/hcl/hcl/token/position.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| package token | ||||
|  | ||||
| import "fmt" | ||||
|  | ||||
| // Pos describes an arbitrary source position | ||||
| // including the file, line, and column location. | ||||
| // A Position is valid if the line number is > 0. | ||||
| type Pos struct { | ||||
| 	Filename string // filename, if any | ||||
| 	Offset   int    // offset, starting at 0 | ||||
| 	Line     int    // line number, starting at 1 | ||||
| 	Column   int    // column number, starting at 1 (character count) | ||||
| } | ||||
|  | ||||
| // IsValid returns true if the position is valid. | ||||
| func (p *Pos) IsValid() bool { return p.Line > 0 } | ||||
|  | ||||
| // String returns a string in one of several forms: | ||||
| // | ||||
| //	file:line:column    valid position with file name | ||||
| //	line:column         valid position without file name | ||||
| //	file                invalid position with file name | ||||
| //	-                   invalid position without file name | ||||
| func (p Pos) String() string { | ||||
| 	s := p.Filename | ||||
| 	if p.IsValid() { | ||||
| 		if s != "" { | ||||
| 			s += ":" | ||||
| 		} | ||||
| 		s += fmt.Sprintf("%d:%d", p.Line, p.Column) | ||||
| 	} | ||||
| 	if s == "" { | ||||
| 		s = "-" | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // Before reports whether the position p is before u. | ||||
| func (p Pos) Before(u Pos) bool { | ||||
| 	return u.Offset > p.Offset || u.Line > p.Line | ||||
| } | ||||
|  | ||||
| // After reports whether the position p is after u. | ||||
| func (p Pos) After(u Pos) bool { | ||||
| 	return u.Offset < p.Offset || u.Line < p.Line | ||||
| } | ||||
							
								
								
									
										219
									
								
								vendor/github.com/hashicorp/hcl/hcl/token/token.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								vendor/github.com/hashicorp/hcl/hcl/token/token.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,219 @@ | ||||
| // Package token defines constants representing the lexical tokens for HCL | ||||
| // (HashiCorp Configuration Language) | ||||
| package token | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	hclstrconv "github.com/hashicorp/hcl/hcl/strconv" | ||||
| ) | ||||
|  | ||||
| // Token defines a single HCL token which can be obtained via the Scanner | ||||
| type Token struct { | ||||
| 	Type Type | ||||
| 	Pos  Pos | ||||
| 	Text string | ||||
| 	JSON bool | ||||
| } | ||||
|  | ||||
| // Type is the set of lexical tokens of the HCL (HashiCorp Configuration Language) | ||||
| type Type int | ||||
|  | ||||
| const ( | ||||
| 	// Special tokens | ||||
| 	ILLEGAL Type = iota | ||||
| 	EOF | ||||
| 	COMMENT | ||||
|  | ||||
| 	identifier_beg | ||||
| 	IDENT // literals | ||||
| 	literal_beg | ||||
| 	NUMBER  // 12345 | ||||
| 	FLOAT   // 123.45 | ||||
| 	BOOL    // true,false | ||||
| 	STRING  // "abc" | ||||
| 	HEREDOC // <<FOO\nbar\nFOO | ||||
| 	literal_end | ||||
| 	identifier_end | ||||
|  | ||||
| 	operator_beg | ||||
| 	LBRACK // [ | ||||
| 	LBRACE // { | ||||
| 	COMMA  // , | ||||
| 	PERIOD // . | ||||
|  | ||||
| 	RBRACK // ] | ||||
| 	RBRACE // } | ||||
|  | ||||
| 	ASSIGN // = | ||||
| 	ADD    // + | ||||
| 	SUB    // - | ||||
| 	operator_end | ||||
| ) | ||||
|  | ||||
| var tokens = [...]string{ | ||||
| 	ILLEGAL: "ILLEGAL", | ||||
|  | ||||
| 	EOF:     "EOF", | ||||
| 	COMMENT: "COMMENT", | ||||
|  | ||||
| 	IDENT:  "IDENT", | ||||
| 	NUMBER: "NUMBER", | ||||
| 	FLOAT:  "FLOAT", | ||||
| 	BOOL:   "BOOL", | ||||
| 	STRING: "STRING", | ||||
|  | ||||
| 	LBRACK:  "LBRACK", | ||||
| 	LBRACE:  "LBRACE", | ||||
| 	COMMA:   "COMMA", | ||||
| 	PERIOD:  "PERIOD", | ||||
| 	HEREDOC: "HEREDOC", | ||||
|  | ||||
| 	RBRACK: "RBRACK", | ||||
| 	RBRACE: "RBRACE", | ||||
|  | ||||
| 	ASSIGN: "ASSIGN", | ||||
| 	ADD:    "ADD", | ||||
| 	SUB:    "SUB", | ||||
| } | ||||
|  | ||||
| // String returns the string corresponding to the token tok. | ||||
| func (t Type) String() string { | ||||
| 	s := "" | ||||
| 	if 0 <= t && t < Type(len(tokens)) { | ||||
| 		s = tokens[t] | ||||
| 	} | ||||
| 	if s == "" { | ||||
| 		s = "token(" + strconv.Itoa(int(t)) + ")" | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // IsIdentifier returns true for tokens corresponding to identifiers and basic | ||||
| // type literals; it returns false otherwise. | ||||
| func (t Type) IsIdentifier() bool { return identifier_beg < t && t < identifier_end } | ||||
|  | ||||
| // IsLiteral returns true for tokens corresponding to basic type literals; it | ||||
| // returns false otherwise. | ||||
| func (t Type) IsLiteral() bool { return literal_beg < t && t < literal_end } | ||||
|  | ||||
| // IsOperator returns true for tokens corresponding to operators and | ||||
| // delimiters; it returns false otherwise. | ||||
| func (t Type) IsOperator() bool { return operator_beg < t && t < operator_end } | ||||
|  | ||||
| // String returns the token's literal text. Note that this is only | ||||
| // applicable for certain token types, such as token.IDENT, | ||||
| // token.STRING, etc.. | ||||
| func (t Token) String() string { | ||||
| 	return fmt.Sprintf("%s %s %s", t.Pos.String(), t.Type.String(), t.Text) | ||||
| } | ||||
|  | ||||
| // Value returns the properly typed value for this token. The type of | ||||
| // the returned interface{} is guaranteed based on the Type field. | ||||
| // | ||||
| // This can only be called for literal types. If it is called for any other | ||||
| // type, this will panic. | ||||
| func (t Token) Value() interface{} { | ||||
| 	switch t.Type { | ||||
| 	case BOOL: | ||||
| 		if t.Text == "true" { | ||||
| 			return true | ||||
| 		} else if t.Text == "false" { | ||||
| 			return false | ||||
| 		} | ||||
|  | ||||
| 		panic("unknown bool value: " + t.Text) | ||||
| 	case FLOAT: | ||||
| 		v, err := strconv.ParseFloat(t.Text, 64) | ||||
| 		if err != nil { | ||||
| 			panic(err) | ||||
| 		} | ||||
|  | ||||
| 		return float64(v) | ||||
| 	case NUMBER: | ||||
| 		v, err := strconv.ParseInt(t.Text, 0, 64) | ||||
| 		if err != nil { | ||||
| 			panic(err) | ||||
| 		} | ||||
|  | ||||
| 		return int64(v) | ||||
| 	case IDENT: | ||||
| 		return t.Text | ||||
| 	case HEREDOC: | ||||
| 		return unindentHeredoc(t.Text) | ||||
| 	case STRING: | ||||
| 		// Determine the Unquote method to use. If it came from JSON, | ||||
| 		// then we need to use the built-in unquote since we have to | ||||
| 		// escape interpolations there. | ||||
| 		f := hclstrconv.Unquote | ||||
| 		if t.JSON { | ||||
| 			f = strconv.Unquote | ||||
| 		} | ||||
|  | ||||
| 		// This case occurs if json null is used | ||||
| 		if t.Text == "" { | ||||
| 			return "" | ||||
| 		} | ||||
|  | ||||
| 		v, err := f(t.Text) | ||||
| 		if err != nil { | ||||
| 			panic(fmt.Sprintf("unquote %s err: %s", t.Text, err)) | ||||
| 		} | ||||
|  | ||||
| 		return v | ||||
| 	default: | ||||
| 		panic(fmt.Sprintf("unimplemented Value for type: %s", t.Type)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // unindentHeredoc returns the string content of a HEREDOC if it is started with << | ||||
| // and the content of a HEREDOC with the hanging indent removed if it is started with | ||||
| // a <<-, and the terminating line is at least as indented as the least indented line. | ||||
| func unindentHeredoc(heredoc string) string { | ||||
| 	// We need to find the end of the marker | ||||
| 	idx := strings.IndexByte(heredoc, '\n') | ||||
| 	if idx == -1 { | ||||
| 		panic("heredoc doesn't contain newline") | ||||
| 	} | ||||
|  | ||||
| 	unindent := heredoc[2] == '-' | ||||
|  | ||||
| 	// We can optimize if the heredoc isn't marked for indentation | ||||
| 	if !unindent { | ||||
| 		return string(heredoc[idx+1 : len(heredoc)-idx+1]) | ||||
| 	} | ||||
|  | ||||
| 	// We need to unindent each line based on the indentation level of the marker | ||||
| 	lines := strings.Split(string(heredoc[idx+1:len(heredoc)-idx+2]), "\n") | ||||
| 	whitespacePrefix := lines[len(lines)-1] | ||||
|  | ||||
| 	isIndented := true | ||||
| 	for _, v := range lines { | ||||
| 		if strings.HasPrefix(v, whitespacePrefix) { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		isIndented = false | ||||
| 		break | ||||
| 	} | ||||
|  | ||||
| 	// If all lines are not at least as indented as the terminating mark, return the | ||||
| 	// heredoc as is, but trim the leading space from the marker on the final line. | ||||
| 	if !isIndented { | ||||
| 		return strings.TrimRight(string(heredoc[idx+1:len(heredoc)-idx+1]), " \t") | ||||
| 	} | ||||
|  | ||||
| 	unindentedLines := make([]string, len(lines)) | ||||
| 	for k, v := range lines { | ||||
| 		if k == len(lines)-1 { | ||||
| 			unindentedLines[k] = "" | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		unindentedLines[k] = strings.TrimPrefix(v, whitespacePrefix) | ||||
| 	} | ||||
|  | ||||
| 	return strings.Join(unindentedLines, "\n") | ||||
| } | ||||
							
								
								
									
										117
									
								
								vendor/github.com/hashicorp/hcl/json/parser/flatten.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								vendor/github.com/hashicorp/hcl/json/parser/flatten.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | ||||
| package parser | ||||
|  | ||||
| import "github.com/hashicorp/hcl/hcl/ast" | ||||
|  | ||||
| // flattenObjects takes an AST node, walks it, and flattens | ||||
| func flattenObjects(node ast.Node) { | ||||
| 	ast.Walk(node, func(n ast.Node) (ast.Node, bool) { | ||||
| 		// We only care about lists, because this is what we modify | ||||
| 		list, ok := n.(*ast.ObjectList) | ||||
| 		if !ok { | ||||
| 			return n, true | ||||
| 		} | ||||
|  | ||||
| 		// Rebuild the item list | ||||
| 		items := make([]*ast.ObjectItem, 0, len(list.Items)) | ||||
| 		frontier := make([]*ast.ObjectItem, len(list.Items)) | ||||
| 		copy(frontier, list.Items) | ||||
| 		for len(frontier) > 0 { | ||||
| 			// Pop the current item | ||||
| 			n := len(frontier) | ||||
| 			item := frontier[n-1] | ||||
| 			frontier = frontier[:n-1] | ||||
|  | ||||
| 			switch v := item.Val.(type) { | ||||
| 			case *ast.ObjectType: | ||||
| 				items, frontier = flattenObjectType(v, item, items, frontier) | ||||
| 			case *ast.ListType: | ||||
| 				items, frontier = flattenListType(v, item, items, frontier) | ||||
| 			default: | ||||
| 				items = append(items, item) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Reverse the list since the frontier model runs things backwards | ||||
| 		for i := len(items)/2 - 1; i >= 0; i-- { | ||||
| 			opp := len(items) - 1 - i | ||||
| 			items[i], items[opp] = items[opp], items[i] | ||||
| 		} | ||||
|  | ||||
| 		// Done! Set the original items | ||||
| 		list.Items = items | ||||
| 		return n, true | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func flattenListType( | ||||
| 	ot *ast.ListType, | ||||
| 	item *ast.ObjectItem, | ||||
| 	items []*ast.ObjectItem, | ||||
| 	frontier []*ast.ObjectItem) ([]*ast.ObjectItem, []*ast.ObjectItem) { | ||||
| 	// If the list is empty, keep the original list | ||||
| 	if len(ot.List) == 0 { | ||||
| 		items = append(items, item) | ||||
| 		return items, frontier | ||||
| 	} | ||||
|  | ||||
| 	// All the elements of this object must also be objects! | ||||
| 	for _, subitem := range ot.List { | ||||
| 		if _, ok := subitem.(*ast.ObjectType); !ok { | ||||
| 			items = append(items, item) | ||||
| 			return items, frontier | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Great! We have a match go through all the items and flatten | ||||
| 	for _, elem := range ot.List { | ||||
| 		// Add it to the frontier so that we can recurse | ||||
| 		frontier = append(frontier, &ast.ObjectItem{ | ||||
| 			Keys:        item.Keys, | ||||
| 			Assign:      item.Assign, | ||||
| 			Val:         elem, | ||||
| 			LeadComment: item.LeadComment, | ||||
| 			LineComment: item.LineComment, | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	return items, frontier | ||||
| } | ||||
|  | ||||
| func flattenObjectType( | ||||
| 	ot *ast.ObjectType, | ||||
| 	item *ast.ObjectItem, | ||||
| 	items []*ast.ObjectItem, | ||||
| 	frontier []*ast.ObjectItem) ([]*ast.ObjectItem, []*ast.ObjectItem) { | ||||
| 	// If the list has no items we do not have to flatten anything | ||||
| 	if ot.List.Items == nil { | ||||
| 		items = append(items, item) | ||||
| 		return items, frontier | ||||
| 	} | ||||
|  | ||||
| 	// All the elements of this object must also be objects! | ||||
| 	for _, subitem := range ot.List.Items { | ||||
| 		if _, ok := subitem.Val.(*ast.ObjectType); !ok { | ||||
| 			items = append(items, item) | ||||
| 			return items, frontier | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Great! We have a match go through all the items and flatten | ||||
| 	for _, subitem := range ot.List.Items { | ||||
| 		// Copy the new key | ||||
| 		keys := make([]*ast.ObjectKey, len(item.Keys)+len(subitem.Keys)) | ||||
| 		copy(keys, item.Keys) | ||||
| 		copy(keys[len(item.Keys):], subitem.Keys) | ||||
|  | ||||
| 		// Add it to the frontier so that we can recurse | ||||
| 		frontier = append(frontier, &ast.ObjectItem{ | ||||
| 			Keys:        keys, | ||||
| 			Assign:      item.Assign, | ||||
| 			Val:         subitem.Val, | ||||
| 			LeadComment: item.LeadComment, | ||||
| 			LineComment: item.LineComment, | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	return items, frontier | ||||
| } | ||||
							
								
								
									
										313
									
								
								vendor/github.com/hashicorp/hcl/json/parser/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										313
									
								
								vendor/github.com/hashicorp/hcl/json/parser/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,313 @@ | ||||
| package parser | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/hashicorp/hcl/hcl/ast" | ||||
| 	hcltoken "github.com/hashicorp/hcl/hcl/token" | ||||
| 	"github.com/hashicorp/hcl/json/scanner" | ||||
| 	"github.com/hashicorp/hcl/json/token" | ||||
| ) | ||||
|  | ||||
| type Parser struct { | ||||
| 	sc *scanner.Scanner | ||||
|  | ||||
| 	// Last read token | ||||
| 	tok       token.Token | ||||
| 	commaPrev token.Token | ||||
|  | ||||
| 	enableTrace bool | ||||
| 	indent      int | ||||
| 	n           int // buffer size (max = 1) | ||||
| } | ||||
|  | ||||
| func newParser(src []byte) *Parser { | ||||
| 	return &Parser{ | ||||
| 		sc: scanner.New(src), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Parse returns the fully parsed source and returns the abstract syntax tree. | ||||
| func Parse(src []byte) (*ast.File, error) { | ||||
| 	p := newParser(src) | ||||
| 	return p.Parse() | ||||
| } | ||||
|  | ||||
| var errEofToken = errors.New("EOF token found") | ||||
|  | ||||
| // Parse returns the fully parsed source and returns the abstract syntax tree. | ||||
| func (p *Parser) Parse() (*ast.File, error) { | ||||
| 	f := &ast.File{} | ||||
| 	var err, scerr error | ||||
| 	p.sc.Error = func(pos token.Pos, msg string) { | ||||
| 		scerr = fmt.Errorf("%s: %s", pos, msg) | ||||
| 	} | ||||
|  | ||||
| 	// The root must be an object in JSON | ||||
| 	object, err := p.object() | ||||
| 	if scerr != nil { | ||||
| 		return nil, scerr | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	// We make our final node an object list so it is more HCL compatible | ||||
| 	f.Node = object.List | ||||
|  | ||||
| 	// Flatten it, which finds patterns and turns them into more HCL-like | ||||
| 	// AST trees. | ||||
| 	flattenObjects(f.Node) | ||||
|  | ||||
| 	return f, nil | ||||
| } | ||||
|  | ||||
| func (p *Parser) objectList() (*ast.ObjectList, error) { | ||||
| 	defer un(trace(p, "ParseObjectList")) | ||||
| 	node := &ast.ObjectList{} | ||||
|  | ||||
| 	for { | ||||
| 		n, err := p.objectItem() | ||||
| 		if err == errEofToken { | ||||
| 			break // we are finished | ||||
| 		} | ||||
|  | ||||
| 		// we don't return a nil node, because might want to use already | ||||
| 		// collected items. | ||||
| 		if err != nil { | ||||
| 			return node, err | ||||
| 		} | ||||
|  | ||||
| 		node.Add(n) | ||||
|  | ||||
| 		// Check for a followup comma. If it isn't a comma, then we're done | ||||
| 		if tok := p.scan(); tok.Type != token.COMMA { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return node, nil | ||||
| } | ||||
|  | ||||
| // objectItem parses a single object item | ||||
| func (p *Parser) objectItem() (*ast.ObjectItem, error) { | ||||
| 	defer un(trace(p, "ParseObjectItem")) | ||||
|  | ||||
| 	keys, err := p.objectKey() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	o := &ast.ObjectItem{ | ||||
| 		Keys: keys, | ||||
| 	} | ||||
|  | ||||
| 	switch p.tok.Type { | ||||
| 	case token.COLON: | ||||
| 		pos := p.tok.Pos | ||||
| 		o.Assign = hcltoken.Pos{ | ||||
| 			Filename: pos.Filename, | ||||
| 			Offset:   pos.Offset, | ||||
| 			Line:     pos.Line, | ||||
| 			Column:   pos.Column, | ||||
| 		} | ||||
|  | ||||
| 		o.Val, err = p.objectValue() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return o, nil | ||||
| } | ||||
|  | ||||
| // objectKey parses an object key and returns a ObjectKey AST | ||||
| func (p *Parser) objectKey() ([]*ast.ObjectKey, error) { | ||||
| 	keyCount := 0 | ||||
| 	keys := make([]*ast.ObjectKey, 0) | ||||
|  | ||||
| 	for { | ||||
| 		tok := p.scan() | ||||
| 		switch tok.Type { | ||||
| 		case token.EOF: | ||||
| 			return nil, errEofToken | ||||
| 		case token.STRING: | ||||
| 			keyCount++ | ||||
| 			keys = append(keys, &ast.ObjectKey{ | ||||
| 				Token: p.tok.HCLToken(), | ||||
| 			}) | ||||
| 		case token.COLON: | ||||
| 			// If we have a zero keycount it means that we never got | ||||
| 			// an object key, i.e. `{ :`. This is a syntax error. | ||||
| 			if keyCount == 0 { | ||||
| 				return nil, fmt.Errorf("expected: STRING got: %s", p.tok.Type) | ||||
| 			} | ||||
|  | ||||
| 			// Done | ||||
| 			return keys, nil | ||||
| 		case token.ILLEGAL: | ||||
| 			return nil, errors.New("illegal") | ||||
| 		default: | ||||
| 			return nil, fmt.Errorf("expected: STRING got: %s", p.tok.Type) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // object parses any type of object, such as number, bool, string, object or | ||||
| // list. | ||||
| func (p *Parser) objectValue() (ast.Node, error) { | ||||
| 	defer un(trace(p, "ParseObjectValue")) | ||||
| 	tok := p.scan() | ||||
|  | ||||
| 	switch tok.Type { | ||||
| 	case token.NUMBER, token.FLOAT, token.BOOL, token.NULL, token.STRING: | ||||
| 		return p.literalType() | ||||
| 	case token.LBRACE: | ||||
| 		return p.objectType() | ||||
| 	case token.LBRACK: | ||||
| 		return p.listType() | ||||
| 	case token.EOF: | ||||
| 		return nil, errEofToken | ||||
| 	} | ||||
|  | ||||
| 	return nil, fmt.Errorf("Expected object value, got unknown token: %+v", tok) | ||||
| } | ||||
|  | ||||
| // object parses any type of object, such as number, bool, string, object or | ||||
| // list. | ||||
| func (p *Parser) object() (*ast.ObjectType, error) { | ||||
| 	defer un(trace(p, "ParseType")) | ||||
| 	tok := p.scan() | ||||
|  | ||||
| 	switch tok.Type { | ||||
| 	case token.LBRACE: | ||||
| 		return p.objectType() | ||||
| 	case token.EOF: | ||||
| 		return nil, errEofToken | ||||
| 	} | ||||
|  | ||||
| 	return nil, fmt.Errorf("Expected object, got unknown token: %+v", tok) | ||||
| } | ||||
|  | ||||
| // objectType parses an object type and returns a ObjectType AST | ||||
| func (p *Parser) objectType() (*ast.ObjectType, error) { | ||||
| 	defer un(trace(p, "ParseObjectType")) | ||||
|  | ||||
| 	// we assume that the currently scanned token is a LBRACE | ||||
| 	o := &ast.ObjectType{} | ||||
|  | ||||
| 	l, err := p.objectList() | ||||
|  | ||||
| 	// if we hit RBRACE, we are good to go (means we parsed all Items), if it's | ||||
| 	// not a RBRACE, it's an syntax error and we just return it. | ||||
| 	if err != nil && p.tok.Type != token.RBRACE { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	o.List = l | ||||
| 	return o, nil | ||||
| } | ||||
|  | ||||
| // listType parses a list type and returns a ListType AST | ||||
| func (p *Parser) listType() (*ast.ListType, error) { | ||||
| 	defer un(trace(p, "ParseListType")) | ||||
|  | ||||
| 	// we assume that the currently scanned token is a LBRACK | ||||
| 	l := &ast.ListType{} | ||||
|  | ||||
| 	for { | ||||
| 		tok := p.scan() | ||||
| 		switch tok.Type { | ||||
| 		case token.NUMBER, token.FLOAT, token.STRING: | ||||
| 			node, err := p.literalType() | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			l.Add(node) | ||||
| 		case token.COMMA: | ||||
| 			continue | ||||
| 		case token.LBRACE: | ||||
| 			node, err := p.objectType() | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
|  | ||||
| 			l.Add(node) | ||||
| 		case token.BOOL: | ||||
| 			// TODO(arslan) should we support? not supported by HCL yet | ||||
| 		case token.LBRACK: | ||||
| 			// TODO(arslan) should we support nested lists? Even though it's | ||||
| 			// written in README of HCL, it's not a part of the grammar | ||||
| 			// (not defined in parse.y) | ||||
| 		case token.RBRACK: | ||||
| 			// finished | ||||
| 			return l, nil | ||||
| 		default: | ||||
| 			return nil, fmt.Errorf("unexpected token while parsing list: %s", tok.Type) | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // literalType parses a literal type and returns a LiteralType AST | ||||
| func (p *Parser) literalType() (*ast.LiteralType, error) { | ||||
| 	defer un(trace(p, "ParseLiteral")) | ||||
|  | ||||
| 	return &ast.LiteralType{ | ||||
| 		Token: p.tok.HCLToken(), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| // scan returns the next token from the underlying scanner. If a token has | ||||
| // been unscanned then read that instead. | ||||
| func (p *Parser) scan() token.Token { | ||||
| 	// If we have a token on the buffer, then return it. | ||||
| 	if p.n != 0 { | ||||
| 		p.n = 0 | ||||
| 		return p.tok | ||||
| 	} | ||||
|  | ||||
| 	p.tok = p.sc.Scan() | ||||
| 	return p.tok | ||||
| } | ||||
|  | ||||
| // unscan pushes the previously read token back onto the buffer. | ||||
| func (p *Parser) unscan() { | ||||
| 	p.n = 1 | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
| // Parsing support | ||||
|  | ||||
| func (p *Parser) printTrace(a ...interface{}) { | ||||
| 	if !p.enableTrace { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " | ||||
| 	const n = len(dots) | ||||
| 	fmt.Printf("%5d:%3d: ", p.tok.Pos.Line, p.tok.Pos.Column) | ||||
|  | ||||
| 	i := 2 * p.indent | ||||
| 	for i > n { | ||||
| 		fmt.Print(dots) | ||||
| 		i -= n | ||||
| 	} | ||||
| 	// i <= n | ||||
| 	fmt.Print(dots[0:i]) | ||||
| 	fmt.Println(a...) | ||||
| } | ||||
|  | ||||
| func trace(p *Parser, msg string) *Parser { | ||||
| 	p.printTrace(msg, "(") | ||||
| 	p.indent++ | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| // Usage pattern: defer un(trace(p, "...")) | ||||
| func un(p *Parser) { | ||||
| 	p.indent-- | ||||
| 	p.printTrace(")") | ||||
| } | ||||
							
								
								
									
										451
									
								
								vendor/github.com/hashicorp/hcl/json/scanner/scanner.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										451
									
								
								vendor/github.com/hashicorp/hcl/json/scanner/scanner.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,451 @@ | ||||
| package scanner | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"unicode" | ||||
| 	"unicode/utf8" | ||||
|  | ||||
| 	"github.com/hashicorp/hcl/json/token" | ||||
| ) | ||||
|  | ||||
| // eof represents a marker rune for the end of the reader. | ||||
| const eof = rune(0) | ||||
|  | ||||
| // Scanner defines a lexical scanner | ||||
| type Scanner struct { | ||||
| 	buf *bytes.Buffer // Source buffer for advancing and scanning | ||||
| 	src []byte        // Source buffer for immutable access | ||||
|  | ||||
| 	// Source Position | ||||
| 	srcPos  token.Pos // current position | ||||
| 	prevPos token.Pos // previous position, used for peek() method | ||||
|  | ||||
| 	lastCharLen int // length of last character in bytes | ||||
| 	lastLineLen int // length of last line in characters (for correct column reporting) | ||||
|  | ||||
| 	tokStart int // token text start position | ||||
| 	tokEnd   int // token text end  position | ||||
|  | ||||
| 	// Error is called for each error encountered. If no Error | ||||
| 	// function is set, the error is reported to os.Stderr. | ||||
| 	Error func(pos token.Pos, msg string) | ||||
|  | ||||
| 	// ErrorCount is incremented by one for each error encountered. | ||||
| 	ErrorCount int | ||||
|  | ||||
| 	// tokPos is the start position of most recently scanned token; set by | ||||
| 	// Scan. The Filename field is always left untouched by the Scanner.  If | ||||
| 	// an error is reported (via Error) and Position is invalid, the scanner is | ||||
| 	// not inside a token. | ||||
| 	tokPos token.Pos | ||||
| } | ||||
|  | ||||
| // New creates and initializes a new instance of Scanner using src as | ||||
| // its source content. | ||||
| func New(src []byte) *Scanner { | ||||
| 	// even though we accept a src, we read from a io.Reader compatible type | ||||
| 	// (*bytes.Buffer). So in the future we might easily change it to streaming | ||||
| 	// read. | ||||
| 	b := bytes.NewBuffer(src) | ||||
| 	s := &Scanner{ | ||||
| 		buf: b, | ||||
| 		src: src, | ||||
| 	} | ||||
|  | ||||
| 	// srcPosition always starts with 1 | ||||
| 	s.srcPos.Line = 1 | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // next reads the next rune from the bufferred reader. Returns the rune(0) if | ||||
| // an error occurs (or io.EOF is returned). | ||||
| func (s *Scanner) next() rune { | ||||
| 	ch, size, err := s.buf.ReadRune() | ||||
| 	if err != nil { | ||||
| 		// advance for error reporting | ||||
| 		s.srcPos.Column++ | ||||
| 		s.srcPos.Offset += size | ||||
| 		s.lastCharLen = size | ||||
| 		return eof | ||||
| 	} | ||||
|  | ||||
| 	if ch == utf8.RuneError && size == 1 { | ||||
| 		s.srcPos.Column++ | ||||
| 		s.srcPos.Offset += size | ||||
| 		s.lastCharLen = size | ||||
| 		s.err("illegal UTF-8 encoding") | ||||
| 		return ch | ||||
| 	} | ||||
|  | ||||
| 	// remember last position | ||||
| 	s.prevPos = s.srcPos | ||||
|  | ||||
| 	s.srcPos.Column++ | ||||
| 	s.lastCharLen = size | ||||
| 	s.srcPos.Offset += size | ||||
|  | ||||
| 	if ch == '\n' { | ||||
| 		s.srcPos.Line++ | ||||
| 		s.lastLineLen = s.srcPos.Column | ||||
| 		s.srcPos.Column = 0 | ||||
| 	} | ||||
|  | ||||
| 	// debug | ||||
| 	// fmt.Printf("ch: %q, offset:column: %d:%d\n", ch, s.srcPos.Offset, s.srcPos.Column) | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // unread unreads the previous read Rune and updates the source position | ||||
| func (s *Scanner) unread() { | ||||
| 	if err := s.buf.UnreadRune(); err != nil { | ||||
| 		panic(err) // this is user fault, we should catch it | ||||
| 	} | ||||
| 	s.srcPos = s.prevPos // put back last position | ||||
| } | ||||
|  | ||||
| // peek returns the next rune without advancing the reader. | ||||
| func (s *Scanner) peek() rune { | ||||
| 	peek, _, err := s.buf.ReadRune() | ||||
| 	if err != nil { | ||||
| 		return eof | ||||
| 	} | ||||
|  | ||||
| 	s.buf.UnreadRune() | ||||
| 	return peek | ||||
| } | ||||
|  | ||||
| // Scan scans the next token and returns the token. | ||||
| func (s *Scanner) Scan() token.Token { | ||||
| 	ch := s.next() | ||||
|  | ||||
| 	// skip white space | ||||
| 	for isWhitespace(ch) { | ||||
| 		ch = s.next() | ||||
| 	} | ||||
|  | ||||
| 	var tok token.Type | ||||
|  | ||||
| 	// token text markings | ||||
| 	s.tokStart = s.srcPos.Offset - s.lastCharLen | ||||
|  | ||||
| 	// token position, initial next() is moving the offset by one(size of rune | ||||
| 	// actually), though we are interested with the starting point | ||||
| 	s.tokPos.Offset = s.srcPos.Offset - s.lastCharLen | ||||
| 	if s.srcPos.Column > 0 { | ||||
| 		// common case: last character was not a '\n' | ||||
| 		s.tokPos.Line = s.srcPos.Line | ||||
| 		s.tokPos.Column = s.srcPos.Column | ||||
| 	} else { | ||||
| 		// last character was a '\n' | ||||
| 		// (we cannot be at the beginning of the source | ||||
| 		// since we have called next() at least once) | ||||
| 		s.tokPos.Line = s.srcPos.Line - 1 | ||||
| 		s.tokPos.Column = s.lastLineLen | ||||
| 	} | ||||
|  | ||||
| 	switch { | ||||
| 	case isLetter(ch): | ||||
| 		lit := s.scanIdentifier() | ||||
| 		if lit == "true" || lit == "false" { | ||||
| 			tok = token.BOOL | ||||
| 		} else if lit == "null" { | ||||
| 			tok = token.NULL | ||||
| 		} else { | ||||
| 			s.err("illegal char") | ||||
| 		} | ||||
| 	case isDecimal(ch): | ||||
| 		tok = s.scanNumber(ch) | ||||
| 	default: | ||||
| 		switch ch { | ||||
| 		case eof: | ||||
| 			tok = token.EOF | ||||
| 		case '"': | ||||
| 			tok = token.STRING | ||||
| 			s.scanString() | ||||
| 		case '.': | ||||
| 			tok = token.PERIOD | ||||
| 			ch = s.peek() | ||||
| 			if isDecimal(ch) { | ||||
| 				tok = token.FLOAT | ||||
| 				ch = s.scanMantissa(ch) | ||||
| 				ch = s.scanExponent(ch) | ||||
| 			} | ||||
| 		case '[': | ||||
| 			tok = token.LBRACK | ||||
| 		case ']': | ||||
| 			tok = token.RBRACK | ||||
| 		case '{': | ||||
| 			tok = token.LBRACE | ||||
| 		case '}': | ||||
| 			tok = token.RBRACE | ||||
| 		case ',': | ||||
| 			tok = token.COMMA | ||||
| 		case ':': | ||||
| 			tok = token.COLON | ||||
| 		case '-': | ||||
| 			if isDecimal(s.peek()) { | ||||
| 				ch := s.next() | ||||
| 				tok = s.scanNumber(ch) | ||||
| 			} else { | ||||
| 				s.err("illegal char") | ||||
| 			} | ||||
| 		default: | ||||
| 			s.err("illegal char: " + string(ch)) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// finish token ending | ||||
| 	s.tokEnd = s.srcPos.Offset | ||||
|  | ||||
| 	// create token literal | ||||
| 	var tokenText string | ||||
| 	if s.tokStart >= 0 { | ||||
| 		tokenText = string(s.src[s.tokStart:s.tokEnd]) | ||||
| 	} | ||||
| 	s.tokStart = s.tokEnd // ensure idempotency of tokenText() call | ||||
|  | ||||
| 	return token.Token{ | ||||
| 		Type: tok, | ||||
| 		Pos:  s.tokPos, | ||||
| 		Text: tokenText, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // scanNumber scans a HCL number definition starting with the given rune | ||||
| func (s *Scanner) scanNumber(ch rune) token.Type { | ||||
| 	zero := ch == '0' | ||||
| 	pos := s.srcPos | ||||
|  | ||||
| 	s.scanMantissa(ch) | ||||
| 	ch = s.next() // seek forward | ||||
| 	if ch == 'e' || ch == 'E' { | ||||
| 		ch = s.scanExponent(ch) | ||||
| 		return token.FLOAT | ||||
| 	} | ||||
|  | ||||
| 	if ch == '.' { | ||||
| 		ch = s.scanFraction(ch) | ||||
| 		if ch == 'e' || ch == 'E' { | ||||
| 			ch = s.next() | ||||
| 			ch = s.scanExponent(ch) | ||||
| 		} | ||||
| 		return token.FLOAT | ||||
| 	} | ||||
|  | ||||
| 	if ch != eof { | ||||
| 		s.unread() | ||||
| 	} | ||||
|  | ||||
| 	// If we have a larger number and this is zero, error | ||||
| 	if zero && pos != s.srcPos { | ||||
| 		s.err("numbers cannot start with 0") | ||||
| 	} | ||||
|  | ||||
| 	return token.NUMBER | ||||
| } | ||||
|  | ||||
| // scanMantissa scans the mantissa beginning from the rune. It returns the next | ||||
| // non decimal rune. It's used to determine wheter it's a fraction or exponent. | ||||
| func (s *Scanner) scanMantissa(ch rune) rune { | ||||
| 	scanned := false | ||||
| 	for isDecimal(ch) { | ||||
| 		ch = s.next() | ||||
| 		scanned = true | ||||
| 	} | ||||
|  | ||||
| 	if scanned && ch != eof { | ||||
| 		s.unread() | ||||
| 	} | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // scanFraction scans the fraction after the '.' rune | ||||
| func (s *Scanner) scanFraction(ch rune) rune { | ||||
| 	if ch == '.' { | ||||
| 		ch = s.peek() // we peek just to see if we can move forward | ||||
| 		ch = s.scanMantissa(ch) | ||||
| 	} | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // scanExponent scans the remaining parts of an exponent after the 'e' or 'E' | ||||
| // rune. | ||||
| func (s *Scanner) scanExponent(ch rune) rune { | ||||
| 	if ch == 'e' || ch == 'E' { | ||||
| 		ch = s.next() | ||||
| 		if ch == '-' || ch == '+' { | ||||
| 			ch = s.next() | ||||
| 		} | ||||
| 		ch = s.scanMantissa(ch) | ||||
| 	} | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // scanString scans a quoted string | ||||
| func (s *Scanner) scanString() { | ||||
| 	braces := 0 | ||||
| 	for { | ||||
| 		// '"' opening already consumed | ||||
| 		// read character after quote | ||||
| 		ch := s.next() | ||||
|  | ||||
| 		if ch == '\n' || ch < 0 || ch == eof { | ||||
| 			s.err("literal not terminated") | ||||
| 			return | ||||
| 		} | ||||
|  | ||||
| 		if ch == '"' { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		// If we're going into a ${} then we can ignore quotes for awhile | ||||
| 		if braces == 0 && ch == '$' && s.peek() == '{' { | ||||
| 			braces++ | ||||
| 			s.next() | ||||
| 		} else if braces > 0 && ch == '{' { | ||||
| 			braces++ | ||||
| 		} | ||||
| 		if braces > 0 && ch == '}' { | ||||
| 			braces-- | ||||
| 		} | ||||
|  | ||||
| 		if ch == '\\' { | ||||
| 			s.scanEscape() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // scanEscape scans an escape sequence | ||||
| func (s *Scanner) scanEscape() rune { | ||||
| 	// http://en.cppreference.com/w/cpp/language/escape | ||||
| 	ch := s.next() // read character after '/' | ||||
| 	switch ch { | ||||
| 	case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"': | ||||
| 		// nothing to do | ||||
| 	case '0', '1', '2', '3', '4', '5', '6', '7': | ||||
| 		// octal notation | ||||
| 		ch = s.scanDigits(ch, 8, 3) | ||||
| 	case 'x': | ||||
| 		// hexademical notation | ||||
| 		ch = s.scanDigits(s.next(), 16, 2) | ||||
| 	case 'u': | ||||
| 		// universal character name | ||||
| 		ch = s.scanDigits(s.next(), 16, 4) | ||||
| 	case 'U': | ||||
| 		// universal character name | ||||
| 		ch = s.scanDigits(s.next(), 16, 8) | ||||
| 	default: | ||||
| 		s.err("illegal char escape") | ||||
| 	} | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // scanDigits scans a rune with the given base for n times. For example an | ||||
| // octal notation \184 would yield in scanDigits(ch, 8, 3) | ||||
| func (s *Scanner) scanDigits(ch rune, base, n int) rune { | ||||
| 	for n > 0 && digitVal(ch) < base { | ||||
| 		ch = s.next() | ||||
| 		n-- | ||||
| 	} | ||||
| 	if n > 0 { | ||||
| 		s.err("illegal char escape") | ||||
| 	} | ||||
|  | ||||
| 	// we scanned all digits, put the last non digit char back | ||||
| 	s.unread() | ||||
| 	return ch | ||||
| } | ||||
|  | ||||
| // scanIdentifier scans an identifier and returns the literal string | ||||
| func (s *Scanner) scanIdentifier() string { | ||||
| 	offs := s.srcPos.Offset - s.lastCharLen | ||||
| 	ch := s.next() | ||||
| 	for isLetter(ch) || isDigit(ch) || ch == '-' { | ||||
| 		ch = s.next() | ||||
| 	} | ||||
|  | ||||
| 	if ch != eof { | ||||
| 		s.unread() // we got identifier, put back latest char | ||||
| 	} | ||||
|  | ||||
| 	return string(s.src[offs:s.srcPos.Offset]) | ||||
| } | ||||
|  | ||||
| // recentPosition returns the position of the character immediately after the | ||||
| // character or token returned by the last call to Scan. | ||||
| func (s *Scanner) recentPosition() (pos token.Pos) { | ||||
| 	pos.Offset = s.srcPos.Offset - s.lastCharLen | ||||
| 	switch { | ||||
| 	case s.srcPos.Column > 0: | ||||
| 		// common case: last character was not a '\n' | ||||
| 		pos.Line = s.srcPos.Line | ||||
| 		pos.Column = s.srcPos.Column | ||||
| 	case s.lastLineLen > 0: | ||||
| 		// last character was a '\n' | ||||
| 		// (we cannot be at the beginning of the source | ||||
| 		// since we have called next() at least once) | ||||
| 		pos.Line = s.srcPos.Line - 1 | ||||
| 		pos.Column = s.lastLineLen | ||||
| 	default: | ||||
| 		// at the beginning of the source | ||||
| 		pos.Line = 1 | ||||
| 		pos.Column = 1 | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // err prints the error of any scanning to s.Error function. If the function is | ||||
| // not defined, by default it prints them to os.Stderr | ||||
| func (s *Scanner) err(msg string) { | ||||
| 	s.ErrorCount++ | ||||
| 	pos := s.recentPosition() | ||||
|  | ||||
| 	if s.Error != nil { | ||||
| 		s.Error(pos, msg) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg) | ||||
| } | ||||
|  | ||||
| // isHexadecimal returns true if the given rune is a letter | ||||
| func isLetter(ch rune) bool { | ||||
| 	return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) | ||||
| } | ||||
|  | ||||
| // isHexadecimal returns true if the given rune is a decimal digit | ||||
| func isDigit(ch rune) bool { | ||||
| 	return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) | ||||
| } | ||||
|  | ||||
| // isHexadecimal returns true if the given rune is a decimal number | ||||
| func isDecimal(ch rune) bool { | ||||
| 	return '0' <= ch && ch <= '9' | ||||
| } | ||||
|  | ||||
| // isHexadecimal returns true if the given rune is an hexadecimal number | ||||
| func isHexadecimal(ch rune) bool { | ||||
| 	return '0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'f' || 'A' <= ch && ch <= 'F' | ||||
| } | ||||
|  | ||||
| // isWhitespace returns true if the rune is a space, tab, newline or carriage return | ||||
| func isWhitespace(ch rune) bool { | ||||
| 	return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' | ||||
| } | ||||
|  | ||||
| // digitVal returns the integer value of a given octal,decimal or hexadecimal rune | ||||
| func digitVal(ch rune) int { | ||||
| 	switch { | ||||
| 	case '0' <= ch && ch <= '9': | ||||
| 		return int(ch - '0') | ||||
| 	case 'a' <= ch && ch <= 'f': | ||||
| 		return int(ch - 'a' + 10) | ||||
| 	case 'A' <= ch && ch <= 'F': | ||||
| 		return int(ch - 'A' + 10) | ||||
| 	} | ||||
| 	return 16 // larger than any legal digit val | ||||
| } | ||||
							
								
								
									
										46
									
								
								vendor/github.com/hashicorp/hcl/json/token/position.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/hashicorp/hcl/json/token/position.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| package token | ||||
|  | ||||
| import "fmt" | ||||
|  | ||||
| // Pos describes an arbitrary source position | ||||
| // including the file, line, and column location. | ||||
| // A Position is valid if the line number is > 0. | ||||
| type Pos struct { | ||||
| 	Filename string // filename, if any | ||||
| 	Offset   int    // offset, starting at 0 | ||||
| 	Line     int    // line number, starting at 1 | ||||
| 	Column   int    // column number, starting at 1 (character count) | ||||
| } | ||||
|  | ||||
| // IsValid returns true if the position is valid. | ||||
| func (p *Pos) IsValid() bool { return p.Line > 0 } | ||||
|  | ||||
| // String returns a string in one of several forms: | ||||
| // | ||||
| //	file:line:column    valid position with file name | ||||
| //	line:column         valid position without file name | ||||
| //	file                invalid position with file name | ||||
| //	-                   invalid position without file name | ||||
| func (p Pos) String() string { | ||||
| 	s := p.Filename | ||||
| 	if p.IsValid() { | ||||
| 		if s != "" { | ||||
| 			s += ":" | ||||
| 		} | ||||
| 		s += fmt.Sprintf("%d:%d", p.Line, p.Column) | ||||
| 	} | ||||
| 	if s == "" { | ||||
| 		s = "-" | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // Before reports whether the position p is before u. | ||||
| func (p Pos) Before(u Pos) bool { | ||||
| 	return u.Offset > p.Offset || u.Line > p.Line | ||||
| } | ||||
|  | ||||
| // After reports whether the position p is after u. | ||||
| func (p Pos) After(u Pos) bool { | ||||
| 	return u.Offset < p.Offset || u.Line < p.Line | ||||
| } | ||||
							
								
								
									
										118
									
								
								vendor/github.com/hashicorp/hcl/json/token/token.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								vendor/github.com/hashicorp/hcl/json/token/token.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| package token | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
|  | ||||
| 	hcltoken "github.com/hashicorp/hcl/hcl/token" | ||||
| ) | ||||
|  | ||||
| // Token defines a single HCL token which can be obtained via the Scanner | ||||
| type Token struct { | ||||
| 	Type Type | ||||
| 	Pos  Pos | ||||
| 	Text string | ||||
| } | ||||
|  | ||||
| // Type is the set of lexical tokens of the HCL (HashiCorp Configuration Language) | ||||
| type Type int | ||||
|  | ||||
| const ( | ||||
| 	// Special tokens | ||||
| 	ILLEGAL Type = iota | ||||
| 	EOF | ||||
|  | ||||
| 	identifier_beg | ||||
| 	literal_beg | ||||
| 	NUMBER // 12345 | ||||
| 	FLOAT  // 123.45 | ||||
| 	BOOL   // true,false | ||||
| 	STRING // "abc" | ||||
| 	NULL   // null | ||||
| 	literal_end | ||||
| 	identifier_end | ||||
|  | ||||
| 	operator_beg | ||||
| 	LBRACK // [ | ||||
| 	LBRACE // { | ||||
| 	COMMA  // , | ||||
| 	PERIOD // . | ||||
| 	COLON  // : | ||||
|  | ||||
| 	RBRACK // ] | ||||
| 	RBRACE // } | ||||
|  | ||||
| 	operator_end | ||||
| ) | ||||
|  | ||||
| var tokens = [...]string{ | ||||
| 	ILLEGAL: "ILLEGAL", | ||||
|  | ||||
| 	EOF: "EOF", | ||||
|  | ||||
| 	NUMBER: "NUMBER", | ||||
| 	FLOAT:  "FLOAT", | ||||
| 	BOOL:   "BOOL", | ||||
| 	STRING: "STRING", | ||||
| 	NULL:   "NULL", | ||||
|  | ||||
| 	LBRACK: "LBRACK", | ||||
| 	LBRACE: "LBRACE", | ||||
| 	COMMA:  "COMMA", | ||||
| 	PERIOD: "PERIOD", | ||||
| 	COLON:  "COLON", | ||||
|  | ||||
| 	RBRACK: "RBRACK", | ||||
| 	RBRACE: "RBRACE", | ||||
| } | ||||
|  | ||||
| // String returns the string corresponding to the token tok. | ||||
| func (t Type) String() string { | ||||
| 	s := "" | ||||
| 	if 0 <= t && t < Type(len(tokens)) { | ||||
| 		s = tokens[t] | ||||
| 	} | ||||
| 	if s == "" { | ||||
| 		s = "token(" + strconv.Itoa(int(t)) + ")" | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // IsIdentifier returns true for tokens corresponding to identifiers and basic | ||||
| // type literals; it returns false otherwise. | ||||
| func (t Type) IsIdentifier() bool { return identifier_beg < t && t < identifier_end } | ||||
|  | ||||
| // IsLiteral returns true for tokens corresponding to basic type literals; it | ||||
| // returns false otherwise. | ||||
| func (t Type) IsLiteral() bool { return literal_beg < t && t < literal_end } | ||||
|  | ||||
| // IsOperator returns true for tokens corresponding to operators and | ||||
| // delimiters; it returns false otherwise. | ||||
| func (t Type) IsOperator() bool { return operator_beg < t && t < operator_end } | ||||
|  | ||||
| // String returns the token's literal text. Note that this is only | ||||
| // applicable for certain token types, such as token.IDENT, | ||||
| // token.STRING, etc.. | ||||
| func (t Token) String() string { | ||||
| 	return fmt.Sprintf("%s %s %s", t.Pos.String(), t.Type.String(), t.Text) | ||||
| } | ||||
|  | ||||
| // HCLToken converts this token to an HCL token. | ||||
| // | ||||
| // The token type must be a literal type or this will panic. | ||||
| func (t Token) HCLToken() hcltoken.Token { | ||||
| 	switch t.Type { | ||||
| 	case BOOL: | ||||
| 		return hcltoken.Token{Type: hcltoken.BOOL, Text: t.Text} | ||||
| 	case FLOAT: | ||||
| 		return hcltoken.Token{Type: hcltoken.FLOAT, Text: t.Text} | ||||
| 	case NULL: | ||||
| 		return hcltoken.Token{Type: hcltoken.STRING, Text: ""} | ||||
| 	case NUMBER: | ||||
| 		return hcltoken.Token{Type: hcltoken.NUMBER, Text: t.Text} | ||||
| 	case STRING: | ||||
| 		return hcltoken.Token{Type: hcltoken.STRING, Text: t.Text, JSON: true} | ||||
| 	default: | ||||
| 		panic(fmt.Sprintf("unimplemented HCLToken for type: %s", t.Type)) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										38
									
								
								vendor/github.com/hashicorp/hcl/lex.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								vendor/github.com/hashicorp/hcl/lex.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | ||||
| package hcl | ||||
|  | ||||
| import ( | ||||
| 	"unicode" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| type lexModeValue byte | ||||
|  | ||||
| const ( | ||||
| 	lexModeUnknown lexModeValue = iota | ||||
| 	lexModeHcl | ||||
| 	lexModeJson | ||||
| ) | ||||
|  | ||||
| // lexMode returns whether we're going to be parsing in JSON | ||||
| // mode or HCL mode. | ||||
| func lexMode(v []byte) lexModeValue { | ||||
| 	var ( | ||||
| 		r      rune | ||||
| 		w      int | ||||
| 		offset int | ||||
| 	) | ||||
|  | ||||
| 	for { | ||||
| 		r, w = utf8.DecodeRune(v[offset:]) | ||||
| 		offset += w | ||||
| 		if unicode.IsSpace(r) { | ||||
| 			continue | ||||
| 		} | ||||
| 		if r == '{' { | ||||
| 			return lexModeJson | ||||
| 		} | ||||
| 		break | ||||
| 	} | ||||
|  | ||||
| 	return lexModeHcl | ||||
| } | ||||
							
								
								
									
										39
									
								
								vendor/github.com/hashicorp/hcl/parse.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								vendor/github.com/hashicorp/hcl/parse.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| package hcl | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/hashicorp/hcl/hcl/ast" | ||||
| 	hclParser "github.com/hashicorp/hcl/hcl/parser" | ||||
| 	jsonParser "github.com/hashicorp/hcl/json/parser" | ||||
| ) | ||||
|  | ||||
| // ParseBytes accepts as input byte slice and returns ast tree. | ||||
| // | ||||
| // Input can be either JSON or HCL | ||||
| func ParseBytes(in []byte) (*ast.File, error) { | ||||
| 	return parse(in) | ||||
| } | ||||
|  | ||||
| // ParseString accepts input as a string and returns ast tree. | ||||
| func ParseString(input string) (*ast.File, error) { | ||||
| 	return parse([]byte(input)) | ||||
| } | ||||
|  | ||||
| func parse(in []byte) (*ast.File, error) { | ||||
| 	switch lexMode(in) { | ||||
| 	case lexModeHcl: | ||||
| 		return hclParser.Parse(in) | ||||
| 	case lexModeJson: | ||||
| 		return jsonParser.Parse(in) | ||||
| 	} | ||||
|  | ||||
| 	return nil, fmt.Errorf("unknown config format") | ||||
| } | ||||
|  | ||||
| // Parse parses the given input and returns the root object. | ||||
| // | ||||
| // The input format can be either HCL or JSON. | ||||
| func Parse(input string) (*ast.File, error) { | ||||
| 	return parse([]byte(input)) | ||||
| } | ||||
							
								
								
									
										201
									
								
								vendor/github.com/inconshreveable/mousetrap/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								vendor/github.com/inconshreveable/mousetrap/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
|  | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
|  | ||||
|    1. Definitions. | ||||
|  | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
|  | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
|  | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
|  | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
|  | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
|  | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
|  | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
|  | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
|  | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
|  | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
|  | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
|  | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
|  | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
|  | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
|  | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
|  | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
|  | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
|  | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
|  | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
|  | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
|  | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
|  | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
|  | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
|  | ||||
|    END OF TERMS AND CONDITIONS | ||||
|  | ||||
|    APPENDIX: How to apply the Apache License to your work. | ||||
|  | ||||
|       To apply the Apache License to your work, attach the following | ||||
|       boilerplate notice, with the fields enclosed by brackets "[]" | ||||
|       replaced with your own identifying information. (Don't include | ||||
|       the brackets!)  The text should be enclosed in the appropriate | ||||
|       comment syntax for the file format. We also recommend that a | ||||
|       file or class name and description of purpose be included on the | ||||
|       same "printed page" as the copyright notice for easier | ||||
|       identification within third-party archives. | ||||
|  | ||||
|    Copyright 2022 Alan Shreve (@inconshreveable) | ||||
|  | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
|  | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
							
								
								
									
										23
									
								
								vendor/github.com/inconshreveable/mousetrap/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/inconshreveable/mousetrap/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| # mousetrap | ||||
|  | ||||
| mousetrap is a tiny library that answers a single question. | ||||
|  | ||||
| On a Windows machine, was the process invoked by someone double clicking on | ||||
| the executable file while browsing in explorer? | ||||
|  | ||||
| ### Motivation | ||||
|  | ||||
| Windows developers unfamiliar with command line tools will often "double-click" | ||||
| the executable for a tool. Because most CLI tools print the help and then exit | ||||
| when invoked without arguments, this is often very frustrating for those users. | ||||
|  | ||||
| mousetrap provides a way to detect these invocations so that you can provide | ||||
| more helpful behavior and instructions on how to run the CLI tool. To see what | ||||
| this looks like, both from an organizational and a technical perspective, see | ||||
| https://inconshreveable.com/09-09-2014/sweat-the-small-stuff/ | ||||
|  | ||||
| ### The interface | ||||
|  | ||||
| The library exposes a single interface: | ||||
|  | ||||
|     func StartedByExplorer() (bool) | ||||
							
								
								
									
										16
									
								
								vendor/github.com/inconshreveable/mousetrap/trap_others.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								vendor/github.com/inconshreveable/mousetrap/trap_others.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| //go:build !windows | ||||
| // +build !windows | ||||
|  | ||||
| package mousetrap | ||||
|  | ||||
| // StartedByExplorer returns true if the program was invoked by the user | ||||
| // double-clicking on the executable from explorer.exe | ||||
| // | ||||
| // It is conservative and returns false if any of the internal calls fail. | ||||
| // It does not guarantee that the program was run from a terminal. It only can tell you | ||||
| // whether it was launched from explorer.exe | ||||
| // | ||||
| // On non-Windows platforms, it always returns false. | ||||
| func StartedByExplorer() bool { | ||||
| 	return false | ||||
| } | ||||
							
								
								
									
										42
									
								
								vendor/github.com/inconshreveable/mousetrap/trap_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/inconshreveable/mousetrap/trap_windows.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| package mousetrap | ||||
|  | ||||
| import ( | ||||
| 	"syscall" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) { | ||||
| 	snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer syscall.CloseHandle(snapshot) | ||||
| 	var procEntry syscall.ProcessEntry32 | ||||
| 	procEntry.Size = uint32(unsafe.Sizeof(procEntry)) | ||||
| 	if err = syscall.Process32First(snapshot, &procEntry); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	for { | ||||
| 		if procEntry.ProcessID == uint32(pid) { | ||||
| 			return &procEntry, nil | ||||
| 		} | ||||
| 		err = syscall.Process32Next(snapshot, &procEntry) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // StartedByExplorer returns true if the program was invoked by the user double-clicking | ||||
| // on the executable from explorer.exe | ||||
| // | ||||
| // It is conservative and returns false if any of the internal calls fail. | ||||
| // It does not guarantee that the program was run from a terminal. It only can tell you | ||||
| // whether it was launched from explorer.exe | ||||
| func StartedByExplorer() bool { | ||||
| 	pe, err := getProcessEntry(syscall.Getppid()) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:]) | ||||
| } | ||||
							
								
								
									
										6
									
								
								vendor/github.com/magiconair/properties/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/magiconair/properties/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| *.sublime-project | ||||
| *.sublime-workspace | ||||
| *.un~ | ||||
| *.swp | ||||
| .idea/ | ||||
| *.iml | ||||
							
								
								
									
										205
									
								
								vendor/github.com/magiconair/properties/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								vendor/github.com/magiconair/properties/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,205 @@ | ||||
| ## Changelog | ||||
|  | ||||
| ### [1.8.7](https://github.com/magiconair/properties/tree/v1.8.7) - 08 Dec 2022 | ||||
|  | ||||
|  * [PR #65](https://github.com/magiconair/properties/pull/65): Speedup Merge | ||||
|  | ||||
|    Thanks to [@AdityaVallabh](https://github.com/AdityaVallabh) for the patch. | ||||
|  | ||||
|  * [PR #66](https://github.com/magiconair/properties/pull/66): use github actions | ||||
|  | ||||
| ### [1.8.6](https://github.com/magiconair/properties/tree/v1.8.6) - 23 Feb 2022 | ||||
|  | ||||
|  * [PR #57](https://github.com/magiconair/properties/pull/57):Fix "unreachable code" lint error | ||||
|  | ||||
|    Thanks to [@ellie](https://github.com/ellie) for the patch. | ||||
|  | ||||
|  * [PR #63](https://github.com/magiconair/properties/pull/63): Make TestMustGetParsedDuration backwards compatible | ||||
|  | ||||
|    This patch ensures that the `TestMustGetParsedDuration` still works with `go1.3` to make the | ||||
|    author happy until it affects real users. | ||||
|  | ||||
|    Thanks to [@maage](https://github.com/maage) for the patch. | ||||
|  | ||||
| ### [1.8.5](https://github.com/magiconair/properties/tree/v1.8.5) - 24 Mar 2021 | ||||
|  | ||||
|  * [PR #55](https://github.com/magiconair/properties/pull/55): Fix: Encoding Bug in Comments | ||||
|  | ||||
|    When reading comments \ are loaded correctly, but when writing they are then | ||||
|    replaced by \\. This leads to wrong comments when writing and reading multiple times. | ||||
|  | ||||
|    Thanks to [@doxsch](https://github.com/doxsch) for the patch. | ||||
|  | ||||
| ### [1.8.4](https://github.com/magiconair/properties/tree/v1.8.4) - 23 Sep 2020 | ||||
|  | ||||
|  * [PR #50](https://github.com/magiconair/properties/pull/50): enhance error message for circular references | ||||
|  | ||||
|    Thanks to [@sriv](https://github.com/sriv) for the patch. | ||||
|  | ||||
| ### [1.8.3](https://github.com/magiconair/properties/tree/v1.8.3) - 14 Sep 2020 | ||||
|  | ||||
|  * [PR #49](https://github.com/magiconair/properties/pull/49): Include the key in error message causing the circular reference | ||||
|  | ||||
|    The change is include the key in the error message which is causing the circular | ||||
|    reference when parsing/loading the properties files. | ||||
|  | ||||
|    Thanks to [@haroon-sheikh](https://github.com/haroon-sheikh) for the patch. | ||||
|  | ||||
| ### [1.8.2](https://github.com/magiconair/properties/tree/v1.8.2) - 25 Aug 2020 | ||||
|  | ||||
|  * [PR #36](https://github.com/magiconair/properties/pull/36): Escape backslash on write | ||||
|  | ||||
|    This patch ensures that backslashes are escaped on write. Existing applications which | ||||
|    rely on the old behavior may need to be updated. | ||||
|  | ||||
|    Thanks to [@apesternikov](https://github.com/apesternikov) for the patch. | ||||
|  | ||||
|  * [PR #42](https://github.com/magiconair/properties/pull/42): Made Content-Type check whitespace agnostic in LoadURL() | ||||
|  | ||||
|    Thanks to [@aliras1](https://github.com/aliras1) for the patch. | ||||
|  | ||||
|  * [PR #41](https://github.com/magiconair/properties/pull/41): Make key/value separator configurable on Write() | ||||
|  | ||||
|    Thanks to [@mkjor](https://github.com/mkjor) for the patch. | ||||
|  | ||||
|  * [PR #40](https://github.com/magiconair/properties/pull/40): Add method to return a sorted list of keys | ||||
|  | ||||
|    Thanks to [@mkjor](https://github.com/mkjor) for the patch. | ||||
|  | ||||
| ### [1.8.1](https://github.com/magiconair/properties/tree/v1.8.1) - 10 May 2019 | ||||
|  | ||||
|  * [PR #35](https://github.com/magiconair/properties/pull/35): Close body always after request | ||||
|  | ||||
|    This patch ensures that in `LoadURL` the response body is always closed. | ||||
|  | ||||
|    Thanks to [@liubog2008](https://github.com/liubog2008) for the patch. | ||||
|  | ||||
| ### [1.8](https://github.com/magiconair/properties/tree/v1.8) - 15 May 2018 | ||||
|  | ||||
|  * [PR #26](https://github.com/magiconair/properties/pull/26): Disable expansion during loading | ||||
|  | ||||
|    This adds the option to disable property expansion during loading. | ||||
|  | ||||
|    Thanks to [@kmala](https://github.com/kmala) for the patch. | ||||
|  | ||||
| ### [1.7.6](https://github.com/magiconair/properties/tree/v1.7.6) - 14 Feb 2018 | ||||
|  | ||||
|  * [PR #29](https://github.com/magiconair/properties/pull/29): Reworked expansion logic to handle more complex cases. | ||||
|  | ||||
|    See PR for an example. | ||||
|  | ||||
|    Thanks to [@yobert](https://github.com/yobert) for the fix. | ||||
|  | ||||
| ### [1.7.5](https://github.com/magiconair/properties/tree/v1.7.5) - 13 Feb 2018 | ||||
|  | ||||
|  * [PR #28](https://github.com/magiconair/properties/pull/28): Support duplicate expansions in the same value | ||||
|  | ||||
|    Values which expand the same key multiple times (e.g. `key=${a} ${a}`) will no longer fail | ||||
|    with a `circular reference error`. | ||||
|  | ||||
|    Thanks to [@yobert](https://github.com/yobert) for the fix. | ||||
|  | ||||
| ### [1.7.4](https://github.com/magiconair/properties/tree/v1.7.4) - 31 Oct 2017 | ||||
|  | ||||
|  * [Issue #23](https://github.com/magiconair/properties/issues/23): Ignore blank lines with whitespaces | ||||
|  | ||||
|  * [PR #24](https://github.com/magiconair/properties/pull/24): Update keys when DisableExpansion is enabled | ||||
|  | ||||
|    Thanks to [@mgurov](https://github.com/mgurov) for the fix. | ||||
|  | ||||
| ### [1.7.3](https://github.com/magiconair/properties/tree/v1.7.3) - 10 Jul 2017 | ||||
|  | ||||
|  * [Issue #17](https://github.com/magiconair/properties/issues/17): Add [SetValue()](http://godoc.org/github.com/magiconair/properties#Properties.SetValue) method to set values generically | ||||
|  * [Issue #22](https://github.com/magiconair/properties/issues/22): Add [LoadMap()](http://godoc.org/github.com/magiconair/properties#LoadMap) function to load properties from a string map | ||||
|  | ||||
| ### [1.7.2](https://github.com/magiconair/properties/tree/v1.7.2) - 20 Mar 2017 | ||||
|  | ||||
|  * [Issue #15](https://github.com/magiconair/properties/issues/15): Drop gocheck dependency | ||||
|  * [PR #21](https://github.com/magiconair/properties/pull/21): Add [Map()](http://godoc.org/github.com/magiconair/properties#Properties.Map) and [FilterFunc()](http://godoc.org/github.com/magiconair/properties#Properties.FilterFunc) | ||||
|  | ||||
| ### [1.7.1](https://github.com/magiconair/properties/tree/v1.7.1) - 13 Jan 2017 | ||||
|  | ||||
|  * [Issue #14](https://github.com/magiconair/properties/issues/14): Decouple TestLoadExpandedFile from `$USER` | ||||
|  * [PR #12](https://github.com/magiconair/properties/pull/12): Load from files and URLs | ||||
|  * [PR #16](https://github.com/magiconair/properties/pull/16): Keep gofmt happy | ||||
|  * [PR #18](https://github.com/magiconair/properties/pull/18): Fix Delete() function | ||||
|  | ||||
| ### [1.7.0](https://github.com/magiconair/properties/tree/v1.7.0) - 20 Mar 2016 | ||||
|  | ||||
|  * [Issue #10](https://github.com/magiconair/properties/issues/10): Add [LoadURL,LoadURLs,MustLoadURL,MustLoadURLs](http://godoc.org/github.com/magiconair/properties#LoadURL) method to load properties from a URL. | ||||
|  * [Issue #11](https://github.com/magiconair/properties/issues/11): Add [LoadString,MustLoadString](http://godoc.org/github.com/magiconair/properties#LoadString) method to load properties from an UTF8 string. | ||||
|  * [PR #8](https://github.com/magiconair/properties/pull/8): Add [MustFlag](http://godoc.org/github.com/magiconair/properties#Properties.MustFlag) method to provide overrides via command line flags. (@pascaldekloe) | ||||
|  | ||||
| ### [1.6.0](https://github.com/magiconair/properties/tree/v1.6.0) - 11 Dec 2015 | ||||
|  | ||||
|  * Add [Decode](http://godoc.org/github.com/magiconair/properties#Properties.Decode) method to populate struct from properties via tags. | ||||
|  | ||||
| ### [1.5.6](https://github.com/magiconair/properties/tree/v1.5.6) - 18 Oct 2015 | ||||
|  | ||||
|  * Vendored in gopkg.in/check.v1 | ||||
|  | ||||
| ### [1.5.5](https://github.com/magiconair/properties/tree/v1.5.5) - 31 Jul 2015 | ||||
|  | ||||
|  * [PR #6](https://github.com/magiconair/properties/pull/6): Add [Delete](http://godoc.org/github.com/magiconair/properties#Properties.Delete) method to remove keys including comments. (@gerbenjacobs) | ||||
|  | ||||
| ### [1.5.4](https://github.com/magiconair/properties/tree/v1.5.4) - 23 Jun 2015 | ||||
|  | ||||
|  * [Issue #5](https://github.com/magiconair/properties/issues/5): Allow disabling of property expansion [DisableExpansion](http://godoc.org/github.com/magiconair/properties#Properties.DisableExpansion). When property expansion is disabled Properties become a simple key/value store and don't check for circular references. | ||||
|  | ||||
| ### [1.5.3](https://github.com/magiconair/properties/tree/v1.5.3) - 02 Jun 2015 | ||||
|  | ||||
|  * [Issue #4](https://github.com/magiconair/properties/issues/4): Maintain key order in [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) and [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp) | ||||
|  | ||||
| ### [1.5.2](https://github.com/magiconair/properties/tree/v1.5.2) - 10 Apr 2015 | ||||
|  | ||||
|  * [Issue #3](https://github.com/magiconair/properties/issues/3): Don't print comments in [WriteComment()](http://godoc.org/github.com/magiconair/properties#Properties.WriteComment) if they are all empty | ||||
|  * Add clickable links to README | ||||
|  | ||||
| ### [1.5.1](https://github.com/magiconair/properties/tree/v1.5.1) - 08 Dec 2014 | ||||
|  | ||||
|  * Added [GetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.GetParsedDuration) and [MustGetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.MustGetParsedDuration) for values specified compatible with | ||||
|    [time.ParseDuration()](http://golang.org/pkg/time/#ParseDuration). | ||||
|  | ||||
| ### [1.5.0](https://github.com/magiconair/properties/tree/v1.5.0) - 18 Nov 2014 | ||||
|  | ||||
|  * Added support for single and multi-line comments (reading, writing and updating) | ||||
|  * The order of keys is now preserved | ||||
|  * Calling [Set()](http://godoc.org/github.com/magiconair/properties#Properties.Set) with an empty key now silently ignores the call and does not create a new entry | ||||
|  * Added a [MustSet()](http://godoc.org/github.com/magiconair/properties#Properties.MustSet) method | ||||
|  * Migrated test library from launchpad.net/gocheck to [gopkg.in/check.v1](http://gopkg.in/check.v1) | ||||
|  | ||||
| ### [1.4.2](https://github.com/magiconair/properties/tree/v1.4.2) - 15 Nov 2014 | ||||
|  | ||||
|  * [Issue #2](https://github.com/magiconair/properties/issues/2): Fixed goroutine leak in parser which created two lexers but cleaned up only one | ||||
|  | ||||
| ### [1.4.1](https://github.com/magiconair/properties/tree/v1.4.1) - 13 Nov 2014 | ||||
|  | ||||
|  * [Issue #1](https://github.com/magiconair/properties/issues/1): Fixed bug in Keys() method which returned an empty string | ||||
|  | ||||
| ### [1.4.0](https://github.com/magiconair/properties/tree/v1.4.0) - 23 Sep 2014 | ||||
|  | ||||
|  * Added [Keys()](http://godoc.org/github.com/magiconair/properties#Properties.Keys) to get the keys | ||||
|  * Added [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp) and [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) to get a subset of the properties | ||||
|  | ||||
| ### [1.3.0](https://github.com/magiconair/properties/tree/v1.3.0) - 18 Mar 2014 | ||||
|  | ||||
| * Added support for time.Duration | ||||
| * Made MustXXX() failure beha[ior configurable (log.Fatal, panic](https://github.com/magiconair/properties/tree/vior configurable (log.Fatal, panic) - custom) | ||||
| * Changed default of MustXXX() failure from panic to log.Fatal | ||||
|  | ||||
| ### [1.2.0](https://github.com/magiconair/properties/tree/v1.2.0) - 05 Mar 2014 | ||||
|  | ||||
| * Added MustGet... functions | ||||
| * Added support for int and uint with range checks on 32 bit platforms | ||||
|  | ||||
| ### [1.1.0](https://github.com/magiconair/properties/tree/v1.1.0) - 20 Jan 2014 | ||||
|  | ||||
| * Renamed from goproperties to properties | ||||
| * Added support for expansion of environment vars in | ||||
|   filenames and value expressions | ||||
| * Fixed bug where value expressions were not at the | ||||
|   start of the string | ||||
|  | ||||
| ### [1.0.0](https://github.com/magiconair/properties/tree/v1.0.0) - 7 Jan 2014 | ||||
|  | ||||
| * Initial release | ||||
							
								
								
									
										24
									
								
								vendor/github.com/magiconair/properties/LICENSE.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/magiconair/properties/LICENSE.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| Copyright (c) 2013-2020, Frank Schroeder | ||||
|  | ||||
| All rights reserved. | ||||
|  | ||||
| Redistribution and use in source and binary forms, with or without | ||||
| modification, are permitted provided that the following conditions are met: | ||||
|  | ||||
|  * Redistributions of source code must retain the above copyright notice, this | ||||
|    list of conditions and the following disclaimer. | ||||
|  | ||||
|  * Redistributions in binary form must reproduce the above copyright notice, | ||||
|    this list of conditions and the following disclaimer in the documentation | ||||
|    and/or other materials provided with the distribution. | ||||
|  | ||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||
| ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||
| WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR | ||||
| ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
| (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
| LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||||
| ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
							
								
								
									
										128
									
								
								vendor/github.com/magiconair/properties/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								vendor/github.com/magiconair/properties/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| [](https://github.com/magiconair/properties/releases) | ||||
| [](https://travis-ci.org/magiconair/properties) | ||||
| [](https://raw.githubusercontent.com/magiconair/properties/master/LICENSE) | ||||
| [](http://godoc.org/github.com/magiconair/properties) | ||||
|  | ||||
| # Overview | ||||
|  | ||||
| #### Please run `git pull --tags` to update the tags. See [below](#updated-git-tags) why. | ||||
|  | ||||
| properties is a Go library for reading and writing properties files. | ||||
|  | ||||
| It supports reading from multiple files or URLs and Spring style recursive | ||||
| property expansion of expressions like `${key}` to their corresponding value. | ||||
| Value expressions can refer to other keys like in `${key}` or to environment | ||||
| variables like in `${USER}`.  Filenames can also contain environment variables | ||||
| like in `/home/${USER}/myapp.properties`. | ||||
|  | ||||
| Properties can be decoded into structs, maps, arrays and values through | ||||
| struct tags. | ||||
|  | ||||
| Comments and the order of keys are preserved. Comments can be modified | ||||
| and can be written to the output. | ||||
|  | ||||
| The properties library supports both ISO-8859-1 and UTF-8 encoded data. | ||||
|  | ||||
| Starting from version 1.3.0 the behavior of the MustXXX() functions is | ||||
| configurable by providing a custom `ErrorHandler` function. The default has | ||||
| changed from `panic` to `log.Fatal` but this is configurable and custom | ||||
| error handling functions can be provided. See the package documentation for | ||||
| details. | ||||
|  | ||||
| Read the full documentation on [](http://godoc.org/github.com/magiconair/properties) | ||||
|  | ||||
| ## Getting Started | ||||
|  | ||||
| ```go | ||||
| import ( | ||||
| 	"flag" | ||||
| 	"github.com/magiconair/properties" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	// init from a file | ||||
| 	p := properties.MustLoadFile("${HOME}/config.properties", properties.UTF8) | ||||
|  | ||||
| 	// or multiple files | ||||
| 	p = properties.MustLoadFiles([]string{ | ||||
| 			"${HOME}/config.properties", | ||||
| 			"${HOME}/config-${USER}.properties", | ||||
| 		}, properties.UTF8, true) | ||||
|  | ||||
| 	// or from a map | ||||
| 	p = properties.LoadMap(map[string]string{"key": "value", "abc": "def"}) | ||||
|  | ||||
| 	// or from a string | ||||
| 	p = properties.MustLoadString("key=value\nabc=def") | ||||
|  | ||||
| 	// or from a URL | ||||
| 	p = properties.MustLoadURL("http://host/path") | ||||
|  | ||||
| 	// or from multiple URLs | ||||
| 	p = properties.MustLoadURL([]string{ | ||||
| 			"http://host/config", | ||||
| 			"http://host/config-${USER}", | ||||
| 		}, true) | ||||
|  | ||||
| 	// or from flags | ||||
| 	p.MustFlag(flag.CommandLine) | ||||
|  | ||||
| 	// get values through getters | ||||
| 	host := p.MustGetString("host") | ||||
| 	port := p.GetInt("port", 8080) | ||||
|  | ||||
| 	// or through Decode | ||||
| 	type Config struct { | ||||
| 		Host    string        `properties:"host"` | ||||
| 		Port    int           `properties:"port,default=9000"` | ||||
| 		Accept  []string      `properties:"accept,default=image/png;image;gif"` | ||||
| 		Timeout time.Duration `properties:"timeout,default=5s"` | ||||
| 	} | ||||
| 	var cfg Config | ||||
| 	if err := p.Decode(&cfg); err != nil { | ||||
| 		log.Fatal(err) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ``` | ||||
|  | ||||
| ## Installation and Upgrade | ||||
|  | ||||
| ``` | ||||
| $ go get -u github.com/magiconair/properties | ||||
| ``` | ||||
|  | ||||
| ## License | ||||
|  | ||||
| 2 clause BSD license. See [LICENSE](https://github.com/magiconair/properties/blob/master/LICENSE) file for details. | ||||
|  | ||||
| ## ToDo | ||||
|  | ||||
| * Dump contents with passwords and secrets obscured | ||||
|  | ||||
| ## Updated Git tags | ||||
|  | ||||
| #### 13 Feb 2018 | ||||
|  | ||||
| I realized that all of the git tags I had pushed before v1.7.5 were lightweight tags | ||||
| and I've only recently learned that this doesn't play well with `git describe` 😞 | ||||
|  | ||||
| I have replaced all lightweight tags with signed tags using this script which should | ||||
| retain the commit date, name and email address. Please run `git pull --tags` to update them. | ||||
|  | ||||
| Worst case you have to reclone the repo. | ||||
|  | ||||
| ```shell | ||||
| #!/bin/bash | ||||
| tag=$1 | ||||
| echo "Updating $tag" | ||||
| date=$(git show ${tag}^0 --format=%aD | head -1) | ||||
| email=$(git show ${tag}^0 --format=%aE | head -1) | ||||
| name=$(git show ${tag}^0 --format=%aN | head -1) | ||||
| GIT_COMMITTER_DATE="$date" GIT_COMMITTER_NAME="$name" GIT_COMMITTER_EMAIL="$email" git tag -s -f ${tag} ${tag}^0 -m ${tag} | ||||
| ``` | ||||
|  | ||||
| I apologize for the inconvenience. | ||||
|  | ||||
| Frank | ||||
|  | ||||
							
								
								
									
										289
									
								
								vendor/github.com/magiconair/properties/decode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										289
									
								
								vendor/github.com/magiconair/properties/decode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,289 @@ | ||||
| // Copyright 2013-2022 Frank Schroeder. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package properties | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // Decode assigns property values to exported fields of a struct. | ||||
| // | ||||
| // Decode traverses v recursively and returns an error if a value cannot be | ||||
| // converted to the field type or a required value is missing for a field. | ||||
| // | ||||
| // The following type dependent decodings are used: | ||||
| // | ||||
| // String, boolean, numeric fields have the value of the property key assigned. | ||||
| // The property key name is the name of the field. A different key and a default | ||||
| // value can be set in the field's tag. Fields without default value are | ||||
| // required. If the value cannot be converted to the field type an error is | ||||
| // returned. | ||||
| // | ||||
| // time.Duration fields have the result of time.ParseDuration() assigned. | ||||
| // | ||||
| // time.Time fields have the vaule of time.Parse() assigned. The default layout | ||||
| // is time.RFC3339 but can be set in the field's tag. | ||||
| // | ||||
| // Arrays and slices of string, boolean, numeric, time.Duration and time.Time | ||||
| // fields have the value interpreted as a comma separated list of values. The | ||||
| // individual values are trimmed of whitespace and empty values are ignored. A | ||||
| // default value can be provided as a semicolon separated list in the field's | ||||
| // tag. | ||||
| // | ||||
| // Struct fields are decoded recursively using the field name plus "." as | ||||
| // prefix. The prefix (without dot) can be overridden in the field's tag. | ||||
| // Default values are not supported in the field's tag. Specify them on the | ||||
| // fields of the inner struct instead. | ||||
| // | ||||
| // Map fields must have a key of type string and are decoded recursively by | ||||
| // using the field's name plus ".' as prefix and the next element of the key | ||||
| // name as map key. The prefix (without dot) can be overridden in the field's | ||||
| // tag. Default values are not supported. | ||||
| // | ||||
| // Examples: | ||||
| // | ||||
| //	// Field is ignored. | ||||
| //	Field int `properties:"-"` | ||||
| // | ||||
| //	// Field is assigned value of 'Field'. | ||||
| //	Field int | ||||
| // | ||||
| //	// Field is assigned value of 'myName'. | ||||
| //	Field int `properties:"myName"` | ||||
| // | ||||
| //	// Field is assigned value of key 'myName' and has a default | ||||
| //	// value 15 if the key does not exist. | ||||
| //	Field int `properties:"myName,default=15"` | ||||
| // | ||||
| //	// Field is assigned value of key 'Field' and has a default | ||||
| //	// value 15 if the key does not exist. | ||||
| //	Field int `properties:",default=15"` | ||||
| // | ||||
| //	// Field is assigned value of key 'date' and the date | ||||
| //	// is in format 2006-01-02 | ||||
| //	Field time.Time `properties:"date,layout=2006-01-02"` | ||||
| // | ||||
| //	// Field is assigned the non-empty and whitespace trimmed | ||||
| //	// values of key 'Field' split by commas. | ||||
| //	Field []string | ||||
| // | ||||
| //	// Field is assigned the non-empty and whitespace trimmed | ||||
| //	// values of key 'Field' split by commas and has a default | ||||
| //	// value ["a", "b", "c"] if the key does not exist. | ||||
| //	Field []string `properties:",default=a;b;c"` | ||||
| // | ||||
| //	// Field is decoded recursively with "Field." as key prefix. | ||||
| //	Field SomeStruct | ||||
| // | ||||
| //	// Field is decoded recursively with "myName." as key prefix. | ||||
| //	Field SomeStruct `properties:"myName"` | ||||
| // | ||||
| //	// Field is decoded recursively with "Field." as key prefix | ||||
| //	// and the next dotted element of the key as map key. | ||||
| //	Field map[string]string | ||||
| // | ||||
| //	// Field is decoded recursively with "myName." as key prefix | ||||
| //	// and the next dotted element of the key as map key. | ||||
| //	Field map[string]string `properties:"myName"` | ||||
| func (p *Properties) Decode(x interface{}) error { | ||||
| 	t, v := reflect.TypeOf(x), reflect.ValueOf(x) | ||||
| 	if t.Kind() != reflect.Ptr || v.Elem().Type().Kind() != reflect.Struct { | ||||
| 		return fmt.Errorf("not a pointer to struct: %s", t) | ||||
| 	} | ||||
| 	if err := dec(p, "", nil, nil, v); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func dec(p *Properties, key string, def *string, opts map[string]string, v reflect.Value) error { | ||||
| 	t := v.Type() | ||||
|  | ||||
| 	// value returns the property value for key or the default if provided. | ||||
| 	value := func() (string, error) { | ||||
| 		if val, ok := p.Get(key); ok { | ||||
| 			return val, nil | ||||
| 		} | ||||
| 		if def != nil { | ||||
| 			return *def, nil | ||||
| 		} | ||||
| 		return "", fmt.Errorf("missing required key %s", key) | ||||
| 	} | ||||
|  | ||||
| 	// conv converts a string to a value of the given type. | ||||
| 	conv := func(s string, t reflect.Type) (val reflect.Value, err error) { | ||||
| 		var v interface{} | ||||
|  | ||||
| 		switch { | ||||
| 		case isDuration(t): | ||||
| 			v, err = time.ParseDuration(s) | ||||
|  | ||||
| 		case isTime(t): | ||||
| 			layout := opts["layout"] | ||||
| 			if layout == "" { | ||||
| 				layout = time.RFC3339 | ||||
| 			} | ||||
| 			v, err = time.Parse(layout, s) | ||||
|  | ||||
| 		case isBool(t): | ||||
| 			v, err = boolVal(s), nil | ||||
|  | ||||
| 		case isString(t): | ||||
| 			v, err = s, nil | ||||
|  | ||||
| 		case isFloat(t): | ||||
| 			v, err = strconv.ParseFloat(s, 64) | ||||
|  | ||||
| 		case isInt(t): | ||||
| 			v, err = strconv.ParseInt(s, 10, 64) | ||||
|  | ||||
| 		case isUint(t): | ||||
| 			v, err = strconv.ParseUint(s, 10, 64) | ||||
|  | ||||
| 		default: | ||||
| 			return reflect.Zero(t), fmt.Errorf("unsupported type %s", t) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return reflect.Zero(t), err | ||||
| 		} | ||||
| 		return reflect.ValueOf(v).Convert(t), nil | ||||
| 	} | ||||
|  | ||||
| 	// keydef returns the property key and the default value based on the | ||||
| 	// name of the struct field and the options in the tag. | ||||
| 	keydef := func(f reflect.StructField) (string, *string, map[string]string) { | ||||
| 		_key, _opts := parseTag(f.Tag.Get("properties")) | ||||
|  | ||||
| 		var _def *string | ||||
| 		if d, ok := _opts["default"]; ok { | ||||
| 			_def = &d | ||||
| 		} | ||||
| 		if _key != "" { | ||||
| 			return _key, _def, _opts | ||||
| 		} | ||||
| 		return f.Name, _def, _opts | ||||
| 	} | ||||
|  | ||||
| 	switch { | ||||
| 	case isDuration(t) || isTime(t) || isBool(t) || isString(t) || isFloat(t) || isInt(t) || isUint(t): | ||||
| 		s, err := value() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		val, err := conv(s, t) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		v.Set(val) | ||||
|  | ||||
| 	case isPtr(t): | ||||
| 		return dec(p, key, def, opts, v.Elem()) | ||||
|  | ||||
| 	case isStruct(t): | ||||
| 		for i := 0; i < v.NumField(); i++ { | ||||
| 			fv := v.Field(i) | ||||
| 			fk, def, opts := keydef(t.Field(i)) | ||||
| 			if !fv.CanSet() { | ||||
| 				return fmt.Errorf("cannot set %s", t.Field(i).Name) | ||||
| 			} | ||||
| 			if fk == "-" { | ||||
| 				continue | ||||
| 			} | ||||
| 			if key != "" { | ||||
| 				fk = key + "." + fk | ||||
| 			} | ||||
| 			if err := dec(p, fk, def, opts, fv); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 		return nil | ||||
|  | ||||
| 	case isArray(t): | ||||
| 		val, err := value() | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		vals := split(val, ";") | ||||
| 		a := reflect.MakeSlice(t, 0, len(vals)) | ||||
| 		for _, s := range vals { | ||||
| 			val, err := conv(s, t.Elem()) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			a = reflect.Append(a, val) | ||||
| 		} | ||||
| 		v.Set(a) | ||||
|  | ||||
| 	case isMap(t): | ||||
| 		valT := t.Elem() | ||||
| 		m := reflect.MakeMap(t) | ||||
| 		for postfix := range p.FilterStripPrefix(key + ".").m { | ||||
| 			pp := strings.SplitN(postfix, ".", 2) | ||||
| 			mk, mv := pp[0], reflect.New(valT) | ||||
| 			if err := dec(p, key+"."+mk, nil, nil, mv); err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 			m.SetMapIndex(reflect.ValueOf(mk), mv.Elem()) | ||||
| 		} | ||||
| 		v.Set(m) | ||||
|  | ||||
| 	default: | ||||
| 		return fmt.Errorf("unsupported type %s", t) | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // split splits a string on sep, trims whitespace of elements | ||||
| // and omits empty elements | ||||
| func split(s string, sep string) []string { | ||||
| 	var a []string | ||||
| 	for _, v := range strings.Split(s, sep) { | ||||
| 		if v = strings.TrimSpace(v); v != "" { | ||||
| 			a = append(a, v) | ||||
| 		} | ||||
| 	} | ||||
| 	return a | ||||
| } | ||||
|  | ||||
| // parseTag parses a "key,k=v,k=v,..." | ||||
| func parseTag(tag string) (key string, opts map[string]string) { | ||||
| 	opts = map[string]string{} | ||||
| 	for i, s := range strings.Split(tag, ",") { | ||||
| 		if i == 0 { | ||||
| 			key = s | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		pp := strings.SplitN(s, "=", 2) | ||||
| 		if len(pp) == 1 { | ||||
| 			opts[pp[0]] = "" | ||||
| 		} else { | ||||
| 			opts[pp[0]] = pp[1] | ||||
| 		} | ||||
| 	} | ||||
| 	return key, opts | ||||
| } | ||||
|  | ||||
| func isArray(t reflect.Type) bool    { return t.Kind() == reflect.Array || t.Kind() == reflect.Slice } | ||||
| func isBool(t reflect.Type) bool     { return t.Kind() == reflect.Bool } | ||||
| func isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) } | ||||
| func isMap(t reflect.Type) bool      { return t.Kind() == reflect.Map } | ||||
| func isPtr(t reflect.Type) bool      { return t.Kind() == reflect.Ptr } | ||||
| func isString(t reflect.Type) bool   { return t.Kind() == reflect.String } | ||||
| func isStruct(t reflect.Type) bool   { return t.Kind() == reflect.Struct } | ||||
| func isTime(t reflect.Type) bool     { return t == reflect.TypeOf(time.Time{}) } | ||||
| func isFloat(t reflect.Type) bool { | ||||
| 	return t.Kind() == reflect.Float32 || t.Kind() == reflect.Float64 | ||||
| } | ||||
| func isInt(t reflect.Type) bool { | ||||
| 	return t.Kind() == reflect.Int || t.Kind() == reflect.Int8 || t.Kind() == reflect.Int16 || t.Kind() == reflect.Int32 || t.Kind() == reflect.Int64 | ||||
| } | ||||
| func isUint(t reflect.Type) bool { | ||||
| 	return t.Kind() == reflect.Uint || t.Kind() == reflect.Uint8 || t.Kind() == reflect.Uint16 || t.Kind() == reflect.Uint32 || t.Kind() == reflect.Uint64 | ||||
| } | ||||
							
								
								
									
										155
									
								
								vendor/github.com/magiconair/properties/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								vendor/github.com/magiconair/properties/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | ||||
| // Copyright 2013-2022 Frank Schroeder. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // Package properties provides functions for reading and writing | ||||
| // ISO-8859-1 and UTF-8 encoded .properties files and has | ||||
| // support for recursive property expansion. | ||||
| // | ||||
| // Java properties files are ISO-8859-1 encoded and use Unicode | ||||
| // literals for characters outside the ISO character set. Unicode | ||||
| // literals can be used in UTF-8 encoded properties files but | ||||
| // aren't necessary. | ||||
| // | ||||
| // To load a single properties file use MustLoadFile(): | ||||
| // | ||||
| //	p := properties.MustLoadFile(filename, properties.UTF8) | ||||
| // | ||||
| // To load multiple properties files use MustLoadFiles() | ||||
| // which loads the files in the given order and merges the | ||||
| // result. Missing properties files can be ignored if the | ||||
| // 'ignoreMissing' flag is set to true. | ||||
| // | ||||
| // Filenames can contain environment variables which are expanded | ||||
| // before loading. | ||||
| // | ||||
| //	f1 := "/etc/myapp/myapp.conf" | ||||
| //	f2 := "/home/${USER}/myapp.conf" | ||||
| //	p := MustLoadFiles([]string{f1, f2}, properties.UTF8, true) | ||||
| // | ||||
| // All of the different key/value delimiters ' ', ':' and '=' are | ||||
| // supported as well as the comment characters '!' and '#' and | ||||
| // multi-line values. | ||||
| // | ||||
| //	! this is a comment | ||||
| //	# and so is this | ||||
| // | ||||
| //	# the following expressions are equal | ||||
| //	key value | ||||
| //	key=value | ||||
| //	key:value | ||||
| //	key = value | ||||
| //	key : value | ||||
| //	key = val\ | ||||
| //	      ue | ||||
| // | ||||
| // Properties stores all comments preceding a key and provides | ||||
| // GetComments() and SetComments() methods to retrieve and | ||||
| // update them. The convenience functions GetComment() and | ||||
| // SetComment() allow access to the last comment. The | ||||
| // WriteComment() method writes properties files including | ||||
| // the comments and with the keys in the original order. | ||||
| // This can be used for sanitizing properties files. | ||||
| // | ||||
| // Property expansion is recursive and circular references | ||||
| // and malformed expressions are not allowed and cause an | ||||
| // error. Expansion of environment variables is supported. | ||||
| // | ||||
| //	# standard property | ||||
| //	key = value | ||||
| // | ||||
| //	# property expansion: key2 = value | ||||
| //	key2 = ${key} | ||||
| // | ||||
| //	# recursive expansion: key3 = value | ||||
| //	key3 = ${key2} | ||||
| // | ||||
| //	# circular reference (error) | ||||
| //	key = ${key} | ||||
| // | ||||
| //	# malformed expression (error) | ||||
| //	key = ${ke | ||||
| // | ||||
| //	# refers to the users' home dir | ||||
| //	home = ${HOME} | ||||
| // | ||||
| //	# local key takes precedence over env var: u = foo | ||||
| //	USER = foo | ||||
| //	u = ${USER} | ||||
| // | ||||
| // The default property expansion format is ${key} but can be | ||||
| // changed by setting different pre- and postfix values on the | ||||
| // Properties object. | ||||
| // | ||||
| //	p := properties.NewProperties() | ||||
| //	p.Prefix = "#[" | ||||
| //	p.Postfix = "]#" | ||||
| // | ||||
| // Properties provides convenience functions for getting typed | ||||
| // values with default values if the key does not exist or the | ||||
| // type conversion failed. | ||||
| // | ||||
| //	# Returns true if the value is either "1", "on", "yes" or "true" | ||||
| //	# Returns false for every other value and the default value if | ||||
| //	# the key does not exist. | ||||
| //	v = p.GetBool("key", false) | ||||
| // | ||||
| //	# Returns the value if the key exists and the format conversion | ||||
| //	# was successful. Otherwise, the default value is returned. | ||||
| //	v = p.GetInt64("key", 999) | ||||
| //	v = p.GetUint64("key", 999) | ||||
| //	v = p.GetFloat64("key", 123.0) | ||||
| //	v = p.GetString("key", "def") | ||||
| //	v = p.GetDuration("key", 999) | ||||
| // | ||||
| // As an alternative properties may be applied with the standard | ||||
| // library's flag implementation at any time. | ||||
| // | ||||
| //	# Standard configuration | ||||
| //	v = flag.Int("key", 999, "help message") | ||||
| //	flag.Parse() | ||||
| // | ||||
| //	# Merge p into the flag set | ||||
| //	p.MustFlag(flag.CommandLine) | ||||
| // | ||||
| // Properties provides several MustXXX() convenience functions | ||||
| // which will terminate the app if an error occurs. The behavior | ||||
| // of the failure is configurable and the default is to call | ||||
| // log.Fatal(err). To have the MustXXX() functions panic instead | ||||
| // of logging the error set a different ErrorHandler before | ||||
| // you use the Properties package. | ||||
| // | ||||
| //	properties.ErrorHandler = properties.PanicHandler | ||||
| // | ||||
| //	# Will panic instead of logging an error | ||||
| //	p := properties.MustLoadFile("config.properties") | ||||
| // | ||||
| // You can also provide your own ErrorHandler function. The only requirement | ||||
| // is that the error handler function must exit after handling the error. | ||||
| // | ||||
| //	  properties.ErrorHandler = func(err error) { | ||||
| //		     fmt.Println(err) | ||||
| //	      os.Exit(1) | ||||
| //	  } | ||||
| // | ||||
| //	  # Will write to stdout and then exit | ||||
| //	  p := properties.MustLoadFile("config.properties") | ||||
| // | ||||
| // Properties can also be loaded into a struct via the `Decode` | ||||
| // method, e.g. | ||||
| // | ||||
| //	type S struct { | ||||
| //	    A string        `properties:"a,default=foo"` | ||||
| //	    D time.Duration `properties:"timeout,default=5s"` | ||||
| //	    E time.Time     `properties:"expires,layout=2006-01-02,default=2015-01-01"` | ||||
| //	} | ||||
| // | ||||
| // See `Decode()` method for the full documentation. | ||||
| // | ||||
| // The following documents provide a description of the properties | ||||
| // file format. | ||||
| // | ||||
| // http://en.wikipedia.org/wiki/.properties | ||||
| // | ||||
| // http://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29 | ||||
| package properties | ||||
							
								
								
									
										35
									
								
								vendor/github.com/magiconair/properties/integrate.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								vendor/github.com/magiconair/properties/integrate.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| // Copyright 2013-2022 Frank Schroeder. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package properties | ||||
|  | ||||
| import "flag" | ||||
|  | ||||
| // MustFlag sets flags that are skipped by dst.Parse when p contains | ||||
| // the respective key for flag.Flag.Name. | ||||
| // | ||||
| // It's use is recommended with command line arguments as in: | ||||
| // | ||||
| //	flag.Parse() | ||||
| //	p.MustFlag(flag.CommandLine) | ||||
| func (p *Properties) MustFlag(dst *flag.FlagSet) { | ||||
| 	m := make(map[string]*flag.Flag) | ||||
| 	dst.VisitAll(func(f *flag.Flag) { | ||||
| 		m[f.Name] = f | ||||
| 	}) | ||||
| 	dst.Visit(func(f *flag.Flag) { | ||||
| 		delete(m, f.Name) // overridden | ||||
| 	}) | ||||
|  | ||||
| 	for name, f := range m { | ||||
| 		v, ok := p.Get(name) | ||||
| 		if !ok { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if err := f.Value.Set(v); err != nil { | ||||
| 			ErrorHandler(err) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										395
									
								
								vendor/github.com/magiconair/properties/lex.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										395
									
								
								vendor/github.com/magiconair/properties/lex.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,395 @@ | ||||
| // Copyright 2013-2022 Frank Schroeder. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
| // | ||||
| // Parts of the lexer are from the template/text/parser package | ||||
| // For these parts the following applies: | ||||
| // | ||||
| // Copyright 2011 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file of the go 1.2 | ||||
| // distribution. | ||||
|  | ||||
| package properties | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| // item represents a token or text string returned from the scanner. | ||||
| type item struct { | ||||
| 	typ itemType // The type of this item. | ||||
| 	pos int      // The starting position, in bytes, of this item in the input string. | ||||
| 	val string   // The value of this item. | ||||
| } | ||||
|  | ||||
| func (i item) String() string { | ||||
| 	switch { | ||||
| 	case i.typ == itemEOF: | ||||
| 		return "EOF" | ||||
| 	case i.typ == itemError: | ||||
| 		return i.val | ||||
| 	case len(i.val) > 10: | ||||
| 		return fmt.Sprintf("%.10q...", i.val) | ||||
| 	} | ||||
| 	return fmt.Sprintf("%q", i.val) | ||||
| } | ||||
|  | ||||
| // itemType identifies the type of lex items. | ||||
| type itemType int | ||||
|  | ||||
| const ( | ||||
| 	itemError itemType = iota // error occurred; value is text of error | ||||
| 	itemEOF | ||||
| 	itemKey     // a key | ||||
| 	itemValue   // a value | ||||
| 	itemComment // a comment | ||||
| ) | ||||
|  | ||||
| // defines a constant for EOF | ||||
| const eof = -1 | ||||
|  | ||||
| // permitted whitespace characters space, FF and TAB | ||||
| const whitespace = " \f\t" | ||||
|  | ||||
| // stateFn represents the state of the scanner as a function that returns the next state. | ||||
| type stateFn func(*lexer) stateFn | ||||
|  | ||||
| // lexer holds the state of the scanner. | ||||
| type lexer struct { | ||||
| 	input   string    // the string being scanned | ||||
| 	state   stateFn   // the next lexing function to enter | ||||
| 	pos     int       // current position in the input | ||||
| 	start   int       // start position of this item | ||||
| 	width   int       // width of last rune read from input | ||||
| 	lastPos int       // position of most recent item returned by nextItem | ||||
| 	runes   []rune    // scanned runes for this item | ||||
| 	items   chan item // channel of scanned items | ||||
| } | ||||
|  | ||||
| // next returns the next rune in the input. | ||||
| func (l *lexer) next() rune { | ||||
| 	if l.pos >= len(l.input) { | ||||
| 		l.width = 0 | ||||
| 		return eof | ||||
| 	} | ||||
| 	r, w := utf8.DecodeRuneInString(l.input[l.pos:]) | ||||
| 	l.width = w | ||||
| 	l.pos += l.width | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| // peek returns but does not consume the next rune in the input. | ||||
| func (l *lexer) peek() rune { | ||||
| 	r := l.next() | ||||
| 	l.backup() | ||||
| 	return r | ||||
| } | ||||
|  | ||||
| // backup steps back one rune. Can only be called once per call of next. | ||||
| func (l *lexer) backup() { | ||||
| 	l.pos -= l.width | ||||
| } | ||||
|  | ||||
| // emit passes an item back to the client. | ||||
| func (l *lexer) emit(t itemType) { | ||||
| 	i := item{t, l.start, string(l.runes)} | ||||
| 	l.items <- i | ||||
| 	l.start = l.pos | ||||
| 	l.runes = l.runes[:0] | ||||
| } | ||||
|  | ||||
| // ignore skips over the pending input before this point. | ||||
| func (l *lexer) ignore() { | ||||
| 	l.start = l.pos | ||||
| } | ||||
|  | ||||
| // appends the rune to the current value | ||||
| func (l *lexer) appendRune(r rune) { | ||||
| 	l.runes = append(l.runes, r) | ||||
| } | ||||
|  | ||||
| // accept consumes the next rune if it's from the valid set. | ||||
| func (l *lexer) accept(valid string) bool { | ||||
| 	if strings.ContainsRune(valid, l.next()) { | ||||
| 		return true | ||||
| 	} | ||||
| 	l.backup() | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // acceptRun consumes a run of runes from the valid set. | ||||
| func (l *lexer) acceptRun(valid string) { | ||||
| 	for strings.ContainsRune(valid, l.next()) { | ||||
| 	} | ||||
| 	l.backup() | ||||
| } | ||||
|  | ||||
| // lineNumber reports which line we're on, based on the position of | ||||
| // the previous item returned by nextItem. Doing it this way | ||||
| // means we don't have to worry about peek double counting. | ||||
| func (l *lexer) lineNumber() int { | ||||
| 	return 1 + strings.Count(l.input[:l.lastPos], "\n") | ||||
| } | ||||
|  | ||||
| // errorf returns an error token and terminates the scan by passing | ||||
| // back a nil pointer that will be the next state, terminating l.nextItem. | ||||
| func (l *lexer) errorf(format string, args ...interface{}) stateFn { | ||||
| 	l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // nextItem returns the next item from the input. | ||||
| func (l *lexer) nextItem() item { | ||||
| 	i := <-l.items | ||||
| 	l.lastPos = i.pos | ||||
| 	return i | ||||
| } | ||||
|  | ||||
| // lex creates a new scanner for the input string. | ||||
| func lex(input string) *lexer { | ||||
| 	l := &lexer{ | ||||
| 		input: input, | ||||
| 		items: make(chan item), | ||||
| 		runes: make([]rune, 0, 32), | ||||
| 	} | ||||
| 	go l.run() | ||||
| 	return l | ||||
| } | ||||
|  | ||||
| // run runs the state machine for the lexer. | ||||
| func (l *lexer) run() { | ||||
| 	for l.state = lexBeforeKey(l); l.state != nil; { | ||||
| 		l.state = l.state(l) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // state functions | ||||
|  | ||||
| // lexBeforeKey scans until a key begins. | ||||
| func lexBeforeKey(l *lexer) stateFn { | ||||
| 	switch r := l.next(); { | ||||
| 	case isEOF(r): | ||||
| 		l.emit(itemEOF) | ||||
| 		return nil | ||||
|  | ||||
| 	case isEOL(r): | ||||
| 		l.ignore() | ||||
| 		return lexBeforeKey | ||||
|  | ||||
| 	case isComment(r): | ||||
| 		return lexComment | ||||
|  | ||||
| 	case isWhitespace(r): | ||||
| 		l.ignore() | ||||
| 		return lexBeforeKey | ||||
|  | ||||
| 	default: | ||||
| 		l.backup() | ||||
| 		return lexKey | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // lexComment scans a comment line. The comment character has already been scanned. | ||||
| func lexComment(l *lexer) stateFn { | ||||
| 	l.acceptRun(whitespace) | ||||
| 	l.ignore() | ||||
| 	for { | ||||
| 		switch r := l.next(); { | ||||
| 		case isEOF(r): | ||||
| 			l.ignore() | ||||
| 			l.emit(itemEOF) | ||||
| 			return nil | ||||
| 		case isEOL(r): | ||||
| 			l.emit(itemComment) | ||||
| 			return lexBeforeKey | ||||
| 		default: | ||||
| 			l.appendRune(r) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // lexKey scans the key up to a delimiter | ||||
| func lexKey(l *lexer) stateFn { | ||||
| 	var r rune | ||||
|  | ||||
| Loop: | ||||
| 	for { | ||||
| 		switch r = l.next(); { | ||||
|  | ||||
| 		case isEscape(r): | ||||
| 			err := l.scanEscapeSequence() | ||||
| 			if err != nil { | ||||
| 				return l.errorf(err.Error()) | ||||
| 			} | ||||
|  | ||||
| 		case isEndOfKey(r): | ||||
| 			l.backup() | ||||
| 			break Loop | ||||
|  | ||||
| 		case isEOF(r): | ||||
| 			break Loop | ||||
|  | ||||
| 		default: | ||||
| 			l.appendRune(r) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(l.runes) > 0 { | ||||
| 		l.emit(itemKey) | ||||
| 	} | ||||
|  | ||||
| 	if isEOF(r) { | ||||
| 		l.emit(itemEOF) | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	return lexBeforeValue | ||||
| } | ||||
|  | ||||
| // lexBeforeValue scans the delimiter between key and value. | ||||
| // Leading and trailing whitespace is ignored. | ||||
| // We expect to be just after the key. | ||||
| func lexBeforeValue(l *lexer) stateFn { | ||||
| 	l.acceptRun(whitespace) | ||||
| 	l.accept(":=") | ||||
| 	l.acceptRun(whitespace) | ||||
| 	l.ignore() | ||||
| 	return lexValue | ||||
| } | ||||
|  | ||||
| // lexValue scans text until the end of the line. We expect to be just after the delimiter. | ||||
| func lexValue(l *lexer) stateFn { | ||||
| 	for { | ||||
| 		switch r := l.next(); { | ||||
| 		case isEscape(r): | ||||
| 			if isEOL(l.peek()) { | ||||
| 				l.next() | ||||
| 				l.acceptRun(whitespace) | ||||
| 			} else { | ||||
| 				err := l.scanEscapeSequence() | ||||
| 				if err != nil { | ||||
| 					return l.errorf(err.Error()) | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 		case isEOL(r): | ||||
| 			l.emit(itemValue) | ||||
| 			l.ignore() | ||||
| 			return lexBeforeKey | ||||
|  | ||||
| 		case isEOF(r): | ||||
| 			l.emit(itemValue) | ||||
| 			l.emit(itemEOF) | ||||
| 			return nil | ||||
|  | ||||
| 		default: | ||||
| 			l.appendRune(r) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // scanEscapeSequence scans either one of the escaped characters | ||||
| // or a unicode literal. We expect to be after the escape character. | ||||
| func (l *lexer) scanEscapeSequence() error { | ||||
| 	switch r := l.next(); { | ||||
|  | ||||
| 	case isEscapedCharacter(r): | ||||
| 		l.appendRune(decodeEscapedCharacter(r)) | ||||
| 		return nil | ||||
|  | ||||
| 	case atUnicodeLiteral(r): | ||||
| 		return l.scanUnicodeLiteral() | ||||
|  | ||||
| 	case isEOF(r): | ||||
| 		return fmt.Errorf("premature EOF") | ||||
|  | ||||
| 	// silently drop the escape character and append the rune as is | ||||
| 	default: | ||||
| 		l.appendRune(r) | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // scans a unicode literal in the form \uXXXX. We expect to be after the \u. | ||||
| func (l *lexer) scanUnicodeLiteral() error { | ||||
| 	// scan the digits | ||||
| 	d := make([]rune, 4) | ||||
| 	for i := 0; i < 4; i++ { | ||||
| 		d[i] = l.next() | ||||
| 		if d[i] == eof || !strings.ContainsRune("0123456789abcdefABCDEF", d[i]) { | ||||
| 			return fmt.Errorf("invalid unicode literal") | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// decode the digits into a rune | ||||
| 	r, err := strconv.ParseInt(string(d), 16, 0) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	l.appendRune(rune(r)) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // decodeEscapedCharacter returns the unescaped rune. We expect to be after the escape character. | ||||
| func decodeEscapedCharacter(r rune) rune { | ||||
| 	switch r { | ||||
| 	case 'f': | ||||
| 		return '\f' | ||||
| 	case 'n': | ||||
| 		return '\n' | ||||
| 	case 'r': | ||||
| 		return '\r' | ||||
| 	case 't': | ||||
| 		return '\t' | ||||
| 	default: | ||||
| 		return r | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // atUnicodeLiteral reports whether we are at a unicode literal. | ||||
| // The escape character has already been consumed. | ||||
| func atUnicodeLiteral(r rune) bool { | ||||
| 	return r == 'u' | ||||
| } | ||||
|  | ||||
| // isComment reports whether we are at the start of a comment. | ||||
| func isComment(r rune) bool { | ||||
| 	return r == '#' || r == '!' | ||||
| } | ||||
|  | ||||
| // isEndOfKey reports whether the rune terminates the current key. | ||||
| func isEndOfKey(r rune) bool { | ||||
| 	return strings.ContainsRune(" \f\t\r\n:=", r) | ||||
| } | ||||
|  | ||||
| // isEOF reports whether we are at EOF. | ||||
| func isEOF(r rune) bool { | ||||
| 	return r == eof | ||||
| } | ||||
|  | ||||
| // isEOL reports whether we are at a new line character. | ||||
| func isEOL(r rune) bool { | ||||
| 	return r == '\n' || r == '\r' | ||||
| } | ||||
|  | ||||
| // isEscape reports whether the rune is the escape character which | ||||
| // prefixes unicode literals and other escaped characters. | ||||
| func isEscape(r rune) bool { | ||||
| 	return r == '\\' | ||||
| } | ||||
|  | ||||
| // isEscapedCharacter reports whether we are at one of the characters that need escaping. | ||||
| // The escape character has already been consumed. | ||||
| func isEscapedCharacter(r rune) bool { | ||||
| 	return strings.ContainsRune(" :=fnrt", r) | ||||
| } | ||||
|  | ||||
| // isWhitespace reports whether the rune is a whitespace character. | ||||
| func isWhitespace(r rune) bool { | ||||
| 	return strings.ContainsRune(whitespace, r) | ||||
| } | ||||
							
								
								
									
										293
									
								
								vendor/github.com/magiconair/properties/load.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								vendor/github.com/magiconair/properties/load.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,293 @@ | ||||
| // Copyright 2013-2022 Frank Schroeder. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package properties | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // Encoding specifies encoding of the input data. | ||||
| type Encoding uint | ||||
|  | ||||
| const ( | ||||
| 	// utf8Default is a private placeholder for the zero value of Encoding to | ||||
| 	// ensure that it has the correct meaning. UTF8 is the default encoding but | ||||
| 	// was assigned a non-zero value which cannot be changed without breaking | ||||
| 	// existing code. Clients should continue to use the public constants. | ||||
| 	utf8Default Encoding = iota | ||||
|  | ||||
| 	// UTF8 interprets the input data as UTF-8. | ||||
| 	UTF8 | ||||
|  | ||||
| 	// ISO_8859_1 interprets the input data as ISO-8859-1. | ||||
| 	ISO_8859_1 | ||||
| ) | ||||
|  | ||||
| type Loader struct { | ||||
| 	// Encoding determines how the data from files and byte buffers | ||||
| 	// is interpreted. For URLs the Content-Type header is used | ||||
| 	// to determine the encoding of the data. | ||||
| 	Encoding Encoding | ||||
|  | ||||
| 	// DisableExpansion configures the property expansion of the | ||||
| 	// returned property object. When set to true, the property values | ||||
| 	// will not be expanded and the Property object will not be checked | ||||
| 	// for invalid expansion expressions. | ||||
| 	DisableExpansion bool | ||||
|  | ||||
| 	// IgnoreMissing configures whether missing files or URLs which return | ||||
| 	// 404 are reported as errors. When set to true, missing files and 404 | ||||
| 	// status codes are not reported as errors. | ||||
| 	IgnoreMissing bool | ||||
| } | ||||
|  | ||||
| // Load reads a buffer into a Properties struct. | ||||
| func (l *Loader) LoadBytes(buf []byte) (*Properties, error) { | ||||
| 	return l.loadBytes(buf, l.Encoding) | ||||
| } | ||||
|  | ||||
| // LoadAll reads the content of multiple URLs or files in the given order into | ||||
| // a Properties struct. If IgnoreMissing is true then a 404 status code or | ||||
| // missing file will not be reported as error. Encoding sets the encoding for | ||||
| // files. For the URLs see LoadURL for the Content-Type header and the | ||||
| // encoding. | ||||
| func (l *Loader) LoadAll(names []string) (*Properties, error) { | ||||
| 	all := NewProperties() | ||||
| 	for _, name := range names { | ||||
| 		n, err := expandName(name) | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		var p *Properties | ||||
| 		switch { | ||||
| 		case strings.HasPrefix(n, "http://"): | ||||
| 			p, err = l.LoadURL(n) | ||||
| 		case strings.HasPrefix(n, "https://"): | ||||
| 			p, err = l.LoadURL(n) | ||||
| 		default: | ||||
| 			p, err = l.LoadFile(n) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		all.Merge(p) | ||||
| 	} | ||||
|  | ||||
| 	all.DisableExpansion = l.DisableExpansion | ||||
| 	if all.DisableExpansion { | ||||
| 		return all, nil | ||||
| 	} | ||||
| 	return all, all.check() | ||||
| } | ||||
|  | ||||
| // LoadFile reads a file into a Properties struct. | ||||
| // If IgnoreMissing is true then a missing file will not be | ||||
| // reported as error. | ||||
| func (l *Loader) LoadFile(filename string) (*Properties, error) { | ||||
| 	data, err := ioutil.ReadFile(filename) | ||||
| 	if err != nil { | ||||
| 		if l.IgnoreMissing && os.IsNotExist(err) { | ||||
| 			LogPrintf("properties: %s not found. skipping", filename) | ||||
| 			return NewProperties(), nil | ||||
| 		} | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return l.loadBytes(data, l.Encoding) | ||||
| } | ||||
|  | ||||
| // LoadURL reads the content of the URL into a Properties struct. | ||||
| // | ||||
| // The encoding is determined via the Content-Type header which | ||||
| // should be set to 'text/plain'. If the 'charset' parameter is | ||||
| // missing, 'iso-8859-1' or 'latin1' the encoding is set to | ||||
| // ISO-8859-1. If the 'charset' parameter is set to 'utf-8' the | ||||
| // encoding is set to UTF-8. A missing content type header is | ||||
| // interpreted as 'text/plain; charset=utf-8'. | ||||
| func (l *Loader) LoadURL(url string) (*Properties, error) { | ||||
| 	resp, err := http.Get(url) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("properties: error fetching %q. %s", url, err) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
|  | ||||
| 	if resp.StatusCode == 404 && l.IgnoreMissing { | ||||
| 		LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode) | ||||
| 		return NewProperties(), nil | ||||
| 	} | ||||
|  | ||||
| 	if resp.StatusCode != 200 { | ||||
| 		return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode) | ||||
| 	} | ||||
|  | ||||
| 	body, err := ioutil.ReadAll(resp.Body) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("properties: %s error reading response. %s", url, err) | ||||
| 	} | ||||
|  | ||||
| 	ct := resp.Header.Get("Content-Type") | ||||
| 	ct = strings.Join(strings.Fields(ct), "") | ||||
| 	var enc Encoding | ||||
| 	switch strings.ToLower(ct) { | ||||
| 	case "text/plain", "text/plain;charset=iso-8859-1", "text/plain;charset=latin1": | ||||
| 		enc = ISO_8859_1 | ||||
| 	case "", "text/plain;charset=utf-8": | ||||
| 		enc = UTF8 | ||||
| 	default: | ||||
| 		return nil, fmt.Errorf("properties: invalid content type %s", ct) | ||||
| 	} | ||||
|  | ||||
| 	return l.loadBytes(body, enc) | ||||
| } | ||||
|  | ||||
| func (l *Loader) loadBytes(buf []byte, enc Encoding) (*Properties, error) { | ||||
| 	p, err := parse(convert(buf, enc)) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	p.DisableExpansion = l.DisableExpansion | ||||
| 	if p.DisableExpansion { | ||||
| 		return p, nil | ||||
| 	} | ||||
| 	return p, p.check() | ||||
| } | ||||
|  | ||||
| // Load reads a buffer into a Properties struct. | ||||
| func Load(buf []byte, enc Encoding) (*Properties, error) { | ||||
| 	l := &Loader{Encoding: enc} | ||||
| 	return l.LoadBytes(buf) | ||||
| } | ||||
|  | ||||
| // LoadString reads an UTF8 string into a properties struct. | ||||
| func LoadString(s string) (*Properties, error) { | ||||
| 	l := &Loader{Encoding: UTF8} | ||||
| 	return l.LoadBytes([]byte(s)) | ||||
| } | ||||
|  | ||||
| // LoadMap creates a new Properties struct from a string map. | ||||
| func LoadMap(m map[string]string) *Properties { | ||||
| 	p := NewProperties() | ||||
| 	for k, v := range m { | ||||
| 		p.Set(k, v) | ||||
| 	} | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| // LoadFile reads a file into a Properties struct. | ||||
| func LoadFile(filename string, enc Encoding) (*Properties, error) { | ||||
| 	l := &Loader{Encoding: enc} | ||||
| 	return l.LoadAll([]string{filename}) | ||||
| } | ||||
|  | ||||
| // LoadFiles reads multiple files in the given order into | ||||
| // a Properties struct. If 'ignoreMissing' is true then | ||||
| // non-existent files will not be reported as error. | ||||
| func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) { | ||||
| 	l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing} | ||||
| 	return l.LoadAll(filenames) | ||||
| } | ||||
|  | ||||
| // LoadURL reads the content of the URL into a Properties struct. | ||||
| // See Loader#LoadURL for details. | ||||
| func LoadURL(url string) (*Properties, error) { | ||||
| 	l := &Loader{Encoding: UTF8} | ||||
| 	return l.LoadAll([]string{url}) | ||||
| } | ||||
|  | ||||
| // LoadURLs reads the content of multiple URLs in the given order into a | ||||
| // Properties struct. If IgnoreMissing is true then a 404 status code will | ||||
| // not be reported as error. See Loader#LoadURL for the Content-Type header | ||||
| // and the encoding. | ||||
| func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) { | ||||
| 	l := &Loader{Encoding: UTF8, IgnoreMissing: ignoreMissing} | ||||
| 	return l.LoadAll(urls) | ||||
| } | ||||
|  | ||||
| // LoadAll reads the content of multiple URLs or files in the given order into a | ||||
| // Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will | ||||
| // not be reported as error. Encoding sets the encoding for files. For the URLs please see | ||||
| // LoadURL for the Content-Type header and the encoding. | ||||
| func LoadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) { | ||||
| 	l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing} | ||||
| 	return l.LoadAll(names) | ||||
| } | ||||
|  | ||||
| // MustLoadString reads an UTF8 string into a Properties struct and | ||||
| // panics on error. | ||||
| func MustLoadString(s string) *Properties { | ||||
| 	return must(LoadString(s)) | ||||
| } | ||||
|  | ||||
| // MustLoadFile reads a file into a Properties struct and | ||||
| // panics on error. | ||||
| func MustLoadFile(filename string, enc Encoding) *Properties { | ||||
| 	return must(LoadFile(filename, enc)) | ||||
| } | ||||
|  | ||||
| // MustLoadFiles reads multiple files in the given order into | ||||
| // a Properties struct and panics on error. If 'ignoreMissing' | ||||
| // is true then non-existent files will not be reported as error. | ||||
| func MustLoadFiles(filenames []string, enc Encoding, ignoreMissing bool) *Properties { | ||||
| 	return must(LoadFiles(filenames, enc, ignoreMissing)) | ||||
| } | ||||
|  | ||||
| // MustLoadURL reads the content of a URL into a Properties struct and | ||||
| // panics on error. | ||||
| func MustLoadURL(url string) *Properties { | ||||
| 	return must(LoadURL(url)) | ||||
| } | ||||
|  | ||||
| // MustLoadURLs reads the content of multiple URLs in the given order into a | ||||
| // Properties struct and panics on error. If 'ignoreMissing' is true then a 404 | ||||
| // status code will not be reported as error. | ||||
| func MustLoadURLs(urls []string, ignoreMissing bool) *Properties { | ||||
| 	return must(LoadURLs(urls, ignoreMissing)) | ||||
| } | ||||
|  | ||||
| // MustLoadAll reads the content of multiple URLs or files in the given order into a | ||||
| // Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will | ||||
| // not be reported as error. Encoding sets the encoding for files. For the URLs please see | ||||
| // LoadURL for the Content-Type header and the encoding. It panics on error. | ||||
| func MustLoadAll(names []string, enc Encoding, ignoreMissing bool) *Properties { | ||||
| 	return must(LoadAll(names, enc, ignoreMissing)) | ||||
| } | ||||
|  | ||||
| func must(p *Properties, err error) *Properties { | ||||
| 	if err != nil { | ||||
| 		ErrorHandler(err) | ||||
| 	} | ||||
| 	return p | ||||
| } | ||||
|  | ||||
| // expandName expands ${ENV_VAR} expressions in a name. | ||||
| // If the environment variable does not exist then it will be replaced | ||||
| // with an empty string. Malformed expressions like "${ENV_VAR" will | ||||
| // be reported as error. | ||||
| func expandName(name string) (string, error) { | ||||
| 	return expand(name, []string{}, "${", "}", make(map[string]string)) | ||||
| } | ||||
|  | ||||
| // Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string. | ||||
| // For ISO-8859-1 we can convert each byte straight into a rune since the | ||||
| // first 256 unicode code points cover ISO-8859-1. | ||||
| func convert(buf []byte, enc Encoding) string { | ||||
| 	switch enc { | ||||
| 	case utf8Default, UTF8: | ||||
| 		return string(buf) | ||||
| 	case ISO_8859_1: | ||||
| 		runes := make([]rune, len(buf)) | ||||
| 		for i, b := range buf { | ||||
| 			runes[i] = rune(b) | ||||
| 		} | ||||
| 		return string(runes) | ||||
| 	default: | ||||
| 		ErrorHandler(fmt.Errorf("unsupported encoding %v", enc)) | ||||
| 	} | ||||
| 	panic("ErrorHandler should exit") | ||||
| } | ||||
							
								
								
									
										86
									
								
								vendor/github.com/magiconair/properties/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								vendor/github.com/magiconair/properties/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | ||||
| // Copyright 2013-2022 Frank Schroeder. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package properties | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"runtime" | ||||
| ) | ||||
|  | ||||
| type parser struct { | ||||
| 	lex *lexer | ||||
| } | ||||
|  | ||||
| func parse(input string) (properties *Properties, err error) { | ||||
| 	p := &parser{lex: lex(input)} | ||||
| 	defer p.recover(&err) | ||||
|  | ||||
| 	properties = NewProperties() | ||||
| 	key := "" | ||||
| 	comments := []string{} | ||||
|  | ||||
| 	for { | ||||
| 		token := p.expectOneOf(itemComment, itemKey, itemEOF) | ||||
| 		switch token.typ { | ||||
| 		case itemEOF: | ||||
| 			goto done | ||||
| 		case itemComment: | ||||
| 			comments = append(comments, token.val) | ||||
| 			continue | ||||
| 		case itemKey: | ||||
| 			key = token.val | ||||
| 			if _, ok := properties.m[key]; !ok { | ||||
| 				properties.k = append(properties.k, key) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		token = p.expectOneOf(itemValue, itemEOF) | ||||
| 		if len(comments) > 0 { | ||||
| 			properties.c[key] = comments | ||||
| 			comments = []string{} | ||||
| 		} | ||||
| 		switch token.typ { | ||||
| 		case itemEOF: | ||||
| 			properties.m[key] = "" | ||||
| 			goto done | ||||
| 		case itemValue: | ||||
| 			properties.m[key] = token.val | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| done: | ||||
| 	return properties, nil | ||||
| } | ||||
|  | ||||
| func (p *parser) errorf(format string, args ...interface{}) { | ||||
| 	format = fmt.Sprintf("properties: Line %d: %s", p.lex.lineNumber(), format) | ||||
| 	panic(fmt.Errorf(format, args...)) | ||||
| } | ||||
|  | ||||
| func (p *parser) expectOneOf(expected ...itemType) (token item) { | ||||
| 	token = p.lex.nextItem() | ||||
| 	for _, v := range expected { | ||||
| 		if token.typ == v { | ||||
| 			return token | ||||
| 		} | ||||
| 	} | ||||
| 	p.unexpected(token) | ||||
| 	panic("unexpected token") | ||||
| } | ||||
|  | ||||
| func (p *parser) unexpected(token item) { | ||||
| 	p.errorf(token.String()) | ||||
| } | ||||
|  | ||||
| // recover is the handler that turns panics into returns from the top level of Parse. | ||||
| func (p *parser) recover(errp *error) { | ||||
| 	e := recover() | ||||
| 	if e != nil { | ||||
| 		if _, ok := e.(runtime.Error); ok { | ||||
| 			panic(e) | ||||
| 		} | ||||
| 		*errp = e.(error) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										848
									
								
								vendor/github.com/magiconair/properties/properties.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										848
									
								
								vendor/github.com/magiconair/properties/properties.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,848 @@ | ||||
| // Copyright 2013-2022 Frank Schroeder. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package properties | ||||
|  | ||||
| // BUG(frank): Set() does not check for invalid unicode literals since this is currently handled by the lexer. | ||||
| // BUG(frank): Write() does not allow to configure the newline character. Therefore, on Windows LF is used. | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| const maxExpansionDepth = 64 | ||||
|  | ||||
| // ErrorHandlerFunc defines the type of function which handles failures | ||||
| // of the MustXXX() functions. An error handler function must exit | ||||
| // the application after handling the error. | ||||
| type ErrorHandlerFunc func(error) | ||||
|  | ||||
| // ErrorHandler is the function which handles failures of the MustXXX() | ||||
| // functions. The default is LogFatalHandler. | ||||
| var ErrorHandler ErrorHandlerFunc = LogFatalHandler | ||||
|  | ||||
| // LogHandlerFunc defines the function prototype for logging errors. | ||||
| type LogHandlerFunc func(fmt string, args ...interface{}) | ||||
|  | ||||
| // LogPrintf defines a log handler which uses log.Printf. | ||||
| var LogPrintf LogHandlerFunc = log.Printf | ||||
|  | ||||
| // LogFatalHandler handles the error by logging a fatal error and exiting. | ||||
| func LogFatalHandler(err error) { | ||||
| 	log.Fatal(err) | ||||
| } | ||||
|  | ||||
| // PanicHandler handles the error by panicking. | ||||
| func PanicHandler(err error) { | ||||
| 	panic(err) | ||||
| } | ||||
|  | ||||
| // ----------------------------------------------------------------------------- | ||||
|  | ||||
| // A Properties contains the key/value pairs from the properties input. | ||||
| // All values are stored in unexpanded form and are expanded at runtime | ||||
| type Properties struct { | ||||
| 	// Pre-/Postfix for property expansion. | ||||
| 	Prefix  string | ||||
| 	Postfix string | ||||
|  | ||||
| 	// DisableExpansion controls the expansion of properties on Get() | ||||
| 	// and the check for circular references on Set(). When set to | ||||
| 	// true Properties behaves like a simple key/value store and does | ||||
| 	// not check for circular references on Get() or on Set(). | ||||
| 	DisableExpansion bool | ||||
|  | ||||
| 	// Stores the key/value pairs | ||||
| 	m map[string]string | ||||
|  | ||||
| 	// Stores the comments per key. | ||||
| 	c map[string][]string | ||||
|  | ||||
| 	// Stores the keys in order of appearance. | ||||
| 	k []string | ||||
|  | ||||
| 	// WriteSeparator specifies the separator of key and value while writing the properties. | ||||
| 	WriteSeparator string | ||||
| } | ||||
|  | ||||
| // NewProperties creates a new Properties struct with the default | ||||
| // configuration for "${key}" expressions. | ||||
| func NewProperties() *Properties { | ||||
| 	return &Properties{ | ||||
| 		Prefix:  "${", | ||||
| 		Postfix: "}", | ||||
| 		m:       map[string]string{}, | ||||
| 		c:       map[string][]string{}, | ||||
| 		k:       []string{}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Load reads a buffer into the given Properties struct. | ||||
| func (p *Properties) Load(buf []byte, enc Encoding) error { | ||||
| 	l := &Loader{Encoding: enc, DisableExpansion: p.DisableExpansion} | ||||
| 	newProperties, err := l.LoadBytes(buf) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	p.Merge(newProperties) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Get returns the expanded value for the given key if exists. | ||||
| // Otherwise, ok is false. | ||||
| func (p *Properties) Get(key string) (value string, ok bool) { | ||||
| 	v, ok := p.m[key] | ||||
| 	if p.DisableExpansion { | ||||
| 		return v, ok | ||||
| 	} | ||||
| 	if !ok { | ||||
| 		return "", false | ||||
| 	} | ||||
|  | ||||
| 	expanded, err := p.expand(key, v) | ||||
|  | ||||
| 	// we guarantee that the expanded value is free of | ||||
| 	// circular references and malformed expressions | ||||
| 	// so we panic if we still get an error here. | ||||
| 	if err != nil { | ||||
| 		ErrorHandler(err) | ||||
| 	} | ||||
|  | ||||
| 	return expanded, true | ||||
| } | ||||
|  | ||||
| // MustGet returns the expanded value for the given key if exists. | ||||
| // Otherwise, it panics. | ||||
| func (p *Properties) MustGet(key string) string { | ||||
| 	if v, ok := p.Get(key); ok { | ||||
| 		return v | ||||
| 	} | ||||
| 	ErrorHandler(invalidKeyError(key)) | ||||
| 	panic("ErrorHandler should exit") | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // ClearComments removes the comments for all keys. | ||||
| func (p *Properties) ClearComments() { | ||||
| 	p.c = map[string][]string{} | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // GetComment returns the last comment before the given key or an empty string. | ||||
| func (p *Properties) GetComment(key string) string { | ||||
| 	comments, ok := p.c[key] | ||||
| 	if !ok || len(comments) == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
| 	return comments[len(comments)-1] | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // GetComments returns all comments that appeared before the given key or nil. | ||||
| func (p *Properties) GetComments(key string) []string { | ||||
| 	if comments, ok := p.c[key]; ok { | ||||
| 		return comments | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // SetComment sets the comment for the key. | ||||
| func (p *Properties) SetComment(key, comment string) { | ||||
| 	p.c[key] = []string{comment} | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // SetComments sets the comments for the key. If the comments are nil then | ||||
| // all comments for this key are deleted. | ||||
| func (p *Properties) SetComments(key string, comments []string) { | ||||
| 	if comments == nil { | ||||
| 		delete(p.c, key) | ||||
| 		return | ||||
| 	} | ||||
| 	p.c[key] = comments | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // GetBool checks if the expanded value is one of '1', 'yes', | ||||
| // 'true' or 'on' if the key exists. The comparison is case-insensitive. | ||||
| // If the key does not exist the default value is returned. | ||||
| func (p *Properties) GetBool(key string, def bool) bool { | ||||
| 	v, err := p.getBool(key) | ||||
| 	if err != nil { | ||||
| 		return def | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| // MustGetBool checks if the expanded value is one of '1', 'yes', | ||||
| // 'true' or 'on' if the key exists. The comparison is case-insensitive. | ||||
| // If the key does not exist the function panics. | ||||
| func (p *Properties) MustGetBool(key string) bool { | ||||
| 	v, err := p.getBool(key) | ||||
| 	if err != nil { | ||||
| 		ErrorHandler(err) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| func (p *Properties) getBool(key string) (value bool, err error) { | ||||
| 	if v, ok := p.Get(key); ok { | ||||
| 		return boolVal(v), nil | ||||
| 	} | ||||
| 	return false, invalidKeyError(key) | ||||
| } | ||||
|  | ||||
| func boolVal(v string) bool { | ||||
| 	v = strings.ToLower(v) | ||||
| 	return v == "1" || v == "true" || v == "yes" || v == "on" | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // GetDuration parses the expanded value as an time.Duration (in ns) if the | ||||
| // key exists. If key does not exist or the value cannot be parsed the default | ||||
| // value is returned. In almost all cases you want to use GetParsedDuration(). | ||||
| func (p *Properties) GetDuration(key string, def time.Duration) time.Duration { | ||||
| 	v, err := p.getInt64(key) | ||||
| 	if err != nil { | ||||
| 		return def | ||||
| 	} | ||||
| 	return time.Duration(v) | ||||
| } | ||||
|  | ||||
| // MustGetDuration parses the expanded value as an time.Duration (in ns) if | ||||
| // the key exists. If key does not exist or the value cannot be parsed the | ||||
| // function panics. In almost all cases you want to use MustGetParsedDuration(). | ||||
| func (p *Properties) MustGetDuration(key string) time.Duration { | ||||
| 	v, err := p.getInt64(key) | ||||
| 	if err != nil { | ||||
| 		ErrorHandler(err) | ||||
| 	} | ||||
| 	return time.Duration(v) | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // GetParsedDuration parses the expanded value with time.ParseDuration() if the key exists. | ||||
| // If key does not exist or the value cannot be parsed the default | ||||
| // value is returned. | ||||
| func (p *Properties) GetParsedDuration(key string, def time.Duration) time.Duration { | ||||
| 	s, ok := p.Get(key) | ||||
| 	if !ok { | ||||
| 		return def | ||||
| 	} | ||||
| 	v, err := time.ParseDuration(s) | ||||
| 	if err != nil { | ||||
| 		return def | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| // MustGetParsedDuration parses the expanded value with time.ParseDuration() if the key exists. | ||||
| // If key does not exist or the value cannot be parsed the function panics. | ||||
| func (p *Properties) MustGetParsedDuration(key string) time.Duration { | ||||
| 	s, ok := p.Get(key) | ||||
| 	if !ok { | ||||
| 		ErrorHandler(invalidKeyError(key)) | ||||
| 	} | ||||
| 	v, err := time.ParseDuration(s) | ||||
| 	if err != nil { | ||||
| 		ErrorHandler(err) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // GetFloat64 parses the expanded value as a float64 if the key exists. | ||||
| // If key does not exist or the value cannot be parsed the default | ||||
| // value is returned. | ||||
| func (p *Properties) GetFloat64(key string, def float64) float64 { | ||||
| 	v, err := p.getFloat64(key) | ||||
| 	if err != nil { | ||||
| 		return def | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| // MustGetFloat64 parses the expanded value as a float64 if the key exists. | ||||
| // If key does not exist or the value cannot be parsed the function panics. | ||||
| func (p *Properties) MustGetFloat64(key string) float64 { | ||||
| 	v, err := p.getFloat64(key) | ||||
| 	if err != nil { | ||||
| 		ErrorHandler(err) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| func (p *Properties) getFloat64(key string) (value float64, err error) { | ||||
| 	if v, ok := p.Get(key); ok { | ||||
| 		value, err = strconv.ParseFloat(v, 64) | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		return value, nil | ||||
| 	} | ||||
| 	return 0, invalidKeyError(key) | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // GetInt parses the expanded value as an int if the key exists. | ||||
| // If key does not exist or the value cannot be parsed the default | ||||
| // value is returned. If the value does not fit into an int the | ||||
| // function panics with an out of range error. | ||||
| func (p *Properties) GetInt(key string, def int) int { | ||||
| 	v, err := p.getInt64(key) | ||||
| 	if err != nil { | ||||
| 		return def | ||||
| 	} | ||||
| 	return intRangeCheck(key, v) | ||||
| } | ||||
|  | ||||
| // MustGetInt parses the expanded value as an int if the key exists. | ||||
| // If key does not exist or the value cannot be parsed the function panics. | ||||
| // If the value does not fit into an int the function panics with | ||||
| // an out of range error. | ||||
| func (p *Properties) MustGetInt(key string) int { | ||||
| 	v, err := p.getInt64(key) | ||||
| 	if err != nil { | ||||
| 		ErrorHandler(err) | ||||
| 	} | ||||
| 	return intRangeCheck(key, v) | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // GetInt64 parses the expanded value as an int64 if the key exists. | ||||
| // If key does not exist or the value cannot be parsed the default | ||||
| // value is returned. | ||||
| func (p *Properties) GetInt64(key string, def int64) int64 { | ||||
| 	v, err := p.getInt64(key) | ||||
| 	if err != nil { | ||||
| 		return def | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| // MustGetInt64 parses the expanded value as an int if the key exists. | ||||
| // If key does not exist or the value cannot be parsed the function panics. | ||||
| func (p *Properties) MustGetInt64(key string) int64 { | ||||
| 	v, err := p.getInt64(key) | ||||
| 	if err != nil { | ||||
| 		ErrorHandler(err) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| func (p *Properties) getInt64(key string) (value int64, err error) { | ||||
| 	if v, ok := p.Get(key); ok { | ||||
| 		value, err = strconv.ParseInt(v, 10, 64) | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		return value, nil | ||||
| 	} | ||||
| 	return 0, invalidKeyError(key) | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // GetUint parses the expanded value as an uint if the key exists. | ||||
| // If key does not exist or the value cannot be parsed the default | ||||
| // value is returned. If the value does not fit into an int the | ||||
| // function panics with an out of range error. | ||||
| func (p *Properties) GetUint(key string, def uint) uint { | ||||
| 	v, err := p.getUint64(key) | ||||
| 	if err != nil { | ||||
| 		return def | ||||
| 	} | ||||
| 	return uintRangeCheck(key, v) | ||||
| } | ||||
|  | ||||
| // MustGetUint parses the expanded value as an int if the key exists. | ||||
| // If key does not exist or the value cannot be parsed the function panics. | ||||
| // If the value does not fit into an int the function panics with | ||||
| // an out of range error. | ||||
| func (p *Properties) MustGetUint(key string) uint { | ||||
| 	v, err := p.getUint64(key) | ||||
| 	if err != nil { | ||||
| 		ErrorHandler(err) | ||||
| 	} | ||||
| 	return uintRangeCheck(key, v) | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // GetUint64 parses the expanded value as an uint64 if the key exists. | ||||
| // If key does not exist or the value cannot be parsed the default | ||||
| // value is returned. | ||||
| func (p *Properties) GetUint64(key string, def uint64) uint64 { | ||||
| 	v, err := p.getUint64(key) | ||||
| 	if err != nil { | ||||
| 		return def | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| // MustGetUint64 parses the expanded value as an int if the key exists. | ||||
| // If key does not exist or the value cannot be parsed the function panics. | ||||
| func (p *Properties) MustGetUint64(key string) uint64 { | ||||
| 	v, err := p.getUint64(key) | ||||
| 	if err != nil { | ||||
| 		ErrorHandler(err) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| func (p *Properties) getUint64(key string) (value uint64, err error) { | ||||
| 	if v, ok := p.Get(key); ok { | ||||
| 		value, err = strconv.ParseUint(v, 10, 64) | ||||
| 		if err != nil { | ||||
| 			return 0, err | ||||
| 		} | ||||
| 		return value, nil | ||||
| 	} | ||||
| 	return 0, invalidKeyError(key) | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // GetString returns the expanded value for the given key if exists or | ||||
| // the default value otherwise. | ||||
| func (p *Properties) GetString(key, def string) string { | ||||
| 	if v, ok := p.Get(key); ok { | ||||
| 		return v | ||||
| 	} | ||||
| 	return def | ||||
| } | ||||
|  | ||||
| // MustGetString returns the expanded value for the given key if exists or | ||||
| // panics otherwise. | ||||
| func (p *Properties) MustGetString(key string) string { | ||||
| 	if v, ok := p.Get(key); ok { | ||||
| 		return v | ||||
| 	} | ||||
| 	ErrorHandler(invalidKeyError(key)) | ||||
| 	panic("ErrorHandler should exit") | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // Filter returns a new properties object which contains all properties | ||||
| // for which the key matches the pattern. | ||||
| func (p *Properties) Filter(pattern string) (*Properties, error) { | ||||
| 	re, err := regexp.Compile(pattern) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	return p.FilterRegexp(re), nil | ||||
| } | ||||
|  | ||||
| // FilterRegexp returns a new properties object which contains all properties | ||||
| // for which the key matches the regular expression. | ||||
| func (p *Properties) FilterRegexp(re *regexp.Regexp) *Properties { | ||||
| 	pp := NewProperties() | ||||
| 	for _, k := range p.k { | ||||
| 		if re.MatchString(k) { | ||||
| 			// TODO(fs): we are ignoring the error which flags a circular reference. | ||||
| 			// TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed) | ||||
| 			pp.Set(k, p.m[k]) | ||||
| 		} | ||||
| 	} | ||||
| 	return pp | ||||
| } | ||||
|  | ||||
| // FilterPrefix returns a new properties object with a subset of all keys | ||||
| // with the given prefix. | ||||
| func (p *Properties) FilterPrefix(prefix string) *Properties { | ||||
| 	pp := NewProperties() | ||||
| 	for _, k := range p.k { | ||||
| 		if strings.HasPrefix(k, prefix) { | ||||
| 			// TODO(fs): we are ignoring the error which flags a circular reference. | ||||
| 			// TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed) | ||||
| 			pp.Set(k, p.m[k]) | ||||
| 		} | ||||
| 	} | ||||
| 	return pp | ||||
| } | ||||
|  | ||||
| // FilterStripPrefix returns a new properties object with a subset of all keys | ||||
| // with the given prefix and the prefix removed from the keys. | ||||
| func (p *Properties) FilterStripPrefix(prefix string) *Properties { | ||||
| 	pp := NewProperties() | ||||
| 	n := len(prefix) | ||||
| 	for _, k := range p.k { | ||||
| 		if len(k) > len(prefix) && strings.HasPrefix(k, prefix) { | ||||
| 			// TODO(fs): we are ignoring the error which flags a circular reference. | ||||
| 			// TODO(fs): since we are modifying keys I am not entirely sure whether we can create a circular reference | ||||
| 			// TODO(fs): this function should probably return an error but the signature is fixed | ||||
| 			pp.Set(k[n:], p.m[k]) | ||||
| 		} | ||||
| 	} | ||||
| 	return pp | ||||
| } | ||||
|  | ||||
| // Len returns the number of keys. | ||||
| func (p *Properties) Len() int { | ||||
| 	return len(p.m) | ||||
| } | ||||
|  | ||||
| // Keys returns all keys in the same order as in the input. | ||||
| func (p *Properties) Keys() []string { | ||||
| 	keys := make([]string, len(p.k)) | ||||
| 	copy(keys, p.k) | ||||
| 	return keys | ||||
| } | ||||
|  | ||||
| // Set sets the property key to the corresponding value. | ||||
| // If a value for key existed before then ok is true and prev | ||||
| // contains the previous value. If the value contains a | ||||
| // circular reference or a malformed expression then | ||||
| // an error is returned. | ||||
| // An empty key is silently ignored. | ||||
| func (p *Properties) Set(key, value string) (prev string, ok bool, err error) { | ||||
| 	if key == "" { | ||||
| 		return "", false, nil | ||||
| 	} | ||||
|  | ||||
| 	// if expansion is disabled we allow circular references | ||||
| 	if p.DisableExpansion { | ||||
| 		prev, ok = p.Get(key) | ||||
| 		p.m[key] = value | ||||
| 		if !ok { | ||||
| 			p.k = append(p.k, key) | ||||
| 		} | ||||
| 		return prev, ok, nil | ||||
| 	} | ||||
|  | ||||
| 	// to check for a circular reference we temporarily need | ||||
| 	// to set the new value. If there is an error then revert | ||||
| 	// to the previous state. Only if all tests are successful | ||||
| 	// then we add the key to the p.k list. | ||||
| 	prev, ok = p.Get(key) | ||||
| 	p.m[key] = value | ||||
|  | ||||
| 	// now check for a circular reference | ||||
| 	_, err = p.expand(key, value) | ||||
| 	if err != nil { | ||||
|  | ||||
| 		// revert to the previous state | ||||
| 		if ok { | ||||
| 			p.m[key] = prev | ||||
| 		} else { | ||||
| 			delete(p.m, key) | ||||
| 		} | ||||
|  | ||||
| 		return "", false, err | ||||
| 	} | ||||
|  | ||||
| 	if !ok { | ||||
| 		p.k = append(p.k, key) | ||||
| 	} | ||||
|  | ||||
| 	return prev, ok, nil | ||||
| } | ||||
|  | ||||
| // SetValue sets property key to the default string value | ||||
| // as defined by fmt.Sprintf("%v"). | ||||
| func (p *Properties) SetValue(key string, value interface{}) error { | ||||
| 	_, _, err := p.Set(key, fmt.Sprintf("%v", value)) | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| // MustSet sets the property key to the corresponding value. | ||||
| // If a value for key existed before then ok is true and prev | ||||
| // contains the previous value. An empty key is silently ignored. | ||||
| func (p *Properties) MustSet(key, value string) (prev string, ok bool) { | ||||
| 	prev, ok, err := p.Set(key, value) | ||||
| 	if err != nil { | ||||
| 		ErrorHandler(err) | ||||
| 	} | ||||
| 	return prev, ok | ||||
| } | ||||
|  | ||||
| // String returns a string of all expanded 'key = value' pairs. | ||||
| func (p *Properties) String() string { | ||||
| 	var s string | ||||
| 	for _, key := range p.k { | ||||
| 		value, _ := p.Get(key) | ||||
| 		s = fmt.Sprintf("%s%s = %s\n", s, key, value) | ||||
| 	} | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // Sort sorts the properties keys in alphabetical order. | ||||
| // This is helpfully before writing the properties. | ||||
| func (p *Properties) Sort() { | ||||
| 	sort.Strings(p.k) | ||||
| } | ||||
|  | ||||
| // Write writes all unexpanded 'key = value' pairs to the given writer. | ||||
| // Write returns the number of bytes written and any write error encountered. | ||||
| func (p *Properties) Write(w io.Writer, enc Encoding) (n int, err error) { | ||||
| 	return p.WriteComment(w, "", enc) | ||||
| } | ||||
|  | ||||
| // WriteComment writes all unexpanced 'key = value' pairs to the given writer. | ||||
| // If prefix is not empty then comments are written with a blank line and the | ||||
| // given prefix. The prefix should be either "# " or "! " to be compatible with | ||||
| // the properties file format. Otherwise, the properties parser will not be | ||||
| // able to read the file back in. It returns the number of bytes written and | ||||
| // any write error encountered. | ||||
| func (p *Properties) WriteComment(w io.Writer, prefix string, enc Encoding) (n int, err error) { | ||||
| 	var x int | ||||
|  | ||||
| 	for _, key := range p.k { | ||||
| 		value := p.m[key] | ||||
|  | ||||
| 		if prefix != "" { | ||||
| 			if comments, ok := p.c[key]; ok { | ||||
| 				// don't print comments if they are all empty | ||||
| 				allEmpty := true | ||||
| 				for _, c := range comments { | ||||
| 					if c != "" { | ||||
| 						allEmpty = false | ||||
| 						break | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				if !allEmpty { | ||||
| 					// add a blank line between entries but not at the top | ||||
| 					if len(comments) > 0 && n > 0 { | ||||
| 						x, err = fmt.Fprintln(w) | ||||
| 						if err != nil { | ||||
| 							return | ||||
| 						} | ||||
| 						n += x | ||||
| 					} | ||||
|  | ||||
| 					for _, c := range comments { | ||||
| 						x, err = fmt.Fprintf(w, "%s%s\n", prefix, c) | ||||
| 						if err != nil { | ||||
| 							return | ||||
| 						} | ||||
| 						n += x | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		sep := " = " | ||||
| 		if p.WriteSeparator != "" { | ||||
| 			sep = p.WriteSeparator | ||||
| 		} | ||||
| 		x, err = fmt.Fprintf(w, "%s%s%s\n", encode(key, " :", enc), sep, encode(value, "", enc)) | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 		n += x | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Map returns a copy of the properties as a map. | ||||
| func (p *Properties) Map() map[string]string { | ||||
| 	m := make(map[string]string) | ||||
| 	for k, v := range p.m { | ||||
| 		m[k] = v | ||||
| 	} | ||||
| 	return m | ||||
| } | ||||
|  | ||||
| // FilterFunc returns a copy of the properties which includes the values which passed all filters. | ||||
| func (p *Properties) FilterFunc(filters ...func(k, v string) bool) *Properties { | ||||
| 	pp := NewProperties() | ||||
| outer: | ||||
| 	for k, v := range p.m { | ||||
| 		for _, f := range filters { | ||||
| 			if !f(k, v) { | ||||
| 				continue outer | ||||
| 			} | ||||
| 			pp.Set(k, v) | ||||
| 		} | ||||
| 	} | ||||
| 	return pp | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // Delete removes the key and its comments. | ||||
| func (p *Properties) Delete(key string) { | ||||
| 	delete(p.m, key) | ||||
| 	delete(p.c, key) | ||||
| 	newKeys := []string{} | ||||
| 	for _, k := range p.k { | ||||
| 		if k != key { | ||||
| 			newKeys = append(newKeys, k) | ||||
| 		} | ||||
| 	} | ||||
| 	p.k = newKeys | ||||
| } | ||||
|  | ||||
| // Merge merges properties, comments and keys from other *Properties into p | ||||
| func (p *Properties) Merge(other *Properties) { | ||||
| 	for _, k := range other.k { | ||||
| 		if _, ok := p.m[k]; !ok { | ||||
| 			p.k = append(p.k, k) | ||||
| 		} | ||||
| 	} | ||||
| 	for k, v := range other.m { | ||||
| 		p.m[k] = v | ||||
| 	} | ||||
| 	for k, v := range other.c { | ||||
| 		p.c[k] = v | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | ||||
| // check expands all values and returns an error if a circular reference or | ||||
| // a malformed expression was found. | ||||
| func (p *Properties) check() error { | ||||
| 	for key, value := range p.m { | ||||
| 		if _, err := p.expand(key, value); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (p *Properties) expand(key, input string) (string, error) { | ||||
| 	// no pre/postfix -> nothing to expand | ||||
| 	if p.Prefix == "" && p.Postfix == "" { | ||||
| 		return input, nil | ||||
| 	} | ||||
|  | ||||
| 	return expand(input, []string{key}, p.Prefix, p.Postfix, p.m) | ||||
| } | ||||
|  | ||||
| // expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values. | ||||
| // The function keeps track of the keys that were already expanded and stops if it | ||||
| // detects a circular reference or a malformed expression of the form '(prefix)key'. | ||||
| func expand(s string, keys []string, prefix, postfix string, values map[string]string) (string, error) { | ||||
| 	if len(keys) > maxExpansionDepth { | ||||
| 		return "", fmt.Errorf("expansion too deep") | ||||
| 	} | ||||
|  | ||||
| 	for { | ||||
| 		start := strings.Index(s, prefix) | ||||
| 		if start == -1 { | ||||
| 			return s, nil | ||||
| 		} | ||||
|  | ||||
| 		keyStart := start + len(prefix) | ||||
| 		keyLen := strings.Index(s[keyStart:], postfix) | ||||
| 		if keyLen == -1 { | ||||
| 			return "", fmt.Errorf("malformed expression") | ||||
| 		} | ||||
|  | ||||
| 		end := keyStart + keyLen + len(postfix) - 1 | ||||
| 		key := s[keyStart : keyStart+keyLen] | ||||
|  | ||||
| 		// fmt.Printf("s:%q pp:%q start:%d end:%d keyStart:%d keyLen:%d key:%q\n", s, prefix + "..." + postfix, start, end, keyStart, keyLen, key) | ||||
|  | ||||
| 		for _, k := range keys { | ||||
| 			if key == k { | ||||
| 				var b bytes.Buffer | ||||
| 				b.WriteString("circular reference in:\n") | ||||
| 				for _, k1 := range keys { | ||||
| 					fmt.Fprintf(&b, "%s=%s\n", k1, values[k1]) | ||||
| 				} | ||||
| 				return "", fmt.Errorf(b.String()) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		val, ok := values[key] | ||||
| 		if !ok { | ||||
| 			val = os.Getenv(key) | ||||
| 		} | ||||
| 		new_val, err := expand(val, append(keys, key), prefix, postfix, values) | ||||
| 		if err != nil { | ||||
| 			return "", err | ||||
| 		} | ||||
| 		s = s[:start] + new_val + s[end+1:] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters. | ||||
| func encode(s string, special string, enc Encoding) string { | ||||
| 	switch enc { | ||||
| 	case UTF8: | ||||
| 		return encodeUtf8(s, special) | ||||
| 	case ISO_8859_1: | ||||
| 		return encodeIso(s, special) | ||||
| 	default: | ||||
| 		panic(fmt.Sprintf("unsupported encoding %v", enc)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func encodeUtf8(s string, special string) string { | ||||
| 	v := "" | ||||
| 	for pos := 0; pos < len(s); { | ||||
| 		r, w := utf8.DecodeRuneInString(s[pos:]) | ||||
| 		pos += w | ||||
| 		v += escape(r, special) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| func encodeIso(s string, special string) string { | ||||
| 	var r rune | ||||
| 	var w int | ||||
| 	var v string | ||||
| 	for pos := 0; pos < len(s); { | ||||
| 		switch r, w = utf8.DecodeRuneInString(s[pos:]); { | ||||
| 		case r < 1<<8: // single byte rune -> escape special chars only | ||||
| 			v += escape(r, special) | ||||
| 		case r < 1<<16: // two byte rune -> unicode literal | ||||
| 			v += fmt.Sprintf("\\u%04x", r) | ||||
| 		default: // more than two bytes per rune -> can't encode | ||||
| 			v += "?" | ||||
| 		} | ||||
| 		pos += w | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| func escape(r rune, special string) string { | ||||
| 	switch r { | ||||
| 	case '\f': | ||||
| 		return "\\f" | ||||
| 	case '\n': | ||||
| 		return "\\n" | ||||
| 	case '\r': | ||||
| 		return "\\r" | ||||
| 	case '\t': | ||||
| 		return "\\t" | ||||
| 	case '\\': | ||||
| 		return "\\\\" | ||||
| 	default: | ||||
| 		if strings.ContainsRune(special, r) { | ||||
| 			return "\\" + string(r) | ||||
| 		} | ||||
| 		return string(r) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func invalidKeyError(key string) error { | ||||
| 	return fmt.Errorf("unknown property: %s", key) | ||||
| } | ||||
							
								
								
									
										31
									
								
								vendor/github.com/magiconair/properties/rangecheck.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/magiconair/properties/rangecheck.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| // Copyright 2013-2022 Frank Schroeder. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| package properties | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| ) | ||||
|  | ||||
| // make this a var to overwrite it in a test | ||||
| var is32Bit = ^uint(0) == math.MaxUint32 | ||||
|  | ||||
| // intRangeCheck checks if the value fits into the int type and | ||||
| // panics if it does not. | ||||
| func intRangeCheck(key string, v int64) int { | ||||
| 	if is32Bit && (v < math.MinInt32 || v > math.MaxInt32) { | ||||
| 		panic(fmt.Sprintf("Value %d for key %s out of range", v, key)) | ||||
| 	} | ||||
| 	return int(v) | ||||
| } | ||||
|  | ||||
| // uintRangeCheck checks if the value fits into the uint type and | ||||
| // panics if it does not. | ||||
| func uintRangeCheck(key string, v uint64) uint { | ||||
| 	if is32Bit && v > math.MaxUint32 { | ||||
| 		panic(fmt.Sprintf("Value %d for key %s out of range", v, key)) | ||||
| 	} | ||||
| 	return uint(v) | ||||
| } | ||||
							
								
								
									
										96
									
								
								vendor/github.com/mitchellh/mapstructure/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								vendor/github.com/mitchellh/mapstructure/CHANGELOG.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,96 @@ | ||||
| ## 1.5.0 | ||||
|  | ||||
| * New option `IgnoreUntaggedFields` to ignore decoding to any fields | ||||
|   without `mapstructure` (or the configured tag name) set [GH-277] | ||||
| * New option `ErrorUnset` which makes it an error if any fields | ||||
|   in a target struct are not set by the decoding process. [GH-225] | ||||
| * New function `OrComposeDecodeHookFunc` to help compose decode hooks. [GH-240] | ||||
| * Decoding to slice from array no longer crashes [GH-265] | ||||
| * Decode nested struct pointers to map [GH-271] | ||||
| * Fix issue where `,squash` was ignored if `Squash` option was set. [GH-280] | ||||
| * Fix issue where fields with `,omitempty` would sometimes decode | ||||
|   into a map with an empty string key [GH-281] | ||||
|  | ||||
| ## 1.4.3 | ||||
|  | ||||
| * Fix cases where `json.Number` didn't decode properly [GH-261] | ||||
|  | ||||
| ## 1.4.2 | ||||
|  | ||||
| * Custom name matchers to support any sort of casing, formatting, etc. for | ||||
|   field names. [GH-250] | ||||
| * Fix possible panic in ComposeDecodeHookFunc [GH-251] | ||||
|  | ||||
| ## 1.4.1 | ||||
|  | ||||
| * Fix regression where `*time.Time` value would be set to empty and not be sent | ||||
|   to decode hooks properly [GH-232] | ||||
|  | ||||
| ## 1.4.0 | ||||
|  | ||||
| * A new decode hook type `DecodeHookFuncValue` has been added that has | ||||
|   access to the full values. [GH-183] | ||||
| * Squash is now supported with embedded fields that are struct pointers [GH-205] | ||||
| * Empty strings will convert to 0 for all numeric types when weakly decoding [GH-206] | ||||
|  | ||||
| ## 1.3.3 | ||||
|  | ||||
| * Decoding maps from maps creates a settable value for decode hooks [GH-203] | ||||
|  | ||||
| ## 1.3.2 | ||||
|  | ||||
| * Decode into interface type with a struct value is supported [GH-187] | ||||
|  | ||||
| ## 1.3.1 | ||||
|  | ||||
| * Squash should only squash embedded structs. [GH-194] | ||||
|  | ||||
| ## 1.3.0 | ||||
|  | ||||
| * Added `",omitempty"` support. This will ignore zero values in the source | ||||
|   structure when encoding. [GH-145] | ||||
|  | ||||
| ## 1.2.3 | ||||
|  | ||||
| * Fix duplicate entries in Keys list with pointer values. [GH-185] | ||||
|  | ||||
| ## 1.2.2 | ||||
|  | ||||
| * Do not add unsettable (unexported) values to the unused metadata key | ||||
|   or "remain" value. [GH-150] | ||||
|  | ||||
| ## 1.2.1 | ||||
|  | ||||
| * Go modules checksum mismatch fix | ||||
|  | ||||
| ## 1.2.0 | ||||
|  | ||||
| * Added support to capture unused values in a field using the `",remain"` value | ||||
|   in the mapstructure tag. There is an example to showcase usage. | ||||
| * Added `DecoderConfig` option to always squash embedded structs | ||||
| * `json.Number` can decode into `uint` types | ||||
| * Empty slices are preserved and not replaced with nil slices | ||||
| * Fix panic that can occur in when decoding a map into a nil slice of structs | ||||
| * Improved package documentation for godoc | ||||
|  | ||||
| ## 1.1.2 | ||||
|  | ||||
| * Fix error when decode hook decodes interface implementation into interface | ||||
|   type. [GH-140] | ||||
|  | ||||
| ## 1.1.1 | ||||
|  | ||||
| * Fix panic that can happen in `decodePtr` | ||||
|  | ||||
| ## 1.1.0 | ||||
|  | ||||
| * Added `StringToIPHookFunc` to convert `string` to `net.IP` and `net.IPNet` [GH-133] | ||||
| * Support struct to struct decoding [GH-137] | ||||
| * If source map value is nil, then destination map value is nil (instead of empty) | ||||
| * If source slice value is nil, then destination slice value is nil (instead of empty) | ||||
| * If source pointer is nil, then destination pointer is set to nil (instead of | ||||
|   allocated zero value of type) | ||||
|  | ||||
| ## 1.0.0 | ||||
|  | ||||
| * Initial tagged stable release. | ||||
							
								
								
									
										21
									
								
								vendor/github.com/mitchellh/mapstructure/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/mitchellh/mapstructure/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2013 Mitchell Hashimoto | ||||
|  | ||||
| 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: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in | ||||
| all copies or substantial portions of the Software. | ||||
|  | ||||
| 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. | ||||
							
								
								
									
										46
									
								
								vendor/github.com/mitchellh/mapstructure/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								vendor/github.com/mitchellh/mapstructure/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| # mapstructure [](https://godoc.org/github.com/mitchellh/mapstructure) | ||||
|  | ||||
| mapstructure is a Go library for decoding generic map values to structures | ||||
| and vice versa, while providing helpful error handling. | ||||
|  | ||||
| This library is most useful when decoding values from some data stream (JSON, | ||||
| Gob, etc.) where you don't _quite_ know the structure of the underlying data | ||||
| until you read a part of it. You can therefore read a `map[string]interface{}` | ||||
| and use this library to decode it into the proper underlying native Go | ||||
| structure. | ||||
|  | ||||
| ## Installation | ||||
|  | ||||
| Standard `go get`: | ||||
|  | ||||
| ``` | ||||
| $ go get github.com/mitchellh/mapstructure | ||||
| ``` | ||||
|  | ||||
| ## Usage & Example | ||||
|  | ||||
| For usage and examples see the [Godoc](http://godoc.org/github.com/mitchellh/mapstructure). | ||||
|  | ||||
| The `Decode` function has examples associated with it there. | ||||
|  | ||||
| ## But Why?! | ||||
|  | ||||
| Go offers fantastic standard libraries for decoding formats such as JSON. | ||||
| The standard method is to have a struct pre-created, and populate that struct | ||||
| from the bytes of the encoded format. This is great, but the problem is if | ||||
| you have configuration or an encoding that changes slightly depending on | ||||
| specific fields. For example, consider this JSON: | ||||
|  | ||||
| ```json | ||||
| { | ||||
|   "type": "person", | ||||
|   "name": "Mitchell" | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Perhaps we can't populate a specific structure without first reading | ||||
| the "type" field from the JSON. We could always do two passes over the | ||||
| decoding of the JSON (reading the "type" first, and the rest later). | ||||
| However, it is much simpler to just decode this into a `map[string]interface{}` | ||||
| structure, read the "type" key, then use something like this library | ||||
| to decode it into the proper structure. | ||||
							
								
								
									
										279
									
								
								vendor/github.com/mitchellh/mapstructure/decode_hooks.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								vendor/github.com/mitchellh/mapstructure/decode_hooks.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,279 @@ | ||||
| package mapstructure | ||||
|  | ||||
| import ( | ||||
| 	"encoding" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"net" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| // typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns | ||||
| // it into the proper DecodeHookFunc type, such as DecodeHookFuncType. | ||||
| func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc { | ||||
| 	// Create variables here so we can reference them with the reflect pkg | ||||
| 	var f1 DecodeHookFuncType | ||||
| 	var f2 DecodeHookFuncKind | ||||
| 	var f3 DecodeHookFuncValue | ||||
|  | ||||
| 	// Fill in the variables into this interface and the rest is done | ||||
| 	// automatically using the reflect package. | ||||
| 	potential := []interface{}{f1, f2, f3} | ||||
|  | ||||
| 	v := reflect.ValueOf(h) | ||||
| 	vt := v.Type() | ||||
| 	for _, raw := range potential { | ||||
| 		pt := reflect.ValueOf(raw).Type() | ||||
| 		if vt.ConvertibleTo(pt) { | ||||
| 			return v.Convert(pt).Interface() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // DecodeHookExec executes the given decode hook. This should be used | ||||
| // since it'll naturally degrade to the older backwards compatible DecodeHookFunc | ||||
| // that took reflect.Kind instead of reflect.Type. | ||||
| func DecodeHookExec( | ||||
| 	raw DecodeHookFunc, | ||||
| 	from reflect.Value, to reflect.Value) (interface{}, error) { | ||||
|  | ||||
| 	switch f := typedDecodeHook(raw).(type) { | ||||
| 	case DecodeHookFuncType: | ||||
| 		return f(from.Type(), to.Type(), from.Interface()) | ||||
| 	case DecodeHookFuncKind: | ||||
| 		return f(from.Kind(), to.Kind(), from.Interface()) | ||||
| 	case DecodeHookFuncValue: | ||||
| 		return f(from, to) | ||||
| 	default: | ||||
| 		return nil, errors.New("invalid decode hook signature") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ComposeDecodeHookFunc creates a single DecodeHookFunc that | ||||
| // automatically composes multiple DecodeHookFuncs. | ||||
| // | ||||
| // The composed funcs are called in order, with the result of the | ||||
| // previous transformation. | ||||
| func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { | ||||
| 	return func(f reflect.Value, t reflect.Value) (interface{}, error) { | ||||
| 		var err error | ||||
| 		data := f.Interface() | ||||
|  | ||||
| 		newFrom := f | ||||
| 		for _, f1 := range fs { | ||||
| 			data, err = DecodeHookExec(f1, newFrom, t) | ||||
| 			if err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			newFrom = reflect.ValueOf(data) | ||||
| 		} | ||||
|  | ||||
| 		return data, nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned. | ||||
| // If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages. | ||||
| func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc { | ||||
| 	return func(a, b reflect.Value) (interface{}, error) { | ||||
| 		var allErrs string | ||||
| 		var out interface{} | ||||
| 		var err error | ||||
|  | ||||
| 		for _, f := range ff { | ||||
| 			out, err = DecodeHookExec(f, a, b) | ||||
| 			if err != nil { | ||||
| 				allErrs += err.Error() + "\n" | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			return out, nil | ||||
| 		} | ||||
|  | ||||
| 		return nil, errors.New(allErrs) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // StringToSliceHookFunc returns a DecodeHookFunc that converts | ||||
| // string to []string by splitting on the given sep. | ||||
| func StringToSliceHookFunc(sep string) DecodeHookFunc { | ||||
| 	return func( | ||||
| 		f reflect.Kind, | ||||
| 		t reflect.Kind, | ||||
| 		data interface{}) (interface{}, error) { | ||||
| 		if f != reflect.String || t != reflect.Slice { | ||||
| 			return data, nil | ||||
| 		} | ||||
|  | ||||
| 		raw := data.(string) | ||||
| 		if raw == "" { | ||||
| 			return []string{}, nil | ||||
| 		} | ||||
|  | ||||
| 		return strings.Split(raw, sep), nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // StringToTimeDurationHookFunc returns a DecodeHookFunc that converts | ||||
| // strings to time.Duration. | ||||
| func StringToTimeDurationHookFunc() DecodeHookFunc { | ||||
| 	return func( | ||||
| 		f reflect.Type, | ||||
| 		t reflect.Type, | ||||
| 		data interface{}) (interface{}, error) { | ||||
| 		if f.Kind() != reflect.String { | ||||
| 			return data, nil | ||||
| 		} | ||||
| 		if t != reflect.TypeOf(time.Duration(5)) { | ||||
| 			return data, nil | ||||
| 		} | ||||
|  | ||||
| 		// Convert it by parsing | ||||
| 		return time.ParseDuration(data.(string)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // StringToIPHookFunc returns a DecodeHookFunc that converts | ||||
| // strings to net.IP | ||||
| func StringToIPHookFunc() DecodeHookFunc { | ||||
| 	return func( | ||||
| 		f reflect.Type, | ||||
| 		t reflect.Type, | ||||
| 		data interface{}) (interface{}, error) { | ||||
| 		if f.Kind() != reflect.String { | ||||
| 			return data, nil | ||||
| 		} | ||||
| 		if t != reflect.TypeOf(net.IP{}) { | ||||
| 			return data, nil | ||||
| 		} | ||||
|  | ||||
| 		// Convert it by parsing | ||||
| 		ip := net.ParseIP(data.(string)) | ||||
| 		if ip == nil { | ||||
| 			return net.IP{}, fmt.Errorf("failed parsing ip %v", data) | ||||
| 		} | ||||
|  | ||||
| 		return ip, nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // StringToIPNetHookFunc returns a DecodeHookFunc that converts | ||||
| // strings to net.IPNet | ||||
| func StringToIPNetHookFunc() DecodeHookFunc { | ||||
| 	return func( | ||||
| 		f reflect.Type, | ||||
| 		t reflect.Type, | ||||
| 		data interface{}) (interface{}, error) { | ||||
| 		if f.Kind() != reflect.String { | ||||
| 			return data, nil | ||||
| 		} | ||||
| 		if t != reflect.TypeOf(net.IPNet{}) { | ||||
| 			return data, nil | ||||
| 		} | ||||
|  | ||||
| 		// Convert it by parsing | ||||
| 		_, net, err := net.ParseCIDR(data.(string)) | ||||
| 		return net, err | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // StringToTimeHookFunc returns a DecodeHookFunc that converts | ||||
| // strings to time.Time. | ||||
| func StringToTimeHookFunc(layout string) DecodeHookFunc { | ||||
| 	return func( | ||||
| 		f reflect.Type, | ||||
| 		t reflect.Type, | ||||
| 		data interface{}) (interface{}, error) { | ||||
| 		if f.Kind() != reflect.String { | ||||
| 			return data, nil | ||||
| 		} | ||||
| 		if t != reflect.TypeOf(time.Time{}) { | ||||
| 			return data, nil | ||||
| 		} | ||||
|  | ||||
| 		// Convert it by parsing | ||||
| 		return time.Parse(layout, data.(string)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to | ||||
| // the decoder. | ||||
| // | ||||
| // Note that this is significantly different from the WeaklyTypedInput option | ||||
| // of the DecoderConfig. | ||||
| func WeaklyTypedHook( | ||||
| 	f reflect.Kind, | ||||
| 	t reflect.Kind, | ||||
| 	data interface{}) (interface{}, error) { | ||||
| 	dataVal := reflect.ValueOf(data) | ||||
| 	switch t { | ||||
| 	case reflect.String: | ||||
| 		switch f { | ||||
| 		case reflect.Bool: | ||||
| 			if dataVal.Bool() { | ||||
| 				return "1", nil | ||||
| 			} | ||||
| 			return "0", nil | ||||
| 		case reflect.Float32: | ||||
| 			return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil | ||||
| 		case reflect.Int: | ||||
| 			return strconv.FormatInt(dataVal.Int(), 10), nil | ||||
| 		case reflect.Slice: | ||||
| 			dataType := dataVal.Type() | ||||
| 			elemKind := dataType.Elem().Kind() | ||||
| 			if elemKind == reflect.Uint8 { | ||||
| 				return string(dataVal.Interface().([]uint8)), nil | ||||
| 			} | ||||
| 		case reflect.Uint: | ||||
| 			return strconv.FormatUint(dataVal.Uint(), 10), nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return data, nil | ||||
| } | ||||
|  | ||||
| func RecursiveStructToMapHookFunc() DecodeHookFunc { | ||||
| 	return func(f reflect.Value, t reflect.Value) (interface{}, error) { | ||||
| 		if f.Kind() != reflect.Struct { | ||||
| 			return f.Interface(), nil | ||||
| 		} | ||||
|  | ||||
| 		var i interface{} = struct{}{} | ||||
| 		if t.Type() != reflect.TypeOf(&i).Elem() { | ||||
| 			return f.Interface(), nil | ||||
| 		} | ||||
|  | ||||
| 		m := make(map[string]interface{}) | ||||
| 		t.Set(reflect.ValueOf(m)) | ||||
|  | ||||
| 		return f.Interface(), nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TextUnmarshallerHookFunc returns a DecodeHookFunc that applies | ||||
| // strings to the UnmarshalText function, when the target type | ||||
| // implements the encoding.TextUnmarshaler interface | ||||
| func TextUnmarshallerHookFunc() DecodeHookFuncType { | ||||
| 	return func( | ||||
| 		f reflect.Type, | ||||
| 		t reflect.Type, | ||||
| 		data interface{}) (interface{}, error) { | ||||
| 		if f.Kind() != reflect.String { | ||||
| 			return data, nil | ||||
| 		} | ||||
| 		result := reflect.New(t).Interface() | ||||
| 		unmarshaller, ok := result.(encoding.TextUnmarshaler) | ||||
| 		if !ok { | ||||
| 			return data, nil | ||||
| 		} | ||||
| 		if err := unmarshaller.UnmarshalText([]byte(data.(string))); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return result, nil | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										50
									
								
								vendor/github.com/mitchellh/mapstructure/error.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								vendor/github.com/mitchellh/mapstructure/error.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| package mapstructure | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"sort" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| // Error implements the error interface and can represents multiple | ||||
| // errors that occur in the course of a single decode. | ||||
| type Error struct { | ||||
| 	Errors []string | ||||
| } | ||||
|  | ||||
| func (e *Error) Error() string { | ||||
| 	points := make([]string, len(e.Errors)) | ||||
| 	for i, err := range e.Errors { | ||||
| 		points[i] = fmt.Sprintf("* %s", err) | ||||
| 	} | ||||
|  | ||||
| 	sort.Strings(points) | ||||
| 	return fmt.Sprintf( | ||||
| 		"%d error(s) decoding:\n\n%s", | ||||
| 		len(e.Errors), strings.Join(points, "\n")) | ||||
| } | ||||
|  | ||||
| // WrappedErrors implements the errwrap.Wrapper interface to make this | ||||
| // return value more useful with the errwrap and go-multierror libraries. | ||||
| func (e *Error) WrappedErrors() []error { | ||||
| 	if e == nil { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	result := make([]error, len(e.Errors)) | ||||
| 	for i, e := range e.Errors { | ||||
| 		result[i] = errors.New(e) | ||||
| 	} | ||||
|  | ||||
| 	return result | ||||
| } | ||||
|  | ||||
| func appendErrors(errors []string, err error) []string { | ||||
| 	switch e := err.(type) { | ||||
| 	case *Error: | ||||
| 		return append(errors, e.Errors...) | ||||
| 	default: | ||||
| 		return append(errors, e.Error()) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1540
									
								
								vendor/github.com/mitchellh/mapstructure/mapstructure.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1540
									
								
								vendor/github.com/mitchellh/mapstructure/mapstructure.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								vendor/github.com/pelletier/go-toml/v2/.dockerignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/pelletier/go-toml/v2/.dockerignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| cmd/tomll/tomll | ||||
| cmd/tomljson/tomljson | ||||
							
								
								
									
										4
									
								
								vendor/github.com/pelletier/go-toml/v2/.gitattributes
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								vendor/github.com/pelletier/go-toml/v2/.gitattributes
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| * text=auto | ||||
|  | ||||
| benchmark/benchmark.toml text eol=lf | ||||
| testdata/** text eol=lf | ||||
							
								
								
									
										6
									
								
								vendor/github.com/pelletier/go-toml/v2/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/pelletier/go-toml/v2/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| test_program/test_program_bin | ||||
| fuzz/ | ||||
| cmd/tomll/tomll | ||||
| cmd/tomljson/tomljson | ||||
| cmd/tomltestgen/tomltestgen | ||||
| dist | ||||
							
								
								
									
										84
									
								
								vendor/github.com/pelletier/go-toml/v2/.golangci.toml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								vendor/github.com/pelletier/go-toml/v2/.golangci.toml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,84 @@ | ||||
| [service] | ||||
| golangci-lint-version =  "1.39.0" | ||||
|  | ||||
| [linters-settings.wsl] | ||||
| allow-assign-and-anything = true | ||||
|  | ||||
| [linters-settings.exhaustive] | ||||
| default-signifies-exhaustive = true | ||||
|  | ||||
| [linters] | ||||
| disable-all = true | ||||
| enable = [ | ||||
|     "asciicheck", | ||||
|     "bodyclose", | ||||
|     "cyclop", | ||||
|     "deadcode", | ||||
|     "depguard", | ||||
|     "dogsled", | ||||
|     "dupl", | ||||
|     "durationcheck", | ||||
|     "errcheck", | ||||
|     "errorlint", | ||||
|     "exhaustive", | ||||
|     # "exhaustivestruct", | ||||
|     "exportloopref", | ||||
|     "forbidigo", | ||||
|     # "forcetypeassert", | ||||
|     "funlen", | ||||
|     "gci", | ||||
|     # "gochecknoglobals", | ||||
|     "gochecknoinits", | ||||
|     "gocognit", | ||||
|     "goconst", | ||||
|     "gocritic", | ||||
|     "gocyclo", | ||||
|     "godot", | ||||
|     "godox", | ||||
|     # "goerr113", | ||||
|     "gofmt", | ||||
|     "gofumpt", | ||||
|     "goheader", | ||||
|     "goimports", | ||||
|     "golint", | ||||
|     "gomnd", | ||||
|     # "gomoddirectives", | ||||
|     "gomodguard", | ||||
|     "goprintffuncname", | ||||
|     "gosec", | ||||
|     "gosimple", | ||||
|     "govet", | ||||
|     # "ifshort", | ||||
|     "importas", | ||||
|     "ineffassign", | ||||
|     "lll", | ||||
|     "makezero", | ||||
|     "misspell", | ||||
|     "nakedret", | ||||
|     "nestif", | ||||
|     "nilerr", | ||||
|     # "nlreturn", | ||||
|     "noctx", | ||||
|     "nolintlint", | ||||
|     #"paralleltest", | ||||
|     "prealloc", | ||||
|     "predeclared", | ||||
|     "revive", | ||||
|     "rowserrcheck", | ||||
|     "sqlclosecheck", | ||||
|     "staticcheck", | ||||
|     "structcheck", | ||||
|     "stylecheck", | ||||
|     # "testpackage", | ||||
|     "thelper", | ||||
|     "tparallel", | ||||
|     "typecheck", | ||||
|     "unconvert", | ||||
|     "unparam", | ||||
|     "unused", | ||||
|     "varcheck", | ||||
|     "wastedassign", | ||||
|     "whitespace", | ||||
|     # "wrapcheck", | ||||
|     # "wsl" | ||||
| ] | ||||
							
								
								
									
										123
									
								
								vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| before: | ||||
|   hooks: | ||||
|     - go mod tidy | ||||
|     - go fmt ./... | ||||
|     - go test ./... | ||||
| builds: | ||||
|   - id: tomll | ||||
|     main: ./cmd/tomll | ||||
|     binary: tomll | ||||
|     env: | ||||
|       - CGO_ENABLED=0 | ||||
|     flags: | ||||
|       - -trimpath | ||||
|     ldflags: | ||||
|       - -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} | ||||
|     mod_timestamp: '{{ .CommitTimestamp }}' | ||||
|     targets: | ||||
|       - linux_amd64 | ||||
|       - linux_arm64 | ||||
|       - linux_arm | ||||
|       - windows_amd64 | ||||
|       - windows_arm64 | ||||
|       - windows_arm | ||||
|       - darwin_amd64 | ||||
|       - darwin_arm64 | ||||
|   - id: tomljson | ||||
|     main: ./cmd/tomljson | ||||
|     binary: tomljson | ||||
|     env: | ||||
|       - CGO_ENABLED=0 | ||||
|     flags: | ||||
|       - -trimpath | ||||
|     ldflags: | ||||
|       - -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} | ||||
|     mod_timestamp: '{{ .CommitTimestamp }}' | ||||
|     targets: | ||||
|       - linux_amd64 | ||||
|       - linux_arm64 | ||||
|       - linux_arm | ||||
|       - windows_amd64 | ||||
|       - windows_arm64 | ||||
|       - windows_arm | ||||
|       - darwin_amd64 | ||||
|       - darwin_arm64 | ||||
|   - id: jsontoml | ||||
|     main: ./cmd/jsontoml | ||||
|     binary: jsontoml | ||||
|     env: | ||||
|       - CGO_ENABLED=0 | ||||
|     flags: | ||||
|       - -trimpath | ||||
|     ldflags: | ||||
|       - -X main.version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.CommitDate}} | ||||
|     mod_timestamp: '{{ .CommitTimestamp }}' | ||||
|     targets: | ||||
|       - linux_amd64 | ||||
|       - linux_arm64 | ||||
|       - linux_arm | ||||
|       - windows_amd64 | ||||
|       - windows_arm64 | ||||
|       - windows_arm | ||||
|       - darwin_amd64 | ||||
|       - darwin_arm64 | ||||
| universal_binaries: | ||||
|   - id: tomll | ||||
|     replace: true | ||||
|     name_template: tomll | ||||
|   - id: tomljson | ||||
|     replace: true | ||||
|     name_template: tomljson | ||||
|   - id: jsontoml | ||||
|     replace: true | ||||
|     name_template: jsontoml | ||||
| archives: | ||||
| - id: jsontoml | ||||
|   format: tar.xz | ||||
|   builds: | ||||
|     - jsontoml | ||||
|   files: | ||||
|   - none* | ||||
|   name_template: "{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}" | ||||
| - id: tomljson | ||||
|   format: tar.xz | ||||
|   builds: | ||||
|     - tomljson | ||||
|   files: | ||||
|   - none* | ||||
|   name_template: "{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}" | ||||
| - id: tomll | ||||
|   format: tar.xz | ||||
|   builds: | ||||
|     - tomll | ||||
|   files: | ||||
|   - none* | ||||
|   name_template: "{{ .Binary }}_{{.Version}}_{{ .Os }}_{{ .Arch }}" | ||||
| dockers: | ||||
|   - id: tools | ||||
|     goos: linux | ||||
|     goarch: amd64 | ||||
|     ids: | ||||
|       - jsontoml | ||||
|       - tomljson | ||||
|       - tomll | ||||
|     image_templates: | ||||
|       - "ghcr.io/pelletier/go-toml:latest" | ||||
|       - "ghcr.io/pelletier/go-toml:{{ .Tag }}" | ||||
|       - "ghcr.io/pelletier/go-toml:v{{ .Major }}" | ||||
|     skip_push: false | ||||
| checksum: | ||||
|   name_template: 'sha256sums.txt' | ||||
| snapshot: | ||||
|   name_template: "{{ incpatch .Version }}-next" | ||||
| release: | ||||
|   github: | ||||
|     owner: pelletier | ||||
|     name: go-toml | ||||
|   draft: true | ||||
|   prerelease: auto | ||||
|   mode: replace | ||||
| changelog: | ||||
|   use: github-native | ||||
| announce: | ||||
|   skip: true | ||||
							
								
								
									
										196
									
								
								vendor/github.com/pelletier/go-toml/v2/CONTRIBUTING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								vendor/github.com/pelletier/go-toml/v2/CONTRIBUTING.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,196 @@ | ||||
| # Contributing | ||||
|  | ||||
| Thank you for your interest in go-toml! We appreciate you considering | ||||
| contributing to go-toml! | ||||
|  | ||||
| The main goal is the project is to provide an easy-to-use and efficient TOML | ||||
| implementation for Go that gets the job done and gets out of your way – dealing | ||||
| with TOML is probably not the central piece of your project. | ||||
|  | ||||
| As the single maintainer of go-toml, time is scarce. All help, big or small, is | ||||
| more than welcomed! | ||||
|  | ||||
| ## Ask questions | ||||
|  | ||||
| Any question you may have, somebody else might have it too. Always feel free to | ||||
| ask them on the [discussion board][discussions]. We will try to answer them as | ||||
| clearly and quickly as possible, time permitting. | ||||
|  | ||||
| Asking questions also helps us identify areas where the documentation needs | ||||
| improvement, or new features that weren't envisioned before. Sometimes, a | ||||
| seemingly innocent question leads to the fix of a bug. Don't hesitate and ask | ||||
| away! | ||||
|  | ||||
| [discussions]: https://github.com/pelletier/go-toml/discussions | ||||
|  | ||||
| ## Improve the documentation | ||||
|  | ||||
| The best way to share your knowledge and experience with go-toml is to improve | ||||
| the documentation. Fix a typo, clarify an interface, add an example, anything | ||||
| goes! | ||||
|  | ||||
| The documentation is present in the [README][readme] and thorough the source | ||||
| code. On release, it gets updated on [pkg.go.dev][pkg.go.dev]. To make a change | ||||
| to the documentation, create a pull request with your proposed changes. For | ||||
| simple changes like that, the easiest way to go is probably the "Fork this | ||||
| project and edit the file" button on Github, displayed at the top right of the | ||||
| file. Unless it's a trivial change (for example a typo), provide a little bit of | ||||
| context in your pull request description or commit message. | ||||
|  | ||||
| ## Report a bug | ||||
|  | ||||
| Found a bug! Sorry to hear that :(. Help us and other track them down and fix by | ||||
| reporting it. [File a new bug report][bug-report] on the [issues | ||||
| tracker][issues-tracker]. The template should provide enough guidance on what to | ||||
| include. When in doubt: add more details! By reducing ambiguity and providing | ||||
| more information, it decreases back and forth and saves everyone time. | ||||
|  | ||||
| ## Code changes | ||||
|  | ||||
| Want to contribute a patch? Very happy to hear that! | ||||
|  | ||||
| First, some high-level rules: | ||||
|  | ||||
| - A short proposal with some POC code is better than a lengthy piece of text | ||||
|   with no code. Code speaks louder than words. That being said, bigger changes | ||||
|   should probably start with a [discussion][discussions]. | ||||
| - No backward-incompatible patch will be accepted unless discussed. Sometimes | ||||
|   it's hard, but we try not to break people's programs unless we absolutely have | ||||
|   to. | ||||
| - If you are writing a new feature or extending an existing one, make sure to | ||||
|   write some documentation. | ||||
| - Bug fixes need to be accompanied with regression tests. | ||||
| - New code needs to be tested. | ||||
| - Your commit messages need to explain why the change is needed, even if already | ||||
|   included in the PR description. | ||||
|  | ||||
| It does sound like a lot, but those best practices are here to save time overall | ||||
| and continuously improve the quality of the project, which is something everyone | ||||
| benefits from. | ||||
|  | ||||
| ### Get started | ||||
|  | ||||
| The fairly standard code contribution process looks like that: | ||||
|  | ||||
| 1. [Fork the project][fork]. | ||||
| 2. Make your changes, commit on any branch you like. | ||||
| 3. [Open up a pull request][pull-request] | ||||
| 4. Review, potential ask for changes. | ||||
| 5. Merge. | ||||
|  | ||||
| Feel free to ask for help! You can create draft pull requests to gather | ||||
| some early feedback! | ||||
|  | ||||
| ### Run the tests | ||||
|  | ||||
| You can run tests for go-toml using Go's test tool: `go test -race ./...`. | ||||
|  | ||||
| During the pull request process, all tests will be ran on Linux, Windows, and | ||||
| MacOS on the last two versions of Go. | ||||
|  | ||||
| However, given GitHub's new policy to _not_ run Actions on pull requests until a | ||||
| maintainer clicks on button, it is highly recommended that you run them locally | ||||
| as you make changes. | ||||
|  | ||||
| ### Check coverage | ||||
|  | ||||
| We use `go tool cover` to compute test coverage. Most code editors have a way to | ||||
| run and display code coverage, but at the end of the day, we do this: | ||||
|  | ||||
| ``` | ||||
| go test -covermode=atomic -coverprofile=coverage.out | ||||
| go tool cover -func=coverage.out | ||||
| ``` | ||||
|  | ||||
| and verify that the overall percentage of tested code does not go down. This is | ||||
| a requirement. As a rule of thumb, all lines of code touched by your changes | ||||
| should be covered. On Unix you can use `./ci.sh coverage -d v2` to check if your | ||||
| code lowers the coverage. | ||||
|  | ||||
| ### Verify performance | ||||
|  | ||||
| Go-toml aims to stay efficient. We rely on a set of scenarios executed with Go's | ||||
| builtin benchmark systems. Because of their noisy nature, containers provided by | ||||
| Github Actions cannot be reliably used for benchmarking. As a result, you are | ||||
| responsible for checking that your changes do not incur a performance penalty. | ||||
| You can run their following to execute benchmarks: | ||||
|  | ||||
| ``` | ||||
| go test ./... -bench=. -count=10 | ||||
| ``` | ||||
|  | ||||
| Benchmark results should be compared against each other with | ||||
| [benchstat][benchstat]. Typical flow looks like this: | ||||
|  | ||||
| 1. On the `v2` branch, run `go test ./... -bench=. -count 10` and save output to | ||||
|    a file (for example `old.txt`). | ||||
| 2. Make some code changes. | ||||
| 3. Run `go test ....` again, and save the output to an other file (for example | ||||
|    `new.txt`). | ||||
| 4. Run `benchstat old.txt new.txt` to check that time/op does not go up in any | ||||
|    test. | ||||
|  | ||||
| On Unix you can use `./ci.sh benchmark -d v2` to verify how your code impacts | ||||
| performance. | ||||
|  | ||||
| It is highly encouraged to add the benchstat results to your pull request | ||||
| description. Pull requests that lower performance will receive more scrutiny. | ||||
|  | ||||
| [benchstat]: https://pkg.go.dev/golang.org/x/perf/cmd/benchstat | ||||
|  | ||||
| ### Style | ||||
|  | ||||
| Try to look around and follow the same format and structure as the rest of the | ||||
| code. We enforce using `go fmt` on the whole code base. | ||||
|  | ||||
| --- | ||||
|  | ||||
| ## Maintainers-only | ||||
|  | ||||
| ### Merge pull request | ||||
|  | ||||
| Checklist: | ||||
|  | ||||
| - Passing CI. | ||||
| - Does not introduce backward-incompatible changes (unless discussed). | ||||
| - Has relevant doc changes. | ||||
| - Benchstat does not show performance regression. | ||||
| - Pull request is [labeled appropriately][pr-labels]. | ||||
| - Title will be understandable in the changelog. | ||||
|  | ||||
| 1. Merge using "squash and merge". | ||||
| 2. Make sure to edit the commit message to keep all the useful information | ||||
|    nice and clean. | ||||
| 3. Make sure the commit title is clear and contains the PR number (#123). | ||||
|  | ||||
| ### New release | ||||
|  | ||||
| 1. Decide on the next version number. Use semver. | ||||
| 2. Generate release notes using [`gh`][gh]. Example: | ||||
| ``` | ||||
| $ gh api -X POST \ | ||||
|   -F tag_name='v2.0.0-beta.5' \ | ||||
|   -F target_commitish='v2' \ | ||||
|   -F previous_tag_name='v2.0.0-beta.4' \ | ||||
|   --jq '.body' \ | ||||
|   repos/pelletier/go-toml/releases/generate-notes | ||||
| ``` | ||||
| 3. Look for "Other changes". That would indicate a pull request not labeled | ||||
|    properly. Tweak labels and pull request titles until changelog looks good for | ||||
|    users. | ||||
| 4. [Draft new release][new-release]. | ||||
| 5. Fill tag and target with the same value used to generate the changelog. | ||||
| 6. Set title to the new tag value. | ||||
| 7. Paste the generated changelog. | ||||
| 8. Check "create discussion", in the "Releases" category. | ||||
| 9. Check pre-release if new version is an alpha or beta. | ||||
|  | ||||
| [issues-tracker]: https://github.com/pelletier/go-toml/issues | ||||
| [bug-report]: https://github.com/pelletier/go-toml/issues/new?template=bug_report.md | ||||
| [pkg.go.dev]: https://pkg.go.dev/github.com/pelletier/go-toml | ||||
| [readme]: ./README.md | ||||
| [fork]: https://help.github.com/articles/fork-a-repo | ||||
| [pull-request]: https://help.github.com/en/articles/creating-a-pull-request | ||||
| [new-release]: https://github.com/pelletier/go-toml/releases/new | ||||
| [gh]: https://github.com/cli/cli | ||||
| [pr-labels]: https://github.com/pelletier/go-toml/blob/v2/.github/release.yml | ||||
							
								
								
									
										5
									
								
								vendor/github.com/pelletier/go-toml/v2/Dockerfile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								vendor/github.com/pelletier/go-toml/v2/Dockerfile
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| FROM scratch | ||||
| ENV PATH "$PATH:/bin" | ||||
| COPY tomll /bin/tomll | ||||
| COPY tomljson /bin/tomljson | ||||
| COPY jsontoml /bin/jsontoml | ||||
							
								
								
									
										21
									
								
								vendor/github.com/pelletier/go-toml/v2/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/pelletier/go-toml/v2/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2013 - 2022 Thomas Pelletier, Eric Anderton | ||||
|  | ||||
| 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: | ||||
|  | ||||
| The above copyright notice and this permission notice shall be included in all | ||||
| copies or substantial portions of the Software. | ||||
|  | ||||
| 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. | ||||
							
								
								
									
										563
									
								
								vendor/github.com/pelletier/go-toml/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										563
									
								
								vendor/github.com/pelletier/go-toml/v2/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,563 @@ | ||||
| # go-toml v2 | ||||
|  | ||||
| Go library for the [TOML](https://toml.io/en/) format. | ||||
|  | ||||
| This library supports [TOML v1.0.0](https://toml.io/en/v1.0.0). | ||||
|  | ||||
| [🐞 Bug Reports](https://github.com/pelletier/go-toml/issues) | ||||
|  | ||||
| [💬 Anything else](https://github.com/pelletier/go-toml/discussions) | ||||
|  | ||||
| ## Documentation | ||||
|  | ||||
| Full API, examples, and implementation notes are available in the Go | ||||
| documentation. | ||||
|  | ||||
| [](https://pkg.go.dev/github.com/pelletier/go-toml/v2) | ||||
|  | ||||
| ## Import | ||||
|  | ||||
| ```go | ||||
| import "github.com/pelletier/go-toml/v2" | ||||
| ``` | ||||
|  | ||||
| See [Modules](#Modules). | ||||
|  | ||||
| ## Features | ||||
|  | ||||
| ### Stdlib behavior | ||||
|  | ||||
| As much as possible, this library is designed to behave similarly as the | ||||
| standard library's `encoding/json`. | ||||
|  | ||||
| ### Performance | ||||
|  | ||||
| While go-toml favors usability, it is written with performance in mind. Most | ||||
| operations should not be shockingly slow. See [benchmarks](#benchmarks). | ||||
|  | ||||
| ### Strict mode | ||||
|  | ||||
| `Decoder` can be set to "strict mode", which makes it error when some parts of | ||||
| the TOML document was not present in the target structure. This is a great way | ||||
| to check for typos. [See example in the documentation][strict]. | ||||
|  | ||||
| [strict]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#example-Decoder.DisallowUnknownFields | ||||
|  | ||||
| ### Contextualized errors | ||||
|  | ||||
| When most decoding errors occur, go-toml returns [`DecodeError`][decode-err]), | ||||
| which contains a human readable contextualized version of the error. For | ||||
| example: | ||||
|  | ||||
| ``` | ||||
| 2| key1 = "value1" | ||||
| 3| key2 = "missing2" | ||||
|  | ~~~~ missing field | ||||
| 4| key3 = "missing3" | ||||
| 5| key4 = "value4" | ||||
| ``` | ||||
|  | ||||
| [decode-err]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#DecodeError | ||||
|  | ||||
| ### Local date and time support | ||||
|  | ||||
| TOML supports native [local date/times][ldt]. It allows to represent a given | ||||
| date, time, or date-time without relation to a timezone or offset. To support | ||||
| this use-case, go-toml provides [`LocalDate`][tld], [`LocalTime`][tlt], and | ||||
| [`LocalDateTime`][tldt]. Those types can be transformed to and from `time.Time`, | ||||
| making them convenient yet unambiguous structures for their respective TOML | ||||
| representation. | ||||
|  | ||||
| [ldt]: https://toml.io/en/v1.0.0#local-date-time | ||||
| [tld]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalDate | ||||
| [tlt]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalTime | ||||
| [tldt]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#LocalDateTime | ||||
|  | ||||
| ## Getting started | ||||
|  | ||||
| Given the following struct, let's see how to read it and write it as TOML: | ||||
|  | ||||
| ```go | ||||
| type MyConfig struct { | ||||
|       Version int | ||||
|       Name    string | ||||
|       Tags    []string | ||||
| } | ||||
| ``` | ||||
|  | ||||
| ### Unmarshaling | ||||
|  | ||||
| [`Unmarshal`][unmarshal] reads a TOML document and fills a Go structure with its | ||||
| content. For example: | ||||
|  | ||||
| ```go | ||||
| doc := ` | ||||
| version = 2 | ||||
| name = "go-toml" | ||||
| tags = ["go", "toml"] | ||||
| ` | ||||
|  | ||||
| var cfg MyConfig | ||||
| err := toml.Unmarshal([]byte(doc), &cfg) | ||||
| if err != nil { | ||||
|       panic(err) | ||||
| } | ||||
| fmt.Println("version:", cfg.Version) | ||||
| fmt.Println("name:", cfg.Name) | ||||
| fmt.Println("tags:", cfg.Tags) | ||||
|  | ||||
| // Output: | ||||
| // version: 2 | ||||
| // name: go-toml | ||||
| // tags: [go toml] | ||||
| ``` | ||||
|  | ||||
| [unmarshal]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Unmarshal | ||||
|  | ||||
| ### Marshaling | ||||
|  | ||||
| [`Marshal`][marshal] is the opposite of Unmarshal: it represents a Go structure | ||||
| as a TOML document: | ||||
|  | ||||
| ```go | ||||
| cfg := MyConfig{ | ||||
|       Version: 2, | ||||
|       Name:    "go-toml", | ||||
|       Tags:    []string{"go", "toml"}, | ||||
| } | ||||
|  | ||||
| b, err := toml.Marshal(cfg) | ||||
| if err != nil { | ||||
|       panic(err) | ||||
| } | ||||
| fmt.Println(string(b)) | ||||
|  | ||||
| // Output: | ||||
| // Version = 2 | ||||
| // Name = 'go-toml' | ||||
| // Tags = ['go', 'toml'] | ||||
| ``` | ||||
|  | ||||
| [marshal]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Marshal | ||||
|  | ||||
| ## Unstable API | ||||
|  | ||||
| This API does not yet follow the backward compatibility guarantees of this | ||||
| library. They provide early access to features that may have rough edges or an | ||||
| API subject to change. | ||||
|  | ||||
| ### Parser | ||||
|  | ||||
| Parser is the unstable API that allows iterative parsing of a TOML document at | ||||
| the AST level. See https://pkg.go.dev/github.com/pelletier/go-toml/v2/unstable. | ||||
|  | ||||
| ## Benchmarks | ||||
|  | ||||
| Execution time speedup compared to other Go TOML libraries: | ||||
|  | ||||
| <table> | ||||
|     <thead> | ||||
|         <tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|         <tr><td>Marshal/HugoFrontMatter-2</td><td>1.9x</td><td>1.9x</td></tr> | ||||
|         <tr><td>Marshal/ReferenceFile/map-2</td><td>1.7x</td><td>1.8x</td></tr> | ||||
|         <tr><td>Marshal/ReferenceFile/struct-2</td><td>2.2x</td><td>2.5x</td></tr> | ||||
|         <tr><td>Unmarshal/HugoFrontMatter-2</td><td>2.9x</td><td>2.9x</td></tr> | ||||
|         <tr><td>Unmarshal/ReferenceFile/map-2</td><td>2.6x</td><td>2.9x</td></tr> | ||||
|         <tr><td>Unmarshal/ReferenceFile/struct-2</td><td>4.4x</td><td>5.3x</td></tr> | ||||
|      </tbody> | ||||
| </table> | ||||
| <details><summary>See more</summary> | ||||
| <p>The table above has the results of the most common use-cases. The table below | ||||
| contains the results of all benchmarks, including unrealistic ones. It is | ||||
| provided for completeness.</p> | ||||
|  | ||||
| <table> | ||||
|     <thead> | ||||
|         <tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|         <tr><td>Marshal/SimpleDocument/map-2</td><td>1.8x</td><td>2.9x</td></tr> | ||||
|         <tr><td>Marshal/SimpleDocument/struct-2</td><td>2.7x</td><td>4.2x</td></tr> | ||||
|         <tr><td>Unmarshal/SimpleDocument/map-2</td><td>4.5x</td><td>3.1x</td></tr> | ||||
|         <tr><td>Unmarshal/SimpleDocument/struct-2</td><td>6.2x</td><td>3.9x</td></tr> | ||||
|         <tr><td>UnmarshalDataset/example-2</td><td>3.1x</td><td>3.5x</td></tr> | ||||
|         <tr><td>UnmarshalDataset/code-2</td><td>2.3x</td><td>3.1x</td></tr> | ||||
|         <tr><td>UnmarshalDataset/twitter-2</td><td>2.5x</td><td>2.6x</td></tr> | ||||
|         <tr><td>UnmarshalDataset/citm_catalog-2</td><td>2.1x</td><td>2.2x</td></tr> | ||||
|         <tr><td>UnmarshalDataset/canada-2</td><td>1.6x</td><td>1.3x</td></tr> | ||||
|         <tr><td>UnmarshalDataset/config-2</td><td>4.3x</td><td>3.2x</td></tr> | ||||
|         <tr><td>[Geo mean]</td><td>2.7x</td><td>2.8x</td></tr> | ||||
|      </tbody> | ||||
| </table> | ||||
| <p>This table can be generated with <code>./ci.sh benchmark -a -html</code>.</p> | ||||
| </details> | ||||
|  | ||||
| ## Modules | ||||
|  | ||||
| go-toml uses Go's standard modules system. | ||||
|  | ||||
| Installation instructions: | ||||
|  | ||||
| - Go ≥ 1.16: Nothing to do. Use the import in your code. The `go` command deals | ||||
|   with it automatically. | ||||
| - Go ≥ 1.13: `GO111MODULE=on go get github.com/pelletier/go-toml/v2`. | ||||
|  | ||||
| In case of trouble: [Go Modules FAQ][mod-faq]. | ||||
|  | ||||
| [mod-faq]: https://github.com/golang/go/wiki/Modules#why-does-installing-a-tool-via-go-get-fail-with-error-cannot-find-main-module | ||||
|  | ||||
| ## Tools | ||||
|  | ||||
| Go-toml provides three handy command line tools: | ||||
|  | ||||
|  * `tomljson`: Reads a TOML file and outputs its JSON representation. | ||||
|  | ||||
|     ``` | ||||
|     $ go install github.com/pelletier/go-toml/v2/cmd/tomljson@latest | ||||
|     $ tomljson --help | ||||
|     ``` | ||||
|  | ||||
|  * `jsontoml`: Reads a JSON file and outputs a TOML representation. | ||||
|  | ||||
|     ``` | ||||
|     $ go install github.com/pelletier/go-toml/v2/cmd/jsontoml@latest | ||||
|     $ jsontoml --help | ||||
|     ``` | ||||
|  | ||||
|  * `tomll`: Lints and reformats a TOML file. | ||||
|  | ||||
|     ``` | ||||
|     $ go install github.com/pelletier/go-toml/v2/cmd/tomll@latest | ||||
|     $ tomll --help | ||||
|     ``` | ||||
|  | ||||
| ### Docker image | ||||
|  | ||||
| Those tools are also available as a [Docker image][docker]. For example, to use | ||||
| `tomljson`: | ||||
|  | ||||
| ``` | ||||
| docker run -i ghcr.io/pelletier/go-toml:v2 tomljson < example.toml | ||||
| ``` | ||||
|  | ||||
| Multiple versions are availble on [ghcr.io][docker]. | ||||
|  | ||||
| [docker]: https://github.com/pelletier/go-toml/pkgs/container/go-toml | ||||
|  | ||||
| ## Migrating from v1 | ||||
|  | ||||
| This section describes the differences between v1 and v2, with some pointers on | ||||
| how to get the original behavior when possible. | ||||
|  | ||||
| ### Decoding / Unmarshal | ||||
|  | ||||
| #### Automatic field name guessing | ||||
|  | ||||
| When unmarshaling to a struct, if a key in the TOML document does not exactly | ||||
| match the name of a struct field or any of the `toml`-tagged field, v1 tries | ||||
| multiple variations of the key ([code][v1-keys]). | ||||
|  | ||||
| V2 instead does a case-insensitive matching, like `encoding/json`. | ||||
|  | ||||
| This could impact you if you are relying on casing to differentiate two fields, | ||||
| and one of them is a not using the `toml` struct tag. The recommended solution | ||||
| is to be specific about tag names for those fields using the `toml` struct tag. | ||||
|  | ||||
| [v1-keys]: https://github.com/pelletier/go-toml/blob/a2e52561804c6cd9392ebf0048ca64fe4af67a43/marshal.go#L775-L781 | ||||
|  | ||||
| #### Ignore preexisting value in interface | ||||
|  | ||||
| When decoding into a non-nil `interface{}`, go-toml v1 uses the type of the | ||||
| element in the interface to decode the object. For example: | ||||
|  | ||||
| ```go | ||||
| type inner struct { | ||||
|   B interface{} | ||||
| } | ||||
| type doc struct { | ||||
|   A interface{} | ||||
| } | ||||
|  | ||||
| d := doc{ | ||||
|   A: inner{ | ||||
|     B: "Before", | ||||
|   }, | ||||
| } | ||||
|  | ||||
| data := ` | ||||
| [A] | ||||
| B = "After" | ||||
| ` | ||||
|  | ||||
| toml.Unmarshal([]byte(data), &d) | ||||
| fmt.Printf("toml v1: %#v\n", d) | ||||
|  | ||||
| // toml v1: main.doc{A:main.inner{B:"After"}} | ||||
| ``` | ||||
|  | ||||
| In this case, field `A` is of type `interface{}`, containing a `inner` struct. | ||||
| V1 sees that type and uses it when decoding the object. | ||||
|  | ||||
| When decoding an object into an `interface{}`, V2 instead disregards whatever | ||||
| value the `interface{}` may contain and replaces it with a | ||||
| `map[string]interface{}`. With the same data structure as above, here is what | ||||
| the result looks like: | ||||
|  | ||||
| ```go | ||||
| toml.Unmarshal([]byte(data), &d) | ||||
| fmt.Printf("toml v2: %#v\n", d) | ||||
|  | ||||
| // toml v2: main.doc{A:map[string]interface {}{"B":"After"}} | ||||
| ``` | ||||
|  | ||||
| This is to match `encoding/json`'s behavior. There is no way to make the v2 | ||||
| decoder behave like v1. | ||||
|  | ||||
| #### Values out of array bounds ignored | ||||
|  | ||||
| When decoding into an array, v1 returns an error when the number of elements | ||||
| contained in the doc is superior to the capacity of the array. For example: | ||||
|  | ||||
| ```go | ||||
| type doc struct { | ||||
|   A [2]string | ||||
| } | ||||
| d := doc{} | ||||
| err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d) | ||||
| fmt.Println(err) | ||||
|  | ||||
| // (1, 1): unmarshal: TOML array length (3) exceeds destination array length (2) | ||||
| ``` | ||||
|  | ||||
| In the same situation, v2 ignores the last value: | ||||
|  | ||||
| ```go | ||||
| err := toml.Unmarshal([]byte(`A = ["one", "two", "many"]`), &d) | ||||
| fmt.Println("err:", err, "d:", d) | ||||
| // err: <nil> d: {[one two]} | ||||
| ``` | ||||
|  | ||||
| This is to match `encoding/json`'s behavior. There is no way to make the v2 | ||||
| decoder behave like v1. | ||||
|  | ||||
| #### Support for `toml.Unmarshaler` has been dropped | ||||
|  | ||||
| This method was not widely used, poorly defined, and added a lot of complexity. | ||||
| A similar effect can be achieved by implementing the `encoding.TextUnmarshaler` | ||||
| interface and use strings. | ||||
|  | ||||
| #### Support for `default` struct tag has been dropped | ||||
|  | ||||
| This feature adds complexity and a poorly defined API for an effect that can be | ||||
| accomplished outside of the library. | ||||
|  | ||||
| It does not seem like other format parsers in Go support that feature (the | ||||
| project referenced in the original ticket #202 has not been updated since 2017). | ||||
| Given that go-toml v2 should not touch values not in the document, the same | ||||
| effect can be achieved by pre-filling the struct with defaults (libraries like | ||||
| [go-defaults][go-defaults] can help). Also, string representation is not well | ||||
| defined for all types: it creates issues like #278. | ||||
|  | ||||
| The recommended replacement is pre-filling the struct before unmarshaling. | ||||
|  | ||||
| [go-defaults]: https://github.com/mcuadros/go-defaults | ||||
|  | ||||
| #### `toml.Tree` replacement | ||||
|  | ||||
| This structure was the initial attempt at providing a document model for | ||||
| go-toml. It allows manipulating the structure of any document, encoding and | ||||
| decoding from their TOML representation. While a more robust feature was | ||||
| initially planned in go-toml v2, this has been ultimately [removed from | ||||
| scope][nodoc] of this library, with no plan to add it back at the moment. The | ||||
| closest equivalent at the moment would be to unmarshal into an `interface{}` and | ||||
| use type assertions and/or reflection to manipulate the arbitrary | ||||
| structure. However this would fall short of providing all of the TOML features | ||||
| such as adding comments and be specific about whitespace. | ||||
|  | ||||
|  | ||||
| #### `toml.Position` are not retrievable anymore | ||||
|  | ||||
| The API for retrieving the position (line, column) of a specific TOML element do | ||||
| not exist anymore. This was done to minimize the amount of concepts introduced | ||||
| by the library (query path), and avoid the performance hit related to storing | ||||
| positions in the absence of a document model, for a feature that seemed to have | ||||
| little use. Errors however have gained more detailed position | ||||
| information. Position retrieval seems better fitted for a document model, which | ||||
| has been [removed from the scope][nodoc] of go-toml v2 at the moment. | ||||
|  | ||||
| ### Encoding / Marshal | ||||
|  | ||||
| #### Default struct fields order | ||||
|  | ||||
| V1 emits struct fields order alphabetically by default. V2 struct fields are | ||||
| emitted in order they are defined. For example: | ||||
|  | ||||
| ```go | ||||
| type S struct { | ||||
| 	B string | ||||
| 	A string | ||||
| } | ||||
|  | ||||
| data := S{ | ||||
| 	B: "B", | ||||
| 	A: "A", | ||||
| } | ||||
|  | ||||
| b, _ := tomlv1.Marshal(data) | ||||
| fmt.Println("v1:\n" + string(b)) | ||||
|  | ||||
| b, _ = tomlv2.Marshal(data) | ||||
| fmt.Println("v2:\n" + string(b)) | ||||
|  | ||||
| // Output: | ||||
| // v1: | ||||
| // A = "A" | ||||
| // B = "B" | ||||
|  | ||||
| // v2: | ||||
| // B = 'B' | ||||
| // A = 'A' | ||||
| ``` | ||||
|  | ||||
| There is no way to make v2 encoder behave like v1. A workaround could be to | ||||
| manually sort the fields alphabetically in the struct definition, or generate | ||||
| struct types using `reflect.StructOf`. | ||||
|  | ||||
| #### No indentation by default | ||||
|  | ||||
| V1 automatically indents content of tables by default. V2 does not. However the | ||||
| same behavior can be obtained using [`Encoder.SetIndentTables`][sit]. For example: | ||||
|  | ||||
| ```go | ||||
| data := map[string]interface{}{ | ||||
| 	"table": map[string]string{ | ||||
| 		"key": "value", | ||||
| 	}, | ||||
| } | ||||
|  | ||||
| b, _ := tomlv1.Marshal(data) | ||||
| fmt.Println("v1:\n" + string(b)) | ||||
|  | ||||
| b, _ = tomlv2.Marshal(data) | ||||
| fmt.Println("v2:\n" + string(b)) | ||||
|  | ||||
| buf := bytes.Buffer{} | ||||
| enc := tomlv2.NewEncoder(&buf) | ||||
| enc.SetIndentTables(true) | ||||
| enc.Encode(data) | ||||
| fmt.Println("v2 Encoder:\n" + string(buf.Bytes())) | ||||
|  | ||||
| // Output: | ||||
| // v1: | ||||
| // | ||||
| // [table] | ||||
| //   key = "value" | ||||
| // | ||||
| // v2: | ||||
| // [table] | ||||
| // key = 'value' | ||||
| // | ||||
| // | ||||
| // v2 Encoder: | ||||
| // [table] | ||||
| //   key = 'value' | ||||
| ``` | ||||
|  | ||||
| [sit]: https://pkg.go.dev/github.com/pelletier/go-toml/v2#Encoder.SetIndentTables | ||||
|  | ||||
| #### Keys and strings are single quoted | ||||
|  | ||||
| V1 always uses double quotes (`"`) around strings and keys that cannot be | ||||
| represented bare (unquoted). V2 uses single quotes instead by default (`'`), | ||||
| unless a character cannot be represented, then falls back to double quotes. As a | ||||
| result of this change, `Encoder.QuoteMapKeys` has been removed, as it is not | ||||
| useful anymore. | ||||
|  | ||||
| There is no way to make v2 encoder behave like v1. | ||||
|  | ||||
| #### `TextMarshaler` emits as a string, not TOML | ||||
|  | ||||
| Types that implement [`encoding.TextMarshaler`][tm] can emit arbitrary TOML in | ||||
| v1. The encoder would append the result to the output directly. In v2 the result | ||||
| is wrapped in a string. As a result, this interface cannot be implemented by the | ||||
| root object. | ||||
|  | ||||
| There is no way to make v2 encoder behave like v1. | ||||
|  | ||||
| [tm]: https://golang.org/pkg/encoding/#TextMarshaler | ||||
|  | ||||
| #### `Encoder.CompactComments` has been removed | ||||
|  | ||||
| Emitting compact comments is now the default behavior of go-toml. This option | ||||
| is not necessary anymore. | ||||
|  | ||||
| #### Struct tags have been merged | ||||
|  | ||||
| V1 used to provide multiple struct tags: `comment`, `commented`, `multiline`, | ||||
| `toml`, and `omitempty`. To behave more like the standard library, v2 has merged | ||||
| `toml`, `multiline`, and `omitempty`. For example: | ||||
|  | ||||
| ```go | ||||
| type doc struct { | ||||
| 	// v1 | ||||
| 	F string `toml:"field" multiline:"true" omitempty:"true"` | ||||
| 	// v2 | ||||
| 	F string `toml:"field,multiline,omitempty"` | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Has a result, the `Encoder.SetTag*` methods have been removed, as there is just | ||||
| one tag now. | ||||
|  | ||||
|  | ||||
| #### `commented` tag has been removed | ||||
|  | ||||
| There is no replacement for the `commented` tag. This feature would be better | ||||
| suited in a proper document model for go-toml v2, which has been [cut from | ||||
| scope][nodoc] at the moment. | ||||
|  | ||||
| #### `Encoder.ArraysWithOneElementPerLine` has been renamed | ||||
|  | ||||
| The new name is `Encoder.SetArraysMultiline`. The behavior should be the same. | ||||
|  | ||||
| #### `Encoder.Indentation` has been renamed | ||||
|  | ||||
| The new name is `Encoder.SetIndentSymbol`. The behavior should be the same. | ||||
|  | ||||
|  | ||||
| #### Embedded structs behave like stdlib | ||||
|  | ||||
| V1 defaults to merging embedded struct fields into the embedding struct. This | ||||
| behavior was unexpected because it does not follow the standard library. To | ||||
| avoid breaking backward compatibility, the `Encoder.PromoteAnonymous` method was | ||||
| added to make the encoder behave correctly. Given backward compatibility is not | ||||
| a problem anymore, v2 does the right thing by default: it follows the behavior | ||||
| of `encoding/json`. `Encoder.PromoteAnonymous` has been removed. | ||||
|  | ||||
| [nodoc]: https://github.com/pelletier/go-toml/discussions/506#discussioncomment-1526038 | ||||
|  | ||||
| ### `query` | ||||
|  | ||||
| go-toml v1 provided the [`go-toml/query`][query] package. It allowed to run | ||||
| JSONPath-style queries on TOML files. This feature is not available in v2. For a | ||||
| replacement, check out [dasel][dasel]. | ||||
|  | ||||
| This package has been removed because it was essentially not supported anymore | ||||
| (last commit May 2020), increased the complexity of the code base, and more | ||||
| complete solutions exist out there. | ||||
|  | ||||
| [query]: https://github.com/pelletier/go-toml/tree/f99d6bbca119636aeafcf351ee52b3d202782627/query | ||||
| [dasel]: https://github.com/TomWright/dasel | ||||
|  | ||||
| ## Versioning | ||||
|  | ||||
| Go-toml follows [Semantic Versioning](http://semver.org/). The supported version | ||||
| of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of | ||||
| this document. The last two major versions of Go are supported | ||||
| (see [Go Release Policy](https://golang.org/doc/devel/release.html#policy)). | ||||
|  | ||||
| ## License | ||||
|  | ||||
| The MIT License (MIT). Read [LICENSE](LICENSE). | ||||
							
								
								
									
										19
									
								
								vendor/github.com/pelletier/go-toml/v2/SECURITY.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								vendor/github.com/pelletier/go-toml/v2/SECURITY.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| # Security Policy | ||||
|  | ||||
| ## Supported Versions | ||||
|  | ||||
| Use this section to tell people about which versions of your project are | ||||
| currently being supported with security updates. | ||||
|  | ||||
| | Version    | Supported          | | ||||
| | ---------- | ------------------ | | ||||
| | Latest 2.x | :white_check_mark: | | ||||
| | All 1.x    | :x:                | | ||||
| | All 0.x    | :x:                | | ||||
|  | ||||
| ## Reporting a Vulnerability | ||||
|  | ||||
| Email a vulnerability report to `security@pelletier.codes`. Make sure to include | ||||
| as many details as possible to reproduce the vulnerability. This is a | ||||
| side-project: I will try to get back to you as quickly as possible, time | ||||
| permitting in my personal life. Providing a working patch helps very much! | ||||
							
								
								
									
										279
									
								
								vendor/github.com/pelletier/go-toml/v2/ci.sh
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										279
									
								
								vendor/github.com/pelletier/go-toml/v2/ci.sh
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,279 @@ | ||||
| #!/usr/bin/env bash | ||||
|  | ||||
|  | ||||
| stderr() { | ||||
|     echo "$@" 1>&2 | ||||
| } | ||||
|  | ||||
| usage() { | ||||
|     b=$(basename "$0") | ||||
|     echo $b: ERROR: "$@" 1>&2 | ||||
|  | ||||
|     cat 1>&2 <<EOF | ||||
|  | ||||
| DESCRIPTION | ||||
|  | ||||
|     $(basename "$0") is the script to run continuous integration commands for | ||||
|     go-toml on unix. | ||||
|  | ||||
|     Requires Go and Git to be available in the PATH. Expects to be ran from the | ||||
|     root of go-toml's Git repository. | ||||
|  | ||||
| USAGE | ||||
|  | ||||
|     $b COMMAND [OPTIONS...] | ||||
|  | ||||
| COMMANDS | ||||
|  | ||||
| benchmark [OPTIONS...] [BRANCH] | ||||
|  | ||||
|     Run benchmarks. | ||||
|  | ||||
|     ARGUMENTS | ||||
|  | ||||
|         BRANCH Optional. Defines which Git branch to use when running | ||||
|                benchmarks. | ||||
|  | ||||
|     OPTIONS | ||||
|  | ||||
|         -d      Compare benchmarks of HEAD with BRANCH using benchstats. In | ||||
|                 this form the BRANCH argument is required. | ||||
|  | ||||
|         -a      Compare benchmarks of HEAD against go-toml v1 and | ||||
|                 BurntSushi/toml. | ||||
|  | ||||
|         -html   When used with -a, emits the output as HTML, ready to be | ||||
|                 embedded in the README. | ||||
|  | ||||
| coverage [OPTIONS...] [BRANCH] | ||||
|  | ||||
|     Generates code coverage. | ||||
|  | ||||
|     ARGUMENTS | ||||
|  | ||||
|         BRANCH  Optional. Defines which Git branch to use when reporting | ||||
|                 coverage. Defaults to HEAD. | ||||
|  | ||||
|     OPTIONS | ||||
|  | ||||
|         -d      Compare coverage of HEAD with the one of BRANCH. In this form, | ||||
|                 the BRANCH argument is required. Exit code is non-zero when | ||||
|                 coverage percentage decreased. | ||||
| EOF | ||||
|     exit 1 | ||||
| } | ||||
|  | ||||
| cover() { | ||||
|     branch="${1}" | ||||
|     dir="$(mktemp -d)" | ||||
|  | ||||
|     stderr "Executing coverage for ${branch} at ${dir}" | ||||
|  | ||||
|     if [ "${branch}" = "HEAD" ]; then | ||||
| 	    cp -r . "${dir}/" | ||||
|     else | ||||
| 	    git worktree add "$dir" "$branch" | ||||
|     fi | ||||
|  | ||||
|     pushd "$dir" | ||||
|     go test -covermode=atomic  -coverpkg=./... -coverprofile=coverage.out.tmp ./... | ||||
|     cat coverage.out.tmp | grep -v testsuite | grep -v tomltestgen | grep -v gotoml-test-decoder > coverage.out | ||||
|     go tool cover -func=coverage.out | ||||
|     popd | ||||
|  | ||||
|     if [ "${branch}" != "HEAD" ]; then | ||||
| 	    git worktree remove --force "$dir" | ||||
|     fi | ||||
| } | ||||
|  | ||||
| coverage() { | ||||
|     case "$1" in | ||||
| 	-d) | ||||
| 	    shift | ||||
| 	    target="${1?Need to provide a target branch argument}" | ||||
|  | ||||
| 	    output_dir="$(mktemp -d)" | ||||
| 	    target_out="${output_dir}/target.txt" | ||||
| 	    head_out="${output_dir}/head.txt" | ||||
| 	     | ||||
| 	    cover "${target}" > "${target_out}" | ||||
| 	    cover "HEAD" > "${head_out}" | ||||
|  | ||||
| 	    cat "${target_out}" | ||||
| 	    cat "${head_out}" | ||||
|  | ||||
| 	    echo "" | ||||
|  | ||||
| 	    target_pct="$(tail -n2 ${target_out} | head -n1 | sed -E 's/.*total.*\t([0-9.]+)%.*/\1/')" | ||||
| 	    head_pct="$(tail -n2 ${head_out} | head -n1 | sed -E 's/.*total.*\t([0-9.]+)%/\1/')" | ||||
| 	    echo "Results: ${target} ${target_pct}% HEAD ${head_pct}%" | ||||
|  | ||||
| 	    delta_pct=$(echo "$head_pct - $target_pct" | bc -l) | ||||
| 	    echo "Delta: ${delta_pct}" | ||||
|  | ||||
| 	    if [[ $delta_pct = \-* ]]; then | ||||
| 		    echo "Regression!"; | ||||
|  | ||||
|             target_diff="${output_dir}/target.diff.txt" | ||||
|             head_diff="${output_dir}/head.diff.txt" | ||||
|             cat "${target_out}" | grep -E '^github.com/pelletier/go-toml' | tr -s "\t " | cut -f 2,3 | sort > "${target_diff}" | ||||
|             cat "${head_out}" | grep -E '^github.com/pelletier/go-toml' | tr -s "\t " | cut -f 2,3 | sort > "${head_diff}" | ||||
|  | ||||
|             diff --side-by-side --suppress-common-lines "${target_diff}" "${head_diff}" | ||||
| 		    return 1 | ||||
| 	    fi | ||||
| 	    return 0 | ||||
| 	    ;; | ||||
|     esac | ||||
|  | ||||
|     cover "${1-HEAD}" | ||||
| } | ||||
|  | ||||
| bench() { | ||||
|     branch="${1}" | ||||
|     out="${2}" | ||||
|     replace="${3}" | ||||
|     dir="$(mktemp -d)" | ||||
|  | ||||
|     stderr "Executing benchmark for ${branch} at ${dir}" | ||||
|  | ||||
|     if [ "${branch}" = "HEAD" ]; then | ||||
|     	cp -r . "${dir}/" | ||||
|     else | ||||
| 	    git worktree add "$dir" "$branch" | ||||
|     fi | ||||
|  | ||||
|     pushd "$dir" | ||||
|  | ||||
|     if [ "${replace}" != "" ]; then | ||||
|         find ./benchmark/ -iname '*.go' -exec sed -i -E "s|github.com/pelletier/go-toml/v2|${replace}|g" {} \; | ||||
|         go get "${replace}" | ||||
|     fi | ||||
|  | ||||
|     export GOMAXPROCS=2 | ||||
|     nice -n -19 taskset --cpu-list 0,1 go test '-bench=^Benchmark(Un)?[mM]arshal' -count=5 -run=Nothing ./... | tee "${out}" | ||||
|     popd | ||||
|  | ||||
|     if [ "${branch}" != "HEAD" ]; then | ||||
| 	    git worktree remove --force "$dir" | ||||
|     fi | ||||
| } | ||||
|  | ||||
| fmktemp() { | ||||
|     if mktemp --version|grep GNU >/dev/null; then | ||||
|         mktemp --suffix=-$1; | ||||
|     else | ||||
|         mktemp -t $1; | ||||
|     fi | ||||
| } | ||||
|  | ||||
| benchstathtml() { | ||||
| python3 - $1 <<'EOF' | ||||
| import sys | ||||
|  | ||||
| lines = [] | ||||
| stop = False | ||||
|  | ||||
| with open(sys.argv[1]) as f: | ||||
|     for line in f.readlines(): | ||||
|         line = line.strip() | ||||
|         if line == "": | ||||
|             stop = True | ||||
|         if not stop: | ||||
|             lines.append(line.split(',')) | ||||
|  | ||||
| results = [] | ||||
| for line in reversed(lines[1:]): | ||||
|     v2 = float(line[1]) | ||||
|     results.append([ | ||||
|         line[0].replace("-32", ""), | ||||
|         "%.1fx" % (float(line[3])/v2),  # v1 | ||||
|         "%.1fx" % (float(line[5])/v2),  # bs | ||||
|     ]) | ||||
| # move geomean to the end | ||||
| results.append(results[0]) | ||||
| del results[0] | ||||
|  | ||||
|  | ||||
| def printtable(data): | ||||
|     print(""" | ||||
| <table> | ||||
|     <thead> | ||||
|         <tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr> | ||||
|     </thead> | ||||
|     <tbody>""") | ||||
|  | ||||
|     for r in data: | ||||
|         print("        <tr><td>{}</td><td>{}</td><td>{}</td></tr>".format(*r)) | ||||
|  | ||||
|     print("""     </tbody> | ||||
| </table>""") | ||||
|  | ||||
|  | ||||
| def match(x): | ||||
|     return "ReferenceFile" in x[0] or "HugoFrontMatter" in x[0] | ||||
|  | ||||
| above = [x for x in results if match(x)] | ||||
| below = [x for x in results if not match(x)] | ||||
|  | ||||
| printtable(above) | ||||
| print("<details><summary>See more</summary>") | ||||
| print("""<p>The table above has the results of the most common use-cases. The table below | ||||
| contains the results of all benchmarks, including unrealistic ones. It is | ||||
| provided for completeness.</p>""") | ||||
| printtable(below) | ||||
| print('<p>This table can be generated with <code>./ci.sh benchmark -a -html</code>.</p>') | ||||
| print("</details>") | ||||
|  | ||||
| EOF | ||||
| } | ||||
|  | ||||
| benchmark() { | ||||
|     case "$1" in | ||||
|     -d) | ||||
|         shift | ||||
|      	target="${1?Need to provide a target branch argument}" | ||||
|  | ||||
|         old=`fmktemp ${target}` | ||||
|         bench "${target}" "${old}" | ||||
|  | ||||
|         new=`fmktemp HEAD` | ||||
|         bench HEAD "${new}" | ||||
|  | ||||
|         benchstat "${old}" "${new}" | ||||
|         return 0 | ||||
|         ;; | ||||
|     -a) | ||||
|         shift | ||||
|  | ||||
|         v2stats=`fmktemp go-toml-v2` | ||||
|         bench HEAD "${v2stats}" "github.com/pelletier/go-toml/v2" | ||||
|         v1stats=`fmktemp go-toml-v1` | ||||
|         bench HEAD "${v1stats}" "github.com/pelletier/go-toml" | ||||
|         bsstats=`fmktemp bs-toml` | ||||
|         bench HEAD "${bsstats}" "github.com/BurntSushi/toml" | ||||
|  | ||||
|         cp "${v2stats}" go-toml-v2.txt | ||||
|         cp "${v1stats}" go-toml-v1.txt | ||||
|         cp "${bsstats}" bs-toml.txt | ||||
|  | ||||
|         if [ "$1" = "-html" ]; then | ||||
|             tmpcsv=`fmktemp csv` | ||||
|             benchstat -csv -geomean go-toml-v2.txt go-toml-v1.txt bs-toml.txt > $tmpcsv | ||||
|             benchstathtml $tmpcsv | ||||
|         else | ||||
|             benchstat -geomean go-toml-v2.txt go-toml-v1.txt bs-toml.txt | ||||
|         fi | ||||
|  | ||||
|         rm -f go-toml-v2.txt go-toml-v1.txt bs-toml.txt | ||||
|         return $? | ||||
|     esac | ||||
|  | ||||
|     bench "${1-HEAD}" `mktemp` | ||||
| } | ||||
|  | ||||
| case "$1" in | ||||
|     coverage) shift; coverage $@;; | ||||
|     benchmark) shift; benchmark $@;; | ||||
|     *) usage "bad argument $1";; | ||||
| esac | ||||
							
								
								
									
										550
									
								
								vendor/github.com/pelletier/go-toml/v2/decode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										550
									
								
								vendor/github.com/pelletier/go-toml/v2/decode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,550 @@ | ||||
| package toml | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/pelletier/go-toml/v2/unstable" | ||||
| ) | ||||
|  | ||||
| func parseInteger(b []byte) (int64, error) { | ||||
| 	if len(b) > 2 && b[0] == '0' { | ||||
| 		switch b[1] { | ||||
| 		case 'x': | ||||
| 			return parseIntHex(b) | ||||
| 		case 'b': | ||||
| 			return parseIntBin(b) | ||||
| 		case 'o': | ||||
| 			return parseIntOct(b) | ||||
| 		default: | ||||
| 			panic(fmt.Errorf("invalid base '%c', should have been checked by scanIntOrFloat", b[1])) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return parseIntDec(b) | ||||
| } | ||||
|  | ||||
| func parseLocalDate(b []byte) (LocalDate, error) { | ||||
| 	// full-date      = date-fullyear "-" date-month "-" date-mday | ||||
| 	// date-fullyear  = 4DIGIT | ||||
| 	// date-month     = 2DIGIT  ; 01-12 | ||||
| 	// date-mday      = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on month/year | ||||
| 	var date LocalDate | ||||
|  | ||||
| 	if len(b) != 10 || b[4] != '-' || b[7] != '-' { | ||||
| 		return date, unstable.NewParserError(b, "dates are expected to have the format YYYY-MM-DD") | ||||
| 	} | ||||
|  | ||||
| 	var err error | ||||
|  | ||||
| 	date.Year, err = parseDecimalDigits(b[0:4]) | ||||
| 	if err != nil { | ||||
| 		return LocalDate{}, err | ||||
| 	} | ||||
|  | ||||
| 	date.Month, err = parseDecimalDigits(b[5:7]) | ||||
| 	if err != nil { | ||||
| 		return LocalDate{}, err | ||||
| 	} | ||||
|  | ||||
| 	date.Day, err = parseDecimalDigits(b[8:10]) | ||||
| 	if err != nil { | ||||
| 		return LocalDate{}, err | ||||
| 	} | ||||
|  | ||||
| 	if !isValidDate(date.Year, date.Month, date.Day) { | ||||
| 		return LocalDate{}, unstable.NewParserError(b, "impossible date") | ||||
| 	} | ||||
|  | ||||
| 	return date, nil | ||||
| } | ||||
|  | ||||
| func parseDecimalDigits(b []byte) (int, error) { | ||||
| 	v := 0 | ||||
|  | ||||
| 	for i, c := range b { | ||||
| 		if c < '0' || c > '9' { | ||||
| 			return 0, unstable.NewParserError(b[i:i+1], "expected digit (0-9)") | ||||
| 		} | ||||
| 		v *= 10 | ||||
| 		v += int(c - '0') | ||||
| 	} | ||||
|  | ||||
| 	return v, nil | ||||
| } | ||||
|  | ||||
| func parseDateTime(b []byte) (time.Time, error) { | ||||
| 	// offset-date-time = full-date time-delim full-time | ||||
| 	// full-time      = partial-time time-offset | ||||
| 	// time-offset    = "Z" / time-numoffset | ||||
| 	// time-numoffset = ( "+" / "-" ) time-hour ":" time-minute | ||||
|  | ||||
| 	dt, b, err := parseLocalDateTime(b) | ||||
| 	if err != nil { | ||||
| 		return time.Time{}, err | ||||
| 	} | ||||
|  | ||||
| 	var zone *time.Location | ||||
|  | ||||
| 	if len(b) == 0 { | ||||
| 		// parser should have checked that when assigning the date time node | ||||
| 		panic("date time should have a timezone") | ||||
| 	} | ||||
|  | ||||
| 	if b[0] == 'Z' || b[0] == 'z' { | ||||
| 		b = b[1:] | ||||
| 		zone = time.UTC | ||||
| 	} else { | ||||
| 		const dateTimeByteLen = 6 | ||||
| 		if len(b) != dateTimeByteLen { | ||||
| 			return time.Time{}, unstable.NewParserError(b, "invalid date-time timezone") | ||||
| 		} | ||||
| 		var direction int | ||||
| 		switch b[0] { | ||||
| 		case '-': | ||||
| 			direction = -1 | ||||
| 		case '+': | ||||
| 			direction = +1 | ||||
| 		default: | ||||
| 			return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset character") | ||||
| 		} | ||||
|  | ||||
| 		if b[3] != ':' { | ||||
| 			return time.Time{}, unstable.NewParserError(b[3:4], "expected a : separator") | ||||
| 		} | ||||
|  | ||||
| 		hours, err := parseDecimalDigits(b[1:3]) | ||||
| 		if err != nil { | ||||
| 			return time.Time{}, err | ||||
| 		} | ||||
| 		if hours > 23 { | ||||
| 			return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset hours") | ||||
| 		} | ||||
|  | ||||
| 		minutes, err := parseDecimalDigits(b[4:6]) | ||||
| 		if err != nil { | ||||
| 			return time.Time{}, err | ||||
| 		} | ||||
| 		if minutes > 59 { | ||||
| 			return time.Time{}, unstable.NewParserError(b[:1], "invalid timezone offset minutes") | ||||
| 		} | ||||
|  | ||||
| 		seconds := direction * (hours*3600 + minutes*60) | ||||
| 		if seconds == 0 { | ||||
| 			zone = time.UTC | ||||
| 		} else { | ||||
| 			zone = time.FixedZone("", seconds) | ||||
| 		} | ||||
| 		b = b[dateTimeByteLen:] | ||||
| 	} | ||||
|  | ||||
| 	if len(b) > 0 { | ||||
| 		return time.Time{}, unstable.NewParserError(b, "extra bytes at the end of the timezone") | ||||
| 	} | ||||
|  | ||||
| 	t := time.Date( | ||||
| 		dt.Year, | ||||
| 		time.Month(dt.Month), | ||||
| 		dt.Day, | ||||
| 		dt.Hour, | ||||
| 		dt.Minute, | ||||
| 		dt.Second, | ||||
| 		dt.Nanosecond, | ||||
| 		zone) | ||||
|  | ||||
| 	return t, nil | ||||
| } | ||||
|  | ||||
| func parseLocalDateTime(b []byte) (LocalDateTime, []byte, error) { | ||||
| 	var dt LocalDateTime | ||||
|  | ||||
| 	const localDateTimeByteMinLen = 11 | ||||
| 	if len(b) < localDateTimeByteMinLen { | ||||
| 		return dt, nil, unstable.NewParserError(b, "local datetimes are expected to have the format YYYY-MM-DDTHH:MM:SS[.NNNNNNNNN]") | ||||
| 	} | ||||
|  | ||||
| 	date, err := parseLocalDate(b[:10]) | ||||
| 	if err != nil { | ||||
| 		return dt, nil, err | ||||
| 	} | ||||
| 	dt.LocalDate = date | ||||
|  | ||||
| 	sep := b[10] | ||||
| 	if sep != 'T' && sep != ' ' && sep != 't' { | ||||
| 		return dt, nil, unstable.NewParserError(b[10:11], "datetime separator is expected to be T or a space") | ||||
| 	} | ||||
|  | ||||
| 	t, rest, err := parseLocalTime(b[11:]) | ||||
| 	if err != nil { | ||||
| 		return dt, nil, err | ||||
| 	} | ||||
| 	dt.LocalTime = t | ||||
|  | ||||
| 	return dt, rest, nil | ||||
| } | ||||
|  | ||||
| // parseLocalTime is a bit different because it also returns the remaining | ||||
| // []byte that is didn't need. This is to allow parseDateTime to parse those | ||||
| // remaining bytes as a timezone. | ||||
| func parseLocalTime(b []byte) (LocalTime, []byte, error) { | ||||
| 	var ( | ||||
| 		nspow = [10]int{0, 1e8, 1e7, 1e6, 1e5, 1e4, 1e3, 1e2, 1e1, 1e0} | ||||
| 		t     LocalTime | ||||
| 	) | ||||
|  | ||||
| 	// check if b matches to have expected format HH:MM:SS[.NNNNNN] | ||||
| 	const localTimeByteLen = 8 | ||||
| 	if len(b) < localTimeByteLen { | ||||
| 		return t, nil, unstable.NewParserError(b, "times are expected to have the format HH:MM:SS[.NNNNNN]") | ||||
| 	} | ||||
|  | ||||
| 	var err error | ||||
|  | ||||
| 	t.Hour, err = parseDecimalDigits(b[0:2]) | ||||
| 	if err != nil { | ||||
| 		return t, nil, err | ||||
| 	} | ||||
|  | ||||
| 	if t.Hour > 23 { | ||||
| 		return t, nil, unstable.NewParserError(b[0:2], "hour cannot be greater 23") | ||||
| 	} | ||||
| 	if b[2] != ':' { | ||||
| 		return t, nil, unstable.NewParserError(b[2:3], "expecting colon between hours and minutes") | ||||
| 	} | ||||
|  | ||||
| 	t.Minute, err = parseDecimalDigits(b[3:5]) | ||||
| 	if err != nil { | ||||
| 		return t, nil, err | ||||
| 	} | ||||
| 	if t.Minute > 59 { | ||||
| 		return t, nil, unstable.NewParserError(b[3:5], "minutes cannot be greater 59") | ||||
| 	} | ||||
| 	if b[5] != ':' { | ||||
| 		return t, nil, unstable.NewParserError(b[5:6], "expecting colon between minutes and seconds") | ||||
| 	} | ||||
|  | ||||
| 	t.Second, err = parseDecimalDigits(b[6:8]) | ||||
| 	if err != nil { | ||||
| 		return t, nil, err | ||||
| 	} | ||||
|  | ||||
| 	if t.Second > 60 { | ||||
| 		return t, nil, unstable.NewParserError(b[6:8], "seconds cannot be greater 60") | ||||
| 	} | ||||
|  | ||||
| 	b = b[8:] | ||||
|  | ||||
| 	if len(b) >= 1 && b[0] == '.' { | ||||
| 		frac := 0 | ||||
| 		precision := 0 | ||||
| 		digits := 0 | ||||
|  | ||||
| 		for i, c := range b[1:] { | ||||
| 			if !isDigit(c) { | ||||
| 				if i == 0 { | ||||
| 					return t, nil, unstable.NewParserError(b[0:1], "need at least one digit after fraction point") | ||||
| 				} | ||||
| 				break | ||||
| 			} | ||||
| 			digits++ | ||||
|  | ||||
| 			const maxFracPrecision = 9 | ||||
| 			if i >= maxFracPrecision { | ||||
| 				// go-toml allows decoding fractional seconds | ||||
| 				// beyond the supported precision of 9 | ||||
| 				// digits. It truncates the fractional component | ||||
| 				// to the supported precision and ignores the | ||||
| 				// remaining digits. | ||||
| 				// | ||||
| 				// https://github.com/pelletier/go-toml/discussions/707 | ||||
| 				continue | ||||
| 			} | ||||
|  | ||||
| 			frac *= 10 | ||||
| 			frac += int(c - '0') | ||||
| 			precision++ | ||||
| 		} | ||||
|  | ||||
| 		if precision == 0 { | ||||
| 			return t, nil, unstable.NewParserError(b[:1], "nanoseconds need at least one digit") | ||||
| 		} | ||||
|  | ||||
| 		t.Nanosecond = frac * nspow[precision] | ||||
| 		t.Precision = precision | ||||
|  | ||||
| 		return t, b[1+digits:], nil | ||||
| 	} | ||||
| 	return t, b, nil | ||||
| } | ||||
|  | ||||
| //nolint:cyclop | ||||
| func parseFloat(b []byte) (float64, error) { | ||||
| 	if len(b) == 4 && (b[0] == '+' || b[0] == '-') && b[1] == 'n' && b[2] == 'a' && b[3] == 'n' { | ||||
| 		return math.NaN(), nil | ||||
| 	} | ||||
|  | ||||
| 	cleaned, err := checkAndRemoveUnderscoresFloats(b) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | ||||
| 	if cleaned[0] == '.' { | ||||
| 		return 0, unstable.NewParserError(b, "float cannot start with a dot") | ||||
| 	} | ||||
|  | ||||
| 	if cleaned[len(cleaned)-1] == '.' { | ||||
| 		return 0, unstable.NewParserError(b, "float cannot end with a dot") | ||||
| 	} | ||||
|  | ||||
| 	dotAlreadySeen := false | ||||
| 	for i, c := range cleaned { | ||||
| 		if c == '.' { | ||||
| 			if dotAlreadySeen { | ||||
| 				return 0, unstable.NewParserError(b[i:i+1], "float can have at most one decimal point") | ||||
| 			} | ||||
| 			if !isDigit(cleaned[i-1]) { | ||||
| 				return 0, unstable.NewParserError(b[i-1:i+1], "float decimal point must be preceded by a digit") | ||||
| 			} | ||||
| 			if !isDigit(cleaned[i+1]) { | ||||
| 				return 0, unstable.NewParserError(b[i:i+2], "float decimal point must be followed by a digit") | ||||
| 			} | ||||
| 			dotAlreadySeen = true | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	start := 0 | ||||
| 	if cleaned[0] == '+' || cleaned[0] == '-' { | ||||
| 		start = 1 | ||||
| 	} | ||||
| 	if cleaned[start] == '0' && isDigit(cleaned[start+1]) { | ||||
| 		return 0, unstable.NewParserError(b, "float integer part cannot have leading zeroes") | ||||
| 	} | ||||
|  | ||||
| 	f, err := strconv.ParseFloat(string(cleaned), 64) | ||||
| 	if err != nil { | ||||
| 		return 0, unstable.NewParserError(b, "unable to parse float: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return f, nil | ||||
| } | ||||
|  | ||||
| func parseIntHex(b []byte) (int64, error) { | ||||
| 	cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:]) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | ||||
| 	i, err := strconv.ParseInt(string(cleaned), 16, 64) | ||||
| 	if err != nil { | ||||
| 		return 0, unstable.NewParserError(b, "couldn't parse hexadecimal number: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return i, nil | ||||
| } | ||||
|  | ||||
| func parseIntOct(b []byte) (int64, error) { | ||||
| 	cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:]) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | ||||
| 	i, err := strconv.ParseInt(string(cleaned), 8, 64) | ||||
| 	if err != nil { | ||||
| 		return 0, unstable.NewParserError(b, "couldn't parse octal number: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return i, nil | ||||
| } | ||||
|  | ||||
| func parseIntBin(b []byte) (int64, error) { | ||||
| 	cleaned, err := checkAndRemoveUnderscoresIntegers(b[2:]) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | ||||
| 	i, err := strconv.ParseInt(string(cleaned), 2, 64) | ||||
| 	if err != nil { | ||||
| 		return 0, unstable.NewParserError(b, "couldn't parse binary number: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return i, nil | ||||
| } | ||||
|  | ||||
| func isSign(b byte) bool { | ||||
| 	return b == '+' || b == '-' | ||||
| } | ||||
|  | ||||
| func parseIntDec(b []byte) (int64, error) { | ||||
| 	cleaned, err := checkAndRemoveUnderscoresIntegers(b) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
|  | ||||
| 	startIdx := 0 | ||||
|  | ||||
| 	if isSign(cleaned[0]) { | ||||
| 		startIdx++ | ||||
| 	} | ||||
|  | ||||
| 	if len(cleaned) > startIdx+1 && cleaned[startIdx] == '0' { | ||||
| 		return 0, unstable.NewParserError(b, "leading zero not allowed on decimal number") | ||||
| 	} | ||||
|  | ||||
| 	i, err := strconv.ParseInt(string(cleaned), 10, 64) | ||||
| 	if err != nil { | ||||
| 		return 0, unstable.NewParserError(b, "couldn't parse decimal number: %w", err) | ||||
| 	} | ||||
|  | ||||
| 	return i, nil | ||||
| } | ||||
|  | ||||
| func checkAndRemoveUnderscoresIntegers(b []byte) ([]byte, error) { | ||||
| 	start := 0 | ||||
| 	if b[start] == '+' || b[start] == '-' { | ||||
| 		start++ | ||||
| 	} | ||||
|  | ||||
| 	if len(b) == start { | ||||
| 		return b, nil | ||||
| 	} | ||||
|  | ||||
| 	if b[start] == '_' { | ||||
| 		return nil, unstable.NewParserError(b[start:start+1], "number cannot start with underscore") | ||||
| 	} | ||||
|  | ||||
| 	if b[len(b)-1] == '_' { | ||||
| 		return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore") | ||||
| 	} | ||||
|  | ||||
| 	// fast path | ||||
| 	i := 0 | ||||
| 	for ; i < len(b); i++ { | ||||
| 		if b[i] == '_' { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if i == len(b) { | ||||
| 		return b, nil | ||||
| 	} | ||||
|  | ||||
| 	before := false | ||||
| 	cleaned := make([]byte, i, len(b)) | ||||
| 	copy(cleaned, b) | ||||
|  | ||||
| 	for i++; i < len(b); i++ { | ||||
| 		c := b[i] | ||||
| 		if c == '_' { | ||||
| 			if !before { | ||||
| 				return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores") | ||||
| 			} | ||||
| 			before = false | ||||
| 		} else { | ||||
| 			before = true | ||||
| 			cleaned = append(cleaned, c) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return cleaned, nil | ||||
| } | ||||
|  | ||||
| func checkAndRemoveUnderscoresFloats(b []byte) ([]byte, error) { | ||||
| 	if b[0] == '_' { | ||||
| 		return nil, unstable.NewParserError(b[0:1], "number cannot start with underscore") | ||||
| 	} | ||||
|  | ||||
| 	if b[len(b)-1] == '_' { | ||||
| 		return nil, unstable.NewParserError(b[len(b)-1:], "number cannot end with underscore") | ||||
| 	} | ||||
|  | ||||
| 	// fast path | ||||
| 	i := 0 | ||||
| 	for ; i < len(b); i++ { | ||||
| 		if b[i] == '_' { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	if i == len(b) { | ||||
| 		return b, nil | ||||
| 	} | ||||
|  | ||||
| 	before := false | ||||
| 	cleaned := make([]byte, 0, len(b)) | ||||
|  | ||||
| 	for i := 0; i < len(b); i++ { | ||||
| 		c := b[i] | ||||
|  | ||||
| 		switch c { | ||||
| 		case '_': | ||||
| 			if !before { | ||||
| 				return nil, unstable.NewParserError(b[i-1:i+1], "number must have at least one digit between underscores") | ||||
| 			} | ||||
| 			if i < len(b)-1 && (b[i+1] == 'e' || b[i+1] == 'E') { | ||||
| 				return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore before exponent") | ||||
| 			} | ||||
| 			before = false | ||||
| 		case '+', '-': | ||||
| 			// signed exponents | ||||
| 			cleaned = append(cleaned, c) | ||||
| 			before = false | ||||
| 		case 'e', 'E': | ||||
| 			if i < len(b)-1 && b[i+1] == '_' { | ||||
| 				return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after exponent") | ||||
| 			} | ||||
| 			cleaned = append(cleaned, c) | ||||
| 		case '.': | ||||
| 			if i < len(b)-1 && b[i+1] == '_' { | ||||
| 				return nil, unstable.NewParserError(b[i+1:i+2], "cannot have underscore after decimal point") | ||||
| 			} | ||||
| 			if i > 0 && b[i-1] == '_' { | ||||
| 				return nil, unstable.NewParserError(b[i-1:i], "cannot have underscore before decimal point") | ||||
| 			} | ||||
| 			cleaned = append(cleaned, c) | ||||
| 		default: | ||||
| 			before = true | ||||
| 			cleaned = append(cleaned, c) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return cleaned, nil | ||||
| } | ||||
|  | ||||
| // isValidDate checks if a provided date is a date that exists. | ||||
| func isValidDate(year int, month int, day int) bool { | ||||
| 	return month > 0 && month < 13 && day > 0 && day <= daysIn(month, year) | ||||
| } | ||||
|  | ||||
| // daysBefore[m] counts the number of days in a non-leap year | ||||
| // before month m begins. There is an entry for m=12, counting | ||||
| // the number of days before January of next year (365). | ||||
| var daysBefore = [...]int32{ | ||||
| 	0, | ||||
| 	31, | ||||
| 	31 + 28, | ||||
| 	31 + 28 + 31, | ||||
| 	31 + 28 + 31 + 30, | ||||
| 	31 + 28 + 31 + 30 + 31, | ||||
| 	31 + 28 + 31 + 30 + 31 + 30, | ||||
| 	31 + 28 + 31 + 30 + 31 + 30 + 31, | ||||
| 	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, | ||||
| 	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, | ||||
| 	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, | ||||
| 	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, | ||||
| 	31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, | ||||
| } | ||||
|  | ||||
| func daysIn(m int, year int) int { | ||||
| 	if m == 2 && isLeap(year) { | ||||
| 		return 29 | ||||
| 	} | ||||
| 	return int(daysBefore[m] - daysBefore[m-1]) | ||||
| } | ||||
|  | ||||
| func isLeap(year int) bool { | ||||
| 	return year%4 == 0 && (year%100 != 0 || year%400 == 0) | ||||
| } | ||||
|  | ||||
| func isDigit(r byte) bool { | ||||
| 	return r >= '0' && r <= '9' | ||||
| } | ||||
							
								
								
									
										2
									
								
								vendor/github.com/pelletier/go-toml/v2/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/pelletier/go-toml/v2/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,2 @@ | ||||
| // Package toml is a library to read and write TOML documents. | ||||
| package toml | ||||
							
								
								
									
										252
									
								
								vendor/github.com/pelletier/go-toml/v2/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										252
									
								
								vendor/github.com/pelletier/go-toml/v2/errors.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,252 @@ | ||||
| package toml | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/pelletier/go-toml/v2/internal/danger" | ||||
| 	"github.com/pelletier/go-toml/v2/unstable" | ||||
| ) | ||||
|  | ||||
| // DecodeError represents an error encountered during the parsing or decoding | ||||
| // of a TOML document. | ||||
| // | ||||
| // In addition to the error message, it contains the position in the document | ||||
| // where it happened, as well as a human-readable representation that shows | ||||
| // where the error occurred in the document. | ||||
| type DecodeError struct { | ||||
| 	message string | ||||
| 	line    int | ||||
| 	column  int | ||||
| 	key     Key | ||||
|  | ||||
| 	human string | ||||
| } | ||||
|  | ||||
| // StrictMissingError occurs in a TOML document that does not have a | ||||
| // corresponding field in the target value. It contains all the missing fields | ||||
| // in Errors. | ||||
| // | ||||
| // Emitted by Decoder when DisallowUnknownFields() was called. | ||||
| type StrictMissingError struct { | ||||
| 	// One error per field that could not be found. | ||||
| 	Errors []DecodeError | ||||
| } | ||||
|  | ||||
| // Error returns the canonical string for this error. | ||||
| func (s *StrictMissingError) Error() string { | ||||
| 	return "strict mode: fields in the document are missing in the target struct" | ||||
| } | ||||
|  | ||||
| // String returns a human readable description of all errors. | ||||
| func (s *StrictMissingError) String() string { | ||||
| 	var buf strings.Builder | ||||
|  | ||||
| 	for i, e := range s.Errors { | ||||
| 		if i > 0 { | ||||
| 			buf.WriteString("\n---\n") | ||||
| 		} | ||||
|  | ||||
| 		buf.WriteString(e.String()) | ||||
| 	} | ||||
|  | ||||
| 	return buf.String() | ||||
| } | ||||
|  | ||||
| type Key []string | ||||
|  | ||||
| // Error returns the error message contained in the DecodeError. | ||||
| func (e *DecodeError) Error() string { | ||||
| 	return "toml: " + e.message | ||||
| } | ||||
|  | ||||
| // String returns the human-readable contextualized error. This string is multi-line. | ||||
| func (e *DecodeError) String() string { | ||||
| 	return e.human | ||||
| } | ||||
|  | ||||
| // Position returns the (line, column) pair indicating where the error | ||||
| // occurred in the document. Positions are 1-indexed. | ||||
| func (e *DecodeError) Position() (row int, column int) { | ||||
| 	return e.line, e.column | ||||
| } | ||||
|  | ||||
| // Key that was being processed when the error occurred. The key is present only | ||||
| // if this DecodeError is part of a StrictMissingError. | ||||
| func (e *DecodeError) Key() Key { | ||||
| 	return e.key | ||||
| } | ||||
|  | ||||
| // decodeErrorFromHighlight creates a DecodeError referencing a highlighted | ||||
| // range of bytes from document. | ||||
| // | ||||
| // highlight needs to be a sub-slice of document, or this function panics. | ||||
| // | ||||
| // The function copies all bytes used in DecodeError, so that document and | ||||
| // highlight can be freely deallocated. | ||||
| // | ||||
| //nolint:funlen | ||||
| func wrapDecodeError(document []byte, de *unstable.ParserError) *DecodeError { | ||||
| 	offset := danger.SubsliceOffset(document, de.Highlight) | ||||
|  | ||||
| 	errMessage := de.Error() | ||||
| 	errLine, errColumn := positionAtEnd(document[:offset]) | ||||
| 	before, after := linesOfContext(document, de.Highlight, offset, 3) | ||||
|  | ||||
| 	var buf strings.Builder | ||||
|  | ||||
| 	maxLine := errLine + len(after) - 1 | ||||
| 	lineColumnWidth := len(strconv.Itoa(maxLine)) | ||||
|  | ||||
| 	// Write the lines of context strictly before the error. | ||||
| 	for i := len(before) - 1; i > 0; i-- { | ||||
| 		line := errLine - i | ||||
| 		buf.WriteString(formatLineNumber(line, lineColumnWidth)) | ||||
| 		buf.WriteString("|") | ||||
|  | ||||
| 		if len(before[i]) > 0 { | ||||
| 			buf.WriteString(" ") | ||||
| 			buf.Write(before[i]) | ||||
| 		} | ||||
|  | ||||
| 		buf.WriteRune('\n') | ||||
| 	} | ||||
|  | ||||
| 	// Write the document line that contains the error. | ||||
|  | ||||
| 	buf.WriteString(formatLineNumber(errLine, lineColumnWidth)) | ||||
| 	buf.WriteString("| ") | ||||
|  | ||||
| 	if len(before) > 0 { | ||||
| 		buf.Write(before[0]) | ||||
| 	} | ||||
|  | ||||
| 	buf.Write(de.Highlight) | ||||
|  | ||||
| 	if len(after) > 0 { | ||||
| 		buf.Write(after[0]) | ||||
| 	} | ||||
|  | ||||
| 	buf.WriteRune('\n') | ||||
|  | ||||
| 	// Write the line with the error message itself (so it does not have a line | ||||
| 	// number). | ||||
|  | ||||
| 	buf.WriteString(strings.Repeat(" ", lineColumnWidth)) | ||||
| 	buf.WriteString("| ") | ||||
|  | ||||
| 	if len(before) > 0 { | ||||
| 		buf.WriteString(strings.Repeat(" ", len(before[0]))) | ||||
| 	} | ||||
|  | ||||
| 	buf.WriteString(strings.Repeat("~", len(de.Highlight))) | ||||
|  | ||||
| 	if len(errMessage) > 0 { | ||||
| 		buf.WriteString(" ") | ||||
| 		buf.WriteString(errMessage) | ||||
| 	} | ||||
|  | ||||
| 	// Write the lines of context strictly after the error. | ||||
|  | ||||
| 	for i := 1; i < len(after); i++ { | ||||
| 		buf.WriteRune('\n') | ||||
| 		line := errLine + i | ||||
| 		buf.WriteString(formatLineNumber(line, lineColumnWidth)) | ||||
| 		buf.WriteString("|") | ||||
|  | ||||
| 		if len(after[i]) > 0 { | ||||
| 			buf.WriteString(" ") | ||||
| 			buf.Write(after[i]) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return &DecodeError{ | ||||
| 		message: errMessage, | ||||
| 		line:    errLine, | ||||
| 		column:  errColumn, | ||||
| 		key:     de.Key, | ||||
| 		human:   buf.String(), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func formatLineNumber(line int, width int) string { | ||||
| 	format := "%" + strconv.Itoa(width) + "d" | ||||
|  | ||||
| 	return fmt.Sprintf(format, line) | ||||
| } | ||||
|  | ||||
| func linesOfContext(document []byte, highlight []byte, offset int, linesAround int) ([][]byte, [][]byte) { | ||||
| 	return beforeLines(document, offset, linesAround), afterLines(document, highlight, offset, linesAround) | ||||
| } | ||||
|  | ||||
| func beforeLines(document []byte, offset int, linesAround int) [][]byte { | ||||
| 	var beforeLines [][]byte | ||||
|  | ||||
| 	// Walk the document backward from the highlight to find previous lines | ||||
| 	// of context. | ||||
| 	rest := document[:offset] | ||||
| backward: | ||||
| 	for o := len(rest) - 1; o >= 0 && len(beforeLines) <= linesAround && len(rest) > 0; { | ||||
| 		switch { | ||||
| 		case rest[o] == '\n': | ||||
| 			// handle individual lines | ||||
| 			beforeLines = append(beforeLines, rest[o+1:]) | ||||
| 			rest = rest[:o] | ||||
| 			o = len(rest) - 1 | ||||
| 		case o == 0: | ||||
| 			// add the first line only if it's non-empty | ||||
| 			beforeLines = append(beforeLines, rest) | ||||
|  | ||||
| 			break backward | ||||
| 		default: | ||||
| 			o-- | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return beforeLines | ||||
| } | ||||
|  | ||||
| func afterLines(document []byte, highlight []byte, offset int, linesAround int) [][]byte { | ||||
| 	var afterLines [][]byte | ||||
|  | ||||
| 	// Walk the document forward from the highlight to find the following | ||||
| 	// lines of context. | ||||
| 	rest := document[offset+len(highlight):] | ||||
| forward: | ||||
| 	for o := 0; o < len(rest) && len(afterLines) <= linesAround; { | ||||
| 		switch { | ||||
| 		case rest[o] == '\n': | ||||
| 			// handle individual lines | ||||
| 			afterLines = append(afterLines, rest[:o]) | ||||
| 			rest = rest[o+1:] | ||||
| 			o = 0 | ||||
|  | ||||
| 		case o == len(rest)-1: | ||||
| 			// add last line only if it's non-empty | ||||
| 			afterLines = append(afterLines, rest) | ||||
|  | ||||
| 			break forward | ||||
| 		default: | ||||
| 			o++ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return afterLines | ||||
| } | ||||
|  | ||||
| func positionAtEnd(b []byte) (row int, column int) { | ||||
| 	row = 1 | ||||
| 	column = 1 | ||||
|  | ||||
| 	for _, c := range b { | ||||
| 		if c == '\n' { | ||||
| 			row++ | ||||
| 			column = 1 | ||||
| 		} else { | ||||
| 			column++ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return | ||||
| } | ||||
							
								
								
									
										42
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/characters/ascii.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| package characters | ||||
|  | ||||
| var invalidAsciiTable = [256]bool{ | ||||
| 	0x00: true, | ||||
| 	0x01: true, | ||||
| 	0x02: true, | ||||
| 	0x03: true, | ||||
| 	0x04: true, | ||||
| 	0x05: true, | ||||
| 	0x06: true, | ||||
| 	0x07: true, | ||||
| 	0x08: true, | ||||
| 	// 0x09 TAB | ||||
| 	// 0x0A LF | ||||
| 	0x0B: true, | ||||
| 	0x0C: true, | ||||
| 	// 0x0D CR | ||||
| 	0x0E: true, | ||||
| 	0x0F: true, | ||||
| 	0x10: true, | ||||
| 	0x11: true, | ||||
| 	0x12: true, | ||||
| 	0x13: true, | ||||
| 	0x14: true, | ||||
| 	0x15: true, | ||||
| 	0x16: true, | ||||
| 	0x17: true, | ||||
| 	0x18: true, | ||||
| 	0x19: true, | ||||
| 	0x1A: true, | ||||
| 	0x1B: true, | ||||
| 	0x1C: true, | ||||
| 	0x1D: true, | ||||
| 	0x1E: true, | ||||
| 	0x1F: true, | ||||
| 	// 0x20 - 0x7E Printable ASCII characters | ||||
| 	0x7F: true, | ||||
| } | ||||
|  | ||||
| func InvalidAscii(b byte) bool { | ||||
| 	return invalidAsciiTable[b] | ||||
| } | ||||
							
								
								
									
										199
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/characters/utf8.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,199 @@ | ||||
| package characters | ||||
|  | ||||
| import ( | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| type utf8Err struct { | ||||
| 	Index int | ||||
| 	Size  int | ||||
| } | ||||
|  | ||||
| func (u utf8Err) Zero() bool { | ||||
| 	return u.Size == 0 | ||||
| } | ||||
|  | ||||
| // Verified that a given string is only made of valid UTF-8 characters allowed | ||||
| // by the TOML spec: | ||||
| // | ||||
| // Any Unicode character may be used except those that must be escaped: | ||||
| // quotation mark, backslash, and the control characters other than tab (U+0000 | ||||
| // to U+0008, U+000A to U+001F, U+007F). | ||||
| // | ||||
| // It is a copy of the Go 1.17 utf8.Valid implementation, tweaked to exit early | ||||
| // when a character is not allowed. | ||||
| // | ||||
| // The returned utf8Err is Zero() if the string is valid, or contains the byte | ||||
| // index and size of the invalid character. | ||||
| // | ||||
| // quotation mark => already checked | ||||
| // backslash => already checked | ||||
| // 0-0x8 => invalid | ||||
| // 0x9 => tab, ok | ||||
| // 0xA - 0x1F => invalid | ||||
| // 0x7F => invalid | ||||
| func Utf8TomlValidAlreadyEscaped(p []byte) (err utf8Err) { | ||||
| 	// Fast path. Check for and skip 8 bytes of ASCII characters per iteration. | ||||
| 	offset := 0 | ||||
| 	for len(p) >= 8 { | ||||
| 		// Combining two 32 bit loads allows the same code to be used | ||||
| 		// for 32 and 64 bit platforms. | ||||
| 		// The compiler can generate a 32bit load for first32 and second32 | ||||
| 		// on many platforms. See test/codegen/memcombine.go. | ||||
| 		first32 := uint32(p[0]) | uint32(p[1])<<8 | uint32(p[2])<<16 | uint32(p[3])<<24 | ||||
| 		second32 := uint32(p[4]) | uint32(p[5])<<8 | uint32(p[6])<<16 | uint32(p[7])<<24 | ||||
| 		if (first32|second32)&0x80808080 != 0 { | ||||
| 			// Found a non ASCII byte (>= RuneSelf). | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		for i, b := range p[:8] { | ||||
| 			if InvalidAscii(b) { | ||||
| 				err.Index = offset + i | ||||
| 				err.Size = 1 | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		p = p[8:] | ||||
| 		offset += 8 | ||||
| 	} | ||||
| 	n := len(p) | ||||
| 	for i := 0; i < n; { | ||||
| 		pi := p[i] | ||||
| 		if pi < utf8.RuneSelf { | ||||
| 			if InvalidAscii(pi) { | ||||
| 				err.Index = offset + i | ||||
| 				err.Size = 1 | ||||
| 				return | ||||
| 			} | ||||
| 			i++ | ||||
| 			continue | ||||
| 		} | ||||
| 		x := first[pi] | ||||
| 		if x == xx { | ||||
| 			// Illegal starter byte. | ||||
| 			err.Index = offset + i | ||||
| 			err.Size = 1 | ||||
| 			return | ||||
| 		} | ||||
| 		size := int(x & 7) | ||||
| 		if i+size > n { | ||||
| 			// Short or invalid. | ||||
| 			err.Index = offset + i | ||||
| 			err.Size = n - i | ||||
| 			return | ||||
| 		} | ||||
| 		accept := acceptRanges[x>>4] | ||||
| 		if c := p[i+1]; c < accept.lo || accept.hi < c { | ||||
| 			err.Index = offset + i | ||||
| 			err.Size = 2 | ||||
| 			return | ||||
| 		} else if size == 2 { | ||||
| 		} else if c := p[i+2]; c < locb || hicb < c { | ||||
| 			err.Index = offset + i | ||||
| 			err.Size = 3 | ||||
| 			return | ||||
| 		} else if size == 3 { | ||||
| 		} else if c := p[i+3]; c < locb || hicb < c { | ||||
| 			err.Index = offset + i | ||||
| 			err.Size = 4 | ||||
| 			return | ||||
| 		} | ||||
| 		i += size | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
|  | ||||
| // Return the size of the next rune if valid, 0 otherwise. | ||||
| func Utf8ValidNext(p []byte) int { | ||||
| 	c := p[0] | ||||
|  | ||||
| 	if c < utf8.RuneSelf { | ||||
| 		if InvalidAscii(c) { | ||||
| 			return 0 | ||||
| 		} | ||||
| 		return 1 | ||||
| 	} | ||||
|  | ||||
| 	x := first[c] | ||||
| 	if x == xx { | ||||
| 		// Illegal starter byte. | ||||
| 		return 0 | ||||
| 	} | ||||
| 	size := int(x & 7) | ||||
| 	if size > len(p) { | ||||
| 		// Short or invalid. | ||||
| 		return 0 | ||||
| 	} | ||||
| 	accept := acceptRanges[x>>4] | ||||
| 	if c := p[1]; c < accept.lo || accept.hi < c { | ||||
| 		return 0 | ||||
| 	} else if size == 2 { | ||||
| 	} else if c := p[2]; c < locb || hicb < c { | ||||
| 		return 0 | ||||
| 	} else if size == 3 { | ||||
| 	} else if c := p[3]; c < locb || hicb < c { | ||||
| 		return 0 | ||||
| 	} | ||||
|  | ||||
| 	return size | ||||
| } | ||||
|  | ||||
| // acceptRange gives the range of valid values for the second byte in a UTF-8 | ||||
| // sequence. | ||||
| type acceptRange struct { | ||||
| 	lo uint8 // lowest value for second byte. | ||||
| 	hi uint8 // highest value for second byte. | ||||
| } | ||||
|  | ||||
| // acceptRanges has size 16 to avoid bounds checks in the code that uses it. | ||||
| var acceptRanges = [16]acceptRange{ | ||||
| 	0: {locb, hicb}, | ||||
| 	1: {0xA0, hicb}, | ||||
| 	2: {locb, 0x9F}, | ||||
| 	3: {0x90, hicb}, | ||||
| 	4: {locb, 0x8F}, | ||||
| } | ||||
|  | ||||
| // first is information about the first byte in a UTF-8 sequence. | ||||
| var first = [256]uint8{ | ||||
| 	//   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F | ||||
| 	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x00-0x0F | ||||
| 	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x10-0x1F | ||||
| 	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x20-0x2F | ||||
| 	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x30-0x3F | ||||
| 	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x40-0x4F | ||||
| 	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x50-0x5F | ||||
| 	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x60-0x6F | ||||
| 	as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, as, // 0x70-0x7F | ||||
| 	//   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F | ||||
| 	xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x80-0x8F | ||||
| 	xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0x90-0x9F | ||||
| 	xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xA0-0xAF | ||||
| 	xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xB0-0xBF | ||||
| 	xx, xx, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xC0-0xCF | ||||
| 	s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, s1, // 0xD0-0xDF | ||||
| 	s2, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s3, s4, s3, s3, // 0xE0-0xEF | ||||
| 	s5, s6, s6, s6, s7, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, xx, // 0xF0-0xFF | ||||
| } | ||||
|  | ||||
| const ( | ||||
| 	// The default lowest and highest continuation byte. | ||||
| 	locb = 0b10000000 | ||||
| 	hicb = 0b10111111 | ||||
|  | ||||
| 	// These names of these constants are chosen to give nice alignment in the | ||||
| 	// table below. The first nibble is an index into acceptRanges or F for | ||||
| 	// special one-byte cases. The second nibble is the Rune length or the | ||||
| 	// Status for the special one-byte case. | ||||
| 	xx = 0xF1 // invalid: size 1 | ||||
| 	as = 0xF0 // ASCII: size 1 | ||||
| 	s1 = 0x02 // accept 0, size 2 | ||||
| 	s2 = 0x13 // accept 1, size 3 | ||||
| 	s3 = 0x03 // accept 0, size 3 | ||||
| 	s4 = 0x23 // accept 2, size 3 | ||||
| 	s5 = 0x34 // accept 3, size 4 | ||||
| 	s6 = 0x04 // accept 0, size 4 | ||||
| 	s7 = 0x44 // accept 4, size 4 | ||||
| ) | ||||
							
								
								
									
										65
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/danger/danger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/danger/danger.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| package danger | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| const maxInt = uintptr(int(^uint(0) >> 1)) | ||||
|  | ||||
| func SubsliceOffset(data []byte, subslice []byte) int { | ||||
| 	datap := (*reflect.SliceHeader)(unsafe.Pointer(&data)) | ||||
| 	hlp := (*reflect.SliceHeader)(unsafe.Pointer(&subslice)) | ||||
|  | ||||
| 	if hlp.Data < datap.Data { | ||||
| 		panic(fmt.Errorf("subslice address (%d) is before data address (%d)", hlp.Data, datap.Data)) | ||||
| 	} | ||||
| 	offset := hlp.Data - datap.Data | ||||
|  | ||||
| 	if offset > maxInt { | ||||
| 		panic(fmt.Errorf("slice offset larger than int (%d)", offset)) | ||||
| 	} | ||||
|  | ||||
| 	intoffset := int(offset) | ||||
|  | ||||
| 	if intoffset > datap.Len { | ||||
| 		panic(fmt.Errorf("slice offset (%d) is farther than data length (%d)", intoffset, datap.Len)) | ||||
| 	} | ||||
|  | ||||
| 	if intoffset+hlp.Len > datap.Len { | ||||
| 		panic(fmt.Errorf("slice ends (%d+%d) is farther than data length (%d)", intoffset, hlp.Len, datap.Len)) | ||||
| 	} | ||||
|  | ||||
| 	return intoffset | ||||
| } | ||||
|  | ||||
| func BytesRange(start []byte, end []byte) []byte { | ||||
| 	if start == nil || end == nil { | ||||
| 		panic("cannot call BytesRange with nil") | ||||
| 	} | ||||
| 	startp := (*reflect.SliceHeader)(unsafe.Pointer(&start)) | ||||
| 	endp := (*reflect.SliceHeader)(unsafe.Pointer(&end)) | ||||
|  | ||||
| 	if startp.Data > endp.Data { | ||||
| 		panic(fmt.Errorf("start pointer address (%d) is after end pointer address (%d)", startp.Data, endp.Data)) | ||||
| 	} | ||||
|  | ||||
| 	l := startp.Len | ||||
| 	endLen := int(endp.Data-startp.Data) + endp.Len | ||||
| 	if endLen > l { | ||||
| 		l = endLen | ||||
| 	} | ||||
|  | ||||
| 	if l > startp.Cap { | ||||
| 		panic(fmt.Errorf("range length is larger than capacity")) | ||||
| 	} | ||||
|  | ||||
| 	return start[:l] | ||||
| } | ||||
|  | ||||
| func Stride(ptr unsafe.Pointer, size uintptr, offset int) unsafe.Pointer { | ||||
| 	// TODO: replace with unsafe.Add when Go 1.17 is released | ||||
| 	//   https://github.com/golang/go/issues/40481 | ||||
| 	return unsafe.Pointer(uintptr(ptr) + uintptr(int(size)*offset)) | ||||
| } | ||||
							
								
								
									
										23
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/danger/typeid.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/danger/typeid.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| package danger | ||||
|  | ||||
| import ( | ||||
| 	"reflect" | ||||
| 	"unsafe" | ||||
| ) | ||||
|  | ||||
| // typeID is used as key in encoder and decoder caches to enable using | ||||
| // the optimize runtime.mapaccess2_fast64 function instead of the more | ||||
| // expensive lookup if we were to use reflect.Type as map key. | ||||
| // | ||||
| // typeID holds the pointer to the reflect.Type value, which is unique | ||||
| // in the program. | ||||
| // | ||||
| // https://github.com/segmentio/encoding/blob/master/json/codec.go#L59-L61 | ||||
| type TypeID unsafe.Pointer | ||||
|  | ||||
| func MakeTypeID(t reflect.Type) TypeID { | ||||
| 	// reflect.Type has the fields: | ||||
| 	// typ unsafe.Pointer | ||||
| 	// ptr unsafe.Pointer | ||||
| 	return TypeID((*[2]unsafe.Pointer)(unsafe.Pointer(&t))[1]) | ||||
| } | ||||
							
								
								
									
										48
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/tracker/key.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| package tracker | ||||
|  | ||||
| import "github.com/pelletier/go-toml/v2/unstable" | ||||
|  | ||||
| // KeyTracker is a tracker that keeps track of the current Key as the AST is | ||||
| // walked. | ||||
| type KeyTracker struct { | ||||
| 	k []string | ||||
| } | ||||
|  | ||||
| // UpdateTable sets the state of the tracker with the AST table node. | ||||
| func (t *KeyTracker) UpdateTable(node *unstable.Node) { | ||||
| 	t.reset() | ||||
| 	t.Push(node) | ||||
| } | ||||
|  | ||||
| // UpdateArrayTable sets the state of the tracker with the AST array table node. | ||||
| func (t *KeyTracker) UpdateArrayTable(node *unstable.Node) { | ||||
| 	t.reset() | ||||
| 	t.Push(node) | ||||
| } | ||||
|  | ||||
| // Push the given key on the stack. | ||||
| func (t *KeyTracker) Push(node *unstable.Node) { | ||||
| 	it := node.Key() | ||||
| 	for it.Next() { | ||||
| 		t.k = append(t.k, string(it.Node().Data)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Pop key from stack. | ||||
| func (t *KeyTracker) Pop(node *unstable.Node) { | ||||
| 	it := node.Key() | ||||
| 	for it.Next() { | ||||
| 		t.k = t.k[:len(t.k)-1] | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Key returns the current key | ||||
| func (t *KeyTracker) Key() []string { | ||||
| 	k := make([]string, len(t.k)) | ||||
| 	copy(k, t.k) | ||||
| 	return k | ||||
| } | ||||
|  | ||||
| func (t *KeyTracker) reset() { | ||||
| 	t.k = t.k[:0] | ||||
| } | ||||
							
								
								
									
										356
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										356
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/tracker/seen.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,356 @@ | ||||
| package tracker | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
|  | ||||
| 	"github.com/pelletier/go-toml/v2/unstable" | ||||
| ) | ||||
|  | ||||
| type keyKind uint8 | ||||
|  | ||||
| const ( | ||||
| 	invalidKind keyKind = iota | ||||
| 	valueKind | ||||
| 	tableKind | ||||
| 	arrayTableKind | ||||
| ) | ||||
|  | ||||
| func (k keyKind) String() string { | ||||
| 	switch k { | ||||
| 	case invalidKind: | ||||
| 		return "invalid" | ||||
| 	case valueKind: | ||||
| 		return "value" | ||||
| 	case tableKind: | ||||
| 		return "table" | ||||
| 	case arrayTableKind: | ||||
| 		return "array table" | ||||
| 	} | ||||
| 	panic("missing keyKind string mapping") | ||||
| } | ||||
|  | ||||
| // SeenTracker tracks which keys have been seen with which TOML type to flag | ||||
| // duplicates and mismatches according to the spec. | ||||
| // | ||||
| // Each node in the visited tree is represented by an entry. Each entry has an | ||||
| // identifier, which is provided by a counter. Entries are stored in the array | ||||
| // entries. As new nodes are discovered (referenced for the first time in the | ||||
| // TOML document), entries are created and appended to the array. An entry | ||||
| // points to its parent using its id. | ||||
| // | ||||
| // To find whether a given key (sequence of []byte) has already been visited, | ||||
| // the entries are linearly searched, looking for one with the right name and | ||||
| // parent id. | ||||
| // | ||||
| // Given that all keys appear in the document after their parent, it is | ||||
| // guaranteed that all descendants of a node are stored after the node, this | ||||
| // speeds up the search process. | ||||
| // | ||||
| // When encountering [[array tables]], the descendants of that node are removed | ||||
| // to allow that branch of the tree to be "rediscovered". To maintain the | ||||
| // invariant above, the deletion process needs to keep the order of entries. | ||||
| // This results in more copies in that case. | ||||
| type SeenTracker struct { | ||||
| 	entries    []entry | ||||
| 	currentIdx int | ||||
| } | ||||
|  | ||||
| var pool sync.Pool | ||||
|  | ||||
| func (s *SeenTracker) reset() { | ||||
| 	// Always contains a root element at index 0. | ||||
| 	s.currentIdx = 0 | ||||
| 	if len(s.entries) == 0 { | ||||
| 		s.entries = make([]entry, 1, 2) | ||||
| 	} else { | ||||
| 		s.entries = s.entries[:1] | ||||
| 	} | ||||
| 	s.entries[0].child = -1 | ||||
| 	s.entries[0].next = -1 | ||||
| } | ||||
|  | ||||
| type entry struct { | ||||
| 	// Use -1 to indicate no child or no sibling. | ||||
| 	child int | ||||
| 	next  int | ||||
|  | ||||
| 	name     []byte | ||||
| 	kind     keyKind | ||||
| 	explicit bool | ||||
| 	kv       bool | ||||
| } | ||||
|  | ||||
| // Find the index of the child of parentIdx with key k. Returns -1 if | ||||
| // it does not exist. | ||||
| func (s *SeenTracker) find(parentIdx int, k []byte) int { | ||||
| 	for i := s.entries[parentIdx].child; i >= 0; i = s.entries[i].next { | ||||
| 		if bytes.Equal(s.entries[i].name, k) { | ||||
| 			return i | ||||
| 		} | ||||
| 	} | ||||
| 	return -1 | ||||
| } | ||||
|  | ||||
| // Remove all descendants of node at position idx. | ||||
| func (s *SeenTracker) clear(idx int) { | ||||
| 	if idx >= len(s.entries) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	for i := s.entries[idx].child; i >= 0; { | ||||
| 		next := s.entries[i].next | ||||
| 		n := s.entries[0].next | ||||
| 		s.entries[0].next = i | ||||
| 		s.entries[i].next = n | ||||
| 		s.entries[i].name = nil | ||||
| 		s.clear(i) | ||||
| 		i = next | ||||
| 	} | ||||
|  | ||||
| 	s.entries[idx].child = -1 | ||||
| } | ||||
|  | ||||
| func (s *SeenTracker) create(parentIdx int, name []byte, kind keyKind, explicit bool, kv bool) int { | ||||
| 	e := entry{ | ||||
| 		child: -1, | ||||
| 		next:  s.entries[parentIdx].child, | ||||
|  | ||||
| 		name:     name, | ||||
| 		kind:     kind, | ||||
| 		explicit: explicit, | ||||
| 		kv:       kv, | ||||
| 	} | ||||
| 	var idx int | ||||
| 	if s.entries[0].next >= 0 { | ||||
| 		idx = s.entries[0].next | ||||
| 		s.entries[0].next = s.entries[idx].next | ||||
| 		s.entries[idx] = e | ||||
| 	} else { | ||||
| 		idx = len(s.entries) | ||||
| 		s.entries = append(s.entries, e) | ||||
| 	} | ||||
|  | ||||
| 	s.entries[parentIdx].child = idx | ||||
|  | ||||
| 	return idx | ||||
| } | ||||
|  | ||||
| func (s *SeenTracker) setExplicitFlag(parentIdx int) { | ||||
| 	for i := s.entries[parentIdx].child; i >= 0; i = s.entries[i].next { | ||||
| 		if s.entries[i].kv { | ||||
| 			s.entries[i].explicit = true | ||||
| 			s.entries[i].kv = false | ||||
| 		} | ||||
| 		s.setExplicitFlag(i) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // CheckExpression takes a top-level node and checks that it does not contain | ||||
| // keys that have been seen in previous calls, and validates that types are | ||||
| // consistent. | ||||
| func (s *SeenTracker) CheckExpression(node *unstable.Node) error { | ||||
| 	if s.entries == nil { | ||||
| 		s.reset() | ||||
| 	} | ||||
| 	switch node.Kind { | ||||
| 	case unstable.KeyValue: | ||||
| 		return s.checkKeyValue(node) | ||||
| 	case unstable.Table: | ||||
| 		return s.checkTable(node) | ||||
| 	case unstable.ArrayTable: | ||||
| 		return s.checkArrayTable(node) | ||||
| 	default: | ||||
| 		panic(fmt.Errorf("this should not be a top level node type: %s", node.Kind)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (s *SeenTracker) checkTable(node *unstable.Node) error { | ||||
| 	if s.currentIdx >= 0 { | ||||
| 		s.setExplicitFlag(s.currentIdx) | ||||
| 	} | ||||
|  | ||||
| 	it := node.Key() | ||||
|  | ||||
| 	parentIdx := 0 | ||||
|  | ||||
| 	// This code is duplicated in checkArrayTable. This is because factoring | ||||
| 	// it in a function requires to copy the iterator, or allocate it to the | ||||
| 	// heap, which is not cheap. | ||||
| 	for it.Next() { | ||||
| 		if it.IsLast() { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		k := it.Node().Data | ||||
|  | ||||
| 		idx := s.find(parentIdx, k) | ||||
|  | ||||
| 		if idx < 0 { | ||||
| 			idx = s.create(parentIdx, k, tableKind, false, false) | ||||
| 		} else { | ||||
| 			entry := s.entries[idx] | ||||
| 			if entry.kind == valueKind { | ||||
| 				return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind) | ||||
| 			} | ||||
| 		} | ||||
| 		parentIdx = idx | ||||
| 	} | ||||
|  | ||||
| 	k := it.Node().Data | ||||
| 	idx := s.find(parentIdx, k) | ||||
|  | ||||
| 	if idx >= 0 { | ||||
| 		kind := s.entries[idx].kind | ||||
| 		if kind != tableKind { | ||||
| 			return fmt.Errorf("toml: key %s should be a table, not a %s", string(k), kind) | ||||
| 		} | ||||
| 		if s.entries[idx].explicit { | ||||
| 			return fmt.Errorf("toml: table %s already exists", string(k)) | ||||
| 		} | ||||
| 		s.entries[idx].explicit = true | ||||
| 	} else { | ||||
| 		idx = s.create(parentIdx, k, tableKind, true, false) | ||||
| 	} | ||||
|  | ||||
| 	s.currentIdx = idx | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *SeenTracker) checkArrayTable(node *unstable.Node) error { | ||||
| 	if s.currentIdx >= 0 { | ||||
| 		s.setExplicitFlag(s.currentIdx) | ||||
| 	} | ||||
|  | ||||
| 	it := node.Key() | ||||
|  | ||||
| 	parentIdx := 0 | ||||
|  | ||||
| 	for it.Next() { | ||||
| 		if it.IsLast() { | ||||
| 			break | ||||
| 		} | ||||
|  | ||||
| 		k := it.Node().Data | ||||
|  | ||||
| 		idx := s.find(parentIdx, k) | ||||
|  | ||||
| 		if idx < 0 { | ||||
| 			idx = s.create(parentIdx, k, tableKind, false, false) | ||||
| 		} else { | ||||
| 			entry := s.entries[idx] | ||||
| 			if entry.kind == valueKind { | ||||
| 				return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		parentIdx = idx | ||||
| 	} | ||||
|  | ||||
| 	k := it.Node().Data | ||||
| 	idx := s.find(parentIdx, k) | ||||
|  | ||||
| 	if idx >= 0 { | ||||
| 		kind := s.entries[idx].kind | ||||
| 		if kind != arrayTableKind { | ||||
| 			return fmt.Errorf("toml: key %s already exists as a %s,  but should be an array table", kind, string(k)) | ||||
| 		} | ||||
| 		s.clear(idx) | ||||
| 	} else { | ||||
| 		idx = s.create(parentIdx, k, arrayTableKind, true, false) | ||||
| 	} | ||||
|  | ||||
| 	s.currentIdx = idx | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *SeenTracker) checkKeyValue(node *unstable.Node) error { | ||||
| 	parentIdx := s.currentIdx | ||||
| 	it := node.Key() | ||||
|  | ||||
| 	for it.Next() { | ||||
| 		k := it.Node().Data | ||||
|  | ||||
| 		idx := s.find(parentIdx, k) | ||||
|  | ||||
| 		if idx < 0 { | ||||
| 			idx = s.create(parentIdx, k, tableKind, false, true) | ||||
| 		} else { | ||||
| 			entry := s.entries[idx] | ||||
| 			if it.IsLast() { | ||||
| 				return fmt.Errorf("toml: key %s is already defined", string(k)) | ||||
| 			} else if entry.kind != tableKind { | ||||
| 				return fmt.Errorf("toml: expected %s to be a table, not a %s", string(k), entry.kind) | ||||
| 			} else if entry.explicit { | ||||
| 				return fmt.Errorf("toml: cannot redefine table %s that has already been explicitly defined", string(k)) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		parentIdx = idx | ||||
| 	} | ||||
|  | ||||
| 	s.entries[parentIdx].kind = valueKind | ||||
|  | ||||
| 	value := node.Value() | ||||
|  | ||||
| 	switch value.Kind { | ||||
| 	case unstable.InlineTable: | ||||
| 		return s.checkInlineTable(value) | ||||
| 	case unstable.Array: | ||||
| 		return s.checkArray(value) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *SeenTracker) checkArray(node *unstable.Node) error { | ||||
| 	it := node.Children() | ||||
| 	for it.Next() { | ||||
| 		n := it.Node() | ||||
| 		switch n.Kind { | ||||
| 		case unstable.InlineTable: | ||||
| 			err := s.checkInlineTable(n) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		case unstable.Array: | ||||
| 			err := s.checkArray(n) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *SeenTracker) checkInlineTable(node *unstable.Node) error { | ||||
| 	if pool.New == nil { | ||||
| 		pool.New = func() interface{} { | ||||
| 			return &SeenTracker{} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	s = pool.Get().(*SeenTracker) | ||||
| 	s.reset() | ||||
|  | ||||
| 	it := node.Children() | ||||
| 	for it.Next() { | ||||
| 		n := it.Node() | ||||
| 		err := s.checkKeyValue(n) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// As inline tables are self-contained, the tracker does not | ||||
| 	// need to retain the details of what they contain. The | ||||
| 	// keyValue element that creates the inline table is kept to | ||||
| 	// mark the presence of the inline table and prevent | ||||
| 	// redefinition of its keys: check* functions cannot walk into | ||||
| 	// a value. | ||||
| 	pool.Put(s) | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										1
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/tracker/tracker.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/pelletier/go-toml/v2/internal/tracker/tracker.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| package tracker | ||||
							
								
								
									
										122
									
								
								vendor/github.com/pelletier/go-toml/v2/localtime.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								vendor/github.com/pelletier/go-toml/v2/localtime.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,122 @@ | ||||
| package toml | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/pelletier/go-toml/v2/unstable" | ||||
| ) | ||||
|  | ||||
| // LocalDate represents a calendar day in no specific timezone. | ||||
| type LocalDate struct { | ||||
| 	Year  int | ||||
| 	Month int | ||||
| 	Day   int | ||||
| } | ||||
|  | ||||
| // AsTime converts d into a specific time instance at midnight in zone. | ||||
| func (d LocalDate) AsTime(zone *time.Location) time.Time { | ||||
| 	return time.Date(d.Year, time.Month(d.Month), d.Day, 0, 0, 0, 0, zone) | ||||
| } | ||||
|  | ||||
| // String returns RFC 3339 representation of d. | ||||
| func (d LocalDate) String() string { | ||||
| 	return fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day) | ||||
| } | ||||
|  | ||||
| // MarshalText returns RFC 3339 representation of d. | ||||
| func (d LocalDate) MarshalText() ([]byte, error) { | ||||
| 	return []byte(d.String()), nil | ||||
| } | ||||
|  | ||||
| // UnmarshalText parses b using RFC 3339 to fill d. | ||||
| func (d *LocalDate) UnmarshalText(b []byte) error { | ||||
| 	res, err := parseLocalDate(b) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*d = res | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // LocalTime represents a time of day of no specific day in no specific | ||||
| // timezone. | ||||
| type LocalTime struct { | ||||
| 	Hour       int // Hour of the day: [0; 24[ | ||||
| 	Minute     int // Minute of the hour: [0; 60[ | ||||
| 	Second     int // Second of the minute: [0; 60[ | ||||
| 	Nanosecond int // Nanoseconds within the second:  [0, 1000000000[ | ||||
| 	Precision  int // Number of digits to display for Nanosecond. | ||||
| } | ||||
|  | ||||
| // String returns RFC 3339 representation of d. | ||||
| // If d.Nanosecond and d.Precision are zero, the time won't have a nanosecond | ||||
| // component. If d.Nanosecond > 0 but d.Precision = 0, then the minimum number | ||||
| // of digits for nanoseconds is provided. | ||||
| func (d LocalTime) String() string { | ||||
| 	s := fmt.Sprintf("%02d:%02d:%02d", d.Hour, d.Minute, d.Second) | ||||
|  | ||||
| 	if d.Precision > 0 { | ||||
| 		s += fmt.Sprintf(".%09d", d.Nanosecond)[:d.Precision+1] | ||||
| 	} else if d.Nanosecond > 0 { | ||||
| 		// Nanoseconds are specified, but precision is not provided. Use the | ||||
| 		// minimum. | ||||
| 		s += strings.Trim(fmt.Sprintf(".%09d", d.Nanosecond), "0") | ||||
| 	} | ||||
|  | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // MarshalText returns RFC 3339 representation of d. | ||||
| func (d LocalTime) MarshalText() ([]byte, error) { | ||||
| 	return []byte(d.String()), nil | ||||
| } | ||||
|  | ||||
| // UnmarshalText parses b using RFC 3339 to fill d. | ||||
| func (d *LocalTime) UnmarshalText(b []byte) error { | ||||
| 	res, left, err := parseLocalTime(b) | ||||
| 	if err == nil && len(left) != 0 { | ||||
| 		err = unstable.NewParserError(left, "extra characters") | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	*d = res | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // LocalDateTime represents a time of a specific day in no specific timezone. | ||||
| type LocalDateTime struct { | ||||
| 	LocalDate | ||||
| 	LocalTime | ||||
| } | ||||
|  | ||||
| // AsTime converts d into a specific time instance in zone. | ||||
| func (d LocalDateTime) AsTime(zone *time.Location) time.Time { | ||||
| 	return time.Date(d.Year, time.Month(d.Month), d.Day, d.Hour, d.Minute, d.Second, d.Nanosecond, zone) | ||||
| } | ||||
|  | ||||
| // String returns RFC 3339 representation of d. | ||||
| func (d LocalDateTime) String() string { | ||||
| 	return d.LocalDate.String() + "T" + d.LocalTime.String() | ||||
| } | ||||
|  | ||||
| // MarshalText returns RFC 3339 representation of d. | ||||
| func (d LocalDateTime) MarshalText() ([]byte, error) { | ||||
| 	return []byte(d.String()), nil | ||||
| } | ||||
|  | ||||
| // UnmarshalText parses b using RFC 3339 to fill d. | ||||
| func (d *LocalDateTime) UnmarshalText(data []byte) error { | ||||
| 	res, left, err := parseLocalDateTime(data) | ||||
| 	if err == nil && len(left) != 0 { | ||||
| 		err = unstable.NewParserError(left, "extra characters") | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	*d = res | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										1042
									
								
								vendor/github.com/pelletier/go-toml/v2/marshaler.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1042
									
								
								vendor/github.com/pelletier/go-toml/v2/marshaler.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										107
									
								
								vendor/github.com/pelletier/go-toml/v2/strict.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								vendor/github.com/pelletier/go-toml/v2/strict.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,107 @@ | ||||
| package toml | ||||
|  | ||||
| import ( | ||||
| 	"github.com/pelletier/go-toml/v2/internal/danger" | ||||
| 	"github.com/pelletier/go-toml/v2/internal/tracker" | ||||
| 	"github.com/pelletier/go-toml/v2/unstable" | ||||
| ) | ||||
|  | ||||
| type strict struct { | ||||
| 	Enabled bool | ||||
|  | ||||
| 	// Tracks the current key being processed. | ||||
| 	key tracker.KeyTracker | ||||
|  | ||||
| 	missing []unstable.ParserError | ||||
| } | ||||
|  | ||||
| func (s *strict) EnterTable(node *unstable.Node) { | ||||
| 	if !s.Enabled { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	s.key.UpdateTable(node) | ||||
| } | ||||
|  | ||||
| func (s *strict) EnterArrayTable(node *unstable.Node) { | ||||
| 	if !s.Enabled { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	s.key.UpdateArrayTable(node) | ||||
| } | ||||
|  | ||||
| func (s *strict) EnterKeyValue(node *unstable.Node) { | ||||
| 	if !s.Enabled { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	s.key.Push(node) | ||||
| } | ||||
|  | ||||
| func (s *strict) ExitKeyValue(node *unstable.Node) { | ||||
| 	if !s.Enabled { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	s.key.Pop(node) | ||||
| } | ||||
|  | ||||
| func (s *strict) MissingTable(node *unstable.Node) { | ||||
| 	if !s.Enabled { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	s.missing = append(s.missing, unstable.ParserError{ | ||||
| 		Highlight: keyLocation(node), | ||||
| 		Message:   "missing table", | ||||
| 		Key:       s.key.Key(), | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (s *strict) MissingField(node *unstable.Node) { | ||||
| 	if !s.Enabled { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	s.missing = append(s.missing, unstable.ParserError{ | ||||
| 		Highlight: keyLocation(node), | ||||
| 		Message:   "missing field", | ||||
| 		Key:       s.key.Key(), | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (s *strict) Error(doc []byte) error { | ||||
| 	if !s.Enabled || len(s.missing) == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
|  | ||||
| 	err := &StrictMissingError{ | ||||
| 		Errors: make([]DecodeError, 0, len(s.missing)), | ||||
| 	} | ||||
|  | ||||
| 	for _, derr := range s.missing { | ||||
| 		derr := derr | ||||
| 		err.Errors = append(err.Errors, *wrapDecodeError(doc, &derr)) | ||||
| 	} | ||||
|  | ||||
| 	return err | ||||
| } | ||||
|  | ||||
| func keyLocation(node *unstable.Node) []byte { | ||||
| 	k := node.Key() | ||||
|  | ||||
| 	hasOne := k.Next() | ||||
| 	if !hasOne { | ||||
| 		panic("should not be called with empty key") | ||||
| 	} | ||||
|  | ||||
| 	start := k.Node().Data | ||||
| 	end := k.Node().Data | ||||
|  | ||||
| 	for k.Next() { | ||||
| 		end = k.Node().Data | ||||
| 	} | ||||
|  | ||||
| 	return danger.BytesRange(start, end) | ||||
| } | ||||
							
								
								
									
										243
									
								
								vendor/github.com/pelletier/go-toml/v2/toml.abnf
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								vendor/github.com/pelletier/go-toml/v2/toml.abnf
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,243 @@ | ||||
| ;; This document describes TOML's syntax, using the ABNF format (defined in | ||||
| ;; RFC 5234 -- https://www.ietf.org/rfc/rfc5234.txt). | ||||
| ;; | ||||
| ;; All valid TOML documents will match this description, however certain | ||||
| ;; invalid documents would need to be rejected as per the semantics described | ||||
| ;; in the supporting text description. | ||||
|  | ||||
| ;; It is possible to try this grammar interactively, using instaparse. | ||||
| ;;     http://instaparse.mojombo.com/ | ||||
| ;; | ||||
| ;; To do so, in the lower right, click on Options and change `:input-format` to | ||||
| ;; ':abnf'. Then paste this entire ABNF document into the grammar entry box | ||||
| ;; (above the options). Then you can type or paste a sample TOML document into | ||||
| ;; the beige box on the left. Tada! | ||||
|  | ||||
| ;; Overall Structure | ||||
|  | ||||
| toml = expression *( newline expression ) | ||||
|  | ||||
| expression =  ws [ comment ] | ||||
| expression =/ ws keyval ws [ comment ] | ||||
| expression =/ ws table ws [ comment ] | ||||
|  | ||||
| ;; Whitespace | ||||
|  | ||||
| ws = *wschar | ||||
| wschar =  %x20  ; Space | ||||
| wschar =/ %x09  ; Horizontal tab | ||||
|  | ||||
| ;; Newline | ||||
|  | ||||
| newline =  %x0A     ; LF | ||||
| newline =/ %x0D.0A  ; CRLF | ||||
|  | ||||
| ;; Comment | ||||
|  | ||||
| comment-start-symbol = %x23 ; # | ||||
| non-ascii = %x80-D7FF / %xE000-10FFFF | ||||
| non-eol = %x09 / %x20-7F / non-ascii | ||||
|  | ||||
| comment = comment-start-symbol *non-eol | ||||
|  | ||||
| ;; Key-Value pairs | ||||
|  | ||||
| keyval = key keyval-sep val | ||||
|  | ||||
| key = simple-key / dotted-key | ||||
| simple-key = quoted-key / unquoted-key | ||||
|  | ||||
| unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _ | ||||
| quoted-key = basic-string / literal-string | ||||
| dotted-key = simple-key 1*( dot-sep simple-key ) | ||||
|  | ||||
| dot-sep   = ws %x2E ws  ; . Period | ||||
| keyval-sep = ws %x3D ws ; = | ||||
|  | ||||
| val = string / boolean / array / inline-table / date-time / float / integer | ||||
|  | ||||
| ;; String | ||||
|  | ||||
| string = ml-basic-string / basic-string / ml-literal-string / literal-string | ||||
|  | ||||
| ;; Basic String | ||||
|  | ||||
| basic-string = quotation-mark *basic-char quotation-mark | ||||
|  | ||||
| quotation-mark = %x22            ; " | ||||
|  | ||||
| basic-char = basic-unescaped / escaped | ||||
| basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii | ||||
| escaped = escape escape-seq-char | ||||
|  | ||||
| escape = %x5C                   ; \ | ||||
| escape-seq-char =  %x22         ; "    quotation mark  U+0022 | ||||
| escape-seq-char =/ %x5C         ; \    reverse solidus U+005C | ||||
| escape-seq-char =/ %x62         ; b    backspace       U+0008 | ||||
| escape-seq-char =/ %x66         ; f    form feed       U+000C | ||||
| escape-seq-char =/ %x6E         ; n    line feed       U+000A | ||||
| escape-seq-char =/ %x72         ; r    carriage return U+000D | ||||
| escape-seq-char =/ %x74         ; t    tab             U+0009 | ||||
| escape-seq-char =/ %x75 4HEXDIG ; uXXXX                U+XXXX | ||||
| escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX            U+XXXXXXXX | ||||
|  | ||||
| ;; Multiline Basic String | ||||
|  | ||||
| ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body | ||||
|                   ml-basic-string-delim | ||||
| ml-basic-string-delim = 3quotation-mark | ||||
| ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ] | ||||
|  | ||||
| mlb-content = mlb-char / newline / mlb-escaped-nl | ||||
| mlb-char = mlb-unescaped / escaped | ||||
| mlb-quotes = 1*2quotation-mark | ||||
| mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii | ||||
| mlb-escaped-nl = escape ws newline *( wschar / newline ) | ||||
|  | ||||
| ;; Literal String | ||||
|  | ||||
| literal-string = apostrophe *literal-char apostrophe | ||||
|  | ||||
| apostrophe = %x27 ; ' apostrophe | ||||
|  | ||||
| literal-char = %x09 / %x20-26 / %x28-7E / non-ascii | ||||
|  | ||||
| ;; Multiline Literal String | ||||
|  | ||||
| ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body | ||||
|                     ml-literal-string-delim | ||||
| ml-literal-string-delim = 3apostrophe | ||||
| ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ] | ||||
|  | ||||
| mll-content = mll-char / newline | ||||
| mll-char = %x09 / %x20-26 / %x28-7E / non-ascii | ||||
| mll-quotes = 1*2apostrophe | ||||
|  | ||||
| ;; Integer | ||||
|  | ||||
| integer = dec-int / hex-int / oct-int / bin-int | ||||
|  | ||||
| minus = %x2D                       ; - | ||||
| plus = %x2B                        ; + | ||||
| underscore = %x5F                  ; _ | ||||
| digit1-9 = %x31-39                 ; 1-9 | ||||
| digit0-7 = %x30-37                 ; 0-7 | ||||
| digit0-1 = %x30-31                 ; 0-1 | ||||
|  | ||||
| hex-prefix = %x30.78               ; 0x | ||||
| oct-prefix = %x30.6F               ; 0o | ||||
| bin-prefix = %x30.62               ; 0b | ||||
|  | ||||
| dec-int = [ minus / plus ] unsigned-dec-int | ||||
| unsigned-dec-int = DIGIT / digit1-9 1*( DIGIT / underscore DIGIT ) | ||||
|  | ||||
| hex-int = hex-prefix HEXDIG *( HEXDIG / underscore HEXDIG ) | ||||
| oct-int = oct-prefix digit0-7 *( digit0-7 / underscore digit0-7 ) | ||||
| bin-int = bin-prefix digit0-1 *( digit0-1 / underscore digit0-1 ) | ||||
|  | ||||
| ;; Float | ||||
|  | ||||
| float = float-int-part ( exp / frac [ exp ] ) | ||||
| float =/ special-float | ||||
|  | ||||
| float-int-part = dec-int | ||||
| frac = decimal-point zero-prefixable-int | ||||
| decimal-point = %x2E               ; . | ||||
| zero-prefixable-int = DIGIT *( DIGIT / underscore DIGIT ) | ||||
|  | ||||
| exp = "e" float-exp-part | ||||
| float-exp-part = [ minus / plus ] zero-prefixable-int | ||||
|  | ||||
| special-float = [ minus / plus ] ( inf / nan ) | ||||
| inf = %x69.6e.66  ; inf | ||||
| nan = %x6e.61.6e  ; nan | ||||
|  | ||||
| ;; Boolean | ||||
|  | ||||
| boolean = true / false | ||||
|  | ||||
| true    = %x74.72.75.65     ; true | ||||
| false   = %x66.61.6C.73.65  ; false | ||||
|  | ||||
| ;; Date and Time (as defined in RFC 3339) | ||||
|  | ||||
| date-time      = offset-date-time / local-date-time / local-date / local-time | ||||
|  | ||||
| date-fullyear  = 4DIGIT | ||||
| date-month     = 2DIGIT  ; 01-12 | ||||
| date-mday      = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on month/year | ||||
| time-delim     = "T" / %x20 ; T, t, or space | ||||
| time-hour      = 2DIGIT  ; 00-23 | ||||
| time-minute    = 2DIGIT  ; 00-59 | ||||
| time-second    = 2DIGIT  ; 00-58, 00-59, 00-60 based on leap second rules | ||||
| time-secfrac   = "." 1*DIGIT | ||||
| time-numoffset = ( "+" / "-" ) time-hour ":" time-minute | ||||
| time-offset    = "Z" / time-numoffset | ||||
|  | ||||
| partial-time   = time-hour ":" time-minute ":" time-second [ time-secfrac ] | ||||
| full-date      = date-fullyear "-" date-month "-" date-mday | ||||
| full-time      = partial-time time-offset | ||||
|  | ||||
| ;; Offset Date-Time | ||||
|  | ||||
| offset-date-time = full-date time-delim full-time | ||||
|  | ||||
| ;; Local Date-Time | ||||
|  | ||||
| local-date-time = full-date time-delim partial-time | ||||
|  | ||||
| ;; Local Date | ||||
|  | ||||
| local-date = full-date | ||||
|  | ||||
| ;; Local Time | ||||
|  | ||||
| local-time = partial-time | ||||
|  | ||||
| ;; Array | ||||
|  | ||||
| array = array-open [ array-values ] ws-comment-newline array-close | ||||
|  | ||||
| array-open =  %x5B ; [ | ||||
| array-close = %x5D ; ] | ||||
|  | ||||
| array-values =  ws-comment-newline val ws-comment-newline array-sep array-values | ||||
| array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ] | ||||
|  | ||||
| array-sep = %x2C  ; , Comma | ||||
|  | ||||
| ws-comment-newline = *( wschar / [ comment ] newline ) | ||||
|  | ||||
| ;; Table | ||||
|  | ||||
| table = std-table / array-table | ||||
|  | ||||
| ;; Standard Table | ||||
|  | ||||
| std-table = std-table-open key std-table-close | ||||
|  | ||||
| std-table-open  = %x5B ws     ; [ Left square bracket | ||||
| std-table-close = ws %x5D     ; ] Right square bracket | ||||
|  | ||||
| ;; Inline Table | ||||
|  | ||||
| inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close | ||||
|  | ||||
| inline-table-open  = %x7B ws     ; { | ||||
| inline-table-close = ws %x7D     ; } | ||||
| inline-table-sep   = ws %x2C ws  ; , Comma | ||||
|  | ||||
| inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ] | ||||
|  | ||||
| ;; Array Table | ||||
|  | ||||
| array-table = array-table-open key array-table-close | ||||
|  | ||||
| array-table-open  = %x5B.5B ws  ; [[ Double left square bracket | ||||
| array-table-close = ws %x5D.5D  ; ]] Double right square bracket | ||||
|  | ||||
| ;; Built-in ABNF terms, reproduced here for clarity | ||||
|  | ||||
| ALPHA = %x41-5A / %x61-7A ; A-Z / a-z | ||||
| DIGIT = %x30-39 ; 0-9 | ||||
| HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F" | ||||
							
								
								
									
										14
									
								
								vendor/github.com/pelletier/go-toml/v2/types.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								vendor/github.com/pelletier/go-toml/v2/types.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| package toml | ||||
|  | ||||
| import ( | ||||
| 	"encoding" | ||||
| 	"reflect" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| var timeType = reflect.TypeOf((*time.Time)(nil)).Elem() | ||||
| var textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() | ||||
| var textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() | ||||
| var mapStringInterfaceType = reflect.TypeOf(map[string]interface{}(nil)) | ||||
| var sliceInterfaceType = reflect.TypeOf([]interface{}(nil)) | ||||
| var stringType = reflect.TypeOf("") | ||||
							
								
								
									
										1227
									
								
								vendor/github.com/pelletier/go-toml/v2/unmarshaler.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1227
									
								
								vendor/github.com/pelletier/go-toml/v2/unmarshaler.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										136
									
								
								vendor/github.com/pelletier/go-toml/v2/unstable/ast.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								vendor/github.com/pelletier/go-toml/v2/unstable/ast.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| package unstable | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"unsafe" | ||||
|  | ||||
| 	"github.com/pelletier/go-toml/v2/internal/danger" | ||||
| ) | ||||
|  | ||||
| // Iterator over a sequence of nodes. | ||||
| // | ||||
| // Starts uninitialized, you need to call Next() first. | ||||
| // | ||||
| // For example: | ||||
| // | ||||
| //	it := n.Children() | ||||
| //	for it.Next() { | ||||
| //		n := it.Node() | ||||
| //		// do something with n | ||||
| //	} | ||||
| type Iterator struct { | ||||
| 	started bool | ||||
| 	node    *Node | ||||
| } | ||||
|  | ||||
| // Next moves the iterator forward and returns true if points to a | ||||
| // node, false otherwise. | ||||
| func (c *Iterator) Next() bool { | ||||
| 	if !c.started { | ||||
| 		c.started = true | ||||
| 	} else if c.node.Valid() { | ||||
| 		c.node = c.node.Next() | ||||
| 	} | ||||
| 	return c.node.Valid() | ||||
| } | ||||
|  | ||||
| // IsLast returns true if the current node of the iterator is the last | ||||
| // one.  Subsequent calls to Next() will return false. | ||||
| func (c *Iterator) IsLast() bool { | ||||
| 	return c.node.next == 0 | ||||
| } | ||||
|  | ||||
| // Node returns a pointer to the node pointed at by the iterator. | ||||
| func (c *Iterator) Node() *Node { | ||||
| 	return c.node | ||||
| } | ||||
|  | ||||
| // Node in a TOML expression AST. | ||||
| // | ||||
| // Depending on Kind, its sequence of children should be interpreted | ||||
| // differently. | ||||
| // | ||||
| //   - Array have one child per element in the array. | ||||
| //   - InlineTable have one child per key-value in the table (each of kind | ||||
| //     InlineTable). | ||||
| //   - KeyValue have at least two children. The first one is the value. The rest | ||||
| //     make a potentially dotted key. | ||||
| //   - Table and ArrayTable's children represent a dotted key (same as | ||||
| //     KeyValue, but without the first node being the value). | ||||
| // | ||||
| // When relevant, Raw describes the range of bytes this node is refering to in | ||||
| // the input document. Use Parser.Raw() to retrieve the actual bytes. | ||||
| type Node struct { | ||||
| 	Kind Kind | ||||
| 	Raw  Range  // Raw bytes from the input. | ||||
| 	Data []byte // Node value (either allocated or referencing the input). | ||||
|  | ||||
| 	// References to other nodes, as offsets in the backing array | ||||
| 	// from this node. References can go backward, so those can be | ||||
| 	// negative. | ||||
| 	next  int // 0 if last element | ||||
| 	child int // 0 if no child | ||||
| } | ||||
|  | ||||
| // Range of bytes in the document. | ||||
| type Range struct { | ||||
| 	Offset uint32 | ||||
| 	Length uint32 | ||||
| } | ||||
|  | ||||
| // Next returns a pointer to the next node, or nil if there is no next node. | ||||
| func (n *Node) Next() *Node { | ||||
| 	if n.next == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	ptr := unsafe.Pointer(n) | ||||
| 	size := unsafe.Sizeof(Node{}) | ||||
| 	return (*Node)(danger.Stride(ptr, size, n.next)) | ||||
| } | ||||
|  | ||||
| // Child returns a pointer to the first child node of this node. Other children | ||||
| // can be accessed calling Next on the first child.  Returns an nil if this Node | ||||
| // has no child. | ||||
| func (n *Node) Child() *Node { | ||||
| 	if n.child == 0 { | ||||
| 		return nil | ||||
| 	} | ||||
| 	ptr := unsafe.Pointer(n) | ||||
| 	size := unsafe.Sizeof(Node{}) | ||||
| 	return (*Node)(danger.Stride(ptr, size, n.child)) | ||||
| } | ||||
|  | ||||
| // Valid returns true if the node's kind is set (not to Invalid). | ||||
| func (n *Node) Valid() bool { | ||||
| 	return n != nil | ||||
| } | ||||
|  | ||||
| // Key returns the children nodes making the Key on a supported node. Panics | ||||
| // otherwise.  They are guaranteed to be all be of the Kind Key. A simple key | ||||
| // would return just one element. | ||||
| func (n *Node) Key() Iterator { | ||||
| 	switch n.Kind { | ||||
| 	case KeyValue: | ||||
| 		value := n.Child() | ||||
| 		if !value.Valid() { | ||||
| 			panic(fmt.Errorf("KeyValue should have at least two children")) | ||||
| 		} | ||||
| 		return Iterator{node: value.Next()} | ||||
| 	case Table, ArrayTable: | ||||
| 		return Iterator{node: n.Child()} | ||||
| 	default: | ||||
| 		panic(fmt.Errorf("Key() is not supported on a %s", n.Kind)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Value returns a pointer to the value node of a KeyValue. | ||||
| // Guaranteed to be non-nil.  Panics if not called on a KeyValue node, | ||||
| // or if the Children are malformed. | ||||
| func (n *Node) Value() *Node { | ||||
| 	return n.Child() | ||||
| } | ||||
|  | ||||
| // Children returns an iterator over a node's children. | ||||
| func (n *Node) Children() Iterator { | ||||
| 	return Iterator{node: n.Child()} | ||||
| } | ||||
							
								
								
									
										71
									
								
								vendor/github.com/pelletier/go-toml/v2/unstable/builder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/pelletier/go-toml/v2/unstable/builder.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| package unstable | ||||
|  | ||||
| // root contains a full AST. | ||||
| // | ||||
| // It is immutable once constructed with Builder. | ||||
| type root struct { | ||||
| 	nodes []Node | ||||
| } | ||||
|  | ||||
| // Iterator over the top level nodes. | ||||
| func (r *root) Iterator() Iterator { | ||||
| 	it := Iterator{} | ||||
| 	if len(r.nodes) > 0 { | ||||
| 		it.node = &r.nodes[0] | ||||
| 	} | ||||
| 	return it | ||||
| } | ||||
|  | ||||
| func (r *root) at(idx reference) *Node { | ||||
| 	return &r.nodes[idx] | ||||
| } | ||||
|  | ||||
| type reference int | ||||
|  | ||||
| const invalidReference reference = -1 | ||||
|  | ||||
| func (r reference) Valid() bool { | ||||
| 	return r != invalidReference | ||||
| } | ||||
|  | ||||
| type builder struct { | ||||
| 	tree    root | ||||
| 	lastIdx int | ||||
| } | ||||
|  | ||||
| func (b *builder) Tree() *root { | ||||
| 	return &b.tree | ||||
| } | ||||
|  | ||||
| func (b *builder) NodeAt(ref reference) *Node { | ||||
| 	return b.tree.at(ref) | ||||
| } | ||||
|  | ||||
| func (b *builder) Reset() { | ||||
| 	b.tree.nodes = b.tree.nodes[:0] | ||||
| 	b.lastIdx = 0 | ||||
| } | ||||
|  | ||||
| func (b *builder) Push(n Node) reference { | ||||
| 	b.lastIdx = len(b.tree.nodes) | ||||
| 	b.tree.nodes = append(b.tree.nodes, n) | ||||
| 	return reference(b.lastIdx) | ||||
| } | ||||
|  | ||||
| func (b *builder) PushAndChain(n Node) reference { | ||||
| 	newIdx := len(b.tree.nodes) | ||||
| 	b.tree.nodes = append(b.tree.nodes, n) | ||||
| 	if b.lastIdx >= 0 { | ||||
| 		b.tree.nodes[b.lastIdx].next = newIdx - b.lastIdx | ||||
| 	} | ||||
| 	b.lastIdx = newIdx | ||||
| 	return reference(b.lastIdx) | ||||
| } | ||||
|  | ||||
| func (b *builder) AttachChild(parent reference, child reference) { | ||||
| 	b.tree.nodes[parent].child = int(child) - int(parent) | ||||
| } | ||||
|  | ||||
| func (b *builder) Chain(from reference, to reference) { | ||||
| 	b.tree.nodes[from].next = int(to) - int(from) | ||||
| } | ||||
							
								
								
									
										3
									
								
								vendor/github.com/pelletier/go-toml/v2/unstable/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/pelletier/go-toml/v2/unstable/doc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| // Package unstable provides APIs that do not meet the backward compatibility | ||||
| // guarantees yet. | ||||
| package unstable | ||||
							
								
								
									
										71
									
								
								vendor/github.com/pelletier/go-toml/v2/unstable/kind.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/pelletier/go-toml/v2/unstable/kind.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| package unstable | ||||
|  | ||||
| import "fmt" | ||||
|  | ||||
| // Kind represents the type of TOML structure contained in a given Node. | ||||
| type Kind int | ||||
|  | ||||
| const ( | ||||
| 	// Meta | ||||
| 	Invalid Kind = iota | ||||
| 	Comment | ||||
| 	Key | ||||
|  | ||||
| 	// Top level structures | ||||
| 	Table | ||||
| 	ArrayTable | ||||
| 	KeyValue | ||||
|  | ||||
| 	// Containers values | ||||
| 	Array | ||||
| 	InlineTable | ||||
|  | ||||
| 	// Values | ||||
| 	String | ||||
| 	Bool | ||||
| 	Float | ||||
| 	Integer | ||||
| 	LocalDate | ||||
| 	LocalTime | ||||
| 	LocalDateTime | ||||
| 	DateTime | ||||
| ) | ||||
|  | ||||
| // String implementation of fmt.Stringer. | ||||
| func (k Kind) String() string { | ||||
| 	switch k { | ||||
| 	case Invalid: | ||||
| 		return "Invalid" | ||||
| 	case Comment: | ||||
| 		return "Comment" | ||||
| 	case Key: | ||||
| 		return "Key" | ||||
| 	case Table: | ||||
| 		return "Table" | ||||
| 	case ArrayTable: | ||||
| 		return "ArrayTable" | ||||
| 	case KeyValue: | ||||
| 		return "KeyValue" | ||||
| 	case Array: | ||||
| 		return "Array" | ||||
| 	case InlineTable: | ||||
| 		return "InlineTable" | ||||
| 	case String: | ||||
| 		return "String" | ||||
| 	case Bool: | ||||
| 		return "Bool" | ||||
| 	case Float: | ||||
| 		return "Float" | ||||
| 	case Integer: | ||||
| 		return "Integer" | ||||
| 	case LocalDate: | ||||
| 		return "LocalDate" | ||||
| 	case LocalTime: | ||||
| 		return "LocalTime" | ||||
| 	case LocalDateTime: | ||||
| 		return "LocalDateTime" | ||||
| 	case DateTime: | ||||
| 		return "DateTime" | ||||
| 	} | ||||
| 	panic(fmt.Errorf("Kind.String() not implemented for '%d'", k)) | ||||
| } | ||||
							
								
								
									
										1147
									
								
								vendor/github.com/pelletier/go-toml/v2/unstable/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1147
									
								
								vendor/github.com/pelletier/go-toml/v2/unstable/parser.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user
	 kardolus
					kardolus