diff --git a/go.mod b/go.mod index c936466e..3cd6042f 100644 --- a/go.mod +++ b/go.mod @@ -1,19 +1,20 @@ module github.com/SovereignCloudStack/csctl-plugin-openstack -go 1.21 +go 1.21.0 + +toolchain go1.21.13 require ( github.com/SovereignCloudStack/csctl v0.0.3 - github.com/goccy/go-yaml v1.12.0 + github.com/goccy/go-yaml v1.19.0 github.com/gophercloud/gophercloud v1.14.0 github.com/minio/minio-go/v7 v7.0.76 - github.com/spf13/cobra v1.8.1 + github.com/spf13/cobra v1.10.2 ) require ( github.com/SovereignCloudStack/cluster-stack-operator v0.1.0-alpha.5 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/fatih/color v1.13.0 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/google/uuid v1.6.0 // indirect @@ -21,17 +22,14 @@ require ( github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect github.com/kr/pretty v0.3.1 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/rs/xid v1.6.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/pflag v1.0.9 // indirect golang.org/x/crypto v0.26.0 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.28.0 // indirect golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.17.0 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 435a47ea..d3c5bbd4 100644 --- a/go.sum +++ b/go.sum @@ -2,28 +2,18 @@ github.com/SovereignCloudStack/cluster-stack-operator v0.1.0-alpha.5 h1:jONI0j2B github.com/SovereignCloudStack/cluster-stack-operator v0.1.0-alpha.5/go.mod h1:zrwUudq/JQae24/yzS5exA1ZwaXxIL2ZtKIQVrYuPqY= github.com/SovereignCloudStack/csctl v0.0.3 h1:OVR5pkoa2JBJWenvQM3KeboGIPPnFATgEPUrABfbmcU= github.com/SovereignCloudStack/csctl v0.0.3/go.mod h1:RzJ62+gjKyWjAMvHD+xoou5ZDZ3KDHTHxzmAlmOBr7Y= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= -github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= -github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE= -github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= -github.com/goccy/go-yaml v1.12.0 h1:/1WHjnMsI1dlIBQutrvSMGZRQufVO3asrHfTwfACoPM= -github.com/goccy/go-yaml v1.12.0/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/goccy/go-yaml v1.19.0 h1:EmkZ9RIsX+Uq4DYFowegAuJo8+xdX3T/2dwNPXbxEYE= +github.com/goccy/go-yaml v1.19.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gophercloud/gophercloud v1.14.0 h1:Bt9zQDhPrbd4qX7EILGmy+i7GP35cc+AAL2+wIJpUE8= @@ -42,16 +32,6 @@ 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/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= github.com/minio/minio-go/v7 v7.0.76 h1:9nxHH2XDai61cT/EFhyIw/wW4vJfpPNvl7lSFpRt+Ng= @@ -64,12 +44,13 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -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/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= @@ -78,15 +59,10 @@ golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -94,8 +70,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/vendor/github.com/fatih/color/LICENSE.md b/vendor/github.com/fatih/color/LICENSE.md deleted file mode 100644 index 25fdaf63..00000000 --- a/vendor/github.com/fatih/color/LICENSE.md +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2013 Fatih Arslan - -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. diff --git a/vendor/github.com/fatih/color/README.md b/vendor/github.com/fatih/color/README.md deleted file mode 100644 index 5152bf59..00000000 --- a/vendor/github.com/fatih/color/README.md +++ /dev/null @@ -1,178 +0,0 @@ -# color [![](https://github.com/fatih/color/workflows/build/badge.svg)](https://github.com/fatih/color/actions) [![PkgGoDev](https://pkg.go.dev/badge/github.com/fatih/color)](https://pkg.go.dev/github.com/fatih/color) - -Color lets you use colorized outputs in terms of [ANSI Escape -Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It -has support for Windows too! The API can be used in several ways, pick one that -suits you. - -![Color](https://user-images.githubusercontent.com/438920/96832689-03b3e000-13f4-11eb-9803-46f4c4de3406.jpg) - - -## Install - -```bash -go get github.com/fatih/color -``` - -## Examples - -### Standard colors - -```go -// Print with default helper functions -color.Cyan("Prints text in cyan.") - -// A newline will be appended automatically -color.Blue("Prints %s in blue.", "text") - -// These are using the default foreground colors -color.Red("We have red") -color.Magenta("And many others ..") - -``` - -### Mix and reuse colors - -```go -// Create a new color object -c := color.New(color.FgCyan).Add(color.Underline) -c.Println("Prints cyan text with an underline.") - -// Or just add them to New() -d := color.New(color.FgCyan, color.Bold) -d.Printf("This prints bold cyan %s\n", "too!.") - -// Mix up foreground and background colors, create new mixes! -red := color.New(color.FgRed) - -boldRed := red.Add(color.Bold) -boldRed.Println("This will print text in bold red.") - -whiteBackground := red.Add(color.BgWhite) -whiteBackground.Println("Red text with white background.") -``` - -### Use your own output (io.Writer) - -```go -// Use your own io.Writer output -color.New(color.FgBlue).Fprintln(myWriter, "blue color!") - -blue := color.New(color.FgBlue) -blue.Fprint(writer, "This will print text in blue.") -``` - -### Custom print functions (PrintFunc) - -```go -// Create a custom print function for convenience -red := color.New(color.FgRed).PrintfFunc() -red("Warning") -red("Error: %s", err) - -// Mix up multiple attributes -notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() -notice("Don't forget this...") -``` - -### Custom fprint functions (FprintFunc) - -```go -blue := color.New(color.FgBlue).FprintfFunc() -blue(myWriter, "important notice: %s", stars) - -// Mix up with multiple attributes -success := color.New(color.Bold, color.FgGreen).FprintlnFunc() -success(myWriter, "Don't forget this...") -``` - -### Insert into noncolor strings (SprintFunc) - -```go -// Create SprintXxx functions to mix strings with other non-colorized strings: -yellow := color.New(color.FgYellow).SprintFunc() -red := color.New(color.FgRed).SprintFunc() -fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error")) - -info := color.New(color.FgWhite, color.BgGreen).SprintFunc() -fmt.Printf("This %s rocks!\n", info("package")) - -// Use helper functions -fmt.Println("This", color.RedString("warning"), "should be not neglected.") -fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.") - -// Windows supported too! Just don't forget to change the output to color.Output -fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) -``` - -### Plug into existing code - -```go -// Use handy standard colors -color.Set(color.FgYellow) - -fmt.Println("Existing text will now be in yellow") -fmt.Printf("This one %s\n", "too") - -color.Unset() // Don't forget to unset - -// You can mix up parameters -color.Set(color.FgMagenta, color.Bold) -defer color.Unset() // Use it in your function - -fmt.Println("All text will now be bold magenta.") -``` - -### Disable/Enable color - -There might be a case where you want to explicitly disable/enable color output. the -`go-isatty` package will automatically disable color output for non-tty output streams -(for example if the output were piped directly to `less`). - -The `color` package also disables color output if the [`NO_COLOR`](https://no-color.org) environment -variable is set (regardless of its value). - -`Color` has support to disable/enable colors programatically both globally and -for single color definitions. For example suppose you have a CLI app and a -`--no-color` bool flag. You can easily disable the color output with: - -```go -var flagNoColor = flag.Bool("no-color", false, "Disable color output") - -if *flagNoColor { - color.NoColor = true // disables colorized output -} -``` - -It also has support for single color definitions (local). You can -disable/enable color output on the fly: - -```go -c := color.New(color.FgCyan) -c.Println("Prints cyan text") - -c.DisableColor() -c.Println("This is printed without any color") - -c.EnableColor() -c.Println("This prints again cyan...") -``` - -## GitHub Actions - -To output color in GitHub Actions (or other CI systems that support ANSI colors), make sure to set `color.NoColor = false` so that it bypasses the check for non-tty output streams. - -## Todo - -* Save/Return previous values -* Evaluate fmt.Formatter interface - - -## Credits - - * [Fatih Arslan](https://github.com/fatih) - * Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable) - -## License - -The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details diff --git a/vendor/github.com/fatih/color/color.go b/vendor/github.com/fatih/color/color.go deleted file mode 100644 index 98a60f3c..00000000 --- a/vendor/github.com/fatih/color/color.go +++ /dev/null @@ -1,618 +0,0 @@ -package color - -import ( - "fmt" - "io" - "os" - "strconv" - "strings" - "sync" - - "github.com/mattn/go-colorable" - "github.com/mattn/go-isatty" -) - -var ( - // NoColor defines if the output is colorized or not. It's dynamically set to - // false or true based on the stdout's file descriptor referring to a terminal - // or not. It's also set to true if the NO_COLOR environment variable is - // set (regardless of its value). This is a global option and affects all - // colors. For more control over each color block use the methods - // DisableColor() individually. - NoColor = noColorExists() || os.Getenv("TERM") == "dumb" || - (!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd())) - - // Output defines the standard output of the print functions. By default - // os.Stdout is used. - Output = colorable.NewColorableStdout() - - // Error defines a color supporting writer for os.Stderr. - Error = colorable.NewColorableStderr() - - // colorsCache is used to reduce the count of created Color objects and - // allows to reuse already created objects with required Attribute. - colorsCache = make(map[Attribute]*Color) - colorsCacheMu sync.Mutex // protects colorsCache -) - -// noColorExists returns true if the environment variable NO_COLOR exists. -func noColorExists() bool { - _, exists := os.LookupEnv("NO_COLOR") - return exists -} - -// Color defines a custom color object which is defined by SGR parameters. -type Color struct { - params []Attribute - noColor *bool -} - -// Attribute defines a single SGR Code -type Attribute int - -const escape = "\x1b" - -// Base attributes -const ( - Reset Attribute = iota - Bold - Faint - Italic - Underline - BlinkSlow - BlinkRapid - ReverseVideo - Concealed - CrossedOut -) - -// Foreground text colors -const ( - FgBlack Attribute = iota + 30 - FgRed - FgGreen - FgYellow - FgBlue - FgMagenta - FgCyan - FgWhite -) - -// Foreground Hi-Intensity text colors -const ( - FgHiBlack Attribute = iota + 90 - FgHiRed - FgHiGreen - FgHiYellow - FgHiBlue - FgHiMagenta - FgHiCyan - FgHiWhite -) - -// Background text colors -const ( - BgBlack Attribute = iota + 40 - BgRed - BgGreen - BgYellow - BgBlue - BgMagenta - BgCyan - BgWhite -) - -// Background Hi-Intensity text colors -const ( - BgHiBlack Attribute = iota + 100 - BgHiRed - BgHiGreen - BgHiYellow - BgHiBlue - BgHiMagenta - BgHiCyan - BgHiWhite -) - -// New returns a newly created color object. -func New(value ...Attribute) *Color { - c := &Color{ - params: make([]Attribute, 0), - } - - if noColorExists() { - c.noColor = boolPtr(true) - } - - c.Add(value...) - return c -} - -// Set sets the given parameters immediately. It will change the color of -// output with the given SGR parameters until color.Unset() is called. -func Set(p ...Attribute) *Color { - c := New(p...) - c.Set() - return c -} - -// Unset resets all escape attributes and clears the output. Usually should -// be called after Set(). -func Unset() { - if NoColor { - return - } - - fmt.Fprintf(Output, "%s[%dm", escape, Reset) -} - -// Set sets the SGR sequence. -func (c *Color) Set() *Color { - if c.isNoColorSet() { - return c - } - - fmt.Fprintf(Output, c.format()) - return c -} - -func (c *Color) unset() { - if c.isNoColorSet() { - return - } - - Unset() -} - -func (c *Color) setWriter(w io.Writer) *Color { - if c.isNoColorSet() { - return c - } - - fmt.Fprintf(w, c.format()) - return c -} - -func (c *Color) unsetWriter(w io.Writer) { - if c.isNoColorSet() { - return - } - - if NoColor { - return - } - - fmt.Fprintf(w, "%s[%dm", escape, Reset) -} - -// Add is used to chain SGR parameters. Use as many as parameters to combine -// and create custom color objects. Example: Add(color.FgRed, color.Underline). -func (c *Color) Add(value ...Attribute) *Color { - c.params = append(c.params, value...) - return c -} - -func (c *Color) prepend(value Attribute) { - c.params = append(c.params, 0) - copy(c.params[1:], c.params[0:]) - c.params[0] = value -} - -// Fprint formats using the default formats for its operands and writes to w. -// Spaces are added between operands when neither is a string. -// It returns the number of bytes written and any write error encountered. -// On Windows, users should wrap w with colorable.NewColorable() if w is of -// type *os.File. -func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) { - c.setWriter(w) - defer c.unsetWriter(w) - - return fmt.Fprint(w, a...) -} - -// Print formats using the default formats for its operands and writes to -// standard output. Spaces are added between operands when neither is a -// string. It returns the number of bytes written and any write error -// encountered. This is the standard fmt.Print() method wrapped with the given -// color. -func (c *Color) Print(a ...interface{}) (n int, err error) { - c.Set() - defer c.unset() - - return fmt.Fprint(Output, a...) -} - -// Fprintf formats according to a format specifier and writes to w. -// It returns the number of bytes written and any write error encountered. -// On Windows, users should wrap w with colorable.NewColorable() if w is of -// type *os.File. -func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { - c.setWriter(w) - defer c.unsetWriter(w) - - return fmt.Fprintf(w, format, a...) -} - -// Printf formats according to a format specifier and writes to standard output. -// It returns the number of bytes written and any write error encountered. -// This is the standard fmt.Printf() method wrapped with the given color. -func (c *Color) Printf(format string, a ...interface{}) (n int, err error) { - c.Set() - defer c.unset() - - return fmt.Fprintf(Output, format, a...) -} - -// Fprintln formats using the default formats for its operands and writes to w. -// Spaces are always added between operands and a newline is appended. -// On Windows, users should wrap w with colorable.NewColorable() if w is of -// type *os.File. -func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { - c.setWriter(w) - defer c.unsetWriter(w) - - return fmt.Fprintln(w, a...) -} - -// Println formats using the default formats for its operands and writes to -// standard output. Spaces are always added between operands and a newline is -// appended. It returns the number of bytes written and any write error -// encountered. This is the standard fmt.Print() method wrapped with the given -// color. -func (c *Color) Println(a ...interface{}) (n int, err error) { - c.Set() - defer c.unset() - - return fmt.Fprintln(Output, a...) -} - -// Sprint is just like Print, but returns a string instead of printing it. -func (c *Color) Sprint(a ...interface{}) string { - return c.wrap(fmt.Sprint(a...)) -} - -// Sprintln is just like Println, but returns a string instead of printing it. -func (c *Color) Sprintln(a ...interface{}) string { - return c.wrap(fmt.Sprintln(a...)) -} - -// Sprintf is just like Printf, but returns a string instead of printing it. -func (c *Color) Sprintf(format string, a ...interface{}) string { - return c.wrap(fmt.Sprintf(format, a...)) -} - -// FprintFunc returns a new function that prints the passed arguments as -// colorized with color.Fprint(). -func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) { - return func(w io.Writer, a ...interface{}) { - c.Fprint(w, a...) - } -} - -// PrintFunc returns a new function that prints the passed arguments as -// colorized with color.Print(). -func (c *Color) PrintFunc() func(a ...interface{}) { - return func(a ...interface{}) { - c.Print(a...) - } -} - -// FprintfFunc returns a new function that prints the passed arguments as -// colorized with color.Fprintf(). -func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) { - return func(w io.Writer, format string, a ...interface{}) { - c.Fprintf(w, format, a...) - } -} - -// PrintfFunc returns a new function that prints the passed arguments as -// colorized with color.Printf(). -func (c *Color) PrintfFunc() func(format string, a ...interface{}) { - return func(format string, a ...interface{}) { - c.Printf(format, a...) - } -} - -// FprintlnFunc returns a new function that prints the passed arguments as -// colorized with color.Fprintln(). -func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) { - return func(w io.Writer, a ...interface{}) { - c.Fprintln(w, a...) - } -} - -// PrintlnFunc returns a new function that prints the passed arguments as -// colorized with color.Println(). -func (c *Color) PrintlnFunc() func(a ...interface{}) { - return func(a ...interface{}) { - c.Println(a...) - } -} - -// SprintFunc returns a new function that returns colorized strings for the -// given arguments with fmt.Sprint(). Useful to put into or mix into other -// string. Windows users should use this in conjunction with color.Output, example: -// -// put := New(FgYellow).SprintFunc() -// fmt.Fprintf(color.Output, "This is a %s", put("warning")) -func (c *Color) SprintFunc() func(a ...interface{}) string { - return func(a ...interface{}) string { - return c.wrap(fmt.Sprint(a...)) - } -} - -// SprintfFunc returns a new function that returns colorized strings for the -// given arguments with fmt.Sprintf(). Useful to put into or mix into other -// string. Windows users should use this in conjunction with color.Output. -func (c *Color) SprintfFunc() func(format string, a ...interface{}) string { - return func(format string, a ...interface{}) string { - return c.wrap(fmt.Sprintf(format, a...)) - } -} - -// SprintlnFunc returns a new function that returns colorized strings for the -// given arguments with fmt.Sprintln(). Useful to put into or mix into other -// string. Windows users should use this in conjunction with color.Output. -func (c *Color) SprintlnFunc() func(a ...interface{}) string { - return func(a ...interface{}) string { - return c.wrap(fmt.Sprintln(a...)) - } -} - -// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m" -// an example output might be: "1;36" -> bold cyan -func (c *Color) sequence() string { - format := make([]string, len(c.params)) - for i, v := range c.params { - format[i] = strconv.Itoa(int(v)) - } - - return strings.Join(format, ";") -} - -// wrap wraps the s string with the colors attributes. The string is ready to -// be printed. -func (c *Color) wrap(s string) string { - if c.isNoColorSet() { - return s - } - - return c.format() + s + c.unformat() -} - -func (c *Color) format() string { - return fmt.Sprintf("%s[%sm", escape, c.sequence()) -} - -func (c *Color) unformat() string { - return fmt.Sprintf("%s[%dm", escape, Reset) -} - -// DisableColor disables the color output. Useful to not change any existing -// code and still being able to output. Can be used for flags like -// "--no-color". To enable back use EnableColor() method. -func (c *Color) DisableColor() { - c.noColor = boolPtr(true) -} - -// EnableColor enables the color output. Use it in conjunction with -// DisableColor(). Otherwise this method has no side effects. -func (c *Color) EnableColor() { - c.noColor = boolPtr(false) -} - -func (c *Color) isNoColorSet() bool { - // check first if we have user set action - if c.noColor != nil { - return *c.noColor - } - - // if not return the global option, which is disabled by default - return NoColor -} - -// Equals returns a boolean value indicating whether two colors are equal. -func (c *Color) Equals(c2 *Color) bool { - if len(c.params) != len(c2.params) { - return false - } - - for _, attr := range c.params { - if !c2.attrExists(attr) { - return false - } - } - - return true -} - -func (c *Color) attrExists(a Attribute) bool { - for _, attr := range c.params { - if attr == a { - return true - } - } - - return false -} - -func boolPtr(v bool) *bool { - return &v -} - -func getCachedColor(p Attribute) *Color { - colorsCacheMu.Lock() - defer colorsCacheMu.Unlock() - - c, ok := colorsCache[p] - if !ok { - c = New(p) - colorsCache[p] = c - } - - return c -} - -func colorPrint(format string, p Attribute, a ...interface{}) { - c := getCachedColor(p) - - if !strings.HasSuffix(format, "\n") { - format += "\n" - } - - if len(a) == 0 { - c.Print(format) - } else { - c.Printf(format, a...) - } -} - -func colorString(format string, p Attribute, a ...interface{}) string { - c := getCachedColor(p) - - if len(a) == 0 { - return c.SprintFunc()(format) - } - - return c.SprintfFunc()(format, a...) -} - -// Black is a convenient helper function to print with black foreground. A -// newline is appended to format by default. -func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) } - -// Red is a convenient helper function to print with red foreground. A -// newline is appended to format by default. -func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) } - -// Green is a convenient helper function to print with green foreground. A -// newline is appended to format by default. -func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) } - -// Yellow is a convenient helper function to print with yellow foreground. -// A newline is appended to format by default. -func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) } - -// Blue is a convenient helper function to print with blue foreground. A -// newline is appended to format by default. -func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) } - -// Magenta is a convenient helper function to print with magenta foreground. -// A newline is appended to format by default. -func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) } - -// Cyan is a convenient helper function to print with cyan foreground. A -// newline is appended to format by default. -func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) } - -// White is a convenient helper function to print with white foreground. A -// newline is appended to format by default. -func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) } - -// BlackString is a convenient helper function to return a string with black -// foreground. -func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) } - -// RedString is a convenient helper function to return a string with red -// foreground. -func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) } - -// GreenString is a convenient helper function to return a string with green -// foreground. -func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) } - -// YellowString is a convenient helper function to return a string with yellow -// foreground. -func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) } - -// BlueString is a convenient helper function to return a string with blue -// foreground. -func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) } - -// MagentaString is a convenient helper function to return a string with magenta -// foreground. -func MagentaString(format string, a ...interface{}) string { - return colorString(format, FgMagenta, a...) -} - -// CyanString is a convenient helper function to return a string with cyan -// foreground. -func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) } - -// WhiteString is a convenient helper function to return a string with white -// foreground. -func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) } - -// HiBlack is a convenient helper function to print with hi-intensity black foreground. A -// newline is appended to format by default. -func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) } - -// HiRed is a convenient helper function to print with hi-intensity red foreground. A -// newline is appended to format by default. -func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) } - -// HiGreen is a convenient helper function to print with hi-intensity green foreground. A -// newline is appended to format by default. -func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) } - -// HiYellow is a convenient helper function to print with hi-intensity yellow foreground. -// A newline is appended to format by default. -func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) } - -// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A -// newline is appended to format by default. -func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) } - -// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground. -// A newline is appended to format by default. -func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) } - -// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A -// newline is appended to format by default. -func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) } - -// HiWhite is a convenient helper function to print with hi-intensity white foreground. A -// newline is appended to format by default. -func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) } - -// HiBlackString is a convenient helper function to return a string with hi-intensity black -// foreground. -func HiBlackString(format string, a ...interface{}) string { - return colorString(format, FgHiBlack, a...) -} - -// HiRedString is a convenient helper function to return a string with hi-intensity red -// foreground. -func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) } - -// HiGreenString is a convenient helper function to return a string with hi-intensity green -// foreground. -func HiGreenString(format string, a ...interface{}) string { - return colorString(format, FgHiGreen, a...) -} - -// HiYellowString is a convenient helper function to return a string with hi-intensity yellow -// foreground. -func HiYellowString(format string, a ...interface{}) string { - return colorString(format, FgHiYellow, a...) -} - -// HiBlueString is a convenient helper function to return a string with hi-intensity blue -// foreground. -func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) } - -// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta -// foreground. -func HiMagentaString(format string, a ...interface{}) string { - return colorString(format, FgHiMagenta, a...) -} - -// HiCyanString is a convenient helper function to return a string with hi-intensity cyan -// foreground. -func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) } - -// HiWhiteString is a convenient helper function to return a string with hi-intensity white -// foreground. -func HiWhiteString(format string, a ...interface{}) string { - return colorString(format, FgHiWhite, a...) -} diff --git a/vendor/github.com/fatih/color/doc.go b/vendor/github.com/fatih/color/doc.go deleted file mode 100644 index 04541de7..00000000 --- a/vendor/github.com/fatih/color/doc.go +++ /dev/null @@ -1,135 +0,0 @@ -/* -Package color is an ANSI color package to output colorized or SGR defined -output to the standard output. The API can be used in several way, pick one -that suits you. - -Use simple and default helper functions with predefined foreground colors: - - color.Cyan("Prints text in cyan.") - - // a newline will be appended automatically - color.Blue("Prints %s in blue.", "text") - - // More default foreground colors.. - color.Red("We have red") - color.Yellow("Yellow color too!") - color.Magenta("And many others ..") - - // Hi-intensity colors - color.HiGreen("Bright green color.") - color.HiBlack("Bright black means gray..") - color.HiWhite("Shiny white color!") - -However there are times where custom color mixes are required. Below are some -examples to create custom color objects and use the print functions of each -separate color object. - - // Create a new color object - c := color.New(color.FgCyan).Add(color.Underline) - c.Println("Prints cyan text with an underline.") - - // Or just add them to New() - d := color.New(color.FgCyan, color.Bold) - d.Printf("This prints bold cyan %s\n", "too!.") - - - // Mix up foreground and background colors, create new mixes! - red := color.New(color.FgRed) - - boldRed := red.Add(color.Bold) - boldRed.Println("This will print text in bold red.") - - whiteBackground := red.Add(color.BgWhite) - whiteBackground.Println("Red text with White background.") - - // Use your own io.Writer output - color.New(color.FgBlue).Fprintln(myWriter, "blue color!") - - blue := color.New(color.FgBlue) - blue.Fprint(myWriter, "This will print text in blue.") - -You can create PrintXxx functions to simplify even more: - - // Create a custom print function for convenient - red := color.New(color.FgRed).PrintfFunc() - red("warning") - red("error: %s", err) - - // Mix up multiple attributes - notice := color.New(color.Bold, color.FgGreen).PrintlnFunc() - notice("don't forget this...") - -You can also FprintXxx functions to pass your own io.Writer: - - blue := color.New(FgBlue).FprintfFunc() - blue(myWriter, "important notice: %s", stars) - - // Mix up with multiple attributes - success := color.New(color.Bold, color.FgGreen).FprintlnFunc() - success(myWriter, don't forget this...") - - -Or create SprintXxx functions to mix strings with other non-colorized strings: - - yellow := New(FgYellow).SprintFunc() - red := New(FgRed).SprintFunc() - - fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error")) - - info := New(FgWhite, BgGreen).SprintFunc() - fmt.Printf("this %s rocks!\n", info("package")) - -Windows support is enabled by default. All Print functions work as intended. -However only for color.SprintXXX functions, user should use fmt.FprintXXX and -set the output to color.Output: - - fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS")) - - info := New(FgWhite, BgGreen).SprintFunc() - fmt.Fprintf(color.Output, "this %s rocks!\n", info("package")) - -Using with existing code is possible. Just use the Set() method to set the -standard output to the given parameters. That way a rewrite of an existing -code is not required. - - // Use handy standard colors. - color.Set(color.FgYellow) - - fmt.Println("Existing text will be now in Yellow") - fmt.Printf("This one %s\n", "too") - - color.Unset() // don't forget to unset - - // You can mix up parameters - color.Set(color.FgMagenta, color.Bold) - defer color.Unset() // use it in your function - - fmt.Println("All text will be now bold magenta.") - -There might be a case where you want to disable color output (for example to -pipe the standard output of your app to somewhere else). `Color` has support to -disable colors both globally and for single color definition. For example -suppose you have a CLI app and a `--no-color` bool flag. You can easily disable -the color output with: - - var flagNoColor = flag.Bool("no-color", false, "Disable color output") - - if *flagNoColor { - color.NoColor = true // disables colorized output - } - -You can also disable the color by setting the NO_COLOR environment variable to any value. - -It also has support for single color definitions (local). You can -disable/enable color output on the fly: - - c := color.New(color.FgCyan) - c.Println("Prints cyan text") - - c.DisableColor() - c.Println("This is printed without any color") - - c.EnableColor() - c.Println("This prints again cyan...") -*/ -package color diff --git a/vendor/github.com/goccy/go-yaml/.gitignore b/vendor/github.com/goccy/go-yaml/.gitignore new file mode 100644 index 00000000..47952bd9 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/.gitignore @@ -0,0 +1,3 @@ +bin/ +.idea/ +cover.out diff --git a/vendor/github.com/goccy/go-yaml/.golangci.yml b/vendor/github.com/goccy/go-yaml/.golangci.yml new file mode 100644 index 00000000..05675c89 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/.golangci.yml @@ -0,0 +1,65 @@ +version: "2" +linters: + default: none + enable: + - errcheck + - govet + - ineffassign + - misspell + - perfsprint + - staticcheck + - unused + settings: + errcheck: + without_tests: true + govet: + disable: + - tests + misspell: + locale: US + perfsprint: + int-conversion: false + err-error: false + errorf: true + sprintf1: false + strconcat: false + staticcheck: + checks: + - -ST1000 + - -ST1005 + - all + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - staticcheck + path: _test\.go + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gci + - gofmt + settings: + gci: + sections: + - standard + - default + - prefix(github.com/goccy/go-yaml) + - blank + - dot + gofmt: + simplify: true + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/vendor/github.com/goccy/go-yaml/Makefile b/vendor/github.com/goccy/go-yaml/Makefile index 1b1d9239..59936164 100644 --- a/vendor/github.com/goccy/go-yaml/Makefile +++ b/vendor/github.com/goccy/go-yaml/Makefile @@ -1,19 +1,55 @@ +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +TESTMOD := testdata/go_test.mod + +$(LOCALBIN): + mkdir -p $(LOCALBIN) + .PHONY: test test: go test -v -race ./... + go test -v -race ./testdata -modfile=$(TESTMOD) .PHONY: simple-test simple-test: go test -v ./... + go test -v ./testdata -modfile=$(TESTMOD) + +.PHONY: fuzz +fuzz: + go test -fuzz=Fuzz -fuzztime 60s .PHONY: cover cover: - go test -coverprofile=cover.out ./... + go test -coverpkg=.,./ast,./lexer,./parser,./printer,./scanner,./token -coverprofile=cover.out -modfile=$(TESTMOD) ./... ./testdata .PHONY: cover-html cover-html: cover go tool cover -html=cover.out .PHONY: ycat/build -ycat/build: - go build -o ycat ./cmd/ycat +ycat/build: $(LOCALBIN) + cd ./cmd/ycat && go build -o $(LOCALBIN)/ycat . + +.PHONY: lint +lint: golangci-lint ## Run golangci-lint + @$(GOLANGCI_LINT) run + +.PHONY: fmt +fmt: golangci-lint ## Ensure consistent code style + @go mod tidy + @go fmt ./... + @$(GOLANGCI_LINT) run --fix + +## Tool Binaries +GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint + +## Tool Versions +GOLANGCI_VERSION := 2.1.2 + +.PHONY: golangci-lint +.PHONY: $(GOLANGCI_LINT) +golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. +$(GOLANGCI_LINT): $(LOCALBIN) + @test -s $(LOCALBIN)/golangci-lint && $(LOCALBIN)/golangci-lint version --short | grep -q $(GOLANGCI_VERSION) || \ + curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(LOCALBIN) v$(GOLANGCI_VERSION) diff --git a/vendor/github.com/goccy/go-yaml/README.md b/vendor/github.com/goccy/go-yaml/README.md index 94523491..d5ac5b7b 100644 --- a/vendor/github.com/goccy/go-yaml/README.md +++ b/vendor/github.com/goccy/go-yaml/README.md @@ -7,27 +7,71 @@ +## This library has **NO** relation to the go-yaml/yaml library + +> [!IMPORTANT] +> This library is developed from scratch to replace [`go-yaml/yaml`](https://github.com/go-yaml/yaml). +> If you're looking for a better YAML library, this one should be helpful. + # Why a new library? -As of this writing, there already exists a de facto standard library for YAML processing for Go: [https://github.com/go-yaml/yaml](https://github.com/go-yaml/yaml). However we feel that some features are lacking, namely: +As of this writing, there already exists a de facto standard library for YAML processing for Go: [https://github.com/go-yaml/yaml](https://github.com/go-yaml/yaml). However, we believe that a new YAML library is necessary for the following reasons: -- Pretty format for error notifications -- Direct manipulation of YAML abstract syntax tree -- Support for `Anchor` and `Alias` when marshaling -- Allow referencing elements declared in another file via anchors +- Not actively maintained +- `go-yaml/yaml` has ported the libyaml written in C to Go, so the source code is not written in Go style +- There is a lot of content that cannot be parsed +- YAML is often used for configuration, and it is common to include validation along with it. However, the errors in `go-yaml/yaml` are not intuitive, and it is difficult to provide meaningful validation errors +- When creating tools that use YAML, there are cases where reversible transformation of YAML is required. However, to perform reversible transformations of content that includes Comments or Anchors/Aliases, manipulating the AST is the only option +- Non-intuitive [Marshaler](https://pkg.go.dev/gopkg.in/yaml.v3#Marshaler) / [Unmarshaler](https://pkg.go.dev/gopkg.in/yaml.v3#Unmarshaler) + +By the way, libraries such as [ghodss/yaml](https://github.com/ghodss/yaml) and [sigs.k8s.io/yaml](https://github.com/kubernetes-sigs/yaml) also depend on go-yaml/yaml, so if you are using these libraries, the same issues apply: they cannot parse things that go-yaml/yaml cannot parse, and they inherit many of the problems that go-yaml/yaml has. # Features +- No dependencies +- A better parser than `go-yaml/yaml`. + - [Support recursive processing](https://github.com/apple/device-management/blob/release/docs/schema.yaml) + - Higher coverage in the [YAML Test Suite](https://github.com/yaml/yaml-test-suite?tab=readme-ov-file) + - YAML Test Suite consists of 402 cases in total, of which `gopkg.in/yaml.v3` passes `295`. In addition to passing all those test cases, `goccy/go-yaml` successfully passes nearly 60 additional test cases ( 2024/12/15 ) + - The test code is [here](https://github.com/goccy/go-yaml/blob/master/yaml_test_suite_test.go#L77) +- Ease and sustainability of maintenance + - The main maintainer is [@goccy](https://github.com/goccy), but we are also building a system to develop as a team with trusted developers + - Since it is written from scratch, the code is easy to read for Gophers +- An API structure that allows the use of not only `Encoder`/`Decoder` but also `Tokenizer` and `Parser` functionalities. + - [lexer.Tokenize](https://pkg.go.dev/github.com/goccy/go-yaml@v1.15.4/lexer#Tokenize) + - [parser.Parse](https://pkg.go.dev/github.com/goccy/go-yaml@v1.15.4/parser#Parse) +- Filtering, replacing, and merging YAML content using YAML Path +- Reversible transformation without using the AST for YAML that includes Anchors, Aliases, and Comments +- Customize the Marshal/Unmarshal behavior for primitive types and third-party library types ([RegisterCustomMarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#RegisterCustomMarshaler), [RegisterCustomUnmarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#RegisterCustomUnmarshaler)) +- Respects `encoding/json` behavior + - Accept the `json` tag. Note that not all options from the `json` tag will have significance when parsing YAML documents. If both tags exist, `yaml` tag will take precedence. + - [json.Marshaler](https://pkg.go.dev/encoding/json#Marshaler) style [marshaler](https://pkg.go.dev/github.com/goccy/go-yaml#BytesMarshaler) + - [json.Unmarshaler](https://pkg.go.dev/encoding/json#Unmarshaler) style [unmarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#BytesUnmarshaler) + - Options for using `MarshalJSON` and `UnmarshalJSON` ([UseJSONMarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#UseJSONMarshaler), [UseJSONUnmarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#UseJSONUnmarshaler)) - Pretty format for error notifications -- Supports `Scanner` or `Lexer` or `Parser` as public API -- Supports `Anchor` and `Alias` to Marshaler +- Smart validation processing combined with [go-playground/validator](https://github.com/go-playground/validator) + - [example test code is here](https://github.com/goccy/go-yaml/blob/45889c98b0a0967240eb595a1bd6896e2f575106/testdata/validate_test.go#L12) - Allow referencing elements declared in another file via anchors -- Extract value or AST by YAMLPath ( YAMLPath is like a JSONPath ) + +# Users + +The repositories that use goccy/go-yaml are listed here. + +- https://github.com/goccy/go-yaml/wiki/Users + +The source data is [here](https://github.com/goccy/go-yaml/network/dependents). +It is already being used in many repositories. Now it's your turn 😄 + +# Playground + +The Playground visualizes how go-yaml processes YAML text. Use it to assist with your debugging or issue reporting. + +https://goccy.github.io/go-yaml # Installation ```sh -go get -u github.com/goccy/go-yaml +go get github.com/goccy/go-yaml ``` # Synopsis @@ -148,7 +192,9 @@ fmt.Printf("%+v\n", v) // {A:{B:1 C:hello}} ### 3.1. Explicitly declared `Anchor` name and `Alias` name -If you want to use `anchor` or `alias`, you can define it as a struct tag. +If you want to use `anchor`, you can define it as a struct tag. +If the value specified for an anchor is a pointer type and the same address as the pointer is found, the value is automatically set to alias. +If an explicit alias name is specified, an error is raised if its value is different from the value specified in the anchor. ```go type T struct { @@ -178,10 +224,7 @@ d: *x If you do not explicitly declare the anchor name, the default behavior is to use the equivalent of `strings.ToLower($FieldName)` as the name of the anchor. - -If you do not explicitly declare the alias name AND the value is a pointer -to another element, we look up the anchor name by finding out which anchor -field the value is assigned to by looking up its pointer address. +If the value specified for an anchor is a pointer type and the same address as the pointer is found, the value is automatically set to alias. ```go type T struct { @@ -191,8 +234,8 @@ type T struct { var v struct { A *T `yaml:"a,anchor"` B *T `yaml:"b,anchor"` - C *T `yaml:"c,alias"` - D *T `yaml:"d,alias"` + C *T `yaml:"c"` + D *T `yaml:"d"` } v.A = &T{I: 1, S: "hello"} v.B = &T{I: 2, S: "world"} @@ -358,9 +401,16 @@ print yaml file with color ### Installation ```sh -go install github.com/goccy/go-yaml/cmd/ycat@latest +git clone https://github.com/goccy/go-yaml.git +cd go-yaml/cmd/ycat && go install . ``` + +# For Developers + +> [!NOTE] +> In this project, we manage such test code under the `testdata` directory to avoid adding dependencies on libraries that are only needed for testing to the top `go.mod` file. Therefore, if you want to add test cases that use 3rd party libraries, please add the test code to the `testdata` directory. + # Looking for Sponsors I'm looking for sponsors this library. This library is being developed as a personal project in my spare time. If you want a quick response or problem resolution when using this library in your project, please register as a [sponsor](https://github.com/sponsors/goccy). I will cooperate as much as possible. Of course, this library is developed as an MIT license, so you can use it freely for free. diff --git a/vendor/github.com/goccy/go-yaml/ast/ast.go b/vendor/github.com/goccy/go-yaml/ast/ast.go index b4d5ec41..ca150538 100644 --- a/vendor/github.com/goccy/go-yaml/ast/ast.go +++ b/vendor/github.com/goccy/go-yaml/ast/ast.go @@ -1,6 +1,7 @@ package ast import ( + "errors" "fmt" "io" "math" @@ -8,13 +9,12 @@ import ( "strings" "github.com/goccy/go-yaml/token" - "golang.org/x/xerrors" ) var ( - ErrInvalidTokenType = xerrors.New("invalid token type") - ErrInvalidAnchorName = xerrors.New("invalid anchor name") - ErrInvalidAliasName = xerrors.New("invalid alias name") + ErrInvalidTokenType = errors.New("invalid token type") + ErrInvalidAnchorName = errors.New("invalid anchor name") + ErrInvalidAliasName = errors.New("invalid alias name") ) // NodeType type identifier of node @@ -51,6 +51,8 @@ const ( MappingValueType // SequenceType type identifier for sequence node SequenceType + // SequenceEntryType type identifier for sequence entry node + SequenceEntryType // AnchorType type identifier for anchor node AnchorType // AliasType type identifier for alias node @@ -98,6 +100,8 @@ func (t NodeType) String() string { return "MappingValue" case SequenceType: return "Sequence" + case SequenceEntryType: + return "SequenceEntry" case AnchorType: return "Anchor" case AliasType: @@ -148,6 +152,8 @@ func (t NodeType) YAMLName() string { return "value" case SequenceType: return "sequence" + case SequenceEntryType: + return "value" case AnchorType: return "anchor" case AliasType: @@ -196,6 +202,7 @@ type Node interface { // MapKeyNode type for map key node type MapKeyNode interface { Node + IsMergeKey() bool // String node to text without comment stringWithoutComment() string } @@ -278,6 +285,49 @@ func readNode(p []byte, node Node) (int, error) { return size, nil } +func checkLineBreak(t *token.Token) bool { + if t.Prev != nil { + lbc := "\n" + prev := t.Prev + var adjustment int + // if the previous type is sequence entry use the previous type for that + if prev.Type == token.SequenceEntryType { + // as well as switching to previous type count any new lines in origin to account for: + // - + // b: c + adjustment = strings.Count(strings.TrimRight(t.Origin, lbc), lbc) + if prev.Prev != nil { + prev = prev.Prev + } + } + lineDiff := t.Position.Line - prev.Position.Line - 1 + if lineDiff > 0 { + if prev.Type == token.StringType { + // Remove any line breaks included in multiline string + adjustment += strings.Count(strings.TrimRight(strings.TrimSpace(prev.Origin), lbc), lbc) + } + // Due to the way that comment parsing works its assumed that when a null value does not have new line in origin + // it was squashed therefore difference is ignored. + // foo: + // bar: + // # comment + // baz: 1 + // becomes + // foo: + // bar: null # comment + // + // baz: 1 + if prev.Type == token.NullType || prev.Type == token.ImplicitNullType { + return strings.Count(prev.Origin, lbc) > 0 + } + if lineDiff-adjustment > 0 { + return true + } + } + } + return false +} + // Null create node for null value func Null(tk *token.Token) *NullNode { return &NullNode{ @@ -298,105 +348,30 @@ func Bool(tk *token.Token) *BoolNode { // Integer create node for integer value func Integer(tk *token.Token) *IntegerNode { - value := removeUnderScoreFromNumber(tk.Value) - switch tk.Type { - case token.BinaryIntegerType: - // skip two characters because binary token starts with '0b' - skipCharacterNum := 2 - negativePrefix := "" - if value[0] == '-' { - skipCharacterNum++ - negativePrefix = "-" - } - if len(negativePrefix) > 0 { - i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 2, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - } - i, _ := strconv.ParseUint(negativePrefix+value[skipCharacterNum:], 2, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - case token.OctetIntegerType: - // octet token starts with '0o' or '-0o' or '0' or '-0' - skipCharacterNum := 1 - negativePrefix := "" - if value[0] == '-' { - skipCharacterNum++ - if len(value) > 2 && value[2] == 'o' { - skipCharacterNum++ - } - negativePrefix = "-" - } else { - if value[1] == 'o' { - skipCharacterNum++ - } - } - if len(negativePrefix) > 0 { - i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 8, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - } - i, _ := strconv.ParseUint(value[skipCharacterNum:], 8, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - case token.HexIntegerType: - // hex token starts with '0x' or '-0x' - skipCharacterNum := 2 - negativePrefix := "" - if value[0] == '-' { - skipCharacterNum++ - negativePrefix = "-" - } - if len(negativePrefix) > 0 { - i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 16, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - } - i, _ := strconv.ParseUint(value[skipCharacterNum:], 16, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } - } - if value[0] == '-' || value[0] == '+' { - i, _ := strconv.ParseInt(value, 10, 64) - return &IntegerNode{ - BaseNode: &BaseNode{}, - Token: tk, - Value: i, - } + var v any + if num := token.ToNumber(tk.Value); num != nil { + v = num.Value } - i, _ := strconv.ParseUint(value, 10, 64) return &IntegerNode{ BaseNode: &BaseNode{}, Token: tk, - Value: i, + Value: v, } } // Float create node for float value func Float(tk *token.Token) *FloatNode { - f, _ := strconv.ParseFloat(removeUnderScoreFromNumber(tk.Value), 64) + var v float64 + if num := token.ToNumber(tk.Value); num != nil && num.Type == token.NumberTypeFloat { + value, ok := num.Value.(float64) + if ok { + v = value + } + } return &FloatNode{ BaseNode: &BaseNode{}, Token: tk, - Value: f, + Value: v, } } @@ -607,7 +582,9 @@ func (d *DocumentNode) String() string { if d.Start != nil { doc = append(doc, d.Start.Value) } - doc = append(doc, d.Body.String()) + if d.Body != nil { + doc = append(doc, d.Body.String()) + } if d.End != nil { doc = append(doc, d.End.Value) } @@ -619,10 +596,6 @@ func (d *DocumentNode) MarshalYAML() ([]byte, error) { return []byte(d.String()), nil } -func removeUnderScoreFromNumber(num string) string { - return strings.ReplaceAll(num, "_", "") -} - // NullNode type of null node type NullNode struct { *BaseNode @@ -654,6 +627,12 @@ func (n *NullNode) GetValue() interface{} { // String returns `null` text func (n *NullNode) String() string { + if n.Token.Type == token.ImplicitNullType { + if n.Comment != nil { + return n.Comment.String() + } + return "" + } if n.Comment != nil { return addCommentString("null", n.Comment) } @@ -669,6 +648,11 @@ func (n *NullNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// IsMergeKey returns whether it is a MergeKey node. +func (n *NullNode) IsMergeKey() bool { + return false +} + // IntegerNode type of integer node type IntegerNode struct { *BaseNode @@ -716,6 +700,11 @@ func (n *IntegerNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// IsMergeKey returns whether it is a MergeKey node. +func (n *IntegerNode) IsMergeKey() bool { + return false +} + // FloatNode type of float node type FloatNode struct { *BaseNode @@ -764,6 +753,11 @@ func (n *FloatNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// IsMergeKey returns whether it is a MergeKey node. +func (n *FloatNode) IsMergeKey() bool { + return false +} + // StringNode type of string node type StringNode struct { *BaseNode @@ -794,6 +788,11 @@ func (n *StringNode) GetValue() interface{} { return n.Value } +// IsMergeKey returns whether it is a MergeKey node. +func (n *StringNode) IsMergeKey() bool { + return false +} + // escapeSingleQuote escapes s to a single quoted scalar. // https://yaml.org/spec/1.2.2/#732-single-quoted-style func escapeSingleQuote(s string) string { @@ -831,11 +830,12 @@ func (n *StringNode) String() string { // It works mostly, but inconsistencies occur if line break characters are mixed. header := token.LiteralBlockHeader(n.Value) space := strings.Repeat(" ", n.Token.Position.Column-1) + indent := strings.Repeat(" ", n.Token.Position.IndentNum) values := []string{} for _, v := range strings.Split(n.Value, lbc) { - values = append(values, fmt.Sprintf("%s %s", space, v)) + values = append(values, fmt.Sprintf("%s%s%s", space, indent, v)) } - block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space)) + block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s%s%s", lbc, indent, space)), fmt.Sprintf("%s%s", indent, space)) return fmt.Sprintf("%s%s%s", header, lbc, block) } else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') { return fmt.Sprintf(`'%s'`, n.Value) @@ -862,11 +862,12 @@ func (n *StringNode) stringWithoutComment() string { // It works mostly, but inconsistencies occur if line break characters are mixed. header := token.LiteralBlockHeader(n.Value) space := strings.Repeat(" ", n.Token.Position.Column-1) + indent := strings.Repeat(" ", n.Token.Position.IndentNum) values := []string{} for _, v := range strings.Split(n.Value, lbc) { - values = append(values, fmt.Sprintf("%s %s", space, v)) + values = append(values, fmt.Sprintf("%s%s%s", space, indent, v)) } - block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space)) + block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s%s%s", lbc, indent, space)), fmt.Sprintf(" %s", space)) return fmt.Sprintf("%s%s%s", header, lbc, block) } else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') { return fmt.Sprintf(`'%s'`, n.Value) @@ -931,6 +932,11 @@ func (n *LiteralNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// IsMergeKey returns whether it is a MergeKey node. +func (n *LiteralNode) IsMergeKey() bool { + return false +} + // MergeKeyNode type of merge key node type MergeKeyNode struct { *BaseNode @@ -974,6 +980,11 @@ func (n *MergeKeyNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// IsMergeKey returns whether it is a MergeKey node. +func (n *MergeKeyNode) IsMergeKey() bool { + return true +} + // BoolNode type of boolean node type BoolNode struct { *BaseNode @@ -1021,6 +1032,11 @@ func (n *BoolNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// IsMergeKey returns whether it is a MergeKey node. +func (n *BoolNode) IsMergeKey() bool { + return false +} + // InfinityNode type of infinity node type InfinityNode struct { *BaseNode @@ -1068,6 +1084,11 @@ func (n *InfinityNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// IsMergeKey returns whether it is a MergeKey node. +func (n *InfinityNode) IsMergeKey() bool { + return false +} + // NanNode type of nan node type NanNode struct { *BaseNode @@ -1114,6 +1135,11 @@ func (n *NanNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// IsMergeKey returns whether it is a MergeKey node. +func (n *NanNode) IsMergeKey() bool { + return false +} + // MapNode interface of MappingValueNode / MappingNode type MapNode interface { MapRange() *MapNodeIter @@ -1147,6 +1173,11 @@ func (m *MapNodeIter) Value() Node { return m.values[m.idx].Value } +// KeyValue returns the MappingValueNode of the iterator's current map node entry. +func (m *MapNodeIter) KeyValue() *MappingValueNode { + return m.values[m.idx] +} + // MappingNode type of mapping node type MappingNode struct { *BaseNode @@ -1317,13 +1348,27 @@ func (n *MappingKeyNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// IsMergeKey returns whether it is a MergeKey node. +func (n *MappingKeyNode) IsMergeKey() bool { + if n.Value == nil { + return false + } + key, ok := n.Value.(MapKeyNode) + if !ok { + return false + } + return key.IsMergeKey() +} + // MappingValueNode type of mapping value type MappingValueNode struct { *BaseNode - Start *token.Token - Key MapKeyNode - Value Node - FootComment *CommentGroupNode + Start *token.Token // delimiter token ':'. + CollectEntry *token.Token // collect entry token ','. + Key MapKeyNode + Value Node + FootComment *CommentGroupNode + IsFlowStyle bool } // Replace replace value node. @@ -1360,6 +1405,7 @@ func (n *MappingValueNode) AddColumn(col int) { // SetIsFlowStyle set value to IsFlowStyle field recursively. func (n *MappingValueNode) SetIsFlowStyle(isFlow bool) { + n.IsFlowStyle = isFlow switch value := n.Value.(type) { case *MappingNode: value.SetIsFlowStyle(isFlow) @@ -1390,22 +1436,39 @@ func (n *MappingValueNode) String() string { func (n *MappingValueNode) toString() string { space := strings.Repeat(" ", n.Key.GetToken().Position.Column-1) + if checkLineBreak(n.Key.GetToken()) { + space = fmt.Sprintf("%s%s", "\n", space) + } keyIndentLevel := n.Key.GetToken().Position.IndentLevel valueIndentLevel := n.Value.GetToken().Position.IndentLevel keyComment := n.Key.GetComment() if _, ok := n.Value.(ScalarNode); ok { - return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) - } else if keyIndentLevel < valueIndentLevel { + value := n.Value.String() + if value == "" { + // implicit null value. + return fmt.Sprintf("%s%s:", space, n.Key.String()) + } + return fmt.Sprintf("%s%s: %s", space, n.Key.String(), value) + } else if keyIndentLevel < valueIndentLevel && !n.IsFlowStyle { + valueStr := n.Value.String() + // For flow-style values indented on the next line, we need to add the proper indentation + if m, ok := n.Value.(*MappingNode); ok && m.IsFlowStyle { + valueIndent := strings.Repeat(" ", n.Value.GetToken().Position.Column-1) + valueStr = valueIndent + valueStr + } else if s, ok := n.Value.(*SequenceNode); ok && s.IsFlowStyle { + valueIndent := strings.Repeat(" ", n.Value.GetToken().Position.Column-1) + valueStr = valueIndent + valueStr + } if keyComment != nil { return fmt.Sprintf( "%s%s: %s\n%s", space, n.Key.stringWithoutComment(), keyComment.String(), - n.Value.String(), + valueStr, ) } - return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String()) + return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), valueStr) } else if m, ok := n.Value.(*MappingNode); ok && (m.IsFlowStyle || len(m.Values) == 0) { return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) } else if s, ok := n.Value.(*SequenceNode); ok && (s.IsFlowStyle || len(s.Values) == 0) { @@ -1414,7 +1477,10 @@ func (n *MappingValueNode) toString() string { return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) } else if _, ok := n.Value.(*AliasNode); ok { return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) + } else if _, ok := n.Value.(*TagNode); ok { + return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String()) } + if keyComment != nil { return fmt.Sprintf( "%s%s: %s\n%s", @@ -1485,13 +1551,14 @@ type SequenceNode struct { IsFlowStyle bool Values []Node ValueHeadComments []*CommentGroupNode + Entries []*SequenceEntryNode FootComment *CommentGroupNode } // Replace replace value node. func (n *SequenceNode) Replace(idx int, value Node) error { if len(n.Values) <= idx { - return xerrors.Errorf( + return fmt.Errorf( "invalid index for sequence: sequence length is %d, but specified %d index", len(n.Values), idx, ) @@ -1507,6 +1574,11 @@ func (n *SequenceNode) Merge(target *SequenceNode) { column := n.Start.Position.Column - target.Start.Position.Column target.AddColumn(column) n.Values = append(n.Values, target.Values...) + if len(target.ValueHeadComments) == 0 { + n.ValueHeadComments = append(n.ValueHeadComments, make([]*CommentGroupNode, len(target.Values))...) + return + } + n.ValueHeadComments = append(n.ValueHeadComments, target.ValueHeadComments...) } // SetIsFlowStyle set value to IsFlowStyle field recursively. @@ -1562,7 +1634,15 @@ func (n *SequenceNode) blockStyleString() string { } for idx, value := range n.Values { + if value == nil { + continue + } valueStr := value.String() + newLinePrefix := "" + if strings.HasPrefix(valueStr, "\n") { + valueStr = valueStr[1:] + newLinePrefix = "\n" + } splittedValues := strings.Split(valueStr, "\n") trimmedFirstValue := strings.TrimLeft(splittedValues[0], " ") diffLength := len(splittedValues[0]) - len(trimmedFirstValue) @@ -1585,9 +1665,10 @@ func (n *SequenceNode) blockStyleString() string { } newValue := strings.Join(newValues, "\n") if len(n.ValueHeadComments) == len(n.Values) && n.ValueHeadComments[idx] != nil { - values = append(values, n.ValueHeadComments[idx].StringWithSpace(n.Start.Position.Column-1)) + values = append(values, fmt.Sprintf("%s%s", newLinePrefix, n.ValueHeadComments[idx].StringWithSpace(n.Start.Position.Column-1))) + newLinePrefix = "" } - values = append(values, fmt.Sprintf("%s- %s", space, newValue)) + values = append(values, fmt.Sprintf("%s%s- %s", newLinePrefix, space, newValue)) } if n.FootComment != nil { values = append(values, n.FootComment.StringWithSpace(n.Start.Position.Column-1)) @@ -1616,6 +1697,87 @@ func (n *SequenceNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// SequenceEntryNode is the sequence entry. +type SequenceEntryNode struct { + *BaseNode + HeadComment *CommentGroupNode // head comment. + LineComment *CommentGroupNode // line comment e.g.) - # comment. + Start *token.Token // entry token. + Value Node // value node. +} + +// String node to text +func (n *SequenceEntryNode) String() string { + return "" // TODO +} + +// GetToken returns token instance +func (n *SequenceEntryNode) GetToken() *token.Token { + return n.Start +} + +// Type returns type of node +func (n *SequenceEntryNode) Type() NodeType { + return SequenceEntryType +} + +// AddColumn add column number to child nodes recursively +func (n *SequenceEntryNode) AddColumn(col int) { + n.Start.AddColumn(col) +} + +// SetComment set line comment. +func (n *SequenceEntryNode) SetComment(cm *CommentGroupNode) error { + n.LineComment = cm + return nil +} + +// Comment returns comment token instance +func (n *SequenceEntryNode) GetComment() *CommentGroupNode { + return n.LineComment +} + +// MarshalYAML +func (n *SequenceEntryNode) MarshalYAML() ([]byte, error) { + return []byte(n.String()), nil +} + +func (n *SequenceEntryNode) Read(p []byte) (int, error) { + return readNode(p, n) +} + +// SequenceEntry creates SequenceEntryNode instance. +func SequenceEntry(start *token.Token, value Node, headComment *CommentGroupNode) *SequenceEntryNode { + return &SequenceEntryNode{ + BaseNode: &BaseNode{}, + HeadComment: headComment, + Start: start, + Value: value, + } +} + +// SequenceMergeValue creates SequenceMergeValueNode instance. +func SequenceMergeValue(values ...MapNode) *SequenceMergeValueNode { + return &SequenceMergeValueNode{ + values: values, + } +} + +// SequenceMergeValueNode is used to convert the Sequence node specified for the merge key into a MapNode format. +type SequenceMergeValueNode struct { + values []MapNode +} + +// MapRange returns MapNodeIter instance. +func (n *SequenceMergeValueNode) MapRange() *MapNodeIter { + ret := &MapNodeIter{idx: startRangeIndex} + for _, value := range n.values { + iter := value.MapRange() + ret.values = append(ret.values, iter.values...) + } + return ret +} + // AnchorNode type of anchor node type AnchorNode struct { *BaseNode @@ -1624,6 +1786,10 @@ type AnchorNode struct { Value Node } +func (n *AnchorNode) stringWithoutComment() string { + return n.Value.String() +} + func (n *AnchorNode) SetName(name string) error { if n.Name == nil { return ErrInvalidAnchorName @@ -1649,6 +1815,10 @@ func (n *AnchorNode) GetToken() *token.Token { return n.Start } +func (n *AnchorNode) GetValue() any { + return n.Value.GetToken().Value +} + // AddColumn add column number to child nodes recursively func (n *AnchorNode) AddColumn(col int) { n.Start.AddColumn(col) @@ -1662,15 +1832,18 @@ func (n *AnchorNode) AddColumn(col int) { // String anchor to text func (n *AnchorNode) String() string { + anchor := "&" + n.Name.String() value := n.Value.String() - if len(strings.Split(value, "\n")) > 1 { - return fmt.Sprintf("&%s\n%s", n.Name.String(), value) - } else if s, ok := n.Value.(*SequenceNode); ok && !s.IsFlowStyle { - return fmt.Sprintf("&%s\n%s", n.Name.String(), value) + if s, ok := n.Value.(*SequenceNode); ok && !s.IsFlowStyle { + return fmt.Sprintf("%s\n%s", anchor, value) } else if m, ok := n.Value.(*MappingNode); ok && !m.IsFlowStyle { - return fmt.Sprintf("&%s\n%s", n.Name.String(), value) + return fmt.Sprintf("%s\n%s", anchor, value) + } + if value == "" { + // implicit null value. + return anchor } - return fmt.Sprintf("&%s %s", n.Name.String(), value) + return fmt.Sprintf("%s %s", anchor, value) } // MarshalYAML encodes to a YAML text @@ -1678,6 +1851,18 @@ func (n *AnchorNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// IsMergeKey returns whether it is a MergeKey node. +func (n *AnchorNode) IsMergeKey() bool { + if n.Value == nil { + return false + } + key, ok := n.Value.(MapKeyNode) + if !ok { + return false + } + return key.IsMergeKey() +} + // AliasNode type of alias node type AliasNode struct { *BaseNode @@ -1685,6 +1870,10 @@ type AliasNode struct { Value Node } +func (n *AliasNode) stringWithoutComment() string { + return n.Value.String() +} + func (n *AliasNode) SetName(name string) error { if n.Value == nil { return ErrInvalidAliasName @@ -1710,6 +1899,10 @@ func (n *AliasNode) GetToken() *token.Token { return n.Start } +func (n *AliasNode) GetValue() any { + return n.Value.GetToken().Value +} + // AddColumn add column number to child nodes recursively func (n *AliasNode) AddColumn(col int) { n.Start.AddColumn(col) @@ -1728,11 +1921,20 @@ func (n *AliasNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// IsMergeKey returns whether it is a MergeKey node. +func (n *AliasNode) IsMergeKey() bool { + return false +} + // DirectiveNode type of directive node type DirectiveNode struct { *BaseNode + // Start is '%' token. Start *token.Token - Value Node + // Name is directive name e.g.) "YAML" or "TAG". + Name Node + // Values is directive values e.g.) "1.2" or "!!" and "tag:clarkevans.com,2002:app/". + Values []Node } // Read implements (io.Reader).Read @@ -1750,14 +1952,21 @@ func (n *DirectiveNode) GetToken() *token.Token { // AddColumn add column number to child nodes recursively func (n *DirectiveNode) AddColumn(col int) { - if n.Value != nil { - n.Value.AddColumn(col) + if n.Name != nil { + n.Name.AddColumn(col) + } + for _, value := range n.Values { + value.AddColumn(col) } } // String directive to text func (n *DirectiveNode) String() string { - return fmt.Sprintf("%s%s", n.Start.Value, n.Value.String()) + values := make([]string, 0, len(n.Values)) + for _, val := range n.Values { + values = append(values, val.String()) + } + return strings.Join(append([]string{"%" + n.Name.String()}, values...), " ") } // MarshalYAML encodes to a YAML text @@ -1768,8 +1977,21 @@ func (n *DirectiveNode) MarshalYAML() ([]byte, error) { // TagNode type of tag node type TagNode struct { *BaseNode - Start *token.Token - Value Node + Directive *DirectiveNode + Start *token.Token + Value Node +} + +func (n *TagNode) GetValue() any { + scalar, ok := n.Value.(ScalarNode) + if !ok { + return nil + } + return scalar.GetValue() +} + +func (n *TagNode) stringWithoutComment() string { + return n.Value.String() } // Read implements (io.Reader).Read @@ -1795,7 +2017,14 @@ func (n *TagNode) AddColumn(col int) { // String tag to text func (n *TagNode) String() string { - return fmt.Sprintf("%s %s", n.Start.Value, n.Value.String()) + value := n.Value.String() + if s, ok := n.Value.(*SequenceNode); ok && !s.IsFlowStyle { + return fmt.Sprintf("%s\n%s", n.Start.Value, value) + } else if m, ok := n.Value.(*MappingNode); ok && !m.IsFlowStyle { + return fmt.Sprintf("%s\n%s", n.Start.Value, value) + } + + return fmt.Sprintf("%s %s", n.Start.Value, value) } // MarshalYAML encodes to a YAML text @@ -1803,6 +2032,26 @@ func (n *TagNode) MarshalYAML() ([]byte, error) { return []byte(n.String()), nil } +// IsMergeKey returns whether it is a MergeKey node. +func (n *TagNode) IsMergeKey() bool { + if n.Value == nil { + return false + } + key, ok := n.Value.(MapKeyNode) + if !ok { + return false + } + return key.IsMergeKey() +} + +func (n *TagNode) ArrayRange() *ArrayNodeIter { + arr, ok := n.Value.(ArrayNode) + if !ok { + return nil + } + return arr.ArrayRange() +} + // CommentNode type of comment node type CommentNode struct { *BaseNode @@ -1880,10 +2129,13 @@ func (n *CommentGroupNode) StringWithSpace(col int) string { values := []string{} space := strings.Repeat(" ", col) for _, comment := range n.Comments { + space := space + if checkLineBreak(comment.Token) { + space = fmt.Sprintf("%s%s", "\n", space) + } values = append(values, space+comment.String()) } return strings.Join(values, "\n") - } // MarshalYAML encodes to a YAML text @@ -1930,7 +2182,10 @@ func Walk(v Visitor, node Node) { Walk(v, n.Value) case *DirectiveNode: walkComment(v, n.BaseNode) - Walk(v, n.Value) + Walk(v, n.Name) + for _, value := range n.Values { + Walk(v, value) + } case *TagNode: walkComment(v, n.BaseNode) Walk(v, n.Value) @@ -2016,7 +2271,14 @@ func (f *parentFinder) walk(parent, node Node) Node { case *LiteralNode: return f.walk(node, n.Value) case *DirectiveNode: - return f.walk(node, n.Value) + if found := f.walk(node, n.Name); found != nil { + return found + } + for _, value := range n.Values { + if found := f.walk(node, value); found != nil { + return found + } + } case *TagNode: return f.walk(node, n.Value) case *DocumentNode: @@ -2092,10 +2354,10 @@ func Merge(dst Node, src Node) error { err := &ErrInvalidMergeType{dst: dst, src: src} switch dst.Type() { case DocumentType: - node := dst.(*DocumentNode) + node, _ := dst.(*DocumentNode) return Merge(node.Body, src) case MappingType: - node := dst.(*MappingNode) + node, _ := dst.(*MappingNode) target, ok := src.(*MappingNode) if !ok { return err @@ -2103,7 +2365,7 @@ func Merge(dst Node, src Node) error { node.Merge(target) return nil case SequenceType: - node := dst.(*SequenceNode) + node, _ := dst.(*SequenceNode) target, ok := src.(*SequenceNode) if !ok { return err diff --git a/vendor/github.com/goccy/go-yaml/context.go b/vendor/github.com/goccy/go-yaml/context.go new file mode 100644 index 00000000..133f05ee --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/context.go @@ -0,0 +1,37 @@ +package yaml + +import "context" + +type ( + ctxMergeKey struct{} + ctxAnchorKey struct{} +) + +func withMerge(ctx context.Context) context.Context { + return context.WithValue(ctx, ctxMergeKey{}, true) +} + +func isMerge(ctx context.Context) bool { + v, ok := ctx.Value(ctxMergeKey{}).(bool) + if !ok { + return false + } + return v +} + +func withAnchor(ctx context.Context, name string) context.Context { + anchorMap := getAnchorMap(ctx) + if anchorMap == nil { + anchorMap = make(map[string]struct{}) + } + anchorMap[name] = struct{}{} + return context.WithValue(ctx, ctxAnchorKey{}, anchorMap) +} + +func getAnchorMap(ctx context.Context) map[string]struct{} { + v, ok := ctx.Value(ctxAnchorKey{}).(map[string]struct{}) + if !ok { + return nil + } + return v +} diff --git a/vendor/github.com/goccy/go-yaml/decode.go b/vendor/github.com/goccy/go-yaml/decode.go index 72af5e22..43c317f8 100644 --- a/vendor/github.com/goccy/go-yaml/decode.go +++ b/vendor/github.com/goccy/go-yaml/decode.go @@ -7,18 +7,19 @@ import ( "encoding/base64" "fmt" "io" + "maps" "math" "os" "path/filepath" "reflect" "sort" "strconv" + "strings" "time" - "golang.org/x/xerrors" - "github.com/goccy/go-yaml/ast" "github.com/goccy/go-yaml/internal/errors" + "github.com/goccy/go-yaml/internal/format" "github.com/goccy/go-yaml/parser" "github.com/goccy/go-yaml/token" ) @@ -29,7 +30,8 @@ type Decoder struct { referenceReaders []io.Reader anchorNodeMap map[string]ast.Node anchorValueMap map[string]reflect.Value - customUnmarshalerMap map[reflect.Type]func(interface{}, []byte) error + customUnmarshalerMap map[reflect.Type]func(context.Context, interface{}, []byte) error + commentMaps []CommentMap toCommentMap CommentMap opts []DecodeOption referenceFiles []string @@ -38,11 +40,13 @@ type Decoder struct { isResolvedReference bool validator StructValidator disallowUnknownField bool - disallowDuplicateKey bool + allowedFieldPrefixes []string + allowDuplicateMapKey bool useOrderedMap bool useJSONUnmarshaler bool parsedFile *ast.File streamIndex int + decodeDepth int } // NewDecoder returns a new decoder that reads from r. @@ -51,7 +55,7 @@ func NewDecoder(r io.Reader, opts ...DecodeOption) *Decoder { reader: r, anchorNodeMap: map[string]ast.Node{}, anchorValueMap: map[string]reflect.Value{}, - customUnmarshalerMap: map[reflect.Type]func(interface{}, []byte) error{}, + customUnmarshalerMap: map[reflect.Type]func(context.Context, interface{}, []byte) error{}, opts: opts, referenceReaders: []io.Reader{}, referenceFiles: []string{}, @@ -59,11 +63,25 @@ func NewDecoder(r io.Reader, opts ...DecodeOption) *Decoder { isRecursiveDir: false, isResolvedReference: false, disallowUnknownField: false, - disallowDuplicateKey: false, + allowDuplicateMapKey: false, useOrderedMap: false, } } +const maxDecodeDepth = 10000 + +func (d *Decoder) stepIn() { + d.decodeDepth++ +} + +func (d *Decoder) stepOut() { + d.decodeDepth-- +} + +func (d *Decoder) isExceededMaxDepth() bool { + return d.decodeDepth > maxDecodeDepth +} + func (d *Decoder) castToFloat(v interface{}) interface{} { switch vv := v.(type) { case int: @@ -98,63 +116,111 @@ func (d *Decoder) castToFloat(v interface{}) interface{} { return 0 } -func (d *Decoder) mergeValueNode(value ast.Node) ast.Node { - if value.Type() == ast.AliasType { - aliasNode := value.(*ast.AliasNode) - aliasName := aliasNode.Value.GetToken().Value - return d.anchorNodeMap[aliasName] +func (d *Decoder) mapKeyNodeToString(ctx context.Context, node ast.MapKeyNode) (string, error) { + key, err := d.nodeToValue(ctx, node) + if err != nil { + return "", err } - return value -} - -func (d *Decoder) mapKeyNodeToString(node ast.MapKeyNode) string { - key := d.nodeToValue(node) if key == nil { - return "null" + return "null", nil } if k, ok := key.(string); ok { - return k + return k, nil } - return fmt.Sprint(key) + return fmt.Sprint(key), nil } -func (d *Decoder) setToMapValue(node ast.Node, m map[string]interface{}) { +func (d *Decoder) setToMapValue(ctx context.Context, node ast.Node, m map[string]interface{}) error { + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return ErrExceededMaxDepth + } + d.setPathToCommentMap(node) switch n := node.(type) { case *ast.MappingValueNode: - if n.Key.Type() == ast.MergeKeyType { - d.setToMapValue(d.mergeValueNode(n.Value), m) + if n.Key.IsMergeKey() { + value, err := d.getMapNode(n.Value, true) + if err != nil { + return err + } + iter := value.MapRange() + for iter.Next() { + if err := d.setToMapValue(ctx, iter.KeyValue(), m); err != nil { + return err + } + } } else { - key := d.mapKeyNodeToString(n.Key) - m[key] = d.nodeToValue(n.Value) + key, err := d.mapKeyNodeToString(ctx, n.Key) + if err != nil { + return err + } + v, err := d.nodeToValue(ctx, n.Value) + if err != nil { + return err + } + m[key] = v } case *ast.MappingNode: for _, value := range n.Values { - d.setToMapValue(value, m) + if err := d.setToMapValue(ctx, value, m); err != nil { + return err + } } case *ast.AnchorNode: anchorName := n.Name.GetToken().Value d.anchorNodeMap[anchorName] = n.Value } + return nil } -func (d *Decoder) setToOrderedMapValue(node ast.Node, m *MapSlice) { +func (d *Decoder) setToOrderedMapValue(ctx context.Context, node ast.Node, m *MapSlice) error { + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return ErrExceededMaxDepth + } + + d.setPathToCommentMap(node) switch n := node.(type) { case *ast.MappingValueNode: - if n.Key.Type() == ast.MergeKeyType { - d.setToOrderedMapValue(d.mergeValueNode(n.Value), m) + if n.Key.IsMergeKey() { + value, err := d.getMapNode(n.Value, true) + if err != nil { + return err + } + iter := value.MapRange() + for iter.Next() { + if err := d.setToOrderedMapValue(ctx, iter.KeyValue(), m); err != nil { + return err + } + } } else { - key := d.mapKeyNodeToString(n.Key) - *m = append(*m, MapItem{Key: key, Value: d.nodeToValue(n.Value)}) + key, err := d.mapKeyNodeToString(ctx, n.Key) + if err != nil { + return err + } + value, err := d.nodeToValue(ctx, n.Value) + if err != nil { + return err + } + *m = append(*m, MapItem{Key: key, Value: value}) } case *ast.MappingNode: for _, value := range n.Values { - d.setToOrderedMapValue(value, m) + if err := d.setToOrderedMapValue(ctx, value, m); err != nil { + return err + } } } + return nil } func (d *Decoder) setPathToCommentMap(node ast.Node) { + if node == nil { + return + } if d.toCommentMap == nil { return } @@ -186,6 +252,14 @@ func (d *Decoder) addHeadOrLineCommentToMap(node ast.Node) { } commentPath := node.GetPath() if minCommentLine < targetLine { + switch n := node.(type) { + case *ast.MappingNode: + if len(n.Values) != 0 { + commentPath = n.Values[0].Key.GetPath() + } + case *ast.MappingValueNode: + commentPath = n.Key.GetPath() + } d.addCommentToMap(commentPath, HeadComment(texts...)) } else { d.addCommentToMap(commentPath, LineComment(texts[0])) @@ -222,18 +296,24 @@ func (d *Decoder) addSequenceNodeCommentToMap(node *ast.SequenceNode) { func (d *Decoder) addFootCommentToMap(node ast.Node) { var ( footComment *ast.CommentGroupNode - footCommentPath string = node.GetPath() + footCommentPath = node.GetPath() ) switch n := node.(type) { case *ast.SequenceNode: - if len(n.Values) != 0 { - footCommentPath = n.Values[len(n.Values)-1].GetPath() - } footComment = n.FootComment + if n.FootComment != nil { + footCommentPath = n.FootComment.GetPath() + } case *ast.MappingNode: footComment = n.FootComment + if n.FootComment != nil { + footCommentPath = n.FootComment.GetPath() + } case *ast.MappingValueNode: footComment = n.FootComment + if n.FootComment != nil { + footCommentPath = n.FootComment.GetPath() + } } if footComment == nil { return @@ -260,192 +340,251 @@ func (d *Decoder) addCommentToMap(path string, comment *Comment) { }) } -func (d *Decoder) nodeToValue(node ast.Node) interface{} { +func (d *Decoder) nodeToValue(ctx context.Context, node ast.Node) (any, error) { + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return nil, ErrExceededMaxDepth + } + d.setPathToCommentMap(node) switch n := node.(type) { case *ast.NullNode: - return nil + return nil, nil case *ast.StringNode: - return n.GetValue() + return n.GetValue(), nil case *ast.IntegerNode: - return n.GetValue() + return n.GetValue(), nil case *ast.FloatNode: - return n.GetValue() + return n.GetValue(), nil case *ast.BoolNode: - return n.GetValue() + return n.GetValue(), nil case *ast.InfinityNode: - return n.GetValue() + return n.GetValue(), nil case *ast.NanNode: - return n.GetValue() + return n.GetValue(), nil case *ast.TagNode: + if n.Directive != nil { + v, err := d.nodeToValue(ctx, n.Value) + if err != nil { + return nil, err + } + if v == nil { + return "", nil + } + return fmt.Sprint(v), nil + } switch token.ReservedTagKeyword(n.Start.Value) { case token.TimestampTag: - t, _ := d.castToTime(n.Value) - return t + t, _ := d.castToTime(ctx, n.Value) + return t, nil case token.IntegerTag: - i, _ := strconv.Atoi(fmt.Sprint(d.nodeToValue(n.Value))) - return i + v, err := d.nodeToValue(ctx, n.Value) + if err != nil { + return nil, err + } + i, _ := strconv.Atoi(fmt.Sprint(v)) + return i, nil case token.FloatTag: - return d.castToFloat(d.nodeToValue(n.Value)) + v, err := d.nodeToValue(ctx, n.Value) + if err != nil { + return nil, err + } + return d.castToFloat(v), nil case token.NullTag: - return nil + return nil, nil case token.BinaryTag: - b, _ := base64.StdEncoding.DecodeString(d.nodeToValue(n.Value).(string)) - return b + v, err := d.nodeToValue(ctx, n.Value) + if err != nil { + return nil, err + } + str, ok := v.(string) + if !ok { + return nil, errors.ErrSyntax( + fmt.Sprintf("cannot convert %q to string", fmt.Sprint(v)), + n.Value.GetToken(), + ) + } + b, _ := base64.StdEncoding.DecodeString(str) + return b, nil + case token.BooleanTag: + v, err := d.nodeToValue(ctx, n.Value) + if err != nil { + return nil, err + } + str := strings.ToLower(fmt.Sprint(v)) + b, err := strconv.ParseBool(str) + if err == nil { + return b, nil + } + switch str { + case "yes": + return true, nil + case "no": + return false, nil + } + return nil, errors.ErrSyntax(fmt.Sprintf("cannot convert %q to boolean", fmt.Sprint(v)), n.Value.GetToken()) case token.StringTag: - return d.nodeToValue(n.Value) + v, err := d.nodeToValue(ctx, n.Value) + if err != nil { + return nil, err + } + if v == nil { + return "", nil + } + return fmt.Sprint(v), nil case token.MappingTag: - return d.nodeToValue(n.Value) + return d.nodeToValue(ctx, n.Value) + default: + return d.nodeToValue(ctx, n.Value) } case *ast.AnchorNode: anchorName := n.Name.GetToken().Value - anchorValue := d.nodeToValue(n.Value) + + // To handle the case where alias is processed recursively, the result of alias can be set to nil in advance. + d.anchorNodeMap[anchorName] = nil + anchorValue, err := d.nodeToValue(withAnchor(ctx, anchorName), n.Value) + if err != nil { + delete(d.anchorNodeMap, anchorName) + return nil, err + } d.anchorNodeMap[anchorName] = n.Value - return anchorValue + d.anchorValueMap[anchorName] = reflect.ValueOf(anchorValue) + return anchorValue, nil case *ast.AliasNode: + text := n.Value.String() + if _, exists := getAnchorMap(ctx)[text]; exists { + // self recursion. + return nil, nil + } + if v, exists := d.anchorValueMap[text]; exists { + if !v.IsValid() { + return nil, nil + } + return v.Interface(), nil + } aliasName := n.Value.GetToken().Value - node := d.anchorNodeMap[aliasName] - return d.nodeToValue(node) + return nil, errors.ErrSyntax(fmt.Sprintf("could not find alias %q", aliasName), n.Value.GetToken()) case *ast.LiteralNode: - return n.Value.GetValue() + return n.Value.GetValue(), nil case *ast.MappingKeyNode: - return d.nodeToValue(n.Value) + return d.nodeToValue(ctx, n.Value) case *ast.MappingValueNode: - if n.Key.Type() == ast.MergeKeyType { - value := d.mergeValueNode(n.Value) + if n.Key.IsMergeKey() { + value, err := d.getMapNode(n.Value, true) + if err != nil { + return nil, err + } + iter := value.MapRange() if d.useOrderedMap { m := MapSlice{} - d.setToOrderedMapValue(value, &m) - return m + for iter.Next() { + if err := d.setToOrderedMapValue(ctx, iter.KeyValue(), &m); err != nil { + return nil, err + } + } + return m, nil + } + m := make(map[string]any) + for iter.Next() { + if err := d.setToMapValue(ctx, iter.KeyValue(), m); err != nil { + return nil, err + } } - m := map[string]interface{}{} - d.setToMapValue(value, m) - return m + return m, nil + } + key, err := d.mapKeyNodeToString(ctx, n.Key) + if err != nil { + return nil, err } - key := d.mapKeyNodeToString(n.Key) if d.useOrderedMap { - return MapSlice{{Key: key, Value: d.nodeToValue(n.Value)}} + v, err := d.nodeToValue(ctx, n.Value) + if err != nil { + return nil, err + } + return MapSlice{{Key: key, Value: v}}, nil } - return map[string]interface{}{ - key: d.nodeToValue(n.Value), + v, err := d.nodeToValue(ctx, n.Value) + if err != nil { + return nil, err } + return map[string]interface{}{key: v}, nil case *ast.MappingNode: if d.useOrderedMap { m := make(MapSlice, 0, len(n.Values)) for _, value := range n.Values { - d.setToOrderedMapValue(value, &m) + if err := d.setToOrderedMapValue(ctx, value, &m); err != nil { + return nil, err + } } - return m + return m, nil } m := make(map[string]interface{}, len(n.Values)) for _, value := range n.Values { - d.setToMapValue(value, m) - } - return m - case *ast.SequenceNode: - v := make([]interface{}, 0, len(n.Values)) - for _, value := range n.Values { - v = append(v, d.nodeToValue(value)) - } - return v - } - return nil -} - -func (d *Decoder) resolveAlias(node ast.Node) (ast.Node, error) { - switch n := node.(type) { - case *ast.MappingNode: - for idx, v := range n.Values { - value, err := d.resolveAlias(v) - if err != nil { - return nil, err - } - n.Values[idx] = value.(*ast.MappingValueNode) - } - case *ast.TagNode: - value, err := d.resolveAlias(n.Value) - if err != nil { - return nil, err - } - n.Value = value - case *ast.MappingKeyNode: - value, err := d.resolveAlias(n.Value) - if err != nil { - return nil, err - } - n.Value = value - case *ast.MappingValueNode: - if n.Key.Type() == ast.MergeKeyType && n.Value.Type() == ast.AliasType { - value, err := d.resolveAlias(n.Value) - if err != nil { - return nil, err - } - keyColumn := n.Key.GetToken().Position.Column - requiredColumn := keyColumn + 2 - value.AddColumn(requiredColumn) - n.Value = value - } else { - key, err := d.resolveAlias(n.Key) - if err != nil { + if err := d.setToMapValue(ctx, value, m); err != nil { return nil, err } - n.Key = key.(ast.MapKeyNode) - value, err := d.resolveAlias(n.Value) - if err != nil { - return nil, err - } - n.Value = value } + return m, nil case *ast.SequenceNode: - for idx, v := range n.Values { - value, err := d.resolveAlias(v) + v := make([]interface{}, 0, len(n.Values)) + for _, value := range n.Values { + vv, err := d.nodeToValue(ctx, value) if err != nil { return nil, err } - n.Values[idx] = value - } - case *ast.AliasNode: - aliasName := n.Value.GetToken().Value - node := d.anchorNodeMap[aliasName] - if node == nil { - return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName) + v = append(v, vv) } - return d.resolveAlias(node) + return v, nil } - return node, nil + return nil, nil } -func (d *Decoder) getMapNode(node ast.Node) (ast.MapNode, error) { - if _, ok := node.(*ast.NullNode); ok { - return nil, nil +func (d *Decoder) getMapNode(node ast.Node, isMerge bool) (ast.MapNode, error) { + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return nil, ErrExceededMaxDepth } - if anchor, ok := node.(*ast.AnchorNode); ok { - mapNode, ok := anchor.Value.(ast.MapNode) - if ok { - return mapNode, nil - } - return nil, errUnexpectedNodeType(anchor.Value.Type(), ast.MappingType, node.GetToken()) - } - if alias, ok := node.(*ast.AliasNode); ok { - aliasName := alias.Value.GetToken().Value + + switch n := node.(type) { + case ast.MapNode: + return n, nil + case *ast.AnchorNode: + anchorName := n.Name.GetToken().Value + d.anchorNodeMap[anchorName] = n.Value + return d.getMapNode(n.Value, isMerge) + case *ast.AliasNode: + aliasName := n.Value.GetToken().Value node := d.anchorNodeMap[aliasName] if node == nil { - return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName) + return nil, fmt.Errorf("cannot find anchor by alias name %s", aliasName) } - mapNode, ok := node.(ast.MapNode) - if ok { - return mapNode, nil + return d.getMapNode(node, isMerge) + case *ast.SequenceNode: + if !isMerge { + return nil, errors.ErrUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken()) } - return nil, errUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken()) - } - mapNode, ok := node.(ast.MapNode) - if !ok { - return nil, errUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken()) + var mapNodes []ast.MapNode + for _, value := range n.Values { + mapNode, err := d.getMapNode(value, false) + if err != nil { + return nil, err + } + mapNodes = append(mapNodes, mapNode) + } + return ast.SequenceMergeValue(mapNodes...), nil } - return mapNode, nil + return nil, errors.ErrUnexpectedNodeType(node.Type(), ast.MappingType, node.GetToken()) } func (d *Decoder) getArrayNode(node ast.Node) (ast.ArrayNode, error) { + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return nil, ErrExceededMaxDepth + } + if _, ok := node.(*ast.NullNode); ok { return nil, nil } @@ -455,36 +594,27 @@ func (d *Decoder) getArrayNode(node ast.Node) (ast.ArrayNode, error) { return arrayNode, nil } - return nil, errUnexpectedNodeType(anchor.Value.Type(), ast.SequenceType, node.GetToken()) + return nil, errors.ErrUnexpectedNodeType(anchor.Value.Type(), ast.SequenceType, node.GetToken()) } if alias, ok := node.(*ast.AliasNode); ok { aliasName := alias.Value.GetToken().Value node := d.anchorNodeMap[aliasName] if node == nil { - return nil, xerrors.Errorf("cannot find anchor by alias name %s", aliasName) + return nil, fmt.Errorf("cannot find anchor by alias name %s", aliasName) } arrayNode, ok := node.(ast.ArrayNode) if ok { return arrayNode, nil } - return nil, errUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken()) + return nil, errors.ErrUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken()) } arrayNode, ok := node.(ast.ArrayNode) if !ok { - return nil, errUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken()) + return nil, errors.ErrUnexpectedNodeType(node.Type(), ast.SequenceType, node.GetToken()) } return arrayNode, nil } -func (d *Decoder) fileToNode(f *ast.File) ast.Node { - for _, doc := range f.Docs { - if v := d.nodeToValue(doc.Body); v != nil { - return doc.Body - } - } - return nil -} - func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node) (reflect.Value, error) { if typ.Kind() != reflect.String { if !v.Type().ConvertibleTo(typ) { @@ -503,70 +633,34 @@ func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node) // else, fall through to the error below } } - return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken()) + return reflect.Zero(typ), errors.ErrTypeMismatch(typ, v.Type(), src.GetToken()) } return v.Convert(typ), nil } // cast value to string + var strVal string switch v.Type().Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return reflect.ValueOf(fmt.Sprint(v.Int())), nil + strVal = strconv.FormatInt(v.Int(), 10) case reflect.Float32, reflect.Float64: - return reflect.ValueOf(fmt.Sprint(v.Float())), nil + strVal = fmt.Sprint(v.Float()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return reflect.ValueOf(fmt.Sprint(v.Uint())), nil + strVal = strconv.FormatUint(v.Uint(), 10) case reflect.Bool: - return reflect.ValueOf(fmt.Sprint(v.Bool())), nil - } - if !v.Type().ConvertibleTo(typ) { - return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken()) + strVal = strconv.FormatBool(v.Bool()) + default: + if !v.Type().ConvertibleTo(typ) { + return reflect.Zero(typ), errors.ErrTypeMismatch(typ, v.Type(), src.GetToken()) + } + return v.Convert(typ), nil } - return v.Convert(typ), nil -} - -type overflowError struct { - dstType reflect.Type - srcNum string -} - -func (e *overflowError) Error() string { - return fmt.Sprintf("cannot unmarshal %s into Go value of type %s ( overflow )", e.srcNum, e.dstType) -} - -func errOverflow(dstType reflect.Type, num string) *overflowError { - return &overflowError{dstType: dstType, srcNum: num} -} - -func errTypeMismatch(dstType, srcType reflect.Type, token *token.Token) *errors.TypeError { - return &errors.TypeError{DstType: dstType, SrcType: srcType, Token: token} -} - -type unknownFieldError struct { - err error -} - -func (e *unknownFieldError) Error() string { - return e.err.Error() -} - -func errUnknownField(msg string, tk *token.Token) *unknownFieldError { - return &unknownFieldError{err: errors.ErrSyntax(msg, tk)} -} -func errUnexpectedNodeType(actual, expected ast.NodeType, tk *token.Token) error { - return errors.ErrSyntax(fmt.Sprintf("%s was used where %s is expected", actual.YAMLName(), expected.YAMLName()), tk) -} - -type duplicateKeyError struct { - err error -} - -func (e *duplicateKeyError) Error() string { - return e.err.Error() -} - -func errDuplicateKey(msg string, tk *token.Token) *duplicateKeyError { - return &duplicateKeyError{err: errors.ErrSyntax(msg, tk)} + val := reflect.ValueOf(strVal) + if val.Type() != typ { + // Handle named types, e.g., `type MyString string` + val = val.Convert(typ) + } + return val, nil } func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[string]ast.Node) error { @@ -575,7 +669,7 @@ func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[st } structFieldMap, err := structFieldMap(structType) if err != nil { - return errors.Wrapf(err, "failed to create struct field map") + return err } for j := 0; j < structType.NumField(); j++ { @@ -590,7 +684,7 @@ func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[st } if structField.IsInline { - d.deleteStructKeys(field.Type, unknownFields) + _ = d.deleteStructKeys(field.Type, unknownFields) } else { delete(unknownFields, structField.RenderName) } @@ -598,57 +692,18 @@ func (d *Decoder) deleteStructKeys(structType reflect.Type, unknownFields map[st return nil } -func (d *Decoder) lastNode(node ast.Node) ast.Node { - switch n := node.(type) { - case *ast.MappingNode: - if len(n.Values) > 0 { - return d.lastNode(n.Values[len(n.Values)-1]) - } - case *ast.MappingValueNode: - return d.lastNode(n.Value) - case *ast.SequenceNode: - if len(n.Values) > 0 { - return d.lastNode(n.Values[len(n.Values)-1]) - } - } - return node -} - func (d *Decoder) unmarshalableDocument(node ast.Node) ([]byte, error) { - var err error - node, err = d.resolveAlias(node) - if err != nil { - return nil, err - } - doc := node.String() - last := d.lastNode(node) - if last != nil && last.Type() == ast.LiteralType { - doc += "\n" - } + doc := format.FormatNodeWithResolvedAlias(node, d.anchorNodeMap) return []byte(doc), nil } -func (d *Decoder) unmarshalableText(node ast.Node) ([]byte, bool, error) { - var err error - node, err = d.resolveAlias(node) - if err != nil { - return nil, false, err - } - if node.Type() == ast.AnchorType { - node = node.(*ast.AnchorNode).Value - } - switch n := node.(type) { - case *ast.StringNode: - return []byte(n.Value), true, nil - case *ast.LiteralNode: - return []byte(n.Value.GetToken().Value), true, nil - default: - scalar, ok := n.(ast.ScalarNode) - if ok { - return []byte(fmt.Sprint(scalar.GetValue())), true, nil - } +func (d *Decoder) unmarshalableText(node ast.Node) ([]byte, bool) { + doc := format.FormatNodeWithResolvedAlias(node, d.anchorNodeMap) + var v string + if err := Unmarshal([]byte(doc), &v); err != nil { + return nil, false } - return nil, false, nil + return []byte(v), true } type jsonUnmarshaler interface { @@ -668,7 +723,7 @@ func (d *Decoder) existsTypeInCustomUnmarshalerMap(t reflect.Type) bool { return false } -func (d *Decoder) unmarshalerFromCustomUnmarshalerMap(t reflect.Type) (func(interface{}, []byte) error, bool) { +func (d *Decoder) unmarshalerFromCustomUnmarshalerMap(t reflect.Type) (func(context.Context, interface{}, []byte) error, bool) { if unmarshaler, exists := d.customUnmarshalerMap[t]; exists { return unmarshaler, exists } @@ -688,19 +743,15 @@ func (d *Decoder) canDecodeByUnmarshaler(dst reflect.Value) bool { } iface := ptrValue.Interface() switch iface.(type) { - case BytesUnmarshalerContext: - return true - case BytesUnmarshaler: - return true - case InterfaceUnmarshalerContext: - return true - case InterfaceUnmarshaler: - return true - case *time.Time: - return true - case *time.Duration: - return true - case encoding.TextUnmarshaler: + case BytesUnmarshalerContext, + BytesUnmarshaler, + InterfaceUnmarshalerContext, + InterfaceUnmarshaler, + NodeUnmarshaler, + NodeUnmarshalerContext, + *time.Time, + *time.Duration, + encoding.TextUnmarshaler: return true case jsonUnmarshaler: return d.useJSONUnmarshaler @@ -713,10 +764,10 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr if unmarshaler, exists := d.unmarshalerFromCustomUnmarshalerMap(ptrValue.Type()); exists { b, err := d.unmarshalableDocument(src) if err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") + return err } - if err := unmarshaler(ptrValue.Interface(), b); err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") + if err := unmarshaler(ctx, ptrValue.Interface(), b); err != nil { + return err } return nil } @@ -725,10 +776,10 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr if unmarshaler, ok := iface.(BytesUnmarshalerContext); ok { b, err := d.unmarshalableDocument(src) if err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") + return err } if err := unmarshaler.UnmarshalYAML(ctx, b); err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") + return err } return nil } @@ -736,10 +787,10 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr if unmarshaler, ok := iface.(BytesUnmarshaler); ok { b, err := d.unmarshalableDocument(src) if err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") + return err } if err := unmarshaler.UnmarshalYAML(b); err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") + return err } return nil } @@ -748,14 +799,14 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr if err := unmarshaler.UnmarshalYAML(ctx, func(v interface{}) error { rv := reflect.ValueOf(v) if rv.Type().Kind() != reflect.Ptr { - return errors.ErrDecodeRequiredPointerType + return ErrDecodeRequiredPointerType } if err := d.decodeValue(ctx, rv.Elem(), src); err != nil { - return errors.Wrapf(err, "failed to decode value") + return err } return nil }); err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") + return err } return nil } @@ -764,15 +815,31 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr if err := unmarshaler.UnmarshalYAML(func(v interface{}) error { rv := reflect.ValueOf(v) if rv.Type().Kind() != reflect.Ptr { - return errors.ErrDecodeRequiredPointerType + return ErrDecodeRequiredPointerType } if err := d.decodeValue(ctx, rv.Elem(), src); err != nil { - return errors.Wrapf(err, "failed to decode value") + return err } return nil }); err != nil { - return errors.Wrapf(err, "failed to UnmarshalYAML") + return err + } + return nil + } + + if unmarshaler, ok := iface.(NodeUnmarshaler); ok { + if err := unmarshaler.UnmarshalYAML(src); err != nil { + return err } + + return nil + } + + if unmarshaler, ok := iface.(NodeUnmarshalerContext); ok { + if err := unmarshaler.UnmarshalYAML(ctx, src); err != nil { + return err + } + return nil } @@ -785,13 +852,10 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr } if unmarshaler, isText := iface.(encoding.TextUnmarshaler); isText { - b, ok, err := d.unmarshalableText(src) - if err != nil { - return errors.Wrapf(err, "failed to UnmarshalText") - } + b, ok := d.unmarshalableText(src) if ok { if err := unmarshaler.UnmarshalText(b); err != nil { - return errors.Wrapf(err, "failed to UnmarshalText") + return err } return nil } @@ -801,21 +865,21 @@ func (d *Decoder) decodeByUnmarshaler(ctx context.Context, dst reflect.Value, sr if unmarshaler, ok := iface.(jsonUnmarshaler); ok { b, err := d.unmarshalableDocument(src) if err != nil { - return errors.Wrapf(err, "failed to UnmarshalJSON") + return err } jsonBytes, err := YAMLToJSON(b) if err != nil { - return errors.Wrapf(err, "failed to convert yaml to json") + return err } jsonBytes = bytes.TrimRight(jsonBytes, "\n") if err := unmarshaler.UnmarshalJSON(jsonBytes); err != nil { - return errors.Wrapf(err, "failed to UnmarshalJSON") + return err } return nil } } - return xerrors.Errorf("does not implemented Unmarshaler") + return errors.New("does not implemented Unmarshaler") } var ( @@ -823,15 +887,27 @@ var ( ) func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.Node) error { + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return ErrExceededMaxDepth + } + if !dst.IsValid() { + return nil + } + if src.Type() == ast.AnchorType { - anchorName := src.(*ast.AnchorNode).Name.GetToken().Value - if _, exists := d.anchorValueMap[anchorName]; !exists { - d.anchorValueMap[anchorName] = dst + anchor, _ := src.(*ast.AnchorNode) + anchorName := anchor.Name.GetToken().Value + if err := d.decodeValue(withAnchor(ctx, anchorName), dst, anchor.Value); err != nil { + return err } + d.anchorValueMap[anchorName] = dst + return nil } if d.canDecodeByUnmarshaler(dst) { if err := d.decodeByUnmarshaler(ctx, dst, src); err != nil { - return errors.Wrapf(err, "failed to decode by unmarshaler") + return err } return nil } @@ -848,17 +924,27 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No } v := d.createDecodableValue(dst.Type()) if err := d.decodeValue(ctx, v, src); err != nil { - return errors.Wrapf(err, "failed to decode ptr value") + return err + } + castedValue, err := d.castToAssignableValue(v, dst.Type(), src) + if err != nil { + return err } - dst.Set(d.castToAssignableValue(v, dst.Type())) + dst.Set(castedValue) case reflect.Interface: if dst.Type() == astNodeType { dst.Set(reflect.ValueOf(src)) return nil } - v := reflect.ValueOf(d.nodeToValue(src)) + srcVal, err := d.nodeToValue(ctx, src) + if err != nil { + return err + } + v := reflect.ValueOf(srcVal) if v.IsValid() { dst.Set(v) + } else { + dst.Set(reflect.Zero(valueType)) } case reflect.Map: return d.decodeMap(ctx, dst, src) @@ -875,7 +961,10 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No } return d.decodeStruct(ctx, dst, src) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - v := d.nodeToValue(src) + v, err := d.nodeToValue(ctx, src) + if err != nil { + return err + } switch vv := v.(type) { case int64: if !dst.OverflowInt(vv) { @@ -899,14 +988,17 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No return nil } } else { // couldn't be parsed as float - return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) + return errors.ErrTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) } default: - return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) + return errors.ErrTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) } - return errOverflow(valueType, fmt.Sprint(v)) + return errors.ErrOverflow(valueType, fmt.Sprint(v), src.GetToken()) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - v := d.nodeToValue(src) + v, err := d.nodeToValue(ctx, src) + if err != nil { + return err + } switch vv := v.(type) { case int64: if 0 <= vv && !dst.OverflowUint(uint64(vv)) { @@ -930,19 +1022,23 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No return nil } } else { // couldn't be parsed as float - return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) + return errors.ErrTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) } default: - return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) + return errors.ErrTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken()) } - return errOverflow(valueType, fmt.Sprint(v)) + return errors.ErrOverflow(valueType, fmt.Sprint(v), src.GetToken()) } - v := reflect.ValueOf(d.nodeToValue(src)) + srcVal, err := d.nodeToValue(ctx, src) + if err != nil { + return err + } + v := reflect.ValueOf(srcVal) if v.IsValid() { convertedValue, err := d.convertValue(v, dst.Type(), src) if err != nil { - return errors.Wrapf(err, "failed to convert value") + return err } dst.Set(convertedValue) } @@ -960,23 +1056,29 @@ func (d *Decoder) createDecodableValue(typ reflect.Type) reflect.Value { return reflect.New(typ).Elem() } -func (d *Decoder) castToAssignableValue(value reflect.Value, target reflect.Type) reflect.Value { +func (d *Decoder) castToAssignableValue(value reflect.Value, target reflect.Type, src ast.Node) (reflect.Value, error) { if target.Kind() != reflect.Ptr { - return value - } - maxTryCount := 5 - tryCount := 0 - for { - if tryCount > maxTryCount { - return value + if !value.Type().AssignableTo(target) { + return reflect.Value{}, errors.ErrTypeMismatch(target, value.Type(), src.GetToken()) } + return value, nil + } + + const maxAddrCount = 5 + + for i := 0; i < maxAddrCount; i++ { if value.Type().AssignableTo(target) { break } + if !value.CanAddr() { + break + } value = value.Addr() - tryCount++ } - return value + if !value.Type().AssignableTo(target) { + return reflect.Value{}, errors.ErrTypeMismatch(target, value.Type(), src.GetToken()) + } + return value, nil } func (d *Decoder) createDecodedNewValue( @@ -984,61 +1086,79 @@ func (d *Decoder) createDecodedNewValue( ) (reflect.Value, error) { if node.Type() == ast.AliasType { aliasName := node.(*ast.AliasNode).Value.GetToken().Value - newValue := d.anchorValueMap[aliasName] - if newValue.IsValid() { - return newValue, nil + value := d.anchorValueMap[aliasName] + if value.IsValid() { + v, err := d.castToAssignableValue(value, typ, node) + if err == nil { + return v, nil + } + } + anchor, exists := d.anchorNodeMap[aliasName] + if exists { + node = anchor } } + var newValue reflect.Value if node.Type() == ast.NullType { - return reflect.Zero(typ), nil + newValue = reflect.New(typ).Elem() + } else { + newValue = d.createDecodableValue(typ) } - newValue := d.createDecodableValue(typ) for defaultVal.Kind() == reflect.Ptr { defaultVal = defaultVal.Elem() } if defaultVal.IsValid() && defaultVal.Type().AssignableTo(newValue.Type()) { newValue.Set(defaultVal) } - if err := d.decodeValue(ctx, newValue, node); err != nil { - return newValue, errors.Wrapf(err, "failed to decode value") + if node.Type() != ast.NullType { + if err := d.decodeValue(ctx, newValue, node); err != nil { + return reflect.Value{}, err + } } - return newValue, nil + return d.castToAssignableValue(newValue, typ, node) } -func (d *Decoder) keyToNodeMap(node ast.Node, ignoreMergeKey bool, getKeyOrValueNode func(*ast.MapNodeIter) ast.Node) (map[string]ast.Node, error) { - mapNode, err := d.getMapNode(node) +func (d *Decoder) keyToNodeMap(ctx context.Context, node ast.Node, ignoreMergeKey bool, getKeyOrValueNode func(*ast.MapNodeIter) ast.Node) (map[string]ast.Node, error) { + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return nil, ErrExceededMaxDepth + } + + mapNode, err := d.getMapNode(node, false) if err != nil { - return nil, errors.Wrapf(err, "failed to get map node") + return nil, err } keyMap := map[string]struct{}{} keyToNodeMap := map[string]ast.Node{} - if mapNode == nil { - return keyToNodeMap, nil - } mapIter := mapNode.MapRange() for mapIter.Next() { keyNode := mapIter.Key() - if keyNode.Type() == ast.MergeKeyType { + if keyNode.IsMergeKey() { if ignoreMergeKey { continue } - mergeMap, err := d.keyToNodeMap(mapIter.Value(), ignoreMergeKey, getKeyOrValueNode) + mergeMap, err := d.keyToNodeMap(ctx, mapIter.Value(), ignoreMergeKey, getKeyOrValueNode) if err != nil { - return nil, errors.Wrapf(err, "failed to get keyToNodeMap by MergeKey node") + return nil, err } for k, v := range mergeMap { if err := d.validateDuplicateKey(keyMap, k, v); err != nil { - return nil, errors.Wrapf(err, "invalid struct key") + return nil, err } keyToNodeMap[k] = v } } else { - key, ok := d.nodeToValue(keyNode).(string) + keyVal, err := d.nodeToValue(ctx, keyNode) + if err != nil { + return nil, err + } + key, ok := keyVal.(string) if !ok { - return nil, errors.Wrapf(err, "failed to decode map key") + return nil, err } if err := d.validateDuplicateKey(keyMap, key, keyNode); err != nil { - return nil, errors.Wrapf(err, "invalid struct key") + return nil, err } keyToNodeMap[key] = getKeyOrValueNode(mapIter) } @@ -1046,30 +1166,33 @@ func (d *Decoder) keyToNodeMap(node ast.Node, ignoreMergeKey bool, getKeyOrValue return keyToNodeMap, nil } -func (d *Decoder) keyToKeyNodeMap(node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) { - m, err := d.keyToNodeMap(node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Key() }) +func (d *Decoder) keyToKeyNodeMap(ctx context.Context, node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) { + m, err := d.keyToNodeMap(ctx, node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Key() }) if err != nil { - return nil, errors.Wrapf(err, "failed to get keyToNodeMap") + return nil, err } return m, nil } -func (d *Decoder) keyToValueNodeMap(node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) { - m, err := d.keyToNodeMap(node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Value() }) +func (d *Decoder) keyToValueNodeMap(ctx context.Context, node ast.Node, ignoreMergeKey bool) (map[string]ast.Node, error) { + m, err := d.keyToNodeMap(ctx, node, ignoreMergeKey, func(nodeMap *ast.MapNodeIter) ast.Node { return nodeMap.Value() }) if err != nil { - return nil, errors.Wrapf(err, "failed to get keyToNodeMap") + return nil, err } return m, nil } func (d *Decoder) setDefaultValueIfConflicted(v reflect.Value, fieldMap StructFieldMap) error { + for v.Type().Kind() == reflect.Ptr { + v = v.Elem() + } typ := v.Type() if typ.Kind() != reflect.Struct { return nil } embeddedStructFieldMap, err := structFieldMap(typ) if err != nil { - return errors.Wrapf(err, "failed to get struct field map by embedded type") + return err } for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) @@ -1098,17 +1221,20 @@ var allowedTimestampFormats = []string{ "2006-1-2", // date only } -func (d *Decoder) castToTime(src ast.Node) (time.Time, error) { +func (d *Decoder) castToTime(ctx context.Context, src ast.Node) (time.Time, error) { if src == nil { return time.Time{}, nil } - v := d.nodeToValue(src) + v, err := d.nodeToValue(ctx, src) + if err != nil { + return time.Time{}, err + } if t, ok := v.(time.Time); ok { return t, nil } s, ok := v.(string) if !ok { - return time.Time{}, errTypeMismatch(reflect.TypeOf(time.Time{}), reflect.TypeOf(v), src.GetToken()) + return time.Time{}, errors.ErrTypeMismatch(reflect.TypeOf(time.Time{}), reflect.TypeOf(v), src.GetToken()) } for _, format := range allowedTimestampFormats { t, err := time.Parse(format, s) @@ -1122,37 +1248,40 @@ func (d *Decoder) castToTime(src ast.Node) (time.Time, error) { } func (d *Decoder) decodeTime(ctx context.Context, dst reflect.Value, src ast.Node) error { - t, err := d.castToTime(src) + t, err := d.castToTime(ctx, src) if err != nil { - return errors.Wrapf(err, "failed to convert to time") + return err } dst.Set(reflect.ValueOf(t)) return nil } -func (d *Decoder) castToDuration(src ast.Node) (time.Duration, error) { +func (d *Decoder) castToDuration(ctx context.Context, src ast.Node) (time.Duration, error) { if src == nil { return 0, nil } - v := d.nodeToValue(src) + v, err := d.nodeToValue(ctx, src) + if err != nil { + return 0, err + } if t, ok := v.(time.Duration); ok { return t, nil } s, ok := v.(string) if !ok { - return 0, errTypeMismatch(reflect.TypeOf(time.Duration(0)), reflect.TypeOf(v), src.GetToken()) + return 0, errors.ErrTypeMismatch(reflect.TypeOf(time.Duration(0)), reflect.TypeOf(v), src.GetToken()) } t, err := time.ParseDuration(s) if err != nil { - return 0, errors.Wrapf(err, "failed to parse duration") + return 0, err } return t, nil } func (d *Decoder) decodeDuration(ctx context.Context, dst reflect.Value, src ast.Node) error { - t, err := d.castToDuration(src) + t, err := d.castToDuration(ctx, src) if err != nil { - return errors.Wrapf(err, "failed to convert to duration") + return err } dst.Set(reflect.ValueOf(t)) return nil @@ -1160,18 +1289,15 @@ func (d *Decoder) decodeDuration(ctx context.Context, dst reflect.Value, src ast // getMergeAliasName support single alias only func (d *Decoder) getMergeAliasName(src ast.Node) string { - mapNode, err := d.getMapNode(src) + mapNode, err := d.getMapNode(src, true) if err != nil { return "" } - if mapNode == nil { - return "" - } mapIter := mapNode.MapRange() for mapIter.Next() { key := mapIter.Key() value := mapIter.Value() - if key.Type() == ast.MergeKeyType && value.Type() == ast.AliasType { + if key.IsMergeKey() && value.Type() == ast.AliasType { return value.(*ast.AliasNode).Value.GetToken().Value } } @@ -1182,6 +1308,12 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N if src == nil { return nil } + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return ErrExceededMaxDepth + } + structType := dst.Type() srcValue := reflect.ValueOf(src) srcType := srcValue.Type() @@ -1196,18 +1328,18 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N } structFieldMap, err := structFieldMap(structType) if err != nil { - return errors.Wrapf(err, "failed to create struct field map") + return err } ignoreMergeKey := structFieldMap.hasMergeProperty() - keyToNodeMap, err := d.keyToValueNodeMap(src, ignoreMergeKey) + keyToNodeMap, err := d.keyToValueNodeMap(ctx, src, ignoreMergeKey) if err != nil { - return errors.Wrapf(err, "failed to get keyToValueNodeMap") + return err } var unknownFields map[string]ast.Node if d.disallowUnknownField { - unknownFields, err = d.keyToKeyNodeMap(src, ignoreMergeKey) + unknownFields, err = d.keyToKeyNodeMap(ctx, src, ignoreMergeKey) if err != nil { - return errors.Wrapf(err, "failed to get keyToKeyNodeMap") + return err } } @@ -1226,13 +1358,17 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N if aliasName != "" { newFieldValue := d.anchorValueMap[aliasName] if newFieldValue.IsValid() { - fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type())) + value, err := d.castToAssignableValue(newFieldValue, fieldValue.Type(), d.anchorNodeMap[aliasName]) + if err != nil { + return err + } + fieldValue.Set(value) } } continue } if !fieldValue.CanSet() { - return xerrors.Errorf("cannot set embedded type as unexported field %s.%s", field.PkgPath, field.Name) + return fmt.Errorf("cannot set embedded type as unexported field %s.%s", field.PkgPath, field.Name) } if fieldValue.Type().Kind() == reflect.Ptr && src.Type() == ast.NullType { // set nil value to pointer @@ -1247,7 +1383,7 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N newFieldValue, err := d.createDecodedNewValue(ctx, fieldValue.Type(), fieldValue, mapNode) if d.disallowUnknownField { if err := d.deleteStructKeys(fieldValue.Type(), unknownFields); err != nil { - return errors.Wrapf(err, "cannot delete struct keys") + return err } } @@ -1256,7 +1392,7 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N continue } var te *errors.TypeError - if xerrors.As(err, &te) { + if errors.As(err, &te) { if te.StructFieldName != nil { fieldName := fmt.Sprintf("%s.%s", structType.Name(), *te.StructFieldName) te.StructFieldName = &fieldName @@ -1271,8 +1407,8 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N } continue } - d.setDefaultValueIfConflicted(newFieldValue, structFieldMap) - fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type())) + _ = d.setDefaultValueIfConflicted(newFieldValue, structFieldMap) + fieldValue.Set(newFieldValue) continue } v, exists := keyToNodeMap[structField.RenderName] @@ -1292,7 +1428,7 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N continue } var te *errors.TypeError - if xerrors.As(err, &te) { + if errors.As(err, &te) { fieldName := fmt.Sprintf("%s.%s", structType.Name(), field.Name) te.StructFieldName = &fieldName foundErr = te @@ -1301,17 +1437,26 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N } continue } - fieldValue.Set(d.castToAssignableValue(newFieldValue, fieldValue.Type())) + fieldValue.Set(newFieldValue) } if foundErr != nil { - return errors.Wrapf(foundErr, "failed to decode value") + return foundErr } // Ignore unknown fields when parsing an inline struct (recognized by a nil token). // Unknown fields are expected (they could be fields from the parent struct). if len(unknownFields) != 0 && d.disallowUnknownField && src.GetToken() != nil { for key, node := range unknownFields { - return errUnknownField(fmt.Sprintf(`unknown field "%s"`, key), node.GetToken()) + var ok bool + for _, prefix := range d.allowedFieldPrefixes { + if strings.HasPrefix(key, prefix) { + ok = true + break + } + } + if !ok { + return errors.ErrUnknownField(fmt.Sprintf(`unknown field "%s"`, key), node.GetToken()) + } } } @@ -1332,7 +1477,10 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N node, exists := keyToNodeMap[structField.RenderName] if exists { // TODO: to make FieldError message cutomizable - return errors.ErrSyntax(fmt.Sprintf("%s", err), node.GetToken()) + return errors.ErrSyntax( + fmt.Sprintf("%s", err), + d.getParentMapTokenIfExistsForValidationError(node.Type(), node.GetToken()), + ) } else if t := src.GetToken(); t != nil && t.Prev != nil && t.Prev.Prev != nil { // A missing required field will not be in the keyToNodeMap // the error needs to be associated with the parent of the source node @@ -1346,10 +1494,47 @@ func (d *Decoder) decodeStruct(ctx context.Context, dst reflect.Value, src ast.N return nil } +// getParentMapTokenIfExists if the NodeType is a container type such as MappingType or SequenceType, +// it is necessary to return the parent MapNode's colon token to represent the entire container. +func (d *Decoder) getParentMapTokenIfExistsForValidationError(typ ast.NodeType, tk *token.Token) *token.Token { + if tk == nil { + return nil + } + if typ == ast.MappingType { + // map: + // key: value + // ^ current token ( colon ) + if tk.Prev == nil { + return tk + } + key := tk.Prev + if key.Prev == nil { + return tk + } + return key.Prev + } + if typ == ast.SequenceType { + // map: + // - value + // ^ current token ( sequence entry ) + if tk.Prev == nil { + return tk + } + return tk.Prev + } + return tk +} + func (d *Decoder) decodeArray(ctx context.Context, dst reflect.Value, src ast.Node) error { + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return ErrExceededMaxDepth + } + arrayNode, err := d.getArrayNode(src) if err != nil { - return errors.Wrapf(err, "failed to get array node") + return err } if arrayNode == nil { return nil @@ -1373,23 +1558,28 @@ func (d *Decoder) decodeArray(ctx context.Context, dst reflect.Value, src ast.No foundErr = err } continue - } else { - arrayValue.Index(idx).Set(d.castToAssignableValue(dstValue, elemType)) } + arrayValue.Index(idx).Set(dstValue) } idx++ } dst.Set(arrayValue) if foundErr != nil { - return errors.Wrapf(foundErr, "failed to decode value") + return foundErr } return nil } func (d *Decoder) decodeSlice(ctx context.Context, dst reflect.Value, src ast.Node) error { + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return ErrExceededMaxDepth + } + arrayNode, err := d.getArrayNode(src) if err != nil { - return errors.Wrapf(err, "failed to get array node") + return err } if arrayNode == nil { return nil @@ -1414,22 +1604,25 @@ func (d *Decoder) decodeSlice(ctx context.Context, dst reflect.Value, src ast.No } continue } - sliceValue = reflect.Append(sliceValue, d.castToAssignableValue(dstValue, elemType)) + sliceValue = reflect.Append(sliceValue, dstValue) } dst.Set(sliceValue) if foundErr != nil { - return errors.Wrapf(foundErr, "failed to decode value") + return foundErr } return nil } func (d *Decoder) decodeMapItem(ctx context.Context, dst *MapItem, src ast.Node) error { - mapNode, err := d.getMapNode(src) - if err != nil { - return errors.Wrapf(err, "failed to get map node") + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return ErrExceededMaxDepth } - if mapNode == nil { - return nil + + mapNode, err := d.getMapNode(src, isMerge(ctx)) + if err != nil { + return err } mapIter := mapNode.MapRange() if !mapIter.Next() { @@ -1437,16 +1630,21 @@ func (d *Decoder) decodeMapItem(ctx context.Context, dst *MapItem, src ast.Node) } key := mapIter.Key() value := mapIter.Value() - if key.Type() == ast.MergeKeyType { - if err := d.decodeMapItem(ctx, dst, value); err != nil { - return errors.Wrapf(err, "failed to decode map with merge key") + if key.IsMergeKey() { + if err := d.decodeMapItem(withMerge(ctx), dst, value); err != nil { + return err } return nil } - *dst = MapItem{ - Key: d.nodeToValue(key), - Value: d.nodeToValue(value), + k, err := d.nodeToValue(ctx, key) + if err != nil { + return err + } + v, err := d.nodeToValue(ctx, value) + if err != nil { + return err } + *dst = MapItem{Key: k, Value: v} return nil } @@ -1455,9 +1653,9 @@ func (d *Decoder) validateDuplicateKey(keyMap map[string]struct{}, key interface if !ok { return nil } - if d.disallowDuplicateKey { + if !d.allowDuplicateMapKey { if _, exists := keyMap[k]; exists { - return errDuplicateKey(fmt.Sprintf(`duplicate key "%s"`, k), keyNode.GetToken()) + return errors.ErrDuplicateKey(fmt.Sprintf(`duplicate key "%s"`, k), keyNode.GetToken()) } } keyMap[k] = struct{}{} @@ -1465,12 +1663,15 @@ func (d *Decoder) validateDuplicateKey(keyMap map[string]struct{}, key interface } func (d *Decoder) decodeMapSlice(ctx context.Context, dst *MapSlice, src ast.Node) error { - mapNode, err := d.getMapNode(src) - if err != nil { - return errors.Wrapf(err, "failed to get map node") + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return ErrExceededMaxDepth } - if mapNode == nil { - return nil + + mapNode, err := d.getMapNode(src, isMerge(ctx)) + if err != nil { + return err } mapSlice := MapSlice{} mapIter := mapNode.MapRange() @@ -1478,39 +1679,46 @@ func (d *Decoder) decodeMapSlice(ctx context.Context, dst *MapSlice, src ast.Nod for mapIter.Next() { key := mapIter.Key() value := mapIter.Value() - if key.Type() == ast.MergeKeyType { + if key.IsMergeKey() { var m MapSlice - if err := d.decodeMapSlice(ctx, &m, value); err != nil { - return errors.Wrapf(err, "failed to decode map with merge key") + if err := d.decodeMapSlice(withMerge(ctx), &m, value); err != nil { + return err } for _, v := range m { if err := d.validateDuplicateKey(keyMap, v.Key, value); err != nil { - return errors.Wrapf(err, "invalid map key") + return err } mapSlice = append(mapSlice, v) } continue } - k := d.nodeToValue(key) + k, err := d.nodeToValue(ctx, key) + if err != nil { + return err + } if err := d.validateDuplicateKey(keyMap, k, key); err != nil { - return errors.Wrapf(err, "invalid map key") + return err } - mapSlice = append(mapSlice, MapItem{ - Key: k, - Value: d.nodeToValue(value), - }) + v, err := d.nodeToValue(ctx, value) + if err != nil { + return err + } + mapSlice = append(mapSlice, MapItem{Key: k, Value: v}) } *dst = mapSlice return nil } func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node) error { - mapNode, err := d.getMapNode(src) - if err != nil { - return errors.Wrapf(err, "failed to get map node") + d.stepIn() + defer d.stepOut() + if d.isExceededMaxDepth() { + return ErrExceededMaxDepth } - if mapNode == nil { - return nil + + mapNode, err := d.getMapNode(src, isMerge(ctx)) + if err != nil { + return err } mapType := dst.Type() mapValue := reflect.MakeMap(mapType) @@ -1522,14 +1730,14 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node for mapIter.Next() { key := mapIter.Key() value := mapIter.Value() - if key.Type() == ast.MergeKeyType { - if err := d.decodeMap(ctx, dst, value); err != nil { - return errors.Wrapf(err, "failed to decode map with merge key") + if key.IsMergeKey() { + if err := d.decodeMap(withMerge(ctx), dst, value); err != nil { + return err } iter := dst.MapRange() for iter.Next() { if err := d.validateDuplicateKey(keyMap, iter.Key(), value); err != nil { - return errors.Wrapf(err, "invalid map key") + return err } mapValue.SetMapIndex(iter.Key(), iter.Value()) } @@ -1539,10 +1747,14 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node k := d.createDecodableValue(keyType) if d.canDecodeByUnmarshaler(k) { if err := d.decodeByUnmarshaler(ctx, k, key); err != nil { - return errors.Wrapf(err, "failed to decode by unmarshaler") + return err } } else { - k = reflect.ValueOf(d.nodeToValue(key)) + keyVal, err := d.nodeToValue(ctx, key) + if err != nil { + return err + } + k = reflect.ValueOf(keyVal) if k.IsValid() && k.Type().ConvertibleTo(keyType) { k = k.Convert(keyType) } @@ -1550,7 +1762,7 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node if k.IsValid() { if err := d.validateDuplicateKey(keyMap, k.Interface(), key); err != nil { - return errors.Wrapf(err, "invalid map key") + return err } } if valueType.Kind() == reflect.Ptr && value.Type() == ast.NullType { @@ -1566,14 +1778,20 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node } if !k.IsValid() { // expect nil key - mapValue.SetMapIndex(d.createDecodableValue(keyType), d.castToAssignableValue(dstValue, valueType)) + mapValue.SetMapIndex(d.createDecodableValue(keyType), dstValue) continue } - mapValue.SetMapIndex(k, d.castToAssignableValue(dstValue, valueType)) + if keyType.Kind() != k.Kind() { + return errors.ErrSyntax( + fmt.Sprintf("cannot convert %q type to %q type", k.Kind(), keyType.Kind()), + key.GetToken(), + ) + } + mapValue.SetMapIndex(k, dstValue) } dst.Set(mapValue) if foundErr != nil { - return errors.Wrapf(foundErr, "failed to decode value") + return foundErr } return nil } @@ -1581,7 +1799,7 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node func (d *Decoder) fileToReader(file string) (io.Reader, error) { reader, err := os.Open(file) if err != nil { - return nil, errors.Wrapf(err, "failed to open file") + return nil, err } return reader, nil } @@ -1601,7 +1819,7 @@ func (d *Decoder) readersUnderDir(dir string) ([]io.Reader, error) { pattern := fmt.Sprintf("%s/*", dir) matches, err := filepath.Glob(pattern) if err != nil { - return nil, errors.Wrapf(err, "failed to get files by %s", pattern) + return nil, err } readers := []io.Reader{} for _, match := range matches { @@ -1610,7 +1828,7 @@ func (d *Decoder) readersUnderDir(dir string) ([]io.Reader, error) { } reader, err := d.fileToReader(match) if err != nil { - return nil, errors.Wrapf(err, "failed to get reader") + return nil, err } readers = append(readers, reader) } @@ -1619,32 +1837,32 @@ func (d *Decoder) readersUnderDir(dir string) ([]io.Reader, error) { func (d *Decoder) readersUnderDirRecursive(dir string) ([]io.Reader, error) { readers := []io.Reader{} - if err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { + if err := filepath.Walk(dir, func(path string, info os.FileInfo, _ error) error { if !d.isYAMLFile(path) { return nil } - reader, err := d.fileToReader(path) - if err != nil { - return errors.Wrapf(err, "failed to get reader") + reader, readerErr := d.fileToReader(path) + if readerErr != nil { + return readerErr } readers = append(readers, reader) return nil }); err != nil { - return nil, errors.Wrapf(err, "interrupt walk in %s", dir) + return nil, err } return readers, nil } -func (d *Decoder) resolveReference() error { +func (d *Decoder) resolveReference(ctx context.Context) error { for _, opt := range d.opts { if err := opt(d); err != nil { - return errors.Wrapf(err, "failed to exec option") + return err } } for _, file := range d.referenceFiles { reader, err := d.fileToReader(file) if err != nil { - return errors.Wrapf(err, "failed to get reader") + return err } d.referenceReaders = append(d.referenceReaders, reader) } @@ -1652,13 +1870,13 @@ func (d *Decoder) resolveReference() error { if !d.isRecursiveDir { readers, err := d.readersUnderDir(dir) if err != nil { - return errors.Wrapf(err, "failed to get readers from under the %s", dir) + return err } d.referenceReaders = append(d.referenceReaders, readers...) } else { readers, err := d.readersUnderDirRecursive(dir) if err != nil { - return errors.Wrapf(err, "failed to get readers from under the %s", dir) + return err } d.referenceReaders = append(d.referenceReaders, readers...) } @@ -1666,32 +1884,46 @@ func (d *Decoder) resolveReference() error { for _, reader := range d.referenceReaders { bytes, err := io.ReadAll(reader) if err != nil { - return errors.Wrapf(err, "failed to read buffer") + return err } // assign new anchor definition to anchorMap - if _, err := d.parse(bytes); err != nil { - return errors.Wrapf(err, "failed to decode") + if _, err := d.parse(ctx, bytes); err != nil { + return err } } d.isResolvedReference = true return nil } -func (d *Decoder) parse(bytes []byte) (*ast.File, error) { +func (d *Decoder) parse(ctx context.Context, bytes []byte) (*ast.File, error) { var parseMode parser.Mode if d.toCommentMap != nil { parseMode = parser.ParseComments } - f, err := parser.ParseBytes(bytes, parseMode) + var opts []parser.Option + if d.allowDuplicateMapKey { + opts = append(opts, parser.AllowDuplicateMapKey()) + } + f, err := parser.ParseBytes(bytes, parseMode, opts...) if err != nil { - return nil, errors.Wrapf(err, "failed to parse yaml") + return nil, err } normalizedFile := &ast.File{} for _, doc := range f.Docs { // try to decode ast.Node to value and map anchor value to anchorMap - if v := d.nodeToValue(doc.Body); v != nil { + v, err := d.nodeToValue(ctx, doc.Body) + if err != nil { + return nil, err + } + if v != nil || (doc.Body != nil && doc.Body.Type() == ast.NullType) { normalizedFile.Docs = append(normalizedFile.Docs, doc) + cm := CommentMap{} + maps.Copy(cm, d.toCommentMap) + d.commentMaps = append(d.commentMaps, cm) + } + for k := range d.toCommentMap { + delete(d.toCommentMap, k) } } return normalizedFile, nil @@ -1701,25 +1933,34 @@ func (d *Decoder) isInitialized() bool { return d.parsedFile != nil } -func (d *Decoder) decodeInit() error { +func (d *Decoder) decodeInit(ctx context.Context) error { if !d.isResolvedReference { - if err := d.resolveReference(); err != nil { - return errors.Wrapf(err, "failed to resolve reference") + if err := d.resolveReference(ctx); err != nil { + return err } } var buf bytes.Buffer if _, err := io.Copy(&buf, d.reader); err != nil { - return errors.Wrapf(err, "failed to copy from reader") + return err } - file, err := d.parse(buf.Bytes()) + file, err := d.parse(ctx, buf.Bytes()) if err != nil { - return errors.Wrapf(err, "failed to decode") + return err } d.parsedFile = file return nil } func (d *Decoder) decode(ctx context.Context, v reflect.Value) error { + d.decodeDepth = 0 + d.anchorValueMap = make(map[string]reflect.Value) + if len(d.parsedFile.Docs) == 0 { + // empty document. + dst := v.Elem() + if dst.IsValid() { + dst.Set(reflect.Zero(dst.Type())) + } + } if len(d.parsedFile.Docs) <= d.streamIndex { return io.EOF } @@ -1727,8 +1968,11 @@ func (d *Decoder) decode(ctx context.Context, v reflect.Value) error { if body == nil { return nil } + if len(d.commentMaps) > d.streamIndex { + maps.Copy(d.toCommentMap, d.commentMaps[d.streamIndex]) + } if err := d.decodeValue(ctx, v.Elem(), body); err != nil { - return errors.Wrapf(err, "failed to decode value") + return err } d.streamIndex++ return nil @@ -1747,26 +1991,20 @@ func (d *Decoder) Decode(v interface{}) error { // and stores it in the value pointed to by v with context.Context. func (d *Decoder) DecodeContext(ctx context.Context, v interface{}) error { rv := reflect.ValueOf(v) - if rv.Type().Kind() != reflect.Ptr { - return errors.ErrDecodeRequiredPointerType + if !rv.IsValid() || rv.Type().Kind() != reflect.Ptr { + return ErrDecodeRequiredPointerType } if d.isInitialized() { if err := d.decode(ctx, rv); err != nil { - if err == io.EOF { - return err - } - return errors.Wrapf(err, "failed to decode") + return err } return nil } - if err := d.decodeInit(); err != nil { - return errors.Wrapf(err, "failed to decodeInit") + if err := d.decodeInit(ctx); err != nil { + return err } if err := d.decode(ctx, rv); err != nil { - if err == io.EOF { - return err - } - return errors.Wrapf(err, "failed to decode") + return err } return nil } @@ -1780,17 +2018,19 @@ func (d *Decoder) DecodeFromNode(node ast.Node, v interface{}) error { func (d *Decoder) DecodeFromNodeContext(ctx context.Context, node ast.Node, v interface{}) error { rv := reflect.ValueOf(v) if rv.Type().Kind() != reflect.Ptr { - return errors.ErrDecodeRequiredPointerType + return ErrDecodeRequiredPointerType } if !d.isInitialized() { - if err := d.decodeInit(); err != nil { - return errors.Wrapf(err, "failed to decodInit") + if err := d.decodeInit(ctx); err != nil { + return err } } // resolve references to the anchor on the same file - d.nodeToValue(node) + if _, err := d.nodeToValue(ctx, node); err != nil { + return err + } if err := d.decodeValue(ctx, rv.Elem(), node); err != nil { - return errors.Wrapf(err, "failed to decode value") + return err } return nil } diff --git a/vendor/github.com/goccy/go-yaml/encode.go b/vendor/github.com/goccy/go-yaml/encode.go index 3b9b2981..e810608c 100644 --- a/vendor/github.com/goccy/go-yaml/encode.go +++ b/vendor/github.com/goccy/go-yaml/encode.go @@ -17,7 +17,6 @@ import ( "github.com/goccy/go-yaml/parser" "github.com/goccy/go-yaml/printer" "github.com/goccy/go-yaml/token" - "golang.org/x/xerrors" ) const ( @@ -29,24 +28,29 @@ const ( type Encoder struct { writer io.Writer opts []EncodeOption - indent int - indentSequence bool singleQuote bool isFlowStyle bool isJSONStyle bool useJSONMarshaler bool + enableSmartAnchor bool + aliasRefToName map[uintptr]string + anchorRefToName map[uintptr]string + anchorNameMap map[string]struct{} anchorCallback func(*ast.AnchorNode, interface{}) error - anchorPtrToNameMap map[uintptr]string - customMarshalerMap map[reflect.Type]func(interface{}) ([]byte, error) + customMarshalerMap map[reflect.Type]func(context.Context, interface{}) ([]byte, error) + omitZero bool + omitEmpty bool + autoInt bool useLiteralStyleIfMultiline bool commentMap map[*Path][]*Comment written bool - line int - column int - offset int - indentNum int - indentLevel int + line int + column int + offset int + indentNum int + indentLevel int + indentSequence bool } // NewEncoder returns a new encoder that writes to w. @@ -55,12 +59,14 @@ func NewEncoder(w io.Writer, opts ...EncodeOption) *Encoder { return &Encoder{ writer: w, opts: opts, - indent: DefaultIndentSpaces, - anchorPtrToNameMap: map[uintptr]string{}, - customMarshalerMap: map[reflect.Type]func(interface{}) ([]byte, error){}, + customMarshalerMap: map[reflect.Type]func(context.Context, interface{}) ([]byte, error){}, line: 1, column: 1, offset: 0, + indentNum: DefaultIndentSpaces, + anchorRefToName: make(map[uintptr]string), + anchorNameMap: make(map[string]struct{}), + aliasRefToName: make(map[uintptr]string), } } @@ -84,19 +90,19 @@ func (e *Encoder) Encode(v interface{}) error { func (e *Encoder) EncodeContext(ctx context.Context, v interface{}) error { node, err := e.EncodeToNodeContext(ctx, v) if err != nil { - return errors.Wrapf(err, "failed to encode to node") + return err } if err := e.setCommentByCommentMap(node); err != nil { - return errors.Wrapf(err, "failed to set comment by comment map") + return err } if !e.written { e.written = true } else { // write document separator - e.writer.Write([]byte("---\n")) + _, _ = e.writer.Write([]byte("---\n")) } var p printer.Printer - e.writer.Write(p.PrintNode(node)) + _, _ = e.writer.Write(p.PrintNode(node)) return nil } @@ -109,12 +115,19 @@ func (e *Encoder) EncodeToNode(v interface{}) (ast.Node, error) { func (e *Encoder) EncodeToNodeContext(ctx context.Context, v interface{}) (ast.Node, error) { for _, opt := range e.opts { if err := opt(e); err != nil { - return nil, errors.Wrapf(err, "failed to run option for encoder") + return nil, err } } + if e.enableSmartAnchor { + // during the first encoding, store all mappings between alias addresses and their names. + if _, err := e.encodeValue(ctx, reflect.ValueOf(v), 1); err != nil { + return nil, err + } + e.clearSmartAnchorRef() + } node, err := e.encodeValue(ctx, reflect.ValueOf(v), 1) if err != nil { - return nil, errors.Wrapf(err, "failed to encode value") + return nil, err } return node, nil } @@ -126,7 +139,7 @@ func (e *Encoder) setCommentByCommentMap(node ast.Node) error { for path, comments := range e.commentMap { n, err := path.FilterNode(node) if err != nil { - return errors.Wrapf(err, "failed to filter node") + return err } if n == nil { continue @@ -140,15 +153,15 @@ func (e *Encoder) setCommentByCommentMap(node ast.Node) error { switch comment.Position { case CommentHeadPosition: if err := e.setHeadComment(node, n, commentGroup); err != nil { - return errors.Wrapf(err, "failed to set head comment") + return err } case CommentLinePosition: if err := e.setLineComment(node, n, commentGroup); err != nil { - return errors.Wrapf(err, "failed to set line comment") + return err } case CommentFootPosition: if err := e.setFootComment(node, n, commentGroup); err != nil { - return errors.Wrapf(err, "failed to set foot comment") + return err } default: return ErrUnknownCommentPositionType @@ -166,11 +179,11 @@ func (e *Encoder) setHeadComment(node ast.Node, filtered ast.Node, comment *ast. switch p := parent.(type) { case *ast.MappingValueNode: if err := p.SetComment(comment); err != nil { - return errors.Wrapf(err, "failed to set comment") + return err } case *ast.MappingNode: if err := p.SetComment(comment); err != nil { - return errors.Wrapf(err, "failed to set comment") + return err } case *ast.SequenceNode: if len(p.ValueHeadComments) == 0 { @@ -196,11 +209,11 @@ func (e *Encoder) setLineComment(node ast.Node, filtered ast.Node, comment *ast. // Line comment cannot be set for mapping value node. // It should probably be set for the parent map node if err := e.setLineCommentToParentMapNode(node, filtered, comment); err != nil { - return errors.Wrapf(err, "failed to set line comment to parent node") + return err } default: if err := filtered.SetComment(comment); err != nil { - return errors.Wrapf(err, "failed to set comment") + return err } } return nil @@ -214,11 +227,11 @@ func (e *Encoder) setLineCommentToParentMapNode(node ast.Node, filtered ast.Node switch p := parent.(type) { case *ast.MappingValueNode: if err := p.Key.SetComment(comment); err != nil { - return errors.Wrapf(err, "failed to set comment") + return err } case *ast.MappingNode: if err := p.SetComment(comment); err != nil { - return errors.Wrapf(err, "failed to set comment") + return err } default: return ErrUnsupportedLinePositionType(parent) @@ -247,7 +260,7 @@ func (e *Encoder) setFootComment(node ast.Node, filtered ast.Node, comment *ast. func (e *Encoder) encodeDocument(doc []byte) (ast.Node, error) { f, err := parser.ParseBytes(doc, 0) if err != nil { - return nil, errors.Wrapf(err, "failed to parse yaml") + return nil, err } for _, docNode := range f.Docs { if docNode.Body != nil { @@ -288,7 +301,7 @@ func (e *Encoder) existsTypeInCustomMarshalerMap(t reflect.Type) bool { return false } -func (e *Encoder) marshalerFromCustomMarshalerMap(t reflect.Type) (func(interface{}) ([]byte, error), bool) { +func (e *Encoder) marshalerFromCustomMarshalerMap(t reflect.Type) (func(context.Context, interface{}) ([]byte, error), bool) { if marshaler, exists := e.customMarshalerMap[t]; exists { return marshaler, exists } @@ -318,7 +331,7 @@ func (e *Encoder) canEncodeByMarshaler(v reflect.Value) bool { return true case InterfaceMarshaler: return true - case time.Time: + case time.Time, *time.Time: return true case time.Duration: return true @@ -334,13 +347,13 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column iface := v.Interface() if marshaler, exists := e.marshalerFromCustomMarshalerMap(v.Type()); exists { - doc, err := marshaler(iface) + doc, err := marshaler(ctx, iface) if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalYAML") + return nil, err } node, err := e.encodeDocument(doc) if err != nil { - return nil, errors.Wrapf(err, "failed to encode document") + return nil, err } return node, nil } @@ -348,11 +361,11 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column if marshaler, ok := iface.(BytesMarshalerContext); ok { doc, err := marshaler.MarshalYAML(ctx) if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalYAML") + return nil, err } node, err := e.encodeDocument(doc) if err != nil { - return nil, errors.Wrapf(err, "failed to encode document") + return nil, err } return node, nil } @@ -360,11 +373,11 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column if marshaler, ok := iface.(BytesMarshaler); ok { doc, err := marshaler.MarshalYAML() if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalYAML") + return nil, err } node, err := e.encodeDocument(doc) if err != nil { - return nil, errors.Wrapf(err, "failed to encode document") + return nil, err } return node, nil } @@ -372,7 +385,7 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column if marshaler, ok := iface.(InterfaceMarshalerContext); ok { marshalV, err := marshaler.MarshalYAML(ctx) if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalYAML") + return nil, err } return e.encodeValue(ctx, reflect.ValueOf(marshalV), column) } @@ -380,7 +393,7 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column if marshaler, ok := iface.(InterfaceMarshaler); ok { marshalV, err := marshaler.MarshalYAML() if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalYAML") + return nil, err } return e.encodeValue(ctx, reflect.ValueOf(marshalV), column) } @@ -388,20 +401,21 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column if t, ok := iface.(time.Time); ok { return e.encodeTime(t, column), nil } + // Handle *time.Time explicitly since it implements TextMarshaler and shouldn't be treated as plain text + if t, ok := iface.(*time.Time); ok && t != nil { + return e.encodeTime(*t, column), nil + } if t, ok := iface.(time.Duration); ok { return e.encodeDuration(t, column), nil } if marshaler, ok := iface.(encoding.TextMarshaler); ok { - doc, err := marshaler.MarshalText() + text, err := marshaler.MarshalText() if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalText") - } - node, err := e.encodeDocument(doc) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode document") + return nil, err } + node := e.encodeString(string(text), column) return node, nil } @@ -409,21 +423,21 @@ func (e *Encoder) encodeByMarshaler(ctx context.Context, v reflect.Value, column if marshaler, ok := iface.(jsonMarshaler); ok { jsonBytes, err := marshaler.MarshalJSON() if err != nil { - return nil, errors.Wrapf(err, "failed to MarshalJSON") + return nil, err } doc, err := JSONToYAML(jsonBytes) if err != nil { - return nil, errors.Wrapf(err, "failed to convert json to yaml") + return nil, err } node, err := e.encodeDocument(doc) if err != nil { - return nil, errors.Wrapf(err, "failed to encode document") + return nil, err } return node, nil } } - return nil, xerrors.Errorf("does not implemented Marshaler") + return nil, errors.New("does not implemented Marshaler") } func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int) (ast.Node, error) { @@ -433,7 +447,7 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int) if e.canEncodeByMarshaler(v) { node, err := e.encodeByMarshaler(ctx, v, column) if err != nil { - return nil, errors.Wrapf(err, "failed to encode by marshaler") + return nil, err } return node, nil } @@ -447,12 +461,8 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int) case reflect.Float64: return e.encodeFloat(v.Float(), 64), nil case reflect.Ptr: - anchorName := e.anchorPtrToNameMap[v.Pointer()] - if anchorName != "" { - aliasName := anchorName - alias := ast.Alias(token.New("*", "*", e.pos(column))) - alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) - return alias, nil + if value := e.encodePtrAnchor(v, column); value != nil { + return value, nil } return e.encodeValue(ctx, v.Elem(), column) case reflect.Interface: @@ -465,6 +475,9 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int) if mapSlice, ok := v.Interface().(MapSlice); ok { return e.encodeMapSlice(ctx, mapSlice, column) } + if value := e.encodePtrAnchor(v, column); value != nil { + return value, nil + } return e.encodeSlice(ctx, v) case reflect.Array: return e.encodeArray(ctx, v) @@ -479,12 +492,27 @@ func (e *Encoder) encodeValue(ctx context.Context, v reflect.Value, column int) } return e.encodeStruct(ctx, v, column) case reflect.Map: - return e.encodeMap(ctx, v, column), nil + if value := e.encodePtrAnchor(v, column); value != nil { + return value, nil + } + return e.encodeMap(ctx, v, column) default: - return nil, xerrors.Errorf("unknown value type %s", v.Type().String()) + return nil, fmt.Errorf("unknown value type %s", v.Type().String()) } } +func (e *Encoder) encodePtrAnchor(v reflect.Value, column int) ast.Node { + anchorName, exists := e.getAnchor(v.Pointer()) + if !exists { + return nil + } + aliasName := anchorName + alias := ast.Alias(token.New("*", "*", e.pos(column))) + alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) + e.setSmartAlias(aliasName, v.Pointer()) + return alias +} + func (e *Encoder) pos(column int) *token.Position { return &token.Position{ Line: e.line, @@ -501,12 +529,12 @@ func (e *Encoder) encodeNil() *ast.NullNode { } func (e *Encoder) encodeInt(v int64) *ast.IntegerNode { - value := fmt.Sprint(v) + value := strconv.FormatInt(v, 10) return ast.Integer(token.New(value, value, e.pos(e.column))) } func (e *Encoder) encodeUint(v uint64) *ast.IntegerNode { - value := fmt.Sprint(v) + value := strconv.FormatUint(v, 10) return ast.Integer(token.New(value, value, e.pos(e.column))) } @@ -523,6 +551,9 @@ func (e *Encoder) encodeFloat(v float64, bitSize int) ast.Node { } value := strconv.FormatFloat(v, 'g', -1, bitSize) if !strings.Contains(value, ".") && !strings.Contains(value, "e") { + if e.autoInt { + return ast.Integer(token.New(value, value, e.pos(e.column))) + } // append x.0 suffix to keep float value context value = fmt.Sprintf("%s.0", value) } @@ -539,6 +570,17 @@ func (e *Encoder) isNeedQuoted(v string) bool { if e.isFlowStyle && strings.ContainsAny(v, `]},'"`) { return true } + if e.isFlowStyle { + for i := 0; i < len(v); i++ { + if v[i] != ':' { + continue + } + if i+1 < len(v) && v[i+1] == '/' { + continue + } + return true + } + } if token.IsNeedQuoted(v) { return true } @@ -557,45 +599,41 @@ func (e *Encoder) encodeString(v string, column int) *ast.StringNode { } func (e *Encoder) encodeBool(v bool) *ast.BoolNode { - value := fmt.Sprint(v) + value := strconv.FormatBool(v) return ast.Bool(token.New(value, value, e.pos(e.column))) } func (e *Encoder) encodeSlice(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) { if e.indentSequence { - e.column += e.indent + e.column += e.indentNum + defer func() { e.column -= e.indentNum }() } column := e.column sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle) for i := 0; i < value.Len(); i++ { node, err := e.encodeValue(ctx, value.Index(i), column) if err != nil { - return nil, errors.Wrapf(err, "failed to encode value for slice") + return nil, err } sequence.Values = append(sequence.Values, node) } - if e.indentSequence { - e.column -= e.indent - } return sequence, nil } func (e *Encoder) encodeArray(ctx context.Context, value reflect.Value) (*ast.SequenceNode, error) { if e.indentSequence { - e.column += e.indent + e.column += e.indentNum + defer func() { e.column -= e.indentNum }() } column := e.column sequence := ast.Sequence(token.New("-", "-", e.pos(column)), e.isFlowStyle) for i := 0; i < value.Len(); i++ { node, err := e.encodeValue(ctx, value.Index(i), column) if err != nil { - return nil, errors.Wrapf(err, "failed to encode value for array") + return nil, err } sequence.Values = append(sequence.Values, node) } - if e.indentSequence { - e.column -= e.indent - } return sequence, nil } @@ -604,10 +642,13 @@ func (e *Encoder) encodeMapItem(ctx context.Context, item MapItem, column int) ( v := reflect.ValueOf(item.Value) value, err := e.encodeValue(ctx, v, column) if err != nil { - return nil, errors.Wrapf(err, "failed to encode MapItem") + return nil, err } if e.isMapNode(value) { - value.AddColumn(e.indent) + value.AddColumn(e.indentNum) + } + if e.isTagAndMapNode(value) { + value.AddColumn(e.indentNum) } return ast.MappingValue( token.New("", "", e.pos(column)), @@ -619,11 +660,11 @@ func (e *Encoder) encodeMapItem(ctx context.Context, item MapItem, column int) ( func (e *Encoder) encodeMapSlice(ctx context.Context, value MapSlice, column int) (*ast.MappingNode, error) { node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) for _, item := range value { - value, err := e.encodeMapItem(ctx, item, column) + encoded, err := e.encodeMapItem(ctx, item, column) if err != nil { - return nil, errors.Wrapf(err, "failed to encode MapItem for MapSlice") + return nil, err } - node.Values = append(node.Values, value) + node.Values = append(node.Values, encoded) } return node, nil } @@ -633,7 +674,12 @@ func (e *Encoder) isMapNode(node ast.Node) bool { return ok } -func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int) ast.Node { +func (e *Encoder) isTagAndMapNode(node ast.Node) bool { + tn, ok := node.(*ast.TagNode) + return ok && e.isMapNode(tn.Value) +} + +func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int) (ast.Node, error) { node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) keys := make([]interface{}, len(value.MapKeys())) for i, k := range value.MapKeys() { @@ -645,20 +691,41 @@ func (e *Encoder) encodeMap(ctx context.Context, value reflect.Value, column int for _, key := range keys { k := reflect.ValueOf(key) v := value.MapIndex(k) - value, err := e.encodeValue(ctx, v, column) + encoded, err := e.encodeValue(ctx, v, column) if err != nil { - return nil + return nil, err } - if e.isMapNode(value) { - value.AddColumn(e.indent) + if e.isMapNode(encoded) { + encoded.AddColumn(e.indentNum) + } + if e.isTagAndMapNode(encoded) { + encoded.AddColumn(e.indentNum) + } + keyText := fmt.Sprint(key) + vRef := e.toPointer(v) + + // during the second encoding, an anchor is assigned if it is found to be used by an alias. + if aliasName, exists := e.getSmartAlias(vRef); exists { + anchorName := aliasName + anchorNode := ast.Anchor(token.New("&", "&", e.pos(column))) + anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column))) + anchorNode.Value = encoded + encoded = anchorNode + } + + kn, err := e.encodeValue(ctx, reflect.ValueOf(key), column) + keyNode, ok := kn.(ast.MapKeyNode) + if !ok || err != nil { + keyNode = e.encodeString(fmt.Sprint(key), column) } node.Values = append(node.Values, ast.MappingValue( nil, - e.encodeString(fmt.Sprint(key), column), - value, + keyNode, + encoded, )) + e.setSmartAnchor(vRef, keyText) } - return node + return node, nil } // IsZeroer is used to check whether an object is zero to determine @@ -668,7 +735,7 @@ type IsZeroer interface { IsZero() bool } -func (e *Encoder) isZeroValue(v reflect.Value) bool { +func (e *Encoder) isOmittedByOmitZero(v reflect.Value) bool { kind := v.Kind() if z, ok := v.Interface().(IsZeroer); ok { if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { @@ -677,13 +744,74 @@ func (e *Encoder) isZeroValue(v reflect.Value) bool { return z.IsZero() } switch kind { + case reflect.String: + return len(v.String()) == 0 + case reflect.Interface, reflect.Ptr, reflect.Slice, reflect.Map: + return v.IsNil() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Struct: + vt := v.Type() + for i := v.NumField() - 1; i >= 0; i-- { + if vt.Field(i).PkgPath != "" { + continue // private field + } + if !e.isOmittedByOmitZero(v.Field(i)) { + return false + } + } + return true + } + return false +} + +func (e *Encoder) isOmittedByOmitEmptyOption(v reflect.Value) bool { + switch v.Kind() { case reflect.String: return len(v.String()) == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() - case reflect.Slice: + case reflect.Slice, reflect.Map: return v.Len() == 0 - case reflect.Map: + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Bool: + return !v.Bool() + } + return false +} + +// The current implementation of the omitempty tag combines the functionality of encoding/json's omitempty and omitzero tags. +// This stems from a historical decision to respect the implementation of gopkg.in/yaml.v2, but it has caused confusion, +// so we are working to integrate it into the functionality of encoding/json. (However, this will take some time.) +// In the current implementation, in addition to the exclusion conditions of omitempty, +// if a type implements IsZero, that implementation will be used. +// Furthermore, for non-pointer structs, if all fields are eligible for exclusion, +// the struct itself will also be excluded. These behaviors are originally the functionality of omitzero. +func (e *Encoder) isOmittedByOmitEmptyTag(v reflect.Value) bool { + kind := v.Kind() + if z, ok := v.Interface().(IsZeroer); ok { + if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { + return true + } + return z.IsZero() + } + switch kind { + case reflect.String: + return len(v.String()) == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + case reflect.Slice, reflect.Map: return v.Len() == 0 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return v.Int() == 0 @@ -699,7 +827,7 @@ func (e *Encoder) isZeroValue(v reflect.Value) bool { if vt.Field(i).PkgPath != "" { continue // private field } - if !e.isZeroValue(v.Field(i)) { + if !e.isOmittedByOmitEmptyTag(v.Field(i)) { return false } } @@ -730,14 +858,14 @@ func (e *Encoder) encodeAnchor(anchorName string, value ast.Node, fieldValue ref anchorNode.Value = value if e.anchorCallback != nil { if err := e.anchorCallback(anchorNode, fieldValue.Interface()); err != nil { - return nil, errors.Wrapf(err, "failed to marshal anchor") + return nil, err } if snode, ok := anchorNode.Name.(*ast.StringNode); ok { anchorName = snode.Value } } if fieldValue.Kind() == reflect.Ptr { - e.anchorPtrToNameMap[fieldValue.Pointer()] = anchorName + e.setAnchor(fieldValue.Pointer(), anchorName) } return anchorNode, nil } @@ -745,9 +873,9 @@ func (e *Encoder) encodeAnchor(anchorName string, value ast.Node, fieldValue ref func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column int) (ast.Node, error) { node := ast.Mapping(token.New("", "", e.pos(column)), e.isFlowStyle) structType := value.Type() - structFieldMap, err := structFieldMap(structType) + fieldMap, err := structFieldMap(structType) if err != nil { - return nil, errors.Wrapf(err, "failed to get struct field map") + return nil, err } hasInlineAnchorField := false var inlineAnchorValue reflect.Value @@ -757,119 +885,190 @@ func (e *Encoder) encodeStruct(ctx context.Context, value reflect.Value, column continue } fieldValue := value.FieldByName(field.Name) - structField := structFieldMap[field.Name] - if structField.IsOmitEmpty && e.isZeroValue(fieldValue) { - // omit encoding + sf := fieldMap[field.Name] + if (e.omitZero || sf.IsOmitZero) && e.isOmittedByOmitZero(fieldValue) { + // omit encoding by omitzero tag or OmitZero option. + continue + } + if e.omitEmpty && e.isOmittedByOmitEmptyOption(fieldValue) { + // omit encoding by OmitEmpty option. + continue + } + if sf.IsOmitEmpty && e.isOmittedByOmitEmptyTag(fieldValue) { + // omit encoding by omitempty tag. continue } ve := e - if !e.isFlowStyle && structField.IsFlow { + if !e.isFlowStyle && sf.IsFlow { ve = &Encoder{} *ve = *e ve.isFlowStyle = true } - value, err := ve.encodeValue(ctx, fieldValue, column) + encoded, err := ve.encodeValue(ctx, fieldValue, column) if err != nil { - return nil, errors.Wrapf(err, "failed to encode value") + return nil, err } - if e.isMapNode(value) { - value.AddColumn(e.indent) + if e.isMapNode(encoded) { + encoded.AddColumn(e.indentNum) } - var key ast.MapKeyNode = e.encodeString(structField.RenderName, column) + var key ast.MapKeyNode = e.encodeString(sf.RenderName, column) switch { - case structField.AnchorName != "": - anchorNode, err := e.encodeAnchor(structField.AnchorName, value, fieldValue, column) - if err != nil { - return nil, errors.Wrapf(err, "failed to encode anchor") - } - value = anchorNode - case structField.IsAutoAlias: - if fieldValue.Kind() != reflect.Ptr { - return nil, xerrors.Errorf( - "%s in struct is not pointer type. but required automatically alias detection", - structField.FieldName, - ) - } - anchorName := e.anchorPtrToNameMap[fieldValue.Pointer()] - if anchorName == "" { - return nil, xerrors.Errorf( - "cannot find anchor name from pointer address for automatically alias detection", - ) + case encoded.Type() == ast.AliasType: + if aliasName := sf.AliasName; aliasName != "" { + alias, ok := encoded.(*ast.AliasNode) + if !ok { + return nil, errors.ErrUnexpectedNodeType(encoded.Type(), ast.AliasType, encoded.GetToken()) + } + got := alias.Value.String() + if aliasName != got { + return nil, fmt.Errorf("expected alias name is %q but got %q", aliasName, got) + } } - aliasName := anchorName - alias := ast.Alias(token.New("*", "*", e.pos(column))) - alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) - value = alias - if structField.IsInline { + if sf.IsInline { // if both used alias and inline, output `<<: *alias` key = ast.MergeKey(token.New("<<", "<<", e.pos(column))) } - case structField.AliasName != "": - aliasName := structField.AliasName - alias := ast.Alias(token.New("*", "*", e.pos(column))) - alias.Value = ast.String(token.New(aliasName, aliasName, e.pos(column))) - value = alias - if structField.IsInline { - // if both used alias and inline, output `<<: *alias` - key = ast.MergeKey(token.New("<<", "<<", e.pos(column))) + case sf.AnchorName != "": + anchorNode, err := e.encodeAnchor(sf.AnchorName, encoded, fieldValue, column) + if err != nil { + return nil, err } - case structField.IsInline: - isAutoAnchor := structField.IsAutoAnchor + encoded = anchorNode + case sf.IsInline: + isAutoAnchor := sf.IsAutoAnchor if !hasInlineAnchorField { hasInlineAnchorField = isAutoAnchor } if isAutoAnchor { inlineAnchorValue = fieldValue } - mapNode, ok := value.(ast.MapNode) + mapNode, ok := encoded.(ast.MapNode) if !ok { // if an inline field is null, skip encoding it - if _, ok := value.(*ast.NullNode); ok { + if _, ok := encoded.(*ast.NullNode); ok { continue } - return nil, xerrors.Errorf("inline value is must be map or struct type") + return nil, errors.New("inline value is must be map or struct type") } mapIter := mapNode.MapRange() for mapIter.Next() { - key := mapIter.Key() - value := mapIter.Value() - keyName := key.GetToken().Value - if structFieldMap.isIncludedRenderName(keyName) { - // if declared same key name, skip encoding this field + mapKey := mapIter.Key() + mapValue := mapIter.Value() + keyName := mapKey.GetToken().Value + if fieldMap.isIncludedRenderName(keyName) { + // if declared the same key name, skip encoding this field continue } - key.AddColumn(-e.indent) - value.AddColumn(-e.indent) - node.Values = append(node.Values, ast.MappingValue(nil, key, value)) + mapKey.AddColumn(-e.indentNum) + mapValue.AddColumn(-e.indentNum) + node.Values = append(node.Values, ast.MappingValue(nil, mapKey, mapValue)) } continue - case structField.IsAutoAnchor: - anchorNode, err := e.encodeAnchor(structField.RenderName, value, fieldValue, column) + case sf.IsAutoAnchor: + anchorNode, err := e.encodeAnchor(sf.RenderName, encoded, fieldValue, column) if err != nil { - return nil, errors.Wrapf(err, "failed to encode anchor") + return nil, err } - value = anchorNode + encoded = anchorNode } - node.Values = append(node.Values, ast.MappingValue(nil, key, value)) + node.Values = append(node.Values, ast.MappingValue(nil, key, encoded)) } if hasInlineAnchorField { - node.AddColumn(e.indent) + node.AddColumn(e.indentNum) anchorName := "anchor" anchorNode := ast.Anchor(token.New("&", "&", e.pos(column))) anchorNode.Name = ast.String(token.New(anchorName, anchorName, e.pos(column))) anchorNode.Value = node if e.anchorCallback != nil { if err := e.anchorCallback(anchorNode, value.Addr().Interface()); err != nil { - return nil, errors.Wrapf(err, "failed to marshal anchor") + return nil, err } if snode, ok := anchorNode.Name.(*ast.StringNode); ok { anchorName = snode.Value } } if inlineAnchorValue.Kind() == reflect.Ptr { - e.anchorPtrToNameMap[inlineAnchorValue.Pointer()] = anchorName + e.setAnchor(inlineAnchorValue.Pointer(), anchorName) } return anchorNode, nil } return node, nil } + +func (e *Encoder) toPointer(v reflect.Value) uintptr { + if e.isInvalidValue(v) { + return 0 + } + + switch v.Type().Kind() { + case reflect.Ptr: + return v.Pointer() + case reflect.Interface: + return e.toPointer(v.Elem()) + case reflect.Slice: + return v.Pointer() + case reflect.Map: + return v.Pointer() + } + return 0 +} + +func (e *Encoder) clearSmartAnchorRef() { + if !e.enableSmartAnchor { + return + } + e.anchorRefToName = make(map[uintptr]string) + e.anchorNameMap = make(map[string]struct{}) +} + +func (e *Encoder) setSmartAnchor(ptr uintptr, name string) { + if !e.enableSmartAnchor { + return + } + e.setAnchor(ptr, e.generateAnchorName(name)) +} + +func (e *Encoder) setAnchor(ptr uintptr, name string) { + if ptr == 0 { + return + } + if name == "" { + return + } + e.anchorRefToName[ptr] = name + e.anchorNameMap[name] = struct{}{} +} + +func (e *Encoder) generateAnchorName(base string) string { + if _, exists := e.anchorNameMap[base]; !exists { + return base + } + for i := 1; i < 100; i++ { + name := base + strconv.Itoa(i) + if _, exists := e.anchorNameMap[name]; exists { + continue + } + return name + } + return "" +} + +func (e *Encoder) getAnchor(ref uintptr) (string, bool) { + anchorName, exists := e.anchorRefToName[ref] + return anchorName, exists +} + +func (e *Encoder) setSmartAlias(name string, ref uintptr) { + if !e.enableSmartAnchor { + return + } + e.aliasRefToName[ref] = name +} + +func (e *Encoder) getSmartAlias(ref uintptr) (string, bool) { + if !e.enableSmartAnchor { + return "", false + } + aliasName, exists := e.aliasRefToName[ref] + return aliasName, exists +} diff --git a/vendor/github.com/goccy/go-yaml/error.go b/vendor/github.com/goccy/go-yaml/error.go index 163dcc55..52d3e7e6 100644 --- a/vendor/github.com/goccy/go-yaml/error.go +++ b/vendor/github.com/goccy/go-yaml/error.go @@ -1,62 +1,77 @@ package yaml import ( + "fmt" + "github.com/goccy/go-yaml/ast" - "golang.org/x/xerrors" + "github.com/goccy/go-yaml/internal/errors" ) var ( - ErrInvalidQuery = xerrors.New("invalid query") - ErrInvalidPath = xerrors.New("invalid path instance") - ErrInvalidPathString = xerrors.New("invalid path string") - ErrNotFoundNode = xerrors.New("node not found") - ErrUnknownCommentPositionType = xerrors.New("unknown comment position type") - ErrInvalidCommentMapValue = xerrors.New("invalid comment map value. it must be not nil value") + ErrInvalidQuery = errors.New("invalid query") + ErrInvalidPath = errors.New("invalid path instance") + ErrInvalidPathString = errors.New("invalid path string") + ErrNotFoundNode = errors.New("node not found") + ErrUnknownCommentPositionType = errors.New("unknown comment position type") + ErrInvalidCommentMapValue = errors.New("invalid comment map value. it must be not nil value") + ErrDecodeRequiredPointerType = errors.New("required pointer type value") + ErrExceededMaxDepth = errors.New("exceeded max depth") + FormatErrorWithToken = errors.FormatError +) + +type ( + SyntaxError = errors.SyntaxError + TypeError = errors.TypeError + OverflowError = errors.OverflowError + DuplicateKeyError = errors.DuplicateKeyError + UnknownFieldError = errors.UnknownFieldError + UnexpectedNodeTypeError = errors.UnexpectedNodeTypeError + Error = errors.Error ) func ErrUnsupportedHeadPositionType(node ast.Node) error { - return xerrors.Errorf("unsupported comment head position for %s", node.Type()) + return fmt.Errorf("unsupported comment head position for %s", node.Type()) } func ErrUnsupportedLinePositionType(node ast.Node) error { - return xerrors.Errorf("unsupported comment line position for %s", node.Type()) + return fmt.Errorf("unsupported comment line position for %s", node.Type()) } func ErrUnsupportedFootPositionType(node ast.Node) error { - return xerrors.Errorf("unsupported comment foot position for %s", node.Type()) + return fmt.Errorf("unsupported comment foot position for %s", node.Type()) } // IsInvalidQueryError whether err is ErrInvalidQuery or not. func IsInvalidQueryError(err error) bool { - return xerrors.Is(err, ErrInvalidQuery) + return errors.Is(err, ErrInvalidQuery) } // IsInvalidPathError whether err is ErrInvalidPath or not. func IsInvalidPathError(err error) bool { - return xerrors.Is(err, ErrInvalidPath) + return errors.Is(err, ErrInvalidPath) } // IsInvalidPathStringError whether err is ErrInvalidPathString or not. func IsInvalidPathStringError(err error) bool { - return xerrors.Is(err, ErrInvalidPathString) + return errors.Is(err, ErrInvalidPathString) } // IsNotFoundNodeError whether err is ErrNotFoundNode or not. func IsNotFoundNodeError(err error) bool { - return xerrors.Is(err, ErrNotFoundNode) + return errors.Is(err, ErrNotFoundNode) } // IsInvalidTokenTypeError whether err is ast.ErrInvalidTokenType or not. func IsInvalidTokenTypeError(err error) bool { - return xerrors.Is(err, ast.ErrInvalidTokenType) + return errors.Is(err, ast.ErrInvalidTokenType) } // IsInvalidAnchorNameError whether err is ast.ErrInvalidAnchorName or not. func IsInvalidAnchorNameError(err error) bool { - return xerrors.Is(err, ast.ErrInvalidAnchorName) + return errors.Is(err, ast.ErrInvalidAnchorName) } // IsInvalidAliasNameError whether err is ast.ErrInvalidAliasName or not. func IsInvalidAliasNameError(err error) bool { - return xerrors.Is(err, ast.ErrInvalidAliasName) + return errors.Is(err, ast.ErrInvalidAliasName) } diff --git a/vendor/github.com/goccy/go-yaml/internal/errors/error.go b/vendor/github.com/goccy/go-yaml/internal/errors/error.go index 7f1ea9af..b08a3fc6 100644 --- a/vendor/github.com/goccy/go-yaml/internal/errors/error.go +++ b/vendor/github.com/goccy/go-yaml/internal/errors/error.go @@ -1,260 +1,246 @@ package errors import ( - "bytes" + "errors" "fmt" "reflect" + "github.com/goccy/go-yaml/ast" "github.com/goccy/go-yaml/printer" "github.com/goccy/go-yaml/token" - "golang.org/x/xerrors" +) + +var ( + As = errors.As + Is = errors.Is + New = errors.New ) const ( - defaultColorize = false + defaultFormatColor = false defaultIncludeSource = true ) +type Error interface { + error + GetToken() *token.Token + GetMessage() string + FormatError(bool, bool) string +} + var ( - ErrDecodeRequiredPointerType = xerrors.New("required pointer type value") + _ Error = new(SyntaxError) + _ Error = new(TypeError) + _ Error = new(OverflowError) + _ Error = new(DuplicateKeyError) + _ Error = new(UnknownFieldError) + _ Error = new(UnexpectedNodeTypeError) ) -// Wrapf wrap error for stack trace -func Wrapf(err error, msg string, args ...interface{}) error { - return &wrapError{ - baseError: &baseError{}, - err: xerrors.Errorf(msg, args...), - nextErr: err, - frame: xerrors.Caller(1), - } +type SyntaxError struct { + Message string + Token *token.Token +} + +type TypeError struct { + DstType reflect.Type + SrcType reflect.Type + StructFieldName *string + Token *token.Token +} + +type OverflowError struct { + DstType reflect.Type + SrcNum string + Token *token.Token +} + +type DuplicateKeyError struct { + Message string + Token *token.Token +} + +type UnknownFieldError struct { + Message string + Token *token.Token +} + +type UnexpectedNodeTypeError struct { + Actual ast.NodeType + Expected ast.NodeType + Token *token.Token } // ErrSyntax create syntax error instance with message and token -func ErrSyntax(msg string, tk *token.Token) *syntaxError { - return &syntaxError{ - baseError: &baseError{}, - msg: msg, - token: tk, - frame: xerrors.Caller(1), +func ErrSyntax(msg string, tk *token.Token) *SyntaxError { + return &SyntaxError{ + Message: msg, + Token: tk, } } -type baseError struct { - state fmt.State - verb rune +// ErrOverflow creates an overflow error instance with message and a token. +func ErrOverflow(dstType reflect.Type, num string, tk *token.Token) *OverflowError { + return &OverflowError{ + DstType: dstType, + SrcNum: num, + Token: tk, + } } -func (e *baseError) Error() string { - return "" +// ErrTypeMismatch cerates an type mismatch error instance with token. +func ErrTypeMismatch(dstType, srcType reflect.Type, token *token.Token) *TypeError { + return &TypeError{ + DstType: dstType, + SrcType: srcType, + Token: token, + } } -func (e *baseError) chainStateAndVerb(err error) { - wrapErr, ok := err.(*wrapError) - if ok { - wrapErr.state = e.state - wrapErr.verb = e.verb +// ErrDuplicateKey creates an duplicate key error instance with token. +func ErrDuplicateKey(msg string, tk *token.Token) *DuplicateKeyError { + return &DuplicateKeyError{ + Message: msg, + Token: tk, } - syntaxErr, ok := err.(*syntaxError) - if ok { - syntaxErr.state = e.state - syntaxErr.verb = e.verb +} + +// ErrUnknownField creates an unknown field error instance with token. +func ErrUnknownField(msg string, tk *token.Token) *UnknownFieldError { + return &UnknownFieldError{ + Message: msg, + Token: tk, } } -type wrapError struct { - *baseError - err error - nextErr error - frame xerrors.Frame +func ErrUnexpectedNodeType(actual, expected ast.NodeType, tk *token.Token) *UnexpectedNodeTypeError { + return &UnexpectedNodeTypeError{ + Actual: actual, + Expected: expected, + Token: tk, + } } -type FormatErrorPrinter struct { - xerrors.Printer - Colored bool - InclSource bool +func (e *SyntaxError) GetMessage() string { + return e.Message } -func (e *wrapError) As(target interface{}) bool { - err := e.nextErr - for { - if wrapErr, ok := err.(*wrapError); ok { - err = wrapErr.nextErr - continue - } - break - } - return xerrors.As(err, target) +func (e *SyntaxError) GetToken() *token.Token { + return e.Token } -func (e *wrapError) Unwrap() error { - return e.nextErr +func (e *SyntaxError) Error() string { + return e.FormatError(defaultFormatColor, defaultIncludeSource) } -func (e *wrapError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { - return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) +func (e *SyntaxError) FormatError(colored, inclSource bool) string { + return FormatError(e.Message, e.Token, colored, inclSource) } -func (e *wrapError) FormatError(p xerrors.Printer) error { - if _, ok := p.(*FormatErrorPrinter); !ok { - p = &FormatErrorPrinter{ - Printer: p, - Colored: defaultColorize, - InclSource: defaultIncludeSource, - } - } - if e.verb == 'v' && e.state.Flag('+') { - // print stack trace for debugging - p.Print(e.err, "\n") - e.frame.Format(p) - e.chainStateAndVerb(e.nextErr) - return e.nextErr - } - err := e.nextErr - for { - if wrapErr, ok := err.(*wrapError); ok { - err = wrapErr.nextErr - continue - } - break - } - e.chainStateAndVerb(err) - if fmtErr, ok := err.(xerrors.Formatter); ok { - fmtErr.FormatError(p) - } else { - p.Print(err) - } - return nil +func (e *OverflowError) GetMessage() string { + return e.msg() } -type wrapState struct { - org fmt.State +func (e *OverflowError) GetToken() *token.Token { + return e.Token } -func (s *wrapState) Write(b []byte) (n int, err error) { - return s.org.Write(b) +func (e *OverflowError) Error() string { + return e.FormatError(defaultFormatColor, defaultIncludeSource) } -func (s *wrapState) Width() (wid int, ok bool) { - return s.org.Width() +func (e *OverflowError) FormatError(colored, inclSource bool) string { + return FormatError(e.msg(), e.Token, colored, inclSource) } -func (s *wrapState) Precision() (prec int, ok bool) { - return s.org.Precision() +func (e *OverflowError) msg() string { + return fmt.Sprintf("cannot unmarshal %s into Go value of type %s ( overflow )", e.SrcNum, e.DstType) } -func (s *wrapState) Flag(c int) bool { - // set true to 'printDetail' forced because when p.Detail() is false, xerrors.Printer no output any text - if c == '#' { - // ignore '#' keyword because xerrors.FormatError doesn't set true to printDetail. - // ( see https://github.com/golang/xerrors/blob/master/adaptor.go#L39-L43 ) - return false +func (e *TypeError) msg() string { + if e.StructFieldName != nil { + return fmt.Sprintf("cannot unmarshal %s into Go struct field %s of type %s", e.SrcType, *e.StructFieldName, e.DstType) } - return true + return fmt.Sprintf("cannot unmarshal %s into Go value of type %s", e.SrcType, e.DstType) } -func (e *wrapError) Format(state fmt.State, verb rune) { - e.state = state - e.verb = verb - xerrors.FormatError(e, &wrapState{org: state}, verb) +func (e *TypeError) GetMessage() string { + return e.msg() } -func (e *wrapError) Error() string { - var buf bytes.Buffer - e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource) - return buf.String() +func (e *TypeError) GetToken() *token.Token { + return e.Token } -type syntaxError struct { - *baseError - msg string - token *token.Token - frame xerrors.Frame +func (e *TypeError) Error() string { + return e.FormatError(defaultFormatColor, defaultIncludeSource) } -func (e *syntaxError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { - return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) +func (e *TypeError) FormatError(colored, inclSource bool) string { + return FormatError(e.msg(), e.Token, colored, inclSource) } -func (e *syntaxError) FormatError(p xerrors.Printer) error { - var pp printer.Printer +func (e *DuplicateKeyError) GetMessage() string { + return e.Message +} - var colored, inclSource bool - if fep, ok := p.(*FormatErrorPrinter); ok { - colored = fep.Colored - inclSource = fep.InclSource - } +func (e *DuplicateKeyError) GetToken() *token.Token { + return e.Token +} - pos := fmt.Sprintf("[%d:%d] ", e.token.Position.Line, e.token.Position.Column) - msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.msg), colored) - if inclSource { - msg += "\n" + pp.PrintErrorToken(e.token, colored) - } - p.Print(msg) +func (e *DuplicateKeyError) Error() string { + return e.FormatError(defaultFormatColor, defaultIncludeSource) +} - if e.verb == 'v' && e.state.Flag('+') { - // %+v - // print stack trace for debugging - e.frame.Format(p) - } - return nil +func (e *DuplicateKeyError) FormatError(colored, inclSource bool) string { + return FormatError(e.Message, e.Token, colored, inclSource) } -type PrettyPrinter interface { - PrettyPrint(xerrors.Printer, bool, bool) error +func (e *UnknownFieldError) GetMessage() string { + return e.Message } -type Sink struct{ *bytes.Buffer } -func (es *Sink) Print(args ...interface{}) { - fmt.Fprint(es.Buffer, args...) +func (e *UnknownFieldError) GetToken() *token.Token { + return e.Token } -func (es *Sink) Printf(f string, args ...interface{}) { - fmt.Fprintf(es.Buffer, f, args...) +func (e *UnknownFieldError) Error() string { + return e.FormatError(defaultFormatColor, defaultIncludeSource) } -func (es *Sink) Detail() bool { - return false +func (e *UnknownFieldError) FormatError(colored, inclSource bool) string { + return FormatError(e.Message, e.Token, colored, inclSource) } -func (e *syntaxError) Error() string { - var buf bytes.Buffer - e.PrettyPrint(&Sink{&buf}, defaultColorize, defaultIncludeSource) - return buf.String() +func (e *UnexpectedNodeTypeError) GetMessage() string { + return e.msg() } -type TypeError struct { - DstType reflect.Type - SrcType reflect.Type - StructFieldName *string - Token *token.Token +func (e *UnexpectedNodeTypeError) GetToken() *token.Token { + return e.Token } -func (e *TypeError) Error() string { - if e.StructFieldName != nil { - return fmt.Sprintf("cannot unmarshal %s into Go struct field %s of type %s", e.SrcType, *e.StructFieldName, e.DstType) - } - return fmt.Sprintf("cannot unmarshal %s into Go value of type %s", e.SrcType, e.DstType) +func (e *UnexpectedNodeTypeError) Error() string { + return e.FormatError(defaultFormatColor, defaultIncludeSource) } -func (e *TypeError) PrettyPrint(p xerrors.Printer, colored, inclSource bool) error { - return e.FormatError(&FormatErrorPrinter{Printer: p, Colored: colored, InclSource: inclSource}) +func (e *UnexpectedNodeTypeError) FormatError(colored, inclSource bool) string { + return FormatError(e.msg(), e.Token, colored, inclSource) } -func (e *TypeError) FormatError(p xerrors.Printer) error { - var pp printer.Printer +func (e *UnexpectedNodeTypeError) msg() string { + return fmt.Sprintf("%s was used where %s is expected", e.Actual.YAMLName(), e.Expected.YAMLName()) +} - var colored, inclSource bool - if fep, ok := p.(*FormatErrorPrinter); ok { - colored = fep.Colored - inclSource = fep.InclSource +func FormatError(errMsg string, token *token.Token, colored, inclSource bool) string { + var pp printer.Printer + if token == nil { + return pp.PrintErrorMessage(errMsg, colored) } - - pos := fmt.Sprintf("[%d:%d] ", e.Token.Position.Line, e.Token.Position.Column) - msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, e.Error()), colored) + pos := fmt.Sprintf("[%d:%d] ", token.Position.Line, token.Position.Column) + msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, errMsg), colored) if inclSource { - msg += "\n" + pp.PrintErrorToken(e.Token, colored) + msg += "\n" + pp.PrintErrorToken(token, colored) } - p.Print(msg) - - return nil + return msg } diff --git a/vendor/github.com/goccy/go-yaml/internal/format/format.go b/vendor/github.com/goccy/go-yaml/internal/format/format.go new file mode 100644 index 00000000..2d55652f --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/internal/format/format.go @@ -0,0 +1,539 @@ +package format + +import ( + "strings" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/token" +) + +func FormatNodeWithResolvedAlias(n ast.Node, anchorNodeMap map[string]ast.Node) string { + tk := getFirstToken(n) + if tk == nil { + return "" + } + formatter := newFormatter(tk, hasComment(n)) + formatter.anchorNodeMap = anchorNodeMap + return formatter.format(n) +} + +func FormatNode(n ast.Node) string { + tk := getFirstToken(n) + if tk == nil { + return "" + } + return newFormatter(tk, hasComment(n)).format(n) +} + +func FormatFile(file *ast.File) string { + if len(file.Docs) == 0 { + return "" + } + tk := getFirstToken(file.Docs[0]) + if tk == nil { + return "" + } + return newFormatter(tk, hasCommentFile(file)).formatFile(file) +} + +func hasCommentFile(f *ast.File) bool { + for _, doc := range f.Docs { + if hasComment(doc.Body) { + return true + } + } + return false +} + +func hasComment(n ast.Node) bool { + if n == nil { + return false + } + switch nn := n.(type) { + case *ast.DocumentNode: + return hasComment(nn.Body) + case *ast.NullNode: + return nn.Comment != nil + case *ast.BoolNode: + return nn.Comment != nil + case *ast.IntegerNode: + return nn.Comment != nil + case *ast.FloatNode: + return nn.Comment != nil + case *ast.StringNode: + return nn.Comment != nil + case *ast.InfinityNode: + return nn.Comment != nil + case *ast.NanNode: + return nn.Comment != nil + case *ast.LiteralNode: + return nn.Comment != nil + case *ast.DirectiveNode: + if nn.Comment != nil { + return true + } + for _, value := range nn.Values { + if hasComment(value) { + return true + } + } + case *ast.TagNode: + if nn.Comment != nil { + return true + } + return hasComment(nn.Value) + case *ast.MappingNode: + if nn.Comment != nil || nn.FootComment != nil { + return true + } + for _, value := range nn.Values { + if value.Comment != nil || value.FootComment != nil { + return true + } + if hasComment(value.Key) { + return true + } + if hasComment(value.Value) { + return true + } + } + case *ast.MappingKeyNode: + return nn.Comment != nil + case *ast.MergeKeyNode: + return nn.Comment != nil + case *ast.SequenceNode: + if nn.Comment != nil || nn.FootComment != nil { + return true + } + for _, entry := range nn.Entries { + if entry.Comment != nil || entry.HeadComment != nil || entry.LineComment != nil { + return true + } + if hasComment(entry.Value) { + return true + } + } + case *ast.AnchorNode: + if nn.Comment != nil { + return true + } + if hasComment(nn.Name) || hasComment(nn.Value) { + return true + } + case *ast.AliasNode: + if nn.Comment != nil { + return true + } + if hasComment(nn.Value) { + return true + } + } + return false +} + +func getFirstToken(n ast.Node) *token.Token { + if n == nil { + return nil + } + switch nn := n.(type) { + case *ast.DocumentNode: + if nn.Start != nil { + return nn.Start + } + return getFirstToken(nn.Body) + case *ast.NullNode: + return nn.Token + case *ast.BoolNode: + return nn.Token + case *ast.IntegerNode: + return nn.Token + case *ast.FloatNode: + return nn.Token + case *ast.StringNode: + return nn.Token + case *ast.InfinityNode: + return nn.Token + case *ast.NanNode: + return nn.Token + case *ast.LiteralNode: + return nn.Start + case *ast.DirectiveNode: + return nn.Start + case *ast.TagNode: + return nn.Start + case *ast.MappingNode: + if nn.IsFlowStyle { + return nn.Start + } + if len(nn.Values) == 0 { + return nn.Start + } + return getFirstToken(nn.Values[0].Key) + case *ast.MappingKeyNode: + return nn.Start + case *ast.MergeKeyNode: + return nn.Token + case *ast.SequenceNode: + return nn.Start + case *ast.AnchorNode: + return nn.Start + case *ast.AliasNode: + return nn.Start + } + return nil +} + +type Formatter struct { + existsComment bool + tokenToOriginMap map[*token.Token]string + anchorNodeMap map[string]ast.Node +} + +func newFormatter(tk *token.Token, existsComment bool) *Formatter { + tokenToOriginMap := make(map[*token.Token]string) + for tk.Prev != nil { + tk = tk.Prev + } + tokenToOriginMap[tk] = tk.Origin + + var origin string + for tk.Next != nil { + tk = tk.Next + if tk.Type == token.CommentType { + origin += strings.Repeat("\n", strings.Count(normalizeNewLineChars(tk.Origin), "\n")) + continue + } + origin += tk.Origin + tokenToOriginMap[tk] = origin + origin = "" + } + return &Formatter{ + existsComment: existsComment, + tokenToOriginMap: tokenToOriginMap, + } +} + +func getIndentNumByFirstLineToken(tk *token.Token) int { + defaultIndent := tk.Position.Column - 1 + + // key: value + // ^ + // next + if tk.Type == token.SequenceEntryType { + // If the current token is the sequence entry. + // the indent is calculated from the column value of the current token. + return defaultIndent + } + + // key: value + // ^ + // next + if tk.Next != nil && tk.Next.Type == token.MappingValueType { + // If the current token is the key in the mapping-value, + // the indent is calculated from the column value of the current token. + return defaultIndent + } + + if tk.Prev == nil { + return defaultIndent + } + prev := tk.Prev + + // key: value + // ^ + // prev + if prev.Type == token.MappingValueType { + // If the current token is the value in the mapping-value, + // the indent is calculated from the column value of the key two steps back. + if prev.Prev == nil { + return defaultIndent + } + return prev.Prev.Position.Column - 1 + } + + // - value + // ^ + // prev + if prev.Type == token.SequenceEntryType { + // If the value is not a mapping-value and the previous token was a sequence entry, + // the indent is calculated using the column value of the sequence entry token. + return prev.Position.Column - 1 + } + + return defaultIndent +} + +func (f *Formatter) format(n ast.Node) string { + return f.trimSpacePrefix( + f.trimIndentSpace( + getIndentNumByFirstLineToken(getFirstToken(n)), + f.trimNewLineCharPrefix(f.formatNode(n)), + ), + ) +} + +func (f *Formatter) formatFile(file *ast.File) string { + if len(file.Docs) == 0 { + return "" + } + var ret string + for _, doc := range file.Docs { + ret += f.formatDocument(doc) + } + return ret +} + +func (f *Formatter) origin(tk *token.Token) string { + if tk == nil { + return "" + } + if f.existsComment { + return tk.Origin + } + return f.tokenToOriginMap[tk] +} + +func (f *Formatter) formatDocument(n *ast.DocumentNode) string { + return f.origin(n.Start) + f.formatNode(n.Body) + f.origin(n.End) +} + +func (f *Formatter) formatNull(n *ast.NullNode) string { + return f.origin(n.Token) + f.formatCommentGroup(n.Comment) +} + +func (f *Formatter) formatString(n *ast.StringNode) string { + return f.origin(n.Token) + f.formatCommentGroup(n.Comment) +} + +func (f *Formatter) formatInteger(n *ast.IntegerNode) string { + return f.origin(n.Token) + f.formatCommentGroup(n.Comment) +} + +func (f *Formatter) formatFloat(n *ast.FloatNode) string { + return f.origin(n.Token) + f.formatCommentGroup(n.Comment) +} + +func (f *Formatter) formatBool(n *ast.BoolNode) string { + return f.origin(n.Token) + f.formatCommentGroup(n.Comment) +} + +func (f *Formatter) formatInfinity(n *ast.InfinityNode) string { + return f.origin(n.Token) + f.formatCommentGroup(n.Comment) +} + +func (f *Formatter) formatNan(n *ast.NanNode) string { + return f.origin(n.Token) + f.formatCommentGroup(n.Comment) +} + +func (f *Formatter) formatLiteral(n *ast.LiteralNode) string { + return f.origin(n.Start) + f.formatCommentGroup(n.Comment) + f.origin(n.Value.Token) +} + +func (f *Formatter) formatMergeKey(n *ast.MergeKeyNode) string { + return f.origin(n.Token) +} + +func (f *Formatter) formatMappingValue(n *ast.MappingValueNode) string { + return f.formatCommentGroup(n.Comment) + + f.origin(n.Key.GetToken()) + ":" + f.formatCommentGroup(n.Key.GetComment()) + f.formatNode(n.Value) + + f.formatCommentGroup(n.FootComment) +} + +func (f *Formatter) formatDirective(n *ast.DirectiveNode) string { + ret := f.origin(n.Start) + f.formatNode(n.Name) + for _, val := range n.Values { + ret += f.formatNode(val) + } + return ret +} + +func (f *Formatter) formatMapping(n *ast.MappingNode) string { + var ret string + if n.IsFlowStyle { + ret = f.origin(n.Start) + } + ret += f.formatCommentGroup(n.Comment) + for _, value := range n.Values { + if value.CollectEntry != nil { + ret += f.origin(value.CollectEntry) + } + ret += f.formatMappingValue(value) + } + if n.IsFlowStyle { + ret += f.origin(n.End) + } + return ret +} + +func (f *Formatter) formatTag(n *ast.TagNode) string { + return f.origin(n.Start) + f.formatNode(n.Value) +} + +func (f *Formatter) formatMappingKey(n *ast.MappingKeyNode) string { + return f.origin(n.Start) + f.formatNode(n.Value) +} + +func (f *Formatter) formatSequence(n *ast.SequenceNode) string { + var ret string + if n.IsFlowStyle { + ret = f.origin(n.Start) + } + if n.Comment != nil { + // add head comment. + ret += f.formatCommentGroup(n.Comment) + } + for _, entry := range n.Entries { + ret += f.formatNode(entry) + } + if n.IsFlowStyle { + ret += f.origin(n.End) + } + ret += f.formatCommentGroup(n.FootComment) + return ret +} + +func (f *Formatter) formatSequenceEntry(n *ast.SequenceEntryNode) string { + return f.formatCommentGroup(n.HeadComment) + f.origin(n.Start) + f.formatCommentGroup(n.LineComment) + f.formatNode(n.Value) +} + +func (f *Formatter) formatAnchor(n *ast.AnchorNode) string { + return f.origin(n.Start) + f.formatNode(n.Name) + f.formatNode(n.Value) +} + +func (f *Formatter) formatAlias(n *ast.AliasNode) string { + if f.anchorNodeMap != nil { + anchorName := n.Value.GetToken().Value + node := f.anchorNodeMap[anchorName] + if node != nil { + formatted := f.formatNode(node) + // If formatted text contains newline characters, indentation needs to be considered. + if strings.Contains(formatted, "\n") { + // If the first character is not a newline, the first line should be output without indentation. + isIgnoredFirstLine := !strings.HasPrefix(formatted, "\n") + formatted = f.addIndentSpace(n.GetToken().Position.IndentNum, formatted, isIgnoredFirstLine) + } + return formatted + } + } + return f.origin(n.Start) + f.formatNode(n.Value) +} + +func (f *Formatter) formatNode(n ast.Node) string { + switch nn := n.(type) { + case *ast.DocumentNode: + return f.formatDocument(nn) + case *ast.NullNode: + return f.formatNull(nn) + case *ast.BoolNode: + return f.formatBool(nn) + case *ast.IntegerNode: + return f.formatInteger(nn) + case *ast.FloatNode: + return f.formatFloat(nn) + case *ast.StringNode: + return f.formatString(nn) + case *ast.InfinityNode: + return f.formatInfinity(nn) + case *ast.NanNode: + return f.formatNan(nn) + case *ast.LiteralNode: + return f.formatLiteral(nn) + case *ast.DirectiveNode: + return f.formatDirective(nn) + case *ast.TagNode: + return f.formatTag(nn) + case *ast.MappingNode: + return f.formatMapping(nn) + case *ast.MappingKeyNode: + return f.formatMappingKey(nn) + case *ast.MappingValueNode: + return f.formatMappingValue(nn) + case *ast.MergeKeyNode: + return f.formatMergeKey(nn) + case *ast.SequenceNode: + return f.formatSequence(nn) + case *ast.SequenceEntryNode: + return f.formatSequenceEntry(nn) + case *ast.AnchorNode: + return f.formatAnchor(nn) + case *ast.AliasNode: + return f.formatAlias(nn) + } + return "" +} + +func (f *Formatter) formatCommentGroup(g *ast.CommentGroupNode) string { + if g == nil { + return "" + } + var ret string + for _, cm := range g.Comments { + ret += f.formatComment(cm) + } + return ret +} + +func (f *Formatter) formatComment(n *ast.CommentNode) string { + if n == nil { + return "" + } + return n.Token.Origin +} + +// nolint: unused +func (f *Formatter) formatIndent(col int) string { + if col <= 1 { + return "" + } + return strings.Repeat(" ", col-1) +} + +func (f *Formatter) trimNewLineCharPrefix(v string) string { + return strings.TrimLeftFunc(v, func(r rune) bool { + return r == '\n' || r == '\r' + }) +} + +func (f *Formatter) trimSpacePrefix(v string) string { + return strings.TrimLeftFunc(v, func(r rune) bool { + return r == ' ' + }) +} + +func (f *Formatter) trimIndentSpace(trimIndentNum int, v string) string { + if trimIndentNum == 0 { + return v + } + lines := strings.Split(normalizeNewLineChars(v), "\n") + out := make([]string, 0, len(lines)) + for _, line := range lines { + var cnt int + out = append(out, strings.TrimLeftFunc(line, func(r rune) bool { + cnt++ + return r == ' ' && cnt <= trimIndentNum + })) + } + return strings.Join(out, "\n") +} + +func (f *Formatter) addIndentSpace(indentNum int, v string, isIgnoredFirstLine bool) string { + if indentNum == 0 { + return v + } + indent := strings.Repeat(" ", indentNum) + lines := strings.Split(normalizeNewLineChars(v), "\n") + out := make([]string, 0, len(lines)) + for idx, line := range lines { + if line == "" || (isIgnoredFirstLine && idx == 0) { + out = append(out, line) + continue + } + out = append(out, indent+line) + } + return strings.Join(out, "\n") +} + +// normalizeNewLineChars normalize CRLF and CR to LF. +func normalizeNewLineChars(v string) string { + return strings.ReplaceAll(strings.ReplaceAll(v, "\r\n", "\n"), "\r", "\n") +} diff --git a/vendor/github.com/goccy/go-yaml/option.go b/vendor/github.com/goccy/go-yaml/option.go index eab5d43a..12b8f27f 100644 --- a/vendor/github.com/goccy/go-yaml/option.go +++ b/vendor/github.com/goccy/go-yaml/option.go @@ -1,6 +1,7 @@ package yaml import ( + "context" "io" "reflect" @@ -50,11 +51,10 @@ func Validator(v StructValidator) DecodeOption { } } -// Strict enable DisallowUnknownField and DisallowDuplicateKey +// Strict enable DisallowUnknownField func Strict() DecodeOption { return func(d *Decoder) error { d.disallowUnknownField = true - d.disallowDuplicateKey = true return nil } } @@ -69,10 +69,19 @@ func DisallowUnknownField() DecodeOption { } } -// DisallowDuplicateKey causes an error when mapping keys that are duplicates -func DisallowDuplicateKey() DecodeOption { +// AllowFieldPrefixes, when paired with [DisallowUnknownField], allows fields +// with the specified prefixes to bypass the unknown field check. +func AllowFieldPrefixes(prefixes ...string) DecodeOption { return func(d *Decoder) error { - d.disallowDuplicateKey = true + d.allowedFieldPrefixes = append(d.allowedFieldPrefixes, prefixes...) + return nil + } +} + +// AllowDuplicateMapKey ignore syntax error when mapping keys that are duplicates. +func AllowDuplicateMapKey() DecodeOption { + return func(d *Decoder) error { + d.allowDuplicateMapKey = true return nil } } @@ -102,20 +111,32 @@ func UseJSONUnmarshaler() DecodeOption { func CustomUnmarshaler[T any](unmarshaler func(*T, []byte) error) DecodeOption { return func(d *Decoder) error { var typ *T - d.customUnmarshalerMap[reflect.TypeOf(typ)] = func(v interface{}, b []byte) error { + d.customUnmarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}, b []byte) error { return unmarshaler(v.(*T), b) } return nil } } +// CustomUnmarshalerContext overrides any decoding process for the type specified in generics. +// Similar to CustomUnmarshaler, but allows passing a context to the unmarshaler function. +func CustomUnmarshalerContext[T any](unmarshaler func(context.Context, *T, []byte) error) DecodeOption { + return func(d *Decoder) error { + var typ *T + d.customUnmarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}, b []byte) error { + return unmarshaler(ctx, v.(*T), b) + } + return nil + } +} + // EncodeOption functional option type for Encoder type EncodeOption func(e *Encoder) error // Indent change indent number func Indent(spaces int) EncodeOption { return func(e *Encoder) error { - e.indent = spaces + e.indentNum = spaces return nil } } @@ -144,6 +165,18 @@ func Flow(isFlowStyle bool) EncodeOption { } } +// WithSmartAnchor when multiple map values share the same pointer, +// an anchor is automatically assigned to the first occurrence, and aliases are used for subsequent elements. +// The map key name is used as the anchor name by default. +// If key names conflict, a suffix is automatically added to avoid collisions. +// This is an experimental feature and cannot be used simultaneously with anchor tags. +func WithSmartAnchor() EncodeOption { + return func(e *Encoder) error { + e.enableSmartAnchor = true + return nil + } +} + // UseLiteralStyleIfMultiline causes encoding multiline strings with a literal syntax, // no matter what characters they include func UseLiteralStyleIfMultiline(useLiteralStyleIfMultiline bool) EncodeOption { @@ -188,13 +221,54 @@ func UseJSONMarshaler() EncodeOption { func CustomMarshaler[T any](marshaler func(T) ([]byte, error)) EncodeOption { return func(e *Encoder) error { var typ T - e.customMarshalerMap[reflect.TypeOf(typ)] = func(v interface{}) ([]byte, error) { + e.customMarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}) ([]byte, error) { return marshaler(v.(T)) } return nil } } +// CustomMarshalerContext overrides any encoding process for the type specified in generics. +// Similar to CustomMarshaler, but allows passing a context to the marshaler function. +func CustomMarshalerContext[T any](marshaler func(context.Context, T) ([]byte, error)) EncodeOption { + return func(e *Encoder) error { + var typ T + e.customMarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}) ([]byte, error) { + return marshaler(ctx, v.(T)) + } + return nil + } +} + +// AutoInt automatically converts floating-point numbers to integers when the fractional part is zero. +// For example, a value of 1.0 will be encoded as 1. +func AutoInt() EncodeOption { + return func(e *Encoder) error { + e.autoInt = true + return nil + } +} + +// OmitEmpty behaves in the same way as the interpretation of the omitempty tag in the encoding/json library. +// set on all the fields. +// In the current implementation, the omitempty tag is not implemented in the same way as encoding/json, +// so please specify this option if you expect the same behavior. +func OmitEmpty() EncodeOption { + return func(e *Encoder) error { + e.omitEmpty = true + return nil + } +} + +// OmitZero forces the encoder to assume an `omitzero` struct tag is +// set on all the fields. See `Marshal` commentary for the `omitzero` tag logic. +func OmitZero() EncodeOption { + return func(e *Encoder) error { + e.omitZero = true + return nil + } +} + // CommentPosition type of the position for comment. type CommentPosition int diff --git a/vendor/github.com/goccy/go-yaml/parser/color.go b/vendor/github.com/goccy/go-yaml/parser/color.go new file mode 100644 index 00000000..aeee0dce --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/parser/color.go @@ -0,0 +1,28 @@ +package parser + +import "fmt" + +const ( + colorFgHiBlack int = iota + 90 + colorFgHiRed + colorFgHiGreen + colorFgHiYellow + colorFgHiBlue + colorFgHiMagenta + colorFgHiCyan +) + +var colorTable = []int{ + colorFgHiRed, + colorFgHiGreen, + colorFgHiYellow, + colorFgHiBlue, + colorFgHiMagenta, + colorFgHiCyan, +} + +func colorize(idx int, content string) string { + colorIdx := idx % len(colorTable) + color := colorTable[colorIdx] + return fmt.Sprintf("\x1b[1;%dm", color) + content + "\x1b[22;0m" +} diff --git a/vendor/github.com/goccy/go-yaml/parser/context.go b/vendor/github.com/goccy/go-yaml/parser/context.go index 42cc4f8f..1584b3ec 100644 --- a/vendor/github.com/goccy/go-yaml/parser/context.go +++ b/vendor/github.com/goccy/go-yaml/parser/context.go @@ -9,11 +9,15 @@ import ( // context context at parsing type context struct { - parent *context - idx int + tokenRef *tokenRef + path string + isFlow bool +} + +type tokenRef struct { + tokens []*Token size int - tokens token.Tokens - path string + idx int } var pathSpecialChars = []string{ @@ -36,157 +40,148 @@ func normalizePath(path string) string { return path } -func (c *context) withChild(path string) *context { - ctx := c.copy() - path = normalizePath(path) - ctx.path += fmt.Sprintf(".%s", path) - return ctx +func (c *context) currentToken() *Token { + if c.tokenRef.idx >= c.tokenRef.size { + return nil + } + return c.tokenRef.tokens[c.tokenRef.idx] } -func (c *context) withIndex(idx uint) *context { - ctx := c.copy() - ctx.path += fmt.Sprintf("[%d]", idx) - return ctx +func (c *context) isComment() bool { + return c.currentToken().Type() == token.CommentType } -func (c *context) copy() *context { - return &context{ - parent: c, - idx: c.idx, - size: c.size, - tokens: append(token.Tokens{}, c.tokens...), - path: c.path, +func (c *context) nextToken() *Token { + if c.tokenRef.idx+1 >= c.tokenRef.size { + return nil } + return c.tokenRef.tokens[c.tokenRef.idx+1] } -func (c *context) next() bool { - return c.idx < c.size -} - -func (c *context) previousToken() *token.Token { - if c.idx > 0 { - return c.tokens[c.idx-1] +func (c *context) nextNotCommentToken() *Token { + for i := c.tokenRef.idx + 1; i < c.tokenRef.size; i++ { + tk := c.tokenRef.tokens[i] + if tk.Type() == token.CommentType { + continue + } + return tk } return nil } -func (c *context) insertToken(idx int, tk *token.Token) { - if c.parent != nil { - c.parent.insertToken(idx, tk) - } - if c.size < idx { - return - } - if c.size == idx { - curToken := c.tokens[c.size-1] - tk.Next = curToken - curToken.Prev = tk +func (c *context) isTokenNotFound() bool { + return c.currentToken() == nil +} - c.tokens = append(c.tokens, tk) - c.size = len(c.tokens) - return +func (c *context) withGroup(g *TokenGroup) *context { + ctx := *c + ctx.tokenRef = &tokenRef{ + tokens: g.Tokens, + size: len(g.Tokens), } + return &ctx +} - curToken := c.tokens[idx] - tk.Next = curToken - curToken.Prev = tk +func (c *context) withChild(path string) *context { + ctx := *c + ctx.path = c.path + "." + normalizePath(path) + return &ctx +} - c.tokens = append(c.tokens[:idx+1], c.tokens[idx:]...) - c.tokens[idx] = tk - c.size = len(c.tokens) +func (c *context) withIndex(idx uint) *context { + ctx := *c + ctx.path = c.path + "[" + fmt.Sprint(idx) + "]" + return &ctx } -func (c *context) currentToken() *token.Token { - if c.idx >= c.size { - return nil - } - return c.tokens[c.idx] +func (c *context) withFlow(isFlow bool) *context { + ctx := *c + ctx.isFlow = isFlow + return &ctx } -func (c *context) nextToken() *token.Token { - if c.idx+1 >= c.size { - return nil +func newContext() *context { + return &context{ + path: "$", } - return c.tokens[c.idx+1] } -func (c *context) afterNextToken() *token.Token { - if c.idx+2 >= c.size { - return nil +func (c *context) goNext() { + ref := c.tokenRef + if ref.size <= ref.idx+1 { + ref.idx = ref.size + } else { + ref.idx++ } - return c.tokens[c.idx+2] } -func (c *context) nextNotCommentToken() *token.Token { - for i := c.idx + 1; i < c.size; i++ { - tk := c.tokens[i] - if tk.Type == token.CommentType { - continue - } - return tk - } - return nil +func (c *context) next() bool { + return c.tokenRef.idx < c.tokenRef.size } -func (c *context) afterNextNotCommentToken() *token.Token { - notCommentTokenCount := 0 - for i := c.idx + 1; i < c.size; i++ { - tk := c.tokens[i] - if tk.Type == token.CommentType { - continue - } - notCommentTokenCount++ - if notCommentTokenCount == 2 { - return tk - } - } - return nil +func (c *context) insertNullToken(tk *Token) *Token { + nullToken := c.createImplicitNullToken(tk) + c.insertToken(nullToken) + c.goNext() + + return nullToken } -func (c *context) isCurrentCommentToken() bool { - tk := c.currentToken() - if tk == nil { - return false - } - return tk.Type == token.CommentType +func (c *context) addNullValueToken(tk *Token) *Token { + nullToken := c.createImplicitNullToken(tk) + rawTk := nullToken.RawToken() + + // add space for map or sequence value. + rawTk.Position.Column++ + + c.addToken(nullToken) + c.goNext() + + return nullToken } -func (c *context) progressIgnoreComment(num int) { - if c.parent != nil { - c.parent.progressIgnoreComment(num) - } - if c.size <= c.idx+num { - c.idx = c.size - } else { - c.idx += num - } +func (c *context) createImplicitNullToken(base *Token) *Token { + pos := *(base.RawToken().Position) + pos.Column++ + tk := token.New("null", " null", &pos) + tk.Type = token.ImplicitNullType + return &Token{Token: tk} } -func (c *context) progress(num int) { - if c.isCurrentCommentToken() { +func (c *context) insertToken(tk *Token) { + ref := c.tokenRef + idx := ref.idx + if ref.size < idx { return } - c.progressIgnoreComment(num) -} + if ref.size == idx { + curToken := ref.tokens[ref.size-1] + tk.RawToken().Next = curToken.RawToken() + curToken.RawToken().Prev = tk.RawToken() -func newContext(tokens token.Tokens, mode Mode) *context { - filteredTokens := []*token.Token{} - if mode&ParseComments != 0 { - filteredTokens = tokens - } else { - for _, tk := range tokens { - if tk.Type == token.CommentType { - continue - } - // keep prev/next reference between tokens containing comments - // https://github.com/goccy/go-yaml/issues/254 - filteredTokens = append(filteredTokens, tk) - } + ref.tokens = append(ref.tokens, tk) + ref.size = len(ref.tokens) + return } - return &context{ - idx: 0, - size: len(filteredTokens), - tokens: token.Tokens(filteredTokens), - path: "$", + + curToken := ref.tokens[idx] + tk.RawToken().Next = curToken.RawToken() + curToken.RawToken().Prev = tk.RawToken() + + ref.tokens = append(ref.tokens[:idx+1], ref.tokens[idx:]...) + ref.tokens[idx] = tk + ref.size = len(ref.tokens) +} + +func (c *context) addToken(tk *Token) { + ref := c.tokenRef + lastTk := ref.tokens[ref.size-1] + if lastTk.Group != nil { + lastTk = lastTk.Group.Last() } + lastTk.RawToken().Next = tk.RawToken() + tk.RawToken().Prev = lastTk.RawToken() + + ref.tokens = append(ref.tokens, tk) + ref.size = len(ref.tokens) } diff --git a/vendor/github.com/goccy/go-yaml/parser/node.go b/vendor/github.com/goccy/go-yaml/parser/node.go new file mode 100644 index 00000000..8d35554e --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/parser/node.go @@ -0,0 +1,257 @@ +package parser + +import ( + "fmt" + + "github.com/goccy/go-yaml/ast" + "github.com/goccy/go-yaml/internal/errors" + "github.com/goccy/go-yaml/token" +) + +func newMappingNode(ctx *context, tk *Token, isFlow bool, values ...*ast.MappingValueNode) (*ast.MappingNode, error) { + node := ast.Mapping(tk.RawToken(), isFlow, values...) + node.SetPath(ctx.path) + return node, nil +} + +func newMappingValueNode(ctx *context, colonTk, entryTk *Token, key ast.MapKeyNode, value ast.Node) (*ast.MappingValueNode, error) { + node := ast.MappingValue(colonTk.RawToken(), key, value) + node.SetPath(ctx.path) + node.CollectEntry = entryTk.RawToken() + if key.GetToken().Position.Line == value.GetToken().Position.Line { + // originally key was commented, but now that null value has been added, value must be commented. + if err := setLineComment(ctx, value, colonTk); err != nil { + return nil, err + } + // set line comment by colonTk or entryTk. + if err := setLineComment(ctx, value, entryTk); err != nil { + return nil, err + } + } else { + if err := setLineComment(ctx, key, colonTk); err != nil { + return nil, err + } + // set line comment by colonTk or entryTk. + if err := setLineComment(ctx, key, entryTk); err != nil { + return nil, err + } + } + return node, nil +} + +func newMappingKeyNode(ctx *context, tk *Token) (*ast.MappingKeyNode, error) { + node := ast.MappingKey(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newAnchorNode(ctx *context, tk *Token) (*ast.AnchorNode, error) { + node := ast.Anchor(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newAliasNode(ctx *context, tk *Token) (*ast.AliasNode, error) { + node := ast.Alias(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newDirectiveNode(ctx *context, tk *Token) (*ast.DirectiveNode, error) { + node := ast.Directive(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newMergeKeyNode(ctx *context, tk *Token) (*ast.MergeKeyNode, error) { + node := ast.MergeKey(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newNullNode(ctx *context, tk *Token) (*ast.NullNode, error) { + node := ast.Null(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newBoolNode(ctx *context, tk *Token) (*ast.BoolNode, error) { + node := ast.Bool(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newIntegerNode(ctx *context, tk *Token) (*ast.IntegerNode, error) { + node := ast.Integer(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newFloatNode(ctx *context, tk *Token) (*ast.FloatNode, error) { + node := ast.Float(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newInfinityNode(ctx *context, tk *Token) (*ast.InfinityNode, error) { + node := ast.Infinity(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newNanNode(ctx *context, tk *Token) (*ast.NanNode, error) { + node := ast.Nan(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newStringNode(ctx *context, tk *Token) (*ast.StringNode, error) { + node := ast.String(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newLiteralNode(ctx *context, tk *Token) (*ast.LiteralNode, error) { + node := ast.Literal(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newTagNode(ctx *context, tk *Token) (*ast.TagNode, error) { + node := ast.Tag(tk.RawToken()) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newSequenceNode(ctx *context, tk *Token, isFlow bool) (*ast.SequenceNode, error) { + node := ast.Sequence(tk.RawToken(), isFlow) + node.SetPath(ctx.path) + if err := setLineComment(ctx, node, tk); err != nil { + return nil, err + } + return node, nil +} + +func newTagDefaultScalarValueNode(ctx *context, tag *token.Token) (ast.ScalarNode, error) { + pos := *(tag.Position) + pos.Column++ + + var ( + tk *Token + node ast.ScalarNode + ) + switch token.ReservedTagKeyword(tag.Value) { + case token.IntegerTag: + tk = &Token{Token: token.New("0", "0", &pos)} + n, err := newIntegerNode(ctx, tk) + if err != nil { + return nil, err + } + node = n + case token.FloatTag: + tk = &Token{Token: token.New("0", "0", &pos)} + n, err := newFloatNode(ctx, tk) + if err != nil { + return nil, err + } + node = n + case token.StringTag, token.BinaryTag, token.TimestampTag: + tk = &Token{Token: token.New("", "", &pos)} + n, err := newStringNode(ctx, tk) + if err != nil { + return nil, err + } + node = n + case token.BooleanTag: + tk = &Token{Token: token.New("false", "false", &pos)} + n, err := newBoolNode(ctx, tk) + if err != nil { + return nil, err + } + node = n + case token.NullTag: + tk = &Token{Token: token.New("null", "null", &pos)} + n, err := newNullNode(ctx, tk) + if err != nil { + return nil, err + } + node = n + default: + return nil, errors.ErrSyntax(fmt.Sprintf("cannot assign default value for %q tag", tag.Value), tag) + } + ctx.insertToken(tk) + ctx.goNext() + return node, nil +} + +func setLineComment(ctx *context, node ast.Node, tk *Token) error { + if tk == nil || tk.LineComment == nil { + return nil + } + comment := ast.CommentGroup([]*token.Token{tk.LineComment}) + comment.SetPath(ctx.path) + if err := node.SetComment(comment); err != nil { + return err + } + return nil +} + +func setHeadComment(cm *ast.CommentGroupNode, value ast.Node) error { + if cm == nil { + return nil + } + switch n := value.(type) { + case *ast.MappingNode: + if len(n.Values) != 0 && value.GetComment() == nil { + cm.SetPath(n.Values[0].GetPath()) + return n.Values[0].SetComment(cm) + } + case *ast.MappingValueNode: + cm.SetPath(n.GetPath()) + return n.SetComment(cm) + } + cm.SetPath(value.GetPath()) + return value.SetComment(cm) +} diff --git a/vendor/github.com/goccy/go-yaml/parser/option.go b/vendor/github.com/goccy/go-yaml/parser/option.go new file mode 100644 index 00000000..3121a64a --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/parser/option.go @@ -0,0 +1,12 @@ +package parser + +// Option represents parser's option. +type Option func(p *parser) + +// AllowDuplicateMapKey allow the use of keys with the same name in the same map, +// but by default, this is not permitted. +func AllowDuplicateMapKey() Option { + return func(p *parser) { + p.allowDuplicateMapKey = true + } +} diff --git a/vendor/github.com/goccy/go-yaml/parser/parser.go b/vendor/github.com/goccy/go-yaml/parser/parser.go index 2bec5fea..2c79d369 100644 --- a/vendor/github.com/goccy/go-yaml/parser/parser.go +++ b/vendor/github.com/goccy/go-yaml/parser/parser.go @@ -9,735 +9,1312 @@ import ( "github.com/goccy/go-yaml/internal/errors" "github.com/goccy/go-yaml/lexer" "github.com/goccy/go-yaml/token" - "golang.org/x/xerrors" ) -type parser struct{} +type Mode uint -func (p *parser) parseMapping(ctx *context) (*ast.MappingNode, error) { - mapTk := ctx.currentToken() - node := ast.Mapping(mapTk, true) - node.SetPath(ctx.path) - ctx.progress(1) // skip MappingStart token - for ctx.next() { - tk := ctx.currentToken() - if tk.Type == token.MappingEndType { - node.End = tk - return node, nil - } else if tk.Type == token.CollectEntryType { - ctx.progress(1) - continue +const ( + ParseComments Mode = 1 << iota // parse comments and add them to AST +) + +// ParseBytes parse from byte slice, and returns ast.File +func ParseBytes(bytes []byte, mode Mode, opts ...Option) (*ast.File, error) { + tokens := lexer.Tokenize(string(bytes)) + f, err := Parse(tokens, mode, opts...) + if err != nil { + return nil, err + } + return f, nil +} + +// Parse parse from token instances, and returns ast.File +func Parse(tokens token.Tokens, mode Mode, opts ...Option) (*ast.File, error) { + if tk := tokens.InvalidToken(); tk != nil { + return nil, errors.ErrSyntax(tk.Error, tk) + } + p, err := newParser(tokens, mode, opts) + if err != nil { + return nil, err + } + f, err := p.parse(newContext()) + if err != nil { + return nil, err + } + return f, nil +} + +// Parse parse from filename, and returns ast.File +func ParseFile(filename string, mode Mode, opts ...Option) (*ast.File, error) { + file, err := os.ReadFile(filename) + if err != nil { + return nil, err + } + f, err := ParseBytes(file, mode, opts...) + if err != nil { + return nil, err + } + f.Name = filename + return f, nil +} + +type YAMLVersion string + +const ( + YAML10 YAMLVersion = "1.0" + YAML11 YAMLVersion = "1.1" + YAML12 YAMLVersion = "1.2" + YAML13 YAMLVersion = "1.3" +) + +var yamlVersionMap = map[string]YAMLVersion{ + "1.0": YAML10, + "1.1": YAML11, + "1.2": YAML12, + "1.3": YAML13, +} + +type parser struct { + tokens []*Token + pathMap map[string]ast.Node + yamlVersion YAMLVersion + allowDuplicateMapKey bool + secondaryTagDirective *ast.DirectiveNode +} + +func newParser(tokens token.Tokens, mode Mode, opts []Option) (*parser, error) { + filteredTokens := []*token.Token{} + if mode&ParseComments != 0 { + filteredTokens = tokens + } else { + for _, tk := range tokens { + if tk.Type == token.CommentType { + continue + } + // keep prev/next reference between tokens containing comments + // https://github.com/goccy/go-yaml/issues/254 + filteredTokens = append(filteredTokens, tk) + } + } + tks, err := CreateGroupedTokens(token.Tokens(filteredTokens)) + if err != nil { + return nil, err + } + p := &parser{ + tokens: tks, + pathMap: make(map[string]ast.Node), + } + for _, opt := range opts { + opt(p) + } + return p, nil +} + +func (p *parser) parse(ctx *context) (*ast.File, error) { + file := &ast.File{Docs: []*ast.DocumentNode{}} + for _, token := range p.tokens { + doc, err := p.parseDocument(ctx, token.Group) + if err != nil { + return nil, err } + file.Docs = append(file.Docs, doc) + } + return file, nil +} + +func (p *parser) parseDocument(ctx *context, docGroup *TokenGroup) (*ast.DocumentNode, error) { + if len(docGroup.Tokens) == 0 { + return ast.Document(docGroup.RawToken(), nil), nil + } + + p.pathMap = make(map[string]ast.Node) + + var ( + tokens = docGroup.Tokens + start *token.Token + end *token.Token + ) + if docGroup.First().Type() == token.DocumentHeaderType { + start = docGroup.First().RawToken() + tokens = tokens[1:] + } + if docGroup.Last().Type() == token.DocumentEndType { + end = docGroup.Last().RawToken() + tokens = tokens[:len(tokens)-1] + defer func() { + // clear yaml version value if DocumentEnd token (...) is specified. + p.yamlVersion = "" + }() + } + + if len(tokens) == 0 { + return ast.Document(docGroup.RawToken(), nil), nil + } + + body, err := p.parseDocumentBody(ctx.withGroup(&TokenGroup{ + Type: TokenGroupDocumentBody, + Tokens: tokens, + })) + if err != nil { + return nil, err + } + node := ast.Document(start, body) + node.End = end + return node, nil +} + +func (p *parser) parseDocumentBody(ctx *context) (ast.Node, error) { + node, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, err + } + if ctx.next() { + return nil, errors.ErrSyntax("value is not allowed in this context", ctx.currentToken().RawToken()) + } + return node, nil +} - value, err := p.parseMappingValue(ctx) +func (p *parser) parseToken(ctx *context, tk *Token) (ast.Node, error) { + switch tk.GroupType() { + case TokenGroupMapKey, TokenGroupMapKeyValue: + return p.parseMap(ctx) + case TokenGroupDirective: + node, err := p.parseDirective(ctx.withGroup(tk.Group), tk.Group) + if err != nil { + return nil, err + } + ctx.goNext() + return node, nil + case TokenGroupDirectiveName: + node, err := p.parseDirectiveName(ctx.withGroup(tk.Group)) if err != nil { - return nil, errors.Wrapf(err, "failed to parse mapping value in mapping node") + return nil, err } - mvnode, ok := value.(*ast.MappingValueNode) - if !ok { - return nil, errors.ErrSyntax("failed to parse flow mapping node", value.GetToken()) + ctx.goNext() + return node, nil + case TokenGroupAnchor: + node, err := p.parseAnchor(ctx.withGroup(tk.Group), tk.Group) + if err != nil { + return nil, err + } + ctx.goNext() + return node, nil + case TokenGroupAnchorName: + anchor, err := p.parseAnchorName(ctx.withGroup(tk.Group)) + if err != nil { + return nil, err + } + ctx.goNext() + if ctx.isTokenNotFound() { + return nil, errors.ErrSyntax("could not find anchor value", tk.RawToken()) + } + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, err + } + if _, ok := value.(*ast.AnchorNode); ok { + return nil, errors.ErrSyntax("anchors cannot be used consecutively", value.GetToken()) + } + anchor.Value = value + return anchor, nil + case TokenGroupAlias: + node, err := p.parseAlias(ctx.withGroup(tk.Group)) + if err != nil { + return nil, err + } + ctx.goNext() + return node, nil + case TokenGroupLiteral, TokenGroupFolded: + node, err := p.parseLiteral(ctx.withGroup(tk.Group)) + if err != nil { + return nil, err + } + ctx.goNext() + return node, nil + case TokenGroupScalarTag: + node, err := p.parseTag(ctx.withGroup(tk.Group)) + if err != nil { + return nil, err + } + ctx.goNext() + return node, nil + } + switch tk.Type() { + case token.CommentType: + return p.parseComment(ctx) + case token.TagType: + return p.parseTag(ctx) + case token.MappingStartType: + return p.parseFlowMap(ctx.withFlow(true)) + case token.SequenceStartType: + return p.parseFlowSequence(ctx.withFlow(true)) + case token.SequenceEntryType: + return p.parseSequence(ctx) + case token.SequenceEndType: + // SequenceEndType is always validated in parseFlowSequence. + // Therefore, if this is found in other cases, it is treated as a syntax error. + return nil, errors.ErrSyntax("could not find '[' character corresponding to ']'", tk.RawToken()) + case token.MappingEndType: + // MappingEndType is always validated in parseFlowMap. + // Therefore, if this is found in other cases, it is treated as a syntax error. + return nil, errors.ErrSyntax("could not find '{' character corresponding to '}'", tk.RawToken()) + case token.MappingValueType: + return nil, errors.ErrSyntax("found an invalid key for this map", tk.RawToken()) + } + node, err := p.parseScalarValue(ctx, tk) + if err != nil { + return nil, err + } + ctx.goNext() + return node, nil +} + +func (p *parser) parseScalarValue(ctx *context, tk *Token) (ast.ScalarNode, error) { + if tk.Group != nil { + switch tk.GroupType() { + case TokenGroupAnchor: + return p.parseAnchor(ctx.withGroup(tk.Group), tk.Group) + case TokenGroupAnchorName: + anchor, err := p.parseAnchorName(ctx.withGroup(tk.Group)) + if err != nil { + return nil, err + } + ctx.goNext() + if ctx.isTokenNotFound() { + return nil, errors.ErrSyntax("could not find anchor value", tk.RawToken()) + } + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, err + } + if _, ok := value.(*ast.AnchorNode); ok { + return nil, errors.ErrSyntax("anchors cannot be used consecutively", value.GetToken()) + } + anchor.Value = value + return anchor, nil + case TokenGroupAlias: + return p.parseAlias(ctx.withGroup(tk.Group)) + case TokenGroupLiteral, TokenGroupFolded: + return p.parseLiteral(ctx.withGroup(tk.Group)) + case TokenGroupScalarTag: + return p.parseTag(ctx.withGroup(tk.Group)) + default: + return nil, errors.ErrSyntax("unexpected scalar value", tk.RawToken()) } - node.Values = append(node.Values, mvnode) - ctx.progress(1) } - return nil, errors.ErrSyntax("unterminated flow mapping", node.GetToken()) + switch tk.Type() { + case token.MergeKeyType: + return newMergeKeyNode(ctx, tk) + case token.NullType, token.ImplicitNullType: + return newNullNode(ctx, tk) + case token.BoolType: + return newBoolNode(ctx, tk) + case token.IntegerType, token.BinaryIntegerType, token.OctetIntegerType, token.HexIntegerType: + return newIntegerNode(ctx, tk) + case token.FloatType: + return newFloatNode(ctx, tk) + case token.InfinityType: + return newInfinityNode(ctx, tk) + case token.NanType: + return newNanNode(ctx, tk) + case token.StringType, token.SingleQuoteType, token.DoubleQuoteType: + return newStringNode(ctx, tk) + case token.TagType: + // this case applies when it is a scalar tag and its value does not exist. + // Examples of cases where the value does not exist include cases like `key: !!str,` or `!!str : value`. + return p.parseScalarTag(ctx) + } + return nil, errors.ErrSyntax("unexpected scalar value type", tk.RawToken()) } -func (p *parser) parseSequence(ctx *context) (*ast.SequenceNode, error) { - node := ast.Sequence(ctx.currentToken(), true) - node.SetPath(ctx.path) - ctx.progress(1) // skip SequenceStart token +func (p *parser) parseFlowMap(ctx *context) (*ast.MappingNode, error) { + node, err := newMappingNode(ctx, ctx.currentToken(), true) + if err != nil { + return nil, err + } + ctx.goNext() // skip MappingStart token + + isFirst := true for ctx.next() { tk := ctx.currentToken() - if tk.Type == token.SequenceEndType { - node.End = tk + if tk.Type() == token.MappingEndType { + node.End = tk.RawToken() break - } else if tk.Type == token.CollectEntryType { - ctx.progress(1) - continue } - value, err := p.parseToken(ctx.withIndex(uint(len(node.Values))), tk) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse sequence value in flow sequence node") + var entryTk *Token + if tk.Type() == token.CollectEntryType { + entryTk = tk + ctx.goNext() + } else if !isFirst { + return nil, errors.ErrSyntax("',' or '}' must be specified", tk.RawToken()) } - node.Values = append(node.Values, value) - ctx.progress(1) + + if tk := ctx.currentToken(); tk.Type() == token.MappingEndType { + // this case is here: "{ elem, }". + // In this case, ignore the last element and break mapping parsing. + node.End = tk.RawToken() + break + } + + mapKeyTk := ctx.currentToken() + switch mapKeyTk.GroupType() { + case TokenGroupMapKeyValue: + value, err := p.parseMapKeyValue(ctx.withGroup(mapKeyTk.Group), mapKeyTk.Group, entryTk) + if err != nil { + return nil, err + } + node.Values = append(node.Values, value) + ctx.goNext() + case TokenGroupMapKey: + key, err := p.parseMapKey(ctx.withGroup(mapKeyTk.Group), mapKeyTk.Group) + if err != nil { + return nil, err + } + ctx := ctx.withChild(p.mapKeyText(key)) + colonTk := mapKeyTk.Group.Last() + if p.isFlowMapDelim(ctx.nextToken()) { + value, err := newNullNode(ctx, ctx.insertNullToken(colonTk)) + if err != nil { + return nil, err + } + mapValue, err := newMappingValueNode(ctx, colonTk, entryTk, key, value) + if err != nil { + return nil, err + } + node.Values = append(node.Values, mapValue) + ctx.goNext() + } else { + ctx.goNext() + if ctx.isTokenNotFound() { + return nil, errors.ErrSyntax("could not find map value", colonTk.RawToken()) + } + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, err + } + mapValue, err := newMappingValueNode(ctx, colonTk, entryTk, key, value) + if err != nil { + return nil, err + } + node.Values = append(node.Values, mapValue) + } + default: + if !p.isFlowMapDelim(ctx.nextToken()) { + errTk := mapKeyTk + if errTk == nil { + errTk = tk + } + return nil, errors.ErrSyntax("could not find flow map content", errTk.RawToken()) + } + key, err := p.parseScalarValue(ctx, mapKeyTk) + if err != nil { + return nil, err + } + value, err := newNullNode(ctx, ctx.insertNullToken(mapKeyTk)) + if err != nil { + return nil, err + } + mapValue, err := newMappingValueNode(ctx, mapKeyTk, entryTk, key, value) + if err != nil { + return nil, err + } + node.Values = append(node.Values, mapValue) + ctx.goNext() + } + isFirst = false } + if node.End == nil { + return nil, errors.ErrSyntax("could not find flow mapping end token '}'", node.Start) + } + ctx.goNext() // skip mapping end token. return node, nil } -func (p *parser) parseTag(ctx *context) (*ast.TagNode, error) { - tagToken := ctx.currentToken() - node := ast.Tag(tagToken) - node.SetPath(ctx.path) - ctx.progress(1) // skip tag token - var ( - value ast.Node - err error - ) - switch token.ReservedTagKeyword(tagToken.Value) { - case token.MappingTag, - token.OrderedMapTag: - value, err = p.parseMapping(ctx) - case token.IntegerTag, - token.FloatTag, - token.StringTag, - token.BinaryTag, - token.TimestampTag, - token.NullTag: - typ := ctx.currentToken().Type - if typ == token.LiteralType || typ == token.FoldedType { - value, err = p.parseLiteral(ctx) - } else { - value = p.parseScalarValue(ctx.currentToken()) +func (p *parser) isFlowMapDelim(tk *Token) bool { + return tk.Type() == token.MappingEndType || tk.Type() == token.CollectEntryType +} + +func (p *parser) parseMap(ctx *context) (*ast.MappingNode, error) { + keyTk := ctx.currentToken() + if keyTk.Group == nil { + return nil, errors.ErrSyntax("unexpected map key", keyTk.RawToken()) + } + var keyValueNode *ast.MappingValueNode + if keyTk.GroupType() == TokenGroupMapKeyValue { + node, err := p.parseMapKeyValue(ctx.withGroup(keyTk.Group), keyTk.Group, nil) + if err != nil { + return nil, err } - case token.SequenceTag, - token.SetTag: - err = errors.ErrSyntax(fmt.Sprintf("sorry, currently not supported %s tag", tagToken.Value), tagToken) - default: - // custom tag - value, err = p.parseToken(ctx, ctx.currentToken()) + keyValueNode = node + ctx.goNext() + if err := p.validateMapKeyValueNextToken(ctx, keyTk, ctx.currentToken()); err != nil { + return nil, err + } + } else { + key, err := p.parseMapKey(ctx.withGroup(keyTk.Group), keyTk.Group) + if err != nil { + return nil, err + } + ctx.goNext() + + valueTk := ctx.currentToken() + if keyTk.Line() == valueTk.Line() && valueTk.Type() == token.SequenceEntryType { + return nil, errors.ErrSyntax("block sequence entries are not allowed in this context", valueTk.RawToken()) + } + ctx := ctx.withChild(p.mapKeyText(key)) + value, err := p.parseMapValue(ctx, key, keyTk.Group.Last()) + if err != nil { + return nil, err + } + node, err := newMappingValueNode(ctx, keyTk.Group.Last(), nil, key, value) + if err != nil { + return nil, err + } + keyValueNode = node } + mapNode, err := newMappingNode(ctx, &Token{Token: keyValueNode.GetToken()}, false, keyValueNode) if err != nil { - return nil, errors.Wrapf(err, "failed to parse tag value") + return nil, err } - node.Value = value - return node, nil + var tk *Token + if ctx.isComment() { + tk = ctx.nextNotCommentToken() + } else { + tk = ctx.currentToken() + } + for tk.Column() == keyTk.Column() { + typ := tk.Type() + if ctx.isFlow && typ == token.SequenceEndType { + // [ + // key: value + // ] <= + break + } + if !p.isMapToken(tk) { + return nil, errors.ErrSyntax("non-map value is specified", tk.RawToken()) + } + cm := p.parseHeadComment(ctx) + if typ == token.MappingEndType { + // a: { + // b: c + // } <= + ctx.goNext() + break + } + node, err := p.parseMap(ctx) + if err != nil { + return nil, err + } + if len(node.Values) != 0 { + if err := setHeadComment(cm, node.Values[0]); err != nil { + return nil, err + } + } + mapNode.Values = append(mapNode.Values, node.Values...) + if node.FootComment != nil { + mapNode.Values[len(mapNode.Values)-1].FootComment = node.FootComment + } + tk = ctx.currentToken() + } + if ctx.isComment() { + if keyTk.Column() <= ctx.currentToken().Column() { + // If the comment is in the same or deeper column as the last element column in map value, + // treat it as a footer comment for the last element. + if len(mapNode.Values) == 1 { + mapNode.Values[0].FootComment = p.parseFootComment(ctx, keyTk.Column()) + mapNode.Values[0].FootComment.SetPath(mapNode.Values[0].Key.GetPath()) + } else { + mapNode.FootComment = p.parseFootComment(ctx, keyTk.Column()) + mapNode.FootComment.SetPath(mapNode.GetPath()) + } + } + } + return mapNode, nil } -func (p *parser) removeLeftSideNewLineCharacter(src string) string { - // CR or LF or CRLF - return strings.TrimLeft(strings.TrimLeft(strings.TrimLeft(src, "\r"), "\n"), "\r\n") +func (p *parser) validateMapKeyValueNextToken(ctx *context, keyTk, tk *Token) error { + if tk == nil { + return nil + } + if tk.Column() <= keyTk.Column() { + return nil + } + if ctx.isComment() { + return nil + } + if ctx.isFlow && (tk.Type() == token.CollectEntryType || tk.Type() == token.SequenceEndType) { + return nil + } + // a: b + // c <= this token is invalid. + return errors.ErrSyntax("value is not allowed in this context. map key-value is pre-defined", tk.RawToken()) } -func (p *parser) existsNewLineCharacter(src string) bool { - if strings.Index(src, "\n") > 0 { - return true +func (p *parser) isMapToken(tk *Token) bool { + if tk.Group == nil { + return tk.Type() == token.MappingStartType || tk.Type() == token.MappingEndType + } + g := tk.Group + return g.Type == TokenGroupMapKey || g.Type == TokenGroupMapKeyValue +} + +func (p *parser) parseMapKeyValue(ctx *context, g *TokenGroup, entryTk *Token) (*ast.MappingValueNode, error) { + if g.Type != TokenGroupMapKeyValue { + return nil, errors.ErrSyntax("unexpected map key-value pair", g.RawToken()) + } + if g.First().Group == nil { + return nil, errors.ErrSyntax("unexpected map key", g.RawToken()) } - if strings.Index(src, "\r") > 0 { - return true + keyGroup := g.First().Group + key, err := p.parseMapKey(ctx.withGroup(keyGroup), keyGroup) + if err != nil { + return nil, err } - return false + + c := ctx.withChild(p.mapKeyText(key)) + value, err := p.parseToken(c, g.Last()) + if err != nil { + return nil, err + } + return newMappingValueNode(c, keyGroup.Last(), entryTk, key, value) } -func (p *parser) validateMapKey(tk *token.Token) error { - if tk.Type != token.StringType { +func (p *parser) parseMapKey(ctx *context, g *TokenGroup) (ast.MapKeyNode, error) { + if g.Type != TokenGroupMapKey { + return nil, errors.ErrSyntax("unexpected map key", g.RawToken()) + } + if g.First().Type() == token.MappingKeyType { + mapKeyTk := g.First() + if mapKeyTk.Group != nil { + ctx = ctx.withGroup(mapKeyTk.Group) + } + key, err := newMappingKeyNode(ctx, mapKeyTk) + if err != nil { + return nil, err + } + ctx.goNext() // skip mapping key token + if ctx.isTokenNotFound() { + return nil, errors.ErrSyntax("could not find value for mapping key", mapKeyTk.RawToken()) + } + + scalar, err := p.parseScalarValue(ctx, ctx.currentToken()) + if err != nil { + return nil, err + } + key.Value = scalar + keyText := p.mapKeyText(scalar) + keyPath := ctx.withChild(keyText).path + key.SetPath(keyPath) + if err := p.validateMapKey(ctx, key.GetToken(), keyPath, g.Last()); err != nil { + return nil, err + } + p.pathMap[keyPath] = key + return key, nil + } + if g.Last().Type() != token.MappingValueType { + return nil, errors.ErrSyntax("expected map key-value delimiter ':'", g.Last().RawToken()) + } + + scalar, err := p.parseScalarValue(ctx, g.First()) + if err != nil { + return nil, err + } + key, ok := scalar.(ast.MapKeyNode) + if !ok { + return nil, errors.ErrSyntax("cannot take map-key node", scalar.GetToken()) + } + keyText := p.mapKeyText(key) + keyPath := ctx.withChild(keyText).path + key.SetPath(keyPath) + if err := p.validateMapKey(ctx, key.GetToken(), keyPath, g.Last()); err != nil { + return nil, err + } + p.pathMap[keyPath] = key + return key, nil +} + +func (p *parser) validateMapKey(ctx *context, tk *token.Token, keyPath string, colonTk *Token) error { + if !p.allowDuplicateMapKey { + if n, exists := p.pathMap[keyPath]; exists { + pos := n.GetToken().Position + return errors.ErrSyntax( + fmt.Sprintf("mapping key %q already defined at [%d:%d]", tk.Value, pos.Line, pos.Column), + tk, + ) + } + } + origin := p.removeLeftWhiteSpace(tk.Origin) + if ctx.isFlow { + if tk.Type == token.StringType { + origin = p.removeRightWhiteSpace(origin) + if tk.Position.Line+p.newLineCharacterNum(origin) != colonTk.Line() { + return errors.ErrSyntax("map key definition includes an implicit line break", tk) + } + } + return nil + } + if tk.Type != token.StringType && tk.Type != token.SingleQuoteType && tk.Type != token.DoubleQuoteType { return nil } - origin := p.removeLeftSideNewLineCharacter(tk.Origin) if p.existsNewLineCharacter(origin) { return errors.ErrSyntax("unexpected key name", tk) } return nil } -func (p *parser) createNullToken(base *token.Token) *token.Token { - pos := *(base.Position) - pos.Column++ - return token.New("null", "null", &pos) +func (p *parser) removeLeftWhiteSpace(src string) string { + // CR or LF or CRLF + return strings.TrimLeftFunc(src, func(r rune) bool { + return r == ' ' || r == '\r' || r == '\n' + }) } -func (p *parser) parseMapValue(ctx *context, key ast.MapKeyNode, colonToken *token.Token) (ast.Node, error) { - node, err := p.createMapValueNode(ctx, key, colonToken) - if err != nil { - return nil, errors.Wrapf(err, "failed to create map value node") - } - if node != nil && node.GetPath() == "" { - node.SetPath(ctx.path) +func (p *parser) removeRightWhiteSpace(src string) string { + // CR or LF or CRLF + return strings.TrimRightFunc(src, func(r rune) bool { + return r == ' ' || r == '\r' || r == '\n' + }) +} + +func (p *parser) existsNewLineCharacter(src string) bool { + return p.newLineCharacterNum(src) > 0 +} + +func (p *parser) newLineCharacterNum(src string) int { + var num int + for i := 0; i < len(src); i++ { + switch src[i] { + case '\r': + if len(src) > i+1 && src[i+1] == '\n' { + i++ + } + num++ + case '\n': + num++ + } } - return node, nil + return num } -func (p *parser) createMapValueNode(ctx *context, key ast.MapKeyNode, colonToken *token.Token) (ast.Node, error) { +func (p *parser) mapKeyText(n ast.Node) string { + if n == nil { + return "" + } + switch nn := n.(type) { + case *ast.MappingKeyNode: + return p.mapKeyText(nn.Value) + case *ast.TagNode: + return p.mapKeyText(nn.Value) + case *ast.AnchorNode: + return p.mapKeyText(nn.Value) + case *ast.AliasNode: + return "" + } + return n.GetToken().Value +} + +func (p *parser) parseMapValue(ctx *context, key ast.MapKeyNode, colonTk *Token) (ast.Node, error) { tk := ctx.currentToken() if tk == nil { - nullToken := p.createNullToken(colonToken) - ctx.insertToken(ctx.idx, nullToken) - return ast.Null(nullToken), nil + return newNullNode(ctx, ctx.addNullValueToken(colonTk)) } - var comment *ast.CommentGroupNode - if tk.Type == token.CommentType { - comment = p.parseCommentOnly(ctx) - if comment != nil { - comment.SetPath(ctx.withChild(key.GetToken().Value).path) - } - tk = ctx.currentToken() + + if ctx.isComment() { + tk = ctx.nextNotCommentToken() } - if tk.Position.Column == key.GetToken().Position.Column && tk.Type == token.StringType { + keyCol := key.GetToken().Position.Column + keyLine := key.GetToken().Position.Line + + if tk.Column() != keyCol && tk.Line() == keyLine && (tk.GroupType() == TokenGroupMapKey || tk.GroupType() == TokenGroupMapKeyValue) { + // a: b: + // ^ + // + // a: b: c + // ^ + return nil, errors.ErrSyntax("mapping value is not allowed in this context", tk.RawToken()) + } + + if tk.Column() == keyCol && p.isMapToken(tk) { // in this case, // ---- // key: // next + return newNullNode(ctx, ctx.insertNullToken(colonTk)) + } - nullToken := p.createNullToken(colonToken) - ctx.insertToken(ctx.idx, nullToken) - nullNode := ast.Null(nullToken) - - if comment != nil { - nullNode.SetComment(comment) - } else { - // If there is a comment, it is already bound to the key node, - // so remove the comment from the key to bind it to the null value. - keyComment := key.GetComment() - if keyComment != nil { - if err := key.SetComment(nil); err != nil { - return nil, err - } - nullNode.SetComment(keyComment) - } + if tk.Line() == keyLine && tk.GroupType() == TokenGroupAnchorName && + ctx.nextToken().Column() == keyCol && p.isMapToken(ctx.nextToken()) { + // in this case, + // ---- + // key: &anchor + // next + group := &TokenGroup{ + Type: TokenGroupAnchor, + Tokens: []*Token{tk, ctx.createImplicitNullToken(tk)}, } - return nullNode, nil + anchor, err := p.parseAnchor(ctx.withGroup(group), group) + if err != nil { + return nil, err + } + ctx.goNext() + return anchor, nil + } + + if tk.Column() <= keyCol && tk.GroupType() == TokenGroupAnchorName { + // key: + // &anchor + return nil, errors.ErrSyntax("anchor is not allowed in this context", tk.RawToken()) + } + if tk.Column() <= keyCol && tk.Type() == token.TagType { + // key: + // !!tag + return nil, errors.ErrSyntax("tag is not allowed in this context", tk.RawToken()) + } + + if tk.Column() < keyCol { + // in this case, + // ---- + // key: + // next + return newNullNode(ctx, ctx.insertNullToken(colonTk)) } - if tk.Position.Column < key.GetToken().Position.Column { + if tk.Line() == keyLine && tk.GroupType() == TokenGroupAnchorName && + ctx.nextToken().Column() < keyCol { // in this case, // ---- - // key: + // key: &anchor // next - nullToken := p.createNullToken(colonToken) - ctx.insertToken(ctx.idx, nullToken) - nullNode := ast.Null(nullToken) - if comment != nil { - nullNode.SetComment(comment) + group := &TokenGroup{ + Type: TokenGroupAnchor, + Tokens: []*Token{tk, ctx.createImplicitNullToken(tk)}, + } + anchor, err := p.parseAnchor(ctx.withGroup(group), group) + if err != nil { + return nil, err } - return nullNode, nil + ctx.goNext() + return anchor, nil } value, err := p.parseToken(ctx, ctx.currentToken()) if err != nil { - return nil, errors.Wrapf(err, "failed to parse mapping 'value' node") + return nil, err } - if comment != nil { - value.SetComment(comment) + if err := p.validateAnchorValueInMapOrSeq(value, keyCol); err != nil { + return nil, err } return value, nil } -func (p *parser) validateMapValue(ctx *context, key, value ast.Node) error { - keyColumn := key.GetToken().Position.Column - valueColumn := value.GetToken().Position.Column - if keyColumn != valueColumn { +func (p *parser) validateAnchorValueInMapOrSeq(value ast.Node, col int) error { + anchor, ok := value.(*ast.AnchorNode) + if !ok { return nil } - if value.Type() != ast.StringType { + tag, ok := anchor.Value.(*ast.TagNode) + if !ok { return nil } - ntk := ctx.nextToken() - if ntk == nil || (ntk.Type != token.MappingValueType && ntk.Type != token.SequenceEntryType) { - return errors.ErrSyntax("could not found expected ':' token", value.GetToken()) - } - return nil -} - -func (p *parser) parseMappingValue(ctx *context) (ast.Node, error) { - key, err := p.parseMapKey(ctx) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse map key") - } - keyText := key.GetToken().Value - key.SetPath(ctx.withChild(keyText).path) - if err := p.validateMapKey(key.GetToken()); err != nil { - return nil, errors.Wrapf(err, "validate mapping key error") - } - ctx.progress(1) // progress to mapping value token - tk := ctx.currentToken() // get mapping value token - if tk == nil { - return nil, errors.ErrSyntax("unexpected map", key.GetToken()) - } - ctx.progress(1) // progress to value token - if err := p.setSameLineCommentIfExists(ctx.withChild(keyText), key); err != nil { - return nil, errors.Wrapf(err, "failed to set same line comment to node") - } - if key.GetComment() != nil { - // if current token is comment, GetComment() is not nil. - // then progress to value token - ctx.progressIgnoreComment(1) - } - - value, err := p.parseMapValue(ctx.withChild(keyText), key, tk) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse map value") - } - if err := p.validateMapValue(ctx, key, value); err != nil { - return nil, errors.Wrapf(err, "failed to validate map value") - } - - mvnode := ast.MappingValue(tk, key, value) - mvnode.SetPath(ctx.withChild(keyText).path) - node := ast.Mapping(tk, false, mvnode) - node.SetPath(ctx.withChild(keyText).path) + anchorTk := anchor.GetToken() + tagTk := tag.GetToken() - ntk := ctx.nextNotCommentToken() - antk := ctx.afterNextNotCommentToken() - for antk != nil && antk.Type == token.MappingValueType && - ntk.Position.Column == key.GetToken().Position.Column { - ctx.progressIgnoreComment(1) - value, err := p.parseToken(ctx, ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse mapping node") - } - switch value.Type() { - case ast.MappingType: - c := value.(*ast.MappingNode) - comment := c.GetComment() - for idx, v := range c.Values { - if idx == 0 && comment != nil { - if err := v.SetComment(comment); err != nil { - return nil, errors.Wrapf(err, "failed to set comment token to node") - } - } - node.Values = append(node.Values, v) - } - case ast.MappingValueType: - node.Values = append(node.Values, value.(*ast.MappingValueNode)) - default: - return nil, xerrors.Errorf("failed to parse mapping value node node is %s", value.Type()) - } - ntk = ctx.nextNotCommentToken() - antk = ctx.afterNextNotCommentToken() - } - if len(node.Values) == 1 { - mapKeyCol := mvnode.Key.GetToken().Position.Column - commentTk := ctx.nextToken() - if commentTk != nil && commentTk.Type == token.CommentType && mapKeyCol <= commentTk.Position.Column { - // If the comment is in the same or deeper column as the last element column in map value, - // treat it as a footer comment for the last element. - comment := p.parseFootComment(ctx, mapKeyCol) - mvnode.FootComment = comment - } - return mvnode, nil - } - mapCol := node.GetToken().Position.Column - commentTk := ctx.nextToken() - if commentTk != nil && commentTk.Type == token.CommentType && mapCol <= commentTk.Position.Column { - // If the comment is in the same or deeper column as the last element column in map value, - // treat it as a footer comment for the last element. - comment := p.parseFootComment(ctx, mapCol) - node.FootComment = comment + if anchorTk.Position.Line == tagTk.Position.Line { + // key: + // &anchor !!tag + // + // - &anchor !!tag + return nil } - return node, nil -} -func (p *parser) parseSequenceEntry(ctx *context) (*ast.SequenceNode, error) { - tk := ctx.currentToken() - sequenceNode := ast.Sequence(tk, false) - sequenceNode.SetPath(ctx.path) - curColumn := tk.Position.Column - for tk.Type == token.SequenceEntryType { - ctx.progress(1) // skip sequence token - tk = ctx.currentToken() - if tk == nil { - return nil, errors.ErrSyntax("empty sequence entry", ctx.previousToken()) - } - var comment *ast.CommentGroupNode - if tk.Type == token.CommentType { - comment = p.parseCommentOnly(ctx) - tk = ctx.currentToken() - if tk.Type == token.SequenceEntryType { - ctx.progress(1) // skip sequence token - } - } - value, err := p.parseToken(ctx.withIndex(uint(len(sequenceNode.Values))), ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse sequence") - } - if comment != nil { - comment.SetPath(ctx.withIndex(uint(len(sequenceNode.Values))).path) - sequenceNode.ValueHeadComments = append(sequenceNode.ValueHeadComments, comment) - } else { - sequenceNode.ValueHeadComments = append(sequenceNode.ValueHeadComments, nil) - } - sequenceNode.Values = append(sequenceNode.Values, value) - tk = ctx.nextNotCommentToken() - if tk == nil { - break - } - if tk.Type != token.SequenceEntryType { - break - } - if tk.Position.Column != curColumn { - break - } - ctx.progressIgnoreComment(1) - } - commentTk := ctx.nextToken() - if commentTk != nil && commentTk.Type == token.CommentType && curColumn <= commentTk.Position.Column { - // If the comment is in the same or deeper column as the last element column in sequence value, - // treat it as a footer comment for the last element. - comment := p.parseFootComment(ctx, curColumn) - sequenceNode.FootComment = comment + if tagTk.Position.Column <= col { + // key: &anchor + // !!tag + // + // - &anchor + // !!tag + return errors.ErrSyntax("tag is not allowed in this context", tagTk) } - return sequenceNode, nil + return nil } -func (p *parser) parseAnchor(ctx *context) (*ast.AnchorNode, error) { - tk := ctx.currentToken() - anchor := ast.Anchor(tk) - anchor.SetPath(ctx.path) - ntk := ctx.nextToken() - if ntk == nil { - return nil, errors.ErrSyntax("unexpected anchor. anchor name is undefined", tk) - } - ctx.progress(1) // skip anchor token - name, err := p.parseToken(ctx, ctx.currentToken()) +func (p *parser) parseAnchor(ctx *context, g *TokenGroup) (*ast.AnchorNode, error) { + anchorNameGroup := g.First().Group + anchor, err := p.parseAnchorName(ctx.withGroup(anchorNameGroup)) if err != nil { - return nil, errors.Wrapf(err, "failed to parser anchor name node") + return nil, err } - anchor.Name = name - ntk = ctx.nextToken() - if ntk == nil { - return nil, errors.ErrSyntax("unexpected anchor. anchor value is undefined", ctx.currentToken()) + ctx.goNext() + if ctx.isTokenNotFound() { + return nil, errors.ErrSyntax("could not find anchor value", anchor.GetToken()) } - ctx.progress(1) + value, err := p.parseToken(ctx, ctx.currentToken()) if err != nil { - return nil, errors.Wrapf(err, "failed to parser anchor name node") + return nil, err + } + if _, ok := value.(*ast.AnchorNode); ok { + return nil, errors.ErrSyntax("anchors cannot be used consecutively", value.GetToken()) } anchor.Value = value return anchor, nil } -func (p *parser) parseAlias(ctx *context) (*ast.AliasNode, error) { - tk := ctx.currentToken() - alias := ast.Alias(tk) - alias.SetPath(ctx.path) - ntk := ctx.nextToken() - if ntk == nil { - return nil, errors.ErrSyntax("unexpected alias. alias name is undefined", tk) - } - ctx.progress(1) // skip alias token - name, err := p.parseToken(ctx, ctx.currentToken()) +func (p *parser) parseAnchorName(ctx *context) (*ast.AnchorNode, error) { + anchor, err := newAnchorNode(ctx, ctx.currentToken()) if err != nil { - return nil, errors.Wrapf(err, "failed to parser alias name node") - } - alias.Value = name - return alias, nil -} - -func (p *parser) parseMapKey(ctx *context) (ast.MapKeyNode, error) { - tk := ctx.currentToken() - if value := p.parseScalarValue(tk); value != nil { - return value, nil - } - switch tk.Type { - case token.MergeKeyType: - return ast.MergeKey(tk), nil - case token.MappingKeyType: - return p.parseMappingKey(ctx) + return nil, err } - return nil, errors.ErrSyntax("unexpected mapping key", tk) -} - -func (p *parser) parseStringValue(tk *token.Token) *ast.StringNode { - switch tk.Type { - case token.StringType, - token.SingleQuoteType, - token.DoubleQuoteType: - return ast.String(tk) + ctx.goNext() + if ctx.isTokenNotFound() { + return nil, errors.ErrSyntax("could not find anchor value", anchor.GetToken()) } - return nil -} -func (p *parser) parseScalarValueWithComment(ctx *context, tk *token.Token) (ast.ScalarNode, error) { - node := p.parseScalarValue(tk) - if node == nil { - return nil, nil + anchorName, err := p.parseScalarValue(ctx, ctx.currentToken()) + if err != nil { + return nil, err } - node.SetPath(ctx.path) - if p.isSameLineComment(ctx.nextToken(), node) { - ctx.progress(1) - if err := p.setSameLineCommentIfExists(ctx, node); err != nil { - return nil, errors.Wrapf(err, "failed to set same line comment to node") - } + if anchorName == nil { + return nil, errors.ErrSyntax("unexpected anchor. anchor name is not scalar value", ctx.currentToken().RawToken()) } - return node, nil + anchor.Name = anchorName + return anchor, nil } -func (p *parser) parseScalarValue(tk *token.Token) ast.ScalarNode { - if node := p.parseStringValue(tk); node != nil { - return node +func (p *parser) parseAlias(ctx *context) (*ast.AliasNode, error) { + alias, err := newAliasNode(ctx, ctx.currentToken()) + if err != nil { + return nil, err } - switch tk.Type { - case token.NullType: - return ast.Null(tk) - case token.BoolType: - return ast.Bool(tk) - case token.IntegerType, - token.BinaryIntegerType, - token.OctetIntegerType, - token.HexIntegerType: - return ast.Integer(tk) - case token.FloatType: - return ast.Float(tk) - case token.InfinityType: - return ast.Infinity(tk) - case token.NanType: - return ast.Nan(tk) + ctx.goNext() + if ctx.isTokenNotFound() { + return nil, errors.ErrSyntax("could not find alias value", alias.GetToken()) } - return nil -} -func (p *parser) parseDirective(ctx *context) (*ast.DirectiveNode, error) { - node := ast.Directive(ctx.currentToken()) - ctx.progress(1) // skip directive token - value, err := p.parseToken(ctx, ctx.currentToken()) + aliasName, err := p.parseScalarValue(ctx, ctx.currentToken()) if err != nil { - return nil, errors.Wrapf(err, "failed to parse directive value") - } - node.Value = value - ctx.progress(1) - tk := ctx.currentToken() - if tk == nil { - // Since current token is nil, use the previous token to specify - // the syntax error location. - return nil, errors.ErrSyntax("unexpected directive value. document not started", ctx.previousToken()) + return nil, err } - if tk.Type != token.DocumentHeaderType { - return nil, errors.ErrSyntax("unexpected directive value. document not started", ctx.currentToken()) + if aliasName == nil { + return nil, errors.ErrSyntax("unexpected alias. alias name is not scalar value", ctx.currentToken().RawToken()) } - return node, nil + alias.Value = aliasName + return alias, nil } func (p *parser) parseLiteral(ctx *context) (*ast.LiteralNode, error) { - node := ast.Literal(ctx.currentToken()) - ctx.progress(1) // skip literal/folded token + node, err := newLiteralNode(ctx, ctx.currentToken()) + if err != nil { + return nil, err + } + ctx.goNext() // skip literal/folded token tk := ctx.currentToken() - var comment *ast.CommentGroupNode - if tk.Type == token.CommentType { - comment = p.parseCommentOnly(ctx) - comment.SetPath(ctx.path) - if err := node.SetComment(comment); err != nil { - return nil, errors.Wrapf(err, "failed to set comment to literal") + if tk == nil { + value, err := newStringNode(ctx, &Token{Token: token.New("", "", node.Start.Position)}) + if err != nil { + return nil, err } - tk = ctx.currentToken() + node.Value = value + return node, nil } value, err := p.parseToken(ctx, tk) if err != nil { - return nil, errors.Wrapf(err, "failed to parse literal/folded value") + return nil, err } - snode, ok := value.(*ast.StringNode) + str, ok := value.(*ast.StringNode) if !ok { return nil, errors.ErrSyntax("unexpected token. required string token", value.GetToken()) } - node.Value = snode + node.Value = str return node, nil } -func (p *parser) isSameLineComment(tk *token.Token, node ast.Node) bool { - if tk == nil { - return false - } - if tk.Type != token.CommentType { - return false +func (p *parser) parseScalarTag(ctx *context) (*ast.TagNode, error) { + tag, err := p.parseTag(ctx) + if err != nil { + return nil, err } - return tk.Position.Line == node.GetToken().Position.Line -} - -func (p *parser) setSameLineCommentIfExists(ctx *context, node ast.Node) error { - tk := ctx.currentToken() - if !p.isSameLineComment(tk, node) { - return nil + if tag.Value == nil { + return nil, errors.ErrSyntax("specified not scalar tag", tag.GetToken()) } - comment := ast.CommentGroup([]*token.Token{tk}) - comment.SetPath(ctx.path) - if err := node.SetComment(comment); err != nil { - return errors.Wrapf(err, "failed to set comment token to ast.Node") + if _, ok := tag.Value.(ast.ScalarNode); !ok { + return nil, errors.ErrSyntax("specified not scalar tag", tag.GetToken()) } - return nil + return tag, nil } -func (p *parser) parseDocument(ctx *context) (*ast.DocumentNode, error) { - startTk := ctx.currentToken() - ctx.progress(1) // skip document header token - body, err := p.parseToken(ctx, ctx.currentToken()) +func (p *parser) parseTag(ctx *context) (*ast.TagNode, error) { + tagTk := ctx.currentToken() + tagRawTk := tagTk.RawToken() + node, err := newTagNode(ctx, tagTk) if err != nil { - return nil, errors.Wrapf(err, "failed to parse document body") + return nil, err + } + ctx.goNext() + + comment := p.parseHeadComment(ctx) + + var tagValue ast.Node + if p.secondaryTagDirective != nil { + value, err := newStringNode(ctx, ctx.currentToken()) + if err != nil { + return nil, err + } + tagValue = value + node.Directive = p.secondaryTagDirective + } else { + value, err := p.parseTagValue(ctx, tagRawTk, ctx.currentToken()) + if err != nil { + return nil, err + } + tagValue = value } - node := ast.Document(startTk, body) - if ntk := ctx.nextToken(); ntk != nil && ntk.Type == token.DocumentEndType { - node.End = ntk - ctx.progress(1) + if err := setHeadComment(comment, tagValue); err != nil { + return nil, err } + node.Value = tagValue return node, nil } -func (p *parser) parseCommentOnly(ctx *context) *ast.CommentGroupNode { - commentTokens := []*token.Token{} - for { - tk := ctx.currentToken() - if tk == nil { - break +func (p *parser) parseTagValue(ctx *context, tagRawTk *token.Token, tk *Token) (ast.Node, error) { + if tk == nil { + return newNullNode(ctx, ctx.createImplicitNullToken(&Token{Token: tagRawTk})) + } + switch token.ReservedTagKeyword(tagRawTk.Value) { + case token.MappingTag, token.SetTag: + if !p.isMapToken(tk) { + return nil, errors.ErrSyntax("could not find map", tk.RawToken()) } - if tk.Type != token.CommentType { - break + if tk.Type() == token.MappingStartType { + return p.parseFlowMap(ctx.withFlow(true)) + } + return p.parseMap(ctx) + case token.IntegerTag, token.FloatTag, token.StringTag, token.BinaryTag, token.TimestampTag, token.BooleanTag, token.NullTag: + if tk.GroupType() == TokenGroupLiteral || tk.GroupType() == TokenGroupFolded { + return p.parseLiteral(ctx.withGroup(tk.Group)) + } else if tk.Type() == token.CollectEntryType || tk.Type() == token.MappingValueType { + return newTagDefaultScalarValueNode(ctx, tagRawTk) + } + scalar, err := p.parseScalarValue(ctx, tk) + if err != nil { + return nil, err + } + ctx.goNext() + return scalar, nil + case token.SequenceTag, token.OrderedMapTag: + if tk.Type() == token.SequenceStartType { + return p.parseFlowSequence(ctx.withFlow(true)) } - commentTokens = append(commentTokens, tk) - ctx.progressIgnoreComment(1) // skip comment token + return p.parseSequence(ctx) } - return ast.CommentGroup(commentTokens) + return p.parseToken(ctx, tk) } -func (p *parser) parseFootComment(ctx *context, col int) *ast.CommentGroupNode { - commentTokens := []*token.Token{} - for { - ctx.progressIgnoreComment(1) - commentTokens = append(commentTokens, ctx.currentToken()) +func (p *parser) parseFlowSequence(ctx *context) (*ast.SequenceNode, error) { + node, err := newSequenceNode(ctx, ctx.currentToken(), true) + if err != nil { + return nil, err + } + ctx.goNext() // skip SequenceStart token - nextTk := ctx.nextToken() - if nextTk == nil { + isFirst := true + for ctx.next() { + tk := ctx.currentToken() + if tk.Type() == token.SequenceEndType { + node.End = tk.RawToken() break } - if nextTk.Type != token.CommentType { + + var entryTk *Token + if tk.Type() == token.CollectEntryType { + if isFirst { + return nil, errors.ErrSyntax("expected sequence element, but found ','", tk.RawToken()) + } + entryTk = tk + ctx.goNext() + } else if !isFirst { + return nil, errors.ErrSyntax("',' or ']' must be specified", tk.RawToken()) + } + + if tk := ctx.currentToken(); tk.Type() == token.SequenceEndType { + // this case is here: "[ elem, ]". + // In this case, ignore the last element and break sequence parsing. + node.End = tk.RawToken() break } - if col > nextTk.Position.Column { + + if ctx.isTokenNotFound() { break } - } - return ast.CommentGroup(commentTokens) -} -func (p *parser) parseComment(ctx *context) (ast.Node, error) { - group := p.parseCommentOnly(ctx) - node, err := p.parseToken(ctx, ctx.currentToken()) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse node after comment") - } - if node == nil { - return group, nil + ctx := ctx.withIndex(uint(len(node.Values))) + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, err + } + node.Values = append(node.Values, value) + seqEntry := ast.SequenceEntry(entryTk.RawToken(), value, nil) + if err := setLineComment(ctx, seqEntry, entryTk); err != nil { + return nil, err + } + seqEntry.SetPath(ctx.path) + node.Entries = append(node.Entries, seqEntry) + + isFirst = false } - group.SetPath(node.GetPath()) - if err := node.SetComment(group); err != nil { - return nil, errors.Wrapf(err, "failed to set comment token to node") + if node.End == nil { + return nil, errors.ErrSyntax("sequence end token ']' not found", node.Start) } + ctx.goNext() // skip sequence end token. return node, nil } -func (p *parser) parseMappingKey(ctx *context) (*ast.MappingKeyNode, error) { - keyTk := ctx.currentToken() - node := ast.MappingKey(keyTk) - node.SetPath(ctx.path) - ctx.progress(1) // skip mapping key token - value, err := p.parseToken(ctx.withChild(keyTk.Value), ctx.currentToken()) +func (p *parser) parseSequence(ctx *context) (*ast.SequenceNode, error) { + seqTk := ctx.currentToken() + seqNode, err := newSequenceNode(ctx, seqTk, false) if err != nil { - return nil, errors.Wrapf(err, "failed to parse map key") + return nil, err } - node.Value = value - return node, nil -} -func (p *parser) parseToken(ctx *context, tk *token.Token) (ast.Node, error) { - node, err := p.createNodeFromToken(ctx, tk) - if err != nil { - return nil, errors.Wrapf(err, "failed to create node from token") + tk := seqTk + for tk.Type() == token.SequenceEntryType && tk.Column() == seqTk.Column() { + seqTk := tk + headComment := p.parseHeadComment(ctx) + ctx.goNext() // skip sequence entry token + + ctx := ctx.withIndex(uint(len(seqNode.Values))) + value, err := p.parseSequenceValue(ctx, seqTk) + if err != nil { + return nil, err + } + seqEntry := ast.SequenceEntry(seqTk.RawToken(), value, headComment) + if err := setLineComment(ctx, seqEntry, seqTk); err != nil { + return nil, err + } + seqEntry.SetPath(ctx.path) + seqNode.ValueHeadComments = append(seqNode.ValueHeadComments, headComment) + seqNode.Values = append(seqNode.Values, value) + seqNode.Entries = append(seqNode.Entries, seqEntry) + + if ctx.isComment() { + tk = ctx.nextNotCommentToken() + } else { + tk = ctx.currentToken() + } } - if node != nil && node.GetPath() == "" { - node.SetPath(ctx.path) + if ctx.isComment() { + if seqTk.Column() <= ctx.currentToken().Column() { + // If the comment is in the same or deeper column as the last element column in sequence value, + // treat it as a footer comment for the last element. + seqNode.FootComment = p.parseFootComment(ctx, seqTk.Column()) + if len(seqNode.Values) != 0 { + seqNode.FootComment.SetPath(seqNode.Values[len(seqNode.Values)-1].GetPath()) + } + } } - return node, nil + return seqNode, nil } -func (p *parser) createNodeFromToken(ctx *context, tk *token.Token) (ast.Node, error) { +func (p *parser) parseSequenceValue(ctx *context, seqTk *Token) (ast.Node, error) { + tk := ctx.currentToken() if tk == nil { - return nil, nil + return newNullNode(ctx, ctx.addNullValueToken(seqTk)) } - if tk.NextType() == token.MappingValueType { - node, err := p.parseMappingValue(ctx) - return node, err + + if ctx.isComment() { + tk = ctx.nextNotCommentToken() } - node, err := p.parseScalarValueWithComment(ctx, tk) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse scalar value") + seqCol := seqTk.Column() + seqLine := seqTk.Line() + + if tk.Column() == seqCol && tk.Type() == token.SequenceEntryType { + // in this case, + // ---- + // - + // - + return newNullNode(ctx, ctx.insertNullToken(seqTk)) } - if node != nil { - return node, nil + + if tk.Line() == seqLine && tk.GroupType() == TokenGroupAnchorName && + ctx.nextToken().Column() == seqCol && ctx.nextToken().Type() == token.SequenceEntryType { + // in this case, + // ---- + // - &anchor + // - + group := &TokenGroup{ + Type: TokenGroupAnchor, + Tokens: []*Token{tk, ctx.createImplicitNullToken(tk)}, + } + anchor, err := p.parseAnchor(ctx.withGroup(group), group) + if err != nil { + return nil, err + } + ctx.goNext() + return anchor, nil } - switch tk.Type { - case token.CommentType: - return p.parseComment(ctx) - case token.MappingKeyType: - return p.parseMappingKey(ctx) - case token.DocumentHeaderType: - return p.parseDocument(ctx) - case token.MappingStartType: - return p.parseMapping(ctx) - case token.SequenceStartType: - return p.parseSequence(ctx) - case token.SequenceEntryType: - return p.parseSequenceEntry(ctx) - case token.AnchorType: - return p.parseAnchor(ctx) - case token.AliasType: - return p.parseAlias(ctx) - case token.DirectiveType: - return p.parseDirective(ctx) - case token.TagType: - return p.parseTag(ctx) - case token.LiteralType, token.FoldedType: - return p.parseLiteral(ctx) + + if tk.Column() <= seqCol && tk.GroupType() == TokenGroupAnchorName { + // - + // &anchor + return nil, errors.ErrSyntax("anchor is not allowed in this sequence context", tk.RawToken()) + } + if tk.Column() <= seqCol && tk.Type() == token.TagType { + // - + // !!tag + return nil, errors.ErrSyntax("tag is not allowed in this sequence context", tk.RawToken()) + } + + if tk.Column() < seqCol { + // in this case, + // ---- + // - + // next + return newNullNode(ctx, ctx.insertNullToken(seqTk)) + } + + if tk.Line() == seqLine && tk.GroupType() == TokenGroupAnchorName && + ctx.nextToken().Column() < seqCol { + // in this case, + // ---- + // - &anchor + // next + group := &TokenGroup{ + Type: TokenGroupAnchor, + Tokens: []*Token{tk, ctx.createImplicitNullToken(tk)}, + } + anchor, err := p.parseAnchor(ctx.withGroup(group), group) + if err != nil { + return nil, err + } + ctx.goNext() + return anchor, nil + } + + value, err := p.parseToken(ctx, ctx.currentToken()) + if err != nil { + return nil, err + } + if err := p.validateAnchorValueInMapOrSeq(value, seqCol); err != nil { + return nil, err } - return nil, nil + return value, nil } -func (p *parser) parse(tokens token.Tokens, mode Mode) (*ast.File, error) { - ctx := newContext(tokens, mode) - file := &ast.File{Docs: []*ast.DocumentNode{}} - for ctx.next() { - node, err := p.parseToken(ctx, ctx.currentToken()) +func (p *parser) parseDirective(ctx *context, g *TokenGroup) (*ast.DirectiveNode, error) { + directiveNameGroup := g.First().Group + directive, err := p.parseDirectiveName(ctx.withGroup(directiveNameGroup)) + if err != nil { + return nil, err + } + + switch directive.Name.String() { + case "YAML": + if len(g.Tokens) != 2 { + return nil, errors.ErrSyntax("unexpected format YAML directive", g.First().RawToken()) + } + valueTk := g.Tokens[1] + valueRawTk := valueTk.RawToken() + value := valueRawTk.Value + ver, exists := yamlVersionMap[value] + if !exists { + return nil, errors.ErrSyntax(fmt.Sprintf("unknown YAML version %q", value), valueRawTk) + } + if p.yamlVersion != "" { + return nil, errors.ErrSyntax("YAML version has already been specified", valueRawTk) + } + p.yamlVersion = ver + versionNode, err := newStringNode(ctx, valueTk) if err != nil { - return nil, errors.Wrapf(err, "failed to parse") + return nil, err } - ctx.progressIgnoreComment(1) - if node == nil { - continue + directive.Values = append(directive.Values, versionNode) + case "TAG": + if len(g.Tokens) != 3 { + return nil, errors.ErrSyntax("unexpected format TAG directive", g.First().RawToken()) } - if doc, ok := node.(*ast.DocumentNode); ok { - file.Docs = append(file.Docs, doc) - } else { - file.Docs = append(file.Docs, ast.Document(nil, node)) + tagKey, err := newStringNode(ctx, g.Tokens[1]) + if err != nil { + return nil, err + } + if tagKey.Value == "!!" { + p.secondaryTagDirective = directive + } + tagValue, err := newStringNode(ctx, g.Tokens[2]) + if err != nil { + return nil, err + } + directive.Values = append(directive.Values, tagKey, tagValue) + default: + if len(g.Tokens) > 1 { + for _, tk := range g.Tokens[1:] { + value, err := newStringNode(ctx, tk) + if err != nil { + return nil, err + } + directive.Values = append(directive.Values, value) + } } } - return file, nil + return directive, nil } -type Mode uint - -const ( - ParseComments Mode = 1 << iota // parse comments and add them to AST -) +func (p *parser) parseDirectiveName(ctx *context) (*ast.DirectiveNode, error) { + directive, err := newDirectiveNode(ctx, ctx.currentToken()) + if err != nil { + return nil, err + } + ctx.goNext() + if ctx.isTokenNotFound() { + return nil, errors.ErrSyntax("could not find directive value", directive.GetToken()) + } -// ParseBytes parse from byte slice, and returns ast.File -func ParseBytes(bytes []byte, mode Mode) (*ast.File, error) { - tokens := lexer.Tokenize(string(bytes)) - f, err := Parse(tokens, mode) + directiveName, err := p.parseScalarValue(ctx, ctx.currentToken()) if err != nil { - return nil, errors.Wrapf(err, "failed to parse") + return nil, err } - return f, nil + if directiveName == nil { + return nil, errors.ErrSyntax("unexpected directive. directive name is not scalar value", ctx.currentToken().RawToken()) + } + directive.Name = directiveName + return directive, nil } -// Parse parse from token instances, and returns ast.File -func Parse(tokens token.Tokens, mode Mode) (*ast.File, error) { - var p parser - f, err := p.parse(tokens, mode) +func (p *parser) parseComment(ctx *context) (ast.Node, error) { + cm := p.parseHeadComment(ctx) + if ctx.isTokenNotFound() { + return cm, nil + } + node, err := p.parseToken(ctx, ctx.currentToken()) if err != nil { - return nil, errors.Wrapf(err, "failed to parse") + return nil, err } - return f, nil + if err := setHeadComment(cm, node); err != nil { + return nil, err + } + return node, nil } -// Parse parse from filename, and returns ast.File -func ParseFile(filename string, mode Mode) (*ast.File, error) { - file, err := os.ReadFile(filename) - if err != nil { - return nil, errors.Wrapf(err, "failed to read file: %s", filename) +func (p *parser) parseHeadComment(ctx *context) *ast.CommentGroupNode { + tks := []*token.Token{} + for ctx.isComment() { + tks = append(tks, ctx.currentToken().RawToken()) + ctx.goNext() } - f, err := ParseBytes(file, mode) - if err != nil { - return nil, errors.Wrapf(err, "failed to parse") + if len(tks) == 0 { + return nil } - f.Name = filename - return f, nil + return ast.CommentGroup(tks) +} + +func (p *parser) parseFootComment(ctx *context, col int) *ast.CommentGroupNode { + tks := []*token.Token{} + for ctx.isComment() && col <= ctx.currentToken().Column() { + tks = append(tks, ctx.currentToken().RawToken()) + ctx.goNext() + } + if len(tks) == 0 { + return nil + } + return ast.CommentGroup(tks) } diff --git a/vendor/github.com/goccy/go-yaml/parser/token.go b/vendor/github.com/goccy/go-yaml/parser/token.go new file mode 100644 index 00000000..b07c018c --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/parser/token.go @@ -0,0 +1,746 @@ +package parser + +import ( + "fmt" + "os" + "strings" + + "github.com/goccy/go-yaml/internal/errors" + "github.com/goccy/go-yaml/token" +) + +type TokenGroupType int + +const ( + TokenGroupNone TokenGroupType = iota + TokenGroupDirective + TokenGroupDirectiveName + TokenGroupDocument + TokenGroupDocumentBody + TokenGroupAnchor + TokenGroupAnchorName + TokenGroupAlias + TokenGroupLiteral + TokenGroupFolded + TokenGroupScalarTag + TokenGroupMapKey + TokenGroupMapKeyValue +) + +func (t TokenGroupType) String() string { + switch t { + case TokenGroupNone: + return "none" + case TokenGroupDirective: + return "directive" + case TokenGroupDirectiveName: + return "directive_name" + case TokenGroupDocument: + return "document" + case TokenGroupDocumentBody: + return "document_body" + case TokenGroupAnchor: + return "anchor" + case TokenGroupAnchorName: + return "anchor_name" + case TokenGroupAlias: + return "alias" + case TokenGroupLiteral: + return "literal" + case TokenGroupFolded: + return "folded" + case TokenGroupScalarTag: + return "scalar_tag" + case TokenGroupMapKey: + return "map_key" + case TokenGroupMapKeyValue: + return "map_key_value" + } + return "none" +} + +type Token struct { + Token *token.Token + Group *TokenGroup + LineComment *token.Token +} + +func (t *Token) RawToken() *token.Token { + if t == nil { + return nil + } + if t.Token != nil { + return t.Token + } + return t.Group.RawToken() +} + +func (t *Token) Type() token.Type { + if t == nil { + return 0 + } + if t.Token != nil { + return t.Token.Type + } + return t.Group.TokenType() +} + +func (t *Token) GroupType() TokenGroupType { + if t == nil { + return TokenGroupNone + } + if t.Token != nil { + return TokenGroupNone + } + return t.Group.Type +} + +func (t *Token) Line() int { + if t == nil { + return 0 + } + if t.Token != nil { + return t.Token.Position.Line + } + return t.Group.Line() +} + +func (t *Token) Column() int { + if t == nil { + return 0 + } + if t.Token != nil { + return t.Token.Position.Column + } + return t.Group.Column() +} + +func (t *Token) SetGroupType(typ TokenGroupType) { + if t.Group == nil { + return + } + t.Group.Type = typ +} + +func (t *Token) Dump() { + ctx := new(groupTokenRenderContext) + if t.Token != nil { + fmt.Fprint(os.Stdout, t.Token.Value) + return + } + t.Group.dump(ctx) + fmt.Fprintf(os.Stdout, "\n") +} + +func (t *Token) dump(ctx *groupTokenRenderContext) { + if t.Token != nil { + fmt.Fprint(os.Stdout, t.Token.Value) + return + } + t.Group.dump(ctx) +} + +type groupTokenRenderContext struct { + num int +} + +type TokenGroup struct { + Type TokenGroupType + Tokens []*Token +} + +func (g *TokenGroup) First() *Token { + if len(g.Tokens) == 0 { + return nil + } + return g.Tokens[0] +} + +func (g *TokenGroup) Last() *Token { + if len(g.Tokens) == 0 { + return nil + } + return g.Tokens[len(g.Tokens)-1] +} + +func (g *TokenGroup) dump(ctx *groupTokenRenderContext) { + num := ctx.num + fmt.Fprint(os.Stdout, colorize(num, "(")) + ctx.num++ + for _, tk := range g.Tokens { + tk.dump(ctx) + } + fmt.Fprint(os.Stdout, colorize(num, ")")) +} + +func (g *TokenGroup) RawToken() *token.Token { + if len(g.Tokens) == 0 { + return nil + } + return g.Tokens[0].RawToken() +} + +func (g *TokenGroup) Line() int { + if len(g.Tokens) == 0 { + return 0 + } + return g.Tokens[0].Line() +} + +func (g *TokenGroup) Column() int { + if len(g.Tokens) == 0 { + return 0 + } + return g.Tokens[0].Column() +} + +func (g *TokenGroup) TokenType() token.Type { + if len(g.Tokens) == 0 { + return 0 + } + return g.Tokens[0].Type() +} + +func CreateGroupedTokens(tokens token.Tokens) ([]*Token, error) { + var err error + tks := newTokens(tokens) + tks = createLineCommentTokenGroups(tks) + tks, err = createLiteralAndFoldedTokenGroups(tks) + if err != nil { + return nil, err + } + tks, err = createAnchorAndAliasTokenGroups(tks) + if err != nil { + return nil, err + } + tks, err = createScalarTagTokenGroups(tks) + if err != nil { + return nil, err + } + tks, err = createAnchorWithScalarTagTokenGroups(tks) + if err != nil { + return nil, err + } + tks, err = createMapKeyTokenGroups(tks) + if err != nil { + return nil, err + } + tks = createMapKeyValueTokenGroups(tks) + tks, err = createDirectiveTokenGroups(tks) + if err != nil { + return nil, err + } + tks, err = createDocumentTokens(tks) + if err != nil { + return nil, err + } + return tks, nil +} + +func newTokens(tks token.Tokens) []*Token { + ret := make([]*Token, 0, len(tks)) + for _, tk := range tks { + ret = append(ret, &Token{Token: tk}) + } + return ret +} + +func createLineCommentTokenGroups(tokens []*Token) []*Token { + ret := make([]*Token, 0, len(tokens)) + for i := 0; i < len(tokens); i++ { + tk := tokens[i] + switch tk.Type() { + case token.CommentType: + if i > 0 && tokens[i-1].Line() == tk.Line() { + tokens[i-1].LineComment = tk.RawToken() + } else { + ret = append(ret, tk) + } + default: + ret = append(ret, tk) + } + } + return ret +} + +func createLiteralAndFoldedTokenGroups(tokens []*Token) ([]*Token, error) { + ret := make([]*Token, 0, len(tokens)) + for i := 0; i < len(tokens); i++ { + tk := tokens[i] + switch tk.Type() { + case token.LiteralType: + tks := []*Token{tk} + if i+1 < len(tokens) { + tks = append(tks, tokens[i+1]) + } + ret = append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupLiteral, + Tokens: tks, + }, + }) + i++ + case token.FoldedType: + tks := []*Token{tk} + if i+1 < len(tokens) { + tks = append(tks, tokens[i+1]) + } + ret = append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupFolded, + Tokens: tks, + }, + }) + i++ + default: + ret = append(ret, tk) + } + } + return ret, nil +} + +func createAnchorAndAliasTokenGroups(tokens []*Token) ([]*Token, error) { + ret := make([]*Token, 0, len(tokens)) + for i := 0; i < len(tokens); i++ { + tk := tokens[i] + switch tk.Type() { + case token.AnchorType: + if i+1 >= len(tokens) { + return nil, errors.ErrSyntax("undefined anchor name", tk.RawToken()) + } + if i+2 >= len(tokens) { + return nil, errors.ErrSyntax("undefined anchor value", tk.RawToken()) + } + anchorName := &Token{ + Group: &TokenGroup{ + Type: TokenGroupAnchorName, + Tokens: []*Token{tk, tokens[i+1]}, + }, + } + valueTk := tokens[i+2] + if tk.Line() == valueTk.Line() && valueTk.Type() == token.SequenceEntryType { + return nil, errors.ErrSyntax("sequence entries are not allowed after anchor on the same line", valueTk.RawToken()) + } + if tk.Line() == valueTk.Line() && isScalarType(valueTk) { + ret = append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupAnchor, + Tokens: []*Token{anchorName, valueTk}, + }, + }) + i++ + } else { + ret = append(ret, anchorName) + } + i++ + case token.AliasType: + if i+1 == len(tokens) { + return nil, errors.ErrSyntax("undefined alias name", tk.RawToken()) + } + ret = append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupAlias, + Tokens: []*Token{tk, tokens[i+1]}, + }, + }) + i++ + default: + ret = append(ret, tk) + } + } + return ret, nil +} + +func createScalarTagTokenGroups(tokens []*Token) ([]*Token, error) { + ret := make([]*Token, 0, len(tokens)) + for i := 0; i < len(tokens); i++ { + tk := tokens[i] + if tk.Type() != token.TagType { + ret = append(ret, tk) + continue + } + tag := tk.RawToken() + if strings.HasPrefix(tag.Value, "!!") { + // secondary tag. + switch token.ReservedTagKeyword(tag.Value) { + case token.IntegerTag, token.FloatTag, token.StringTag, token.BinaryTag, token.TimestampTag, token.BooleanTag, token.NullTag: + if len(tokens) <= i+1 { + ret = append(ret, tk) + continue + } + if tk.Line() != tokens[i+1].Line() { + ret = append(ret, tk) + continue + } + if tokens[i+1].GroupType() == TokenGroupAnchorName { + ret = append(ret, tk) + continue + } + if isScalarType(tokens[i+1]) { + ret = append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupScalarTag, + Tokens: []*Token{tk, tokens[i+1]}, + }, + }) + i++ + } else { + ret = append(ret, tk) + } + case token.MergeTag: + if len(tokens) <= i+1 { + ret = append(ret, tk) + continue + } + if tk.Line() != tokens[i+1].Line() { + ret = append(ret, tk) + continue + } + if tokens[i+1].GroupType() == TokenGroupAnchorName { + ret = append(ret, tk) + continue + } + if tokens[i+1].Type() != token.MergeKeyType { + return nil, errors.ErrSyntax("could not find merge key", tokens[i+1].RawToken()) + } + ret = append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupScalarTag, + Tokens: []*Token{tk, tokens[i+1]}, + }, + }) + i++ + default: + ret = append(ret, tk) + } + } else { + if len(tokens) <= i+1 { + ret = append(ret, tk) + continue + } + if tk.Line() != tokens[i+1].Line() { + ret = append(ret, tk) + continue + } + if tokens[i+1].GroupType() == TokenGroupAnchorName { + ret = append(ret, tk) + continue + } + if isFlowType(tokens[i+1]) { + ret = append(ret, tk) + continue + } + ret = append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupScalarTag, + Tokens: []*Token{tk, tokens[i+1]}, + }, + }) + i++ + } + } + return ret, nil +} + +func createAnchorWithScalarTagTokenGroups(tokens []*Token) ([]*Token, error) { + ret := make([]*Token, 0, len(tokens)) + for i := 0; i < len(tokens); i++ { + tk := tokens[i] + switch tk.GroupType() { + case TokenGroupAnchorName: + if i+1 >= len(tokens) { + return nil, errors.ErrSyntax("undefined anchor value", tk.RawToken()) + } + valueTk := tokens[i+1] + if tk.Line() == valueTk.Line() && valueTk.GroupType() == TokenGroupScalarTag { + ret = append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupAnchor, + Tokens: []*Token{tk, tokens[i+1]}, + }, + }) + i++ + } else { + ret = append(ret, tk) + } + default: + ret = append(ret, tk) + } + } + return ret, nil +} + +func createMapKeyTokenGroups(tokens []*Token) ([]*Token, error) { + tks, err := createMapKeyByMappingKey(tokens) + if err != nil { + return nil, err + } + return createMapKeyByMappingValue(tks) +} + +func createMapKeyByMappingKey(tokens []*Token) ([]*Token, error) { + ret := make([]*Token, 0, len(tokens)) + for i := 0; i < len(tokens); i++ { + tk := tokens[i] + switch tk.Type() { + case token.MappingKeyType: + if i+1 >= len(tokens) { + return nil, errors.ErrSyntax("undefined map key", tk.RawToken()) + } + ret = append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupMapKey, + Tokens: []*Token{tk, tokens[i+1]}, + }, + }) + i++ + default: + ret = append(ret, tk) + } + } + return ret, nil +} + +func createMapKeyByMappingValue(tokens []*Token) ([]*Token, error) { + ret := make([]*Token, 0, len(tokens)) + for i := 0; i < len(tokens); i++ { + tk := tokens[i] + switch tk.Type() { + case token.MappingValueType: + if i == 0 { + return nil, errors.ErrSyntax("unexpected key name", tk.RawToken()) + } + mapKeyTk := tokens[i-1] + if isNotMapKeyType(mapKeyTk) { + return nil, errors.ErrSyntax("found an invalid key for this map", tokens[i].RawToken()) + } + newTk := &Token{Token: mapKeyTk.Token, Group: mapKeyTk.Group} + mapKeyTk.Token = nil + mapKeyTk.Group = &TokenGroup{ + Type: TokenGroupMapKey, + Tokens: []*Token{newTk, tk}, + } + default: + ret = append(ret, tk) + } + } + return ret, nil +} + +func createMapKeyValueTokenGroups(tokens []*Token) []*Token { + ret := make([]*Token, 0, len(tokens)) + for i := 0; i < len(tokens); i++ { + tk := tokens[i] + switch tk.GroupType() { + case TokenGroupMapKey: + if len(tokens) <= i+1 { + ret = append(ret, tk) + continue + } + valueTk := tokens[i+1] + if tk.Line() != valueTk.Line() { + ret = append(ret, tk) + continue + } + if valueTk.GroupType() == TokenGroupAnchorName { + ret = append(ret, tk) + continue + } + if valueTk.Type() == token.TagType && valueTk.GroupType() != TokenGroupScalarTag { + ret = append(ret, tk) + continue + } + + if isScalarType(valueTk) || valueTk.Type() == token.TagType { + ret = append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupMapKeyValue, + Tokens: []*Token{tk, valueTk}, + }, + }) + i++ + } else { + ret = append(ret, tk) + continue + } + default: + ret = append(ret, tk) + } + } + return ret +} + +func createDirectiveTokenGroups(tokens []*Token) ([]*Token, error) { + ret := make([]*Token, 0, len(tokens)) + for i := 0; i < len(tokens); i++ { + tk := tokens[i] + switch tk.Type() { + case token.DirectiveType: + if i+1 >= len(tokens) { + return nil, errors.ErrSyntax("undefined directive value", tk.RawToken()) + } + directiveName := &Token{ + Group: &TokenGroup{ + Type: TokenGroupDirectiveName, + Tokens: []*Token{tk, tokens[i+1]}, + }, + } + i++ + var valueTks []*Token + for j := i + 1; j < len(tokens); j++ { + if tokens[j].Line() != tk.Line() { + break + } + valueTks = append(valueTks, tokens[j]) + i++ + } + if i+1 >= len(tokens) || tokens[i+1].Type() != token.DocumentHeaderType { + return nil, errors.ErrSyntax("unexpected directive value. document not started", tk.RawToken()) + } + if len(valueTks) != 0 { + ret = append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupDirective, + Tokens: append([]*Token{directiveName}, valueTks...), + }, + }) + } else { + ret = append(ret, directiveName) + } + default: + ret = append(ret, tk) + } + } + return ret, nil +} + +func createDocumentTokens(tokens []*Token) ([]*Token, error) { + var ret []*Token + for i := 0; i < len(tokens); i++ { + tk := tokens[i] + switch tk.Type() { + case token.DocumentHeaderType: + if i != 0 { + ret = append(ret, &Token{ + Group: &TokenGroup{Tokens: tokens[:i]}, + }) + } + if i+1 == len(tokens) { + // if current token is last token, add DocumentHeader only tokens to ret. + return append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupDocument, + Tokens: []*Token{tk}, + }, + }), nil + } + if tokens[i+1].Type() == token.DocumentHeaderType { + return append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupDocument, + Tokens: []*Token{tk}, + }, + }), nil + } + if tokens[i].Line() == tokens[i+1].Line() { + switch tokens[i+1].GroupType() { + case TokenGroupMapKey, TokenGroupMapKeyValue: + return nil, errors.ErrSyntax("value cannot be placed after document separator", tokens[i+1].RawToken()) + } + switch tokens[i+1].Type() { + case token.SequenceEntryType: + return nil, errors.ErrSyntax("value cannot be placed after document separator", tokens[i+1].RawToken()) + } + } + tks, err := createDocumentTokens(tokens[i+1:]) + if err != nil { + return nil, err + } + if len(tks) != 0 { + tks[0].SetGroupType(TokenGroupDocument) + tks[0].Group.Tokens = append([]*Token{tk}, tks[0].Group.Tokens...) + return append(ret, tks...), nil + } + return append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupDocument, + Tokens: []*Token{tk}, + }, + }), nil + case token.DocumentEndType: + if i != 0 { + ret = append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupDocument, + Tokens: tokens[0 : i+1], + }, + }) + } + if i+1 == len(tokens) { + return ret, nil + } + if isScalarType(tokens[i+1]) { + return nil, errors.ErrSyntax("unexpected end content", tokens[i+1].RawToken()) + } + + tks, err := createDocumentTokens(tokens[i+1:]) + if err != nil { + return nil, err + } + return append(ret, tks...), nil + } + } + return append(ret, &Token{ + Group: &TokenGroup{ + Type: TokenGroupDocument, + Tokens: tokens, + }, + }), nil +} + +func isScalarType(tk *Token) bool { + switch tk.GroupType() { + case TokenGroupMapKey, TokenGroupMapKeyValue: + return false + } + typ := tk.Type() + return typ == token.AnchorType || + typ == token.AliasType || + typ == token.LiteralType || + typ == token.FoldedType || + typ == token.NullType || + typ == token.ImplicitNullType || + typ == token.BoolType || + typ == token.IntegerType || + typ == token.BinaryIntegerType || + typ == token.OctetIntegerType || + typ == token.HexIntegerType || + typ == token.FloatType || + typ == token.InfinityType || + typ == token.NanType || + typ == token.StringType || + typ == token.SingleQuoteType || + typ == token.DoubleQuoteType +} + +func isNotMapKeyType(tk *Token) bool { + typ := tk.Type() + return typ == token.DirectiveType || + typ == token.DocumentHeaderType || + typ == token.DocumentEndType || + typ == token.CollectEntryType || + typ == token.MappingStartType || + typ == token.MappingValueType || + typ == token.MappingEndType || + typ == token.SequenceStartType || + typ == token.SequenceEntryType || + typ == token.SequenceEndType +} + +func isFlowType(tk *Token) bool { + typ := tk.Type() + return typ == token.MappingStartType || + typ == token.MappingEndType || + typ == token.SequenceStartType || + typ == token.SequenceEntryType +} diff --git a/vendor/github.com/goccy/go-yaml/path.go b/vendor/github.com/goccy/go-yaml/path.go index b79c6669..568c4b4e 100644 --- a/vendor/github.com/goccy/go-yaml/path.go +++ b/vendor/github.com/goccy/go-yaml/path.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/goccy/go-yaml/ast" - "github.com/goccy/go-yaml/internal/errors" "github.com/goccy/go-yaml/parser" "github.com/goccy/go-yaml/printer" ) @@ -39,7 +38,7 @@ func PathString(s string) (*Path, error) { case '.': b, buf, c, err := parsePathDot(builder, buf, cursor) if err != nil { - return nil, errors.Wrapf(err, "failed to parse path of dot") + return nil, err } length = len(buf) builder = b @@ -47,13 +46,13 @@ func PathString(s string) (*Path, error) { case '[': b, buf, c, err := parsePathIndex(builder, buf, cursor) if err != nil { - return nil, errors.Wrapf(err, "failed to parse path of index") + return nil, err } length = len(buf) builder = b cursor = c default: - return nil, errors.Wrapf(ErrInvalidPathString, "invalid path at %d", cursor) + return nil, fmt.Errorf("invalid path at %d: %w", cursor, ErrInvalidPathString) } } return builder.Build(), nil @@ -67,28 +66,31 @@ func parsePathRecursive(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, [ c := buf[cursor] switch c { case '$': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '..' character") + return nil, nil, 0, fmt.Errorf("specified '$' after '..' character: %w", ErrInvalidPathString) case '*': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '..' character") + return nil, nil, 0, fmt.Errorf("specified '*' after '..' character: %w", ErrInvalidPathString) case '.', '[': goto end case ']': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '..' character") + return nil, nil, 0, fmt.Errorf("specified ']' after '..' character: %w", ErrInvalidPathString) } } end: if start == cursor { - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "not found recursive selector") + return nil, nil, 0, fmt.Errorf("not found recursive selector: %w", ErrInvalidPathString) } return b.Recursive(string(buf[start:cursor])), buf, cursor, nil } func parsePathDot(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { + if b.root == nil || b.node == nil { + return nil, nil, 0, fmt.Errorf("required '$' character at first: %w", ErrInvalidPathString) + } length := len(buf) if cursor+1 < length && buf[cursor+1] == '.' { b, buf, c, err := parsePathRecursive(b, buf, cursor) if err != nil { - return nil, nil, 0, errors.Wrapf(err, "failed to parse path of recursive") + return nil, nil, 0, err } return b, buf, c, nil } @@ -103,23 +105,27 @@ func parsePathDot(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, c := buf[cursor] switch c { case '$': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '.' character") + return nil, nil, 0, fmt.Errorf("specified '$' after '.' character: %w", ErrInvalidPathString) case '*': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '.' character") + return nil, nil, 0, fmt.Errorf("specified '*' after '.' character: %w", ErrInvalidPathString) case '.', '[': goto end case ']': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '.' character") + return nil, nil, 0, fmt.Errorf("specified ']' after '.' character: %w", ErrInvalidPathString) } } end: if start == cursor { - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "cloud not find by empty key") + return nil, nil, 0, fmt.Errorf("could not find by empty key: %w", ErrInvalidPathString) } return b.child(string(buf[start:cursor])), buf, cursor, nil } func parseQuotedKey(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { + if b.root == nil || b.node == nil { + return nil, nil, 0, fmt.Errorf("required '$' character at first: %w", ErrInvalidPathString) + } + cursor++ // skip single quote start := cursor length := len(buf) @@ -136,31 +142,35 @@ func parseQuotedKey(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []run } end: if !foundEndDelim { - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "could not find end delimiter for key") + return nil, nil, 0, fmt.Errorf("could not find end delimiter for key: %w", ErrInvalidPathString) } if start == cursor { - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "could not find by empty key") + return nil, nil, 0, fmt.Errorf("could not find by empty key: %w", ErrInvalidPathString) } selector := buf[start:cursor] cursor++ if cursor < length { switch buf[cursor] { case '$': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '$' after '.' character") + return nil, nil, 0, fmt.Errorf("specified '$' after '.' character: %w", ErrInvalidPathString) case '*': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified '*' after '.' character") + return nil, nil, 0, fmt.Errorf("specified '*' after '.' character: %w", ErrInvalidPathString) case ']': - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "specified ']' after '.' character") + return nil, nil, 0, fmt.Errorf("specified ']' after '.' character: %w", ErrInvalidPathString) } } return b.child(string(selector)), buf, cursor, nil } func parsePathIndex(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) { + if b.root == nil || b.node == nil { + return nil, nil, 0, fmt.Errorf("required '$' character at first: %w", ErrInvalidPathString) + } + length := len(buf) cursor++ // skip '[' character if length <= cursor { - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "unexpected end of YAML Path") + return nil, nil, 0, fmt.Errorf("unexpected end of YAML Path: %w", ErrInvalidPathString) } c := buf[cursor] switch c { @@ -176,7 +186,7 @@ func parsePathIndex(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []run break } if buf[cursor] != ']' { - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "invalid character %s at %d", string(buf[cursor]), cursor) + return nil, nil, 0, fmt.Errorf("invalid character %s at %d: %w", string(buf[cursor]), cursor, ErrInvalidPathString) } numOrAll := string(buf[start:cursor]) if numOrAll == "*" { @@ -184,11 +194,11 @@ func parsePathIndex(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []run } num, err := strconv.ParseInt(numOrAll, 10, 64) if err != nil { - return nil, nil, 0, errors.Wrapf(err, "failed to parse number") + return nil, nil, 0, err } return b.Index(uint(num)), buf, cursor + 1, nil } - return nil, nil, 0, errors.Wrapf(ErrInvalidPathString, "invalid character %s at %d", c, cursor) + return nil, nil, 0, fmt.Errorf("invalid character %q at %d: %w", c, cursor, ErrInvalidPathString) } // Path represent YAMLPath ( like a JSONPath ). @@ -205,10 +215,10 @@ func (p *Path) String() string { func (p *Path) Read(r io.Reader, v interface{}) error { node, err := p.ReadNode(r) if err != nil { - return errors.Wrapf(err, "failed to read node") + return err } if err := Unmarshal([]byte(node.String()), v); err != nil { - return errors.Wrapf(err, "failed to unmarshal") + return err } return nil } @@ -220,15 +230,15 @@ func (p *Path) ReadNode(r io.Reader) (ast.Node, error) { } var buf bytes.Buffer if _, err := io.Copy(&buf, r); err != nil { - return nil, errors.Wrapf(err, "failed to copy from reader") + return nil, err } f, err := parser.ParseBytes(buf.Bytes(), 0) if err != nil { - return nil, errors.Wrapf(err, "failed to parse yaml") + return nil, err } node, err := p.FilterFile(f) if err != nil { - return nil, errors.Wrapf(err, "failed to filter from ast.File") + return nil, err } return node, nil } @@ -237,10 +247,10 @@ func (p *Path) ReadNode(r io.Reader) (ast.Node, error) { func (p *Path) Filter(target, v interface{}) error { b, err := Marshal(target) if err != nil { - return errors.Wrapf(err, "failed to marshal target value") + return err } if err := p.Read(bytes.NewBuffer(b), v); err != nil { - return errors.Wrapf(err, "failed to read") + return err } return nil } @@ -248,22 +258,29 @@ func (p *Path) Filter(target, v interface{}) error { // FilterFile filter from ast.File by YAMLPath. func (p *Path) FilterFile(f *ast.File) (ast.Node, error) { for _, doc := range f.Docs { + // For simplicity, directives cannot be the target of operations + if doc.Body != nil && doc.Body.Type() == ast.DirectiveType { + continue + } node, err := p.FilterNode(doc.Body) if err != nil { - return nil, errors.Wrapf(err, "failed to filter node by path ( %s )", p.node) + return nil, err } if node != nil { return node, nil } } - return nil, errors.Wrapf(ErrNotFoundNode, "failed to find path ( %s )", p.node) + return nil, fmt.Errorf("failed to find path ( %s ): %w", p.node, ErrNotFoundNode) } // FilterNode filter from node by YAMLPath. func (p *Path) FilterNode(node ast.Node) (ast.Node, error) { + if node == nil { + return nil, nil + } n, err := p.node.filter(node) if err != nil { - return nil, errors.Wrapf(err, "failed to filter node by path ( %s )", p.node) + return nil, err } return n, nil } @@ -272,14 +289,14 @@ func (p *Path) FilterNode(node ast.Node) (ast.Node, error) { func (p *Path) MergeFromReader(dst *ast.File, src io.Reader) error { var buf bytes.Buffer if _, err := io.Copy(&buf, src); err != nil { - return errors.Wrapf(err, "failed to copy from reader") + return err } file, err := parser.ParseBytes(buf.Bytes(), 0) if err != nil { - return errors.Wrapf(err, "failed to parse") + return err } if err := p.MergeFromFile(dst, file); err != nil { - return errors.Wrapf(err, "failed to merge file") + return err } return nil } @@ -288,11 +305,11 @@ func (p *Path) MergeFromReader(dst *ast.File, src io.Reader) error { func (p *Path) MergeFromFile(dst *ast.File, src *ast.File) error { base, err := p.FilterFile(dst) if err != nil { - return errors.Wrapf(err, "failed to filter file") + return err } for _, doc := range src.Docs { if err := ast.Merge(base, doc); err != nil { - return errors.Wrapf(err, "failed to merge") + return err } } return nil @@ -302,10 +319,10 @@ func (p *Path) MergeFromFile(dst *ast.File, src *ast.File) error { func (p *Path) MergeFromNode(dst *ast.File, src ast.Node) error { base, err := p.FilterFile(dst) if err != nil { - return errors.Wrapf(err, "failed to filter file") + return err } if err := ast.Merge(base, src); err != nil { - return errors.Wrapf(err, "failed to merge") + return err } return nil } @@ -314,14 +331,14 @@ func (p *Path) MergeFromNode(dst *ast.File, src ast.Node) error { func (p *Path) ReplaceWithReader(dst *ast.File, src io.Reader) error { var buf bytes.Buffer if _, err := io.Copy(&buf, src); err != nil { - return errors.Wrapf(err, "failed to copy from reader") + return err } file, err := parser.ParseBytes(buf.Bytes(), 0) if err != nil { - return errors.Wrapf(err, "failed to parse") + return err } if err := p.ReplaceWithFile(dst, file); err != nil { - return errors.Wrapf(err, "failed to replace file") + return err } return nil } @@ -330,7 +347,7 @@ func (p *Path) ReplaceWithReader(dst *ast.File, src io.Reader) error { func (p *Path) ReplaceWithFile(dst *ast.File, src *ast.File) error { for _, doc := range src.Docs { if err := p.ReplaceWithNode(dst, doc); err != nil { - return errors.Wrapf(err, "failed to replace file by path ( %s )", p.node) + return err } } return nil @@ -339,11 +356,15 @@ func (p *Path) ReplaceWithFile(dst *ast.File, src *ast.File) error { // ReplaceNode replace ast.File with ast.Node. func (p *Path) ReplaceWithNode(dst *ast.File, node ast.Node) error { for _, doc := range dst.Docs { + // For simplicity, directives cannot be the target of operations + if doc.Body != nil && doc.Body.Type() == ast.DirectiveType { + continue + } if node.Type() == ast.DocumentType { node = node.(*ast.DocumentNode).Body } if err := p.node.replace(doc.Body, node); err != nil { - return errors.Wrapf(err, "failed to replace node by path ( %s )", p.node) + return err } } return nil @@ -351,7 +372,7 @@ func (p *Path) ReplaceWithNode(dst *ast.File, node ast.Node) error { // AnnotateSource add annotation to passed source ( see section 5.1 in README.md ). func (p *Path) AnnotateSource(source []byte, colored bool) ([]byte, error) { - file, err := parser.ParseBytes([]byte(source), 0) + file, err := parser.ParseBytes(source, 0) if err != nil { return nil, err } @@ -472,7 +493,7 @@ func (n *rootNode) filter(node ast.Node) (ast.Node, error) { } filtered, err := n.child.filter(node) if err != nil { - return nil, errors.Wrapf(err, "failed to filter") + return nil, err } return filtered, nil } @@ -482,7 +503,7 @@ func (n *rootNode) replace(node ast.Node, target ast.Node) error { return nil } if err := n.child.replace(node, target); err != nil { - return errors.Wrapf(err, "failed to replace") + return err } return nil } @@ -514,7 +535,7 @@ func (n *selectorNode) filter(node ast.Node) (ast.Node, error) { var err error key, err = strconv.Unquote(key) if err != nil { - return nil, errors.Wrapf(err, "failed to unquote") + return nil, err } case '\'': if len(key) > 1 && key[len(key)-1] == '\'' { @@ -528,13 +549,13 @@ func (n *selectorNode) filter(node ast.Node) (ast.Node, error) { } filtered, err := n.child.filter(value.Value) if err != nil { - return nil, errors.Wrapf(err, "failed to filter") + return nil, err } return filtered, nil } } case ast.MappingValueType: - value := node.(*ast.MappingValueNode) + value, _ := node.(*ast.MappingValueNode) key := value.Key.GetToken().Value if key == selector { if n.child == nil { @@ -542,12 +563,12 @@ func (n *selectorNode) filter(node ast.Node) (ast.Node, error) { } filtered, err := n.child.filter(value.Value) if err != nil { - return nil, errors.Wrapf(err, "failed to filter") + return nil, err } return filtered, nil } default: - return nil, errors.Wrapf(ErrInvalidQuery, "expected node type is map or map value. but got %s", node.Type()) + return nil, fmt.Errorf("expected node type is map or map value. but got %s: %w", node.Type(), ErrInvalidQuery) } return nil, nil } @@ -559,11 +580,11 @@ func (n *selectorNode) replaceMapValue(value *ast.MappingValueNode, target ast.N } if n.child == nil { if err := value.Replace(target); err != nil { - return errors.Wrapf(err, "failed to replace") + return err } } else { if err := n.child.replace(value.Value, target); err != nil { - return errors.Wrapf(err, "failed to replace") + return err } } return nil @@ -574,16 +595,16 @@ func (n *selectorNode) replace(node ast.Node, target ast.Node) error { case ast.MappingType: for _, value := range node.(*ast.MappingNode).Values { if err := n.replaceMapValue(value, target); err != nil { - return errors.Wrapf(err, "failed to replace map value") + return err } } case ast.MappingValueType: - value := node.(*ast.MappingValueNode) + value, _ := node.(*ast.MappingValueNode) if err := n.replaceMapValue(value, target); err != nil { - return errors.Wrapf(err, "failed to replace map value") + return err } default: - return errors.Wrapf(ErrInvalidQuery, "expected node type is map or map value. but got %s", node.Type()) + return fmt.Errorf("expected node type is map or map value. but got %s: %w", node.Type(), ErrInvalidQuery) } return nil } @@ -612,11 +633,11 @@ func newIndexNode(selector uint) *indexNode { func (n *indexNode) filter(node ast.Node) (ast.Node, error) { if node.Type() != ast.SequenceType { - return nil, errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) + return nil, fmt.Errorf("expected sequence type node. but got %s: %w", node.Type(), ErrInvalidQuery) } - sequence := node.(*ast.SequenceNode) + sequence, _ := node.(*ast.SequenceNode) if n.selector >= uint(len(sequence.Values)) { - return nil, errors.Wrapf(ErrInvalidQuery, "expected index is %d. but got sequences has %d items", n.selector, sequence.Values) + return nil, fmt.Errorf("expected index is %d. but got sequences has %d items: %w", n.selector, len(sequence.Values), ErrInvalidQuery) } value := sequence.Values[n.selector] if n.child == nil { @@ -624,27 +645,27 @@ func (n *indexNode) filter(node ast.Node) (ast.Node, error) { } filtered, err := n.child.filter(value) if err != nil { - return nil, errors.Wrapf(err, "failed to filter") + return nil, err } return filtered, nil } func (n *indexNode) replace(node ast.Node, target ast.Node) error { if node.Type() != ast.SequenceType { - return errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) + return fmt.Errorf("expected sequence type node. but got %s: %w", node.Type(), ErrInvalidQuery) } - sequence := node.(*ast.SequenceNode) + sequence, _ := node.(*ast.SequenceNode) if n.selector >= uint(len(sequence.Values)) { - return errors.Wrapf(ErrInvalidQuery, "expected index is %d. but got sequences has %d items", n.selector, sequence.Values) + return fmt.Errorf("expected index is %d. but got sequences has %d items: %w", n.selector, len(sequence.Values), ErrInvalidQuery) } if n.child == nil { if err := sequence.Replace(int(n.selector), target); err != nil { - return errors.Wrapf(err, "failed to replace") + return err } return nil } if err := n.child.replace(sequence.Values[n.selector], target); err != nil { - return errors.Wrapf(err, "failed to replace") + return err } return nil } @@ -677,9 +698,9 @@ func (n *indexAllNode) String() string { func (n *indexAllNode) filter(node ast.Node) (ast.Node, error) { if node.Type() != ast.SequenceType { - return nil, errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) + return nil, fmt.Errorf("expected sequence type node. but got %s: %w", node.Type(), ErrInvalidQuery) } - sequence := node.(*ast.SequenceNode) + sequence, _ := node.(*ast.SequenceNode) if n.child == nil { return sequence, nil } @@ -688,7 +709,7 @@ func (n *indexAllNode) filter(node ast.Node) (ast.Node, error) { for _, value := range sequence.Values { filtered, err := n.child.filter(value) if err != nil { - return nil, errors.Wrapf(err, "failed to filter") + return nil, err } out.Values = append(out.Values, filtered) } @@ -697,20 +718,20 @@ func (n *indexAllNode) filter(node ast.Node) (ast.Node, error) { func (n *indexAllNode) replace(node ast.Node, target ast.Node) error { if node.Type() != ast.SequenceType { - return errors.Wrapf(ErrInvalidQuery, "expected sequence type node. but got %s", node.Type()) + return fmt.Errorf("expected sequence type node. but got %s: %w", node.Type(), ErrInvalidQuery) } - sequence := node.(*ast.SequenceNode) + sequence, _ := node.(*ast.SequenceNode) if n.child == nil { for idx := range sequence.Values { if err := sequence.Replace(idx, target); err != nil { - return errors.Wrapf(err, "failed to replace") + return err } } return nil } for _, value := range sequence.Values { if err := n.child.replace(value, target); err != nil { - return errors.Wrapf(err, "failed to replace") + return err } } return nil @@ -743,7 +764,7 @@ func (n *recursiveNode) filterNode(node ast.Node) (*ast.SequenceNode, error) { for _, value := range typedNode.Values { seq, err := n.filterNode(value) if err != nil { - return nil, errors.Wrapf(err, "failed to filter") + return nil, err } sequence.Values = append(sequence.Values, seq.Values...) } @@ -754,14 +775,14 @@ func (n *recursiveNode) filterNode(node ast.Node) (*ast.SequenceNode, error) { } seq, err := n.filterNode(typedNode.Value) if err != nil { - return nil, errors.Wrapf(err, "failed to filter") + return nil, err } sequence.Values = append(sequence.Values, seq.Values...) case *ast.SequenceNode: for _, value := range typedNode.Values { seq, err := n.filterNode(value) if err != nil { - return nil, errors.Wrapf(err, "failed to filter") + return nil, err } sequence.Values = append(sequence.Values, seq.Values...) } @@ -772,7 +793,7 @@ func (n *recursiveNode) filterNode(node ast.Node) (*ast.SequenceNode, error) { func (n *recursiveNode) filter(node ast.Node) (ast.Node, error) { sequence, err := n.filterNode(node) if err != nil { - return nil, errors.Wrapf(err, "failed to filter") + return nil, err } sequence.Start = node.GetToken() return sequence, nil @@ -783,23 +804,23 @@ func (n *recursiveNode) replaceNode(node ast.Node, target ast.Node) error { case *ast.MappingNode: for _, value := range typedNode.Values { if err := n.replaceNode(value, target); err != nil { - return errors.Wrapf(err, "failed to replace") + return err } } case *ast.MappingValueNode: key := typedNode.Key.GetToken().Value if n.selector == key { if err := typedNode.Replace(target); err != nil { - return errors.Wrapf(err, "failed to replace") + return err } } if err := n.replaceNode(typedNode.Value, target); err != nil { - return errors.Wrapf(err, "failed to replace") + return err } case *ast.SequenceNode: for _, value := range typedNode.Values { if err := n.replaceNode(value, target); err != nil { - return errors.Wrapf(err, "failed to replace") + return err } } } @@ -808,7 +829,7 @@ func (n *recursiveNode) replaceNode(node ast.Node, target ast.Node) error { func (n *recursiveNode) replace(node ast.Node, target ast.Node) error { if err := n.replaceNode(node, target); err != nil { - return errors.Wrapf(err, "failed to replace") + return err } return nil } diff --git a/vendor/github.com/goccy/go-yaml/printer/color.go b/vendor/github.com/goccy/go-yaml/printer/color.go new file mode 100644 index 00000000..79d7d7c6 --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/printer/color.go @@ -0,0 +1,83 @@ +// This source inspired by https://github.com/fatih/color. +package printer + +import ( + "fmt" + "strings" +) + +type ColorAttribute int + +const ( + ColorReset ColorAttribute = iota + ColorBold + ColorFaint + ColorItalic + ColorUnderline + ColorBlinkSlow + ColorBlinkRapid + ColorReverseVideo + ColorConcealed + ColorCrossedOut +) + +const ( + ColorFgHiBlack ColorAttribute = iota + 90 + ColorFgHiRed + ColorFgHiGreen + ColorFgHiYellow + ColorFgHiBlue + ColorFgHiMagenta + ColorFgHiCyan + ColorFgHiWhite +) + +const ( + ColorResetBold ColorAttribute = iota + 22 + ColorResetItalic + ColorResetUnderline + ColorResetBlinking + + ColorResetReversed + ColorResetConcealed + ColorResetCrossedOut +) + +const escape = "\x1b" + +var colorResetMap = map[ColorAttribute]ColorAttribute{ + ColorBold: ColorResetBold, + ColorFaint: ColorResetBold, + ColorItalic: ColorResetItalic, + ColorUnderline: ColorResetUnderline, + ColorBlinkSlow: ColorResetBlinking, + ColorBlinkRapid: ColorResetBlinking, + ColorReverseVideo: ColorResetReversed, + ColorConcealed: ColorResetConcealed, + ColorCrossedOut: ColorResetCrossedOut, +} + +func format(attrs ...ColorAttribute) string { + format := make([]string, 0, len(attrs)) + for _, attr := range attrs { + format = append(format, fmt.Sprint(attr)) + } + return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";")) +} + +func unformat(attrs ...ColorAttribute) string { + format := make([]string, len(attrs)) + for _, attr := range attrs { + v := fmt.Sprint(ColorReset) + reset, exists := colorResetMap[attr] + if exists { + v = fmt.Sprint(reset) + } + format = append(format, v) + } + return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";")) +} + +func colorize(msg string, attrs ...ColorAttribute) string { + return format(attrs...) + msg + unformat(attrs...) +} diff --git a/vendor/github.com/goccy/go-yaml/printer/printer.go b/vendor/github.com/goccy/go-yaml/printer/printer.go index d5e25dc9..9346983d 100644 --- a/vendor/github.com/goccy/go-yaml/printer/printer.go +++ b/vendor/github.com/goccy/go-yaml/printer/printer.go @@ -5,7 +5,6 @@ import ( "math" "strings" - "github.com/fatih/color" "github.com/goccy/go-yaml/ast" "github.com/goccy/go-yaml/token" ) @@ -29,6 +28,7 @@ type Printer struct { Bool PrintFunc String PrintFunc Number PrintFunc + Comment PrintFunc } func defaultLineNumberFormat(num int) string { @@ -82,6 +82,11 @@ func (p *Printer) property(tk *token.Token) *Property { return p.Number() } return prop + case token.CommentType: + if p.Comment != nil { + return p.Comment() + } + return prop default: } return prop @@ -144,47 +149,47 @@ func (p *Printer) PrintNode(node ast.Node) []byte { return []byte(fmt.Sprintf("%+v\n", node)) } -const escape = "\x1b" - -func format(attr color.Attribute) string { - return fmt.Sprintf("%s[%dm", escape, attr) -} - func (p *Printer) setDefaultColorSet() { p.Bool = func() *Property { return &Property{ - Prefix: format(color.FgHiMagenta), - Suffix: format(color.Reset), + Prefix: format(ColorFgHiMagenta), + Suffix: format(ColorReset), } } p.Number = func() *Property { return &Property{ - Prefix: format(color.FgHiMagenta), - Suffix: format(color.Reset), + Prefix: format(ColorFgHiMagenta), + Suffix: format(ColorReset), } } p.MapKey = func() *Property { return &Property{ - Prefix: format(color.FgHiCyan), - Suffix: format(color.Reset), + Prefix: format(ColorFgHiCyan), + Suffix: format(ColorReset), } } p.Anchor = func() *Property { return &Property{ - Prefix: format(color.FgHiYellow), - Suffix: format(color.Reset), + Prefix: format(ColorFgHiYellow), + Suffix: format(ColorReset), } } p.Alias = func() *Property { return &Property{ - Prefix: format(color.FgHiYellow), - Suffix: format(color.Reset), + Prefix: format(ColorFgHiYellow), + Suffix: format(ColorReset), } } p.String = func() *Property { return &Property{ - Prefix: format(color.FgHiGreen), - Suffix: format(color.Reset), + Prefix: format(ColorFgHiGreen), + Suffix: format(ColorReset), + } + } + p.Comment = func() *Property { + return &Property{ + Prefix: format(ColorFgHiBlack), + Suffix: format(ColorReset), } } } @@ -192,9 +197,9 @@ func (p *Printer) setDefaultColorSet() { func (p *Printer) PrintErrorMessage(msg string, isColored bool) string { if isColored { return fmt.Sprintf("%s%s%s", - format(color.FgHiRed), + format(ColorFgHiRed), msg, - format(color.Reset), + format(ColorReset), ) } return msg @@ -246,10 +251,7 @@ func (p *Printer) isNewLineLastChar(s string) bool { } func (p *Printer) printBeforeTokens(tk *token.Token, minLine, extLine int) token.Tokens { - for { - if tk.Prev == nil { - break - } + for tk.Prev != nil { if tk.Prev.Position.Line < minLine { break } @@ -317,8 +319,7 @@ func (p *Printer) setupErrorTokenFormat(annotateLine int, isColored bool) { p.LineNumber = true p.LineNumberFormat = func(num int) string { if isColored { - fn := color.New(color.Bold, color.FgHiWhite).SprintFunc() - return fn(prefix(annotateLine, num)) + return colorize(prefix(annotateLine, num), ColorBold, ColorFgHiWhite) } return prefix(annotateLine, num) } diff --git a/vendor/github.com/goccy/go-yaml/scanner/context.go b/vendor/github.com/goccy/go-yaml/scanner/context.go index 3aaec561..4f3250be 100644 --- a/vendor/github.com/goccy/go-yaml/scanner/context.go +++ b/vendor/github.com/goccy/go-yaml/scanner/context.go @@ -1,13 +1,14 @@ package scanner import ( + "errors" + "strconv" + "strings" "sync" "github.com/goccy/go-yaml/token" ) -const whitespace = ' ' - // Context context at scanning type Context struct { idx int @@ -18,11 +19,20 @@ type Context struct { buf []rune obuf []rune tokens token.Tokens - isRawFolded bool - isLiteral bool - isFolded bool - isSingleLine bool - literalOpt string + mstate *MultiLineState +} + +type MultiLineState struct { + opt string + firstLineIndentColumn int + prevLineIndentColumn int + lineIndentColumn int + lastNotSpaceOnlyLineIndentColumn int + spaceOnlyIndentColumn int + foldedNewLine bool + isRawFolded bool + isLiteral bool + isFolded bool } var ( @@ -35,14 +45,13 @@ var ( func createContext() *Context { return &Context{ - idx: 0, - tokens: token.Tokens{}, - isSingleLine: true, + idx: 0, + tokens: token.Tokens{}, } } func newContext(src []rune) *Context { - ctx := ctxPool.Get().(*Context) + ctx, _ := ctxPool.Get().(*Context) ctx.reset(src) return ctx } @@ -51,17 +60,18 @@ func (c *Context) release() { ctxPool.Put(c) } +func (c *Context) clear() { + c.resetBuffer() + c.mstate = nil +} + func (c *Context) reset(src []rune) { c.idx = 0 c.size = len(src) c.src = src c.tokens = c.tokens[:0] c.resetBuffer() - c.isRawFolded = false - c.isSingleLine = true - c.isLiteral = false - c.isFolded = false - c.literalOpt = "" + c.mstate = nil } func (c *Context) resetBuffer() { @@ -71,15 +81,185 @@ func (c *Context) resetBuffer() { c.notSpaceOrgCharPos = 0 } -func (c *Context) isSaveIndentMode() bool { - return c.isLiteral || c.isFolded || c.isRawFolded +func (c *Context) breakMultiLine() { + c.mstate = nil +} + +func (c *Context) getMultiLineState() *MultiLineState { + return c.mstate +} + +func (c *Context) setLiteral(lastDelimColumn int, opt string) { + mstate := &MultiLineState{ + isLiteral: true, + opt: opt, + } + indent := firstLineIndentColumnByOpt(opt) + if indent > 0 { + mstate.firstLineIndentColumn = lastDelimColumn + indent + } + c.mstate = mstate +} + +func (c *Context) setFolded(lastDelimColumn int, opt string) { + mstate := &MultiLineState{ + isFolded: true, + opt: opt, + } + indent := firstLineIndentColumnByOpt(opt) + if indent > 0 { + mstate.firstLineIndentColumn = lastDelimColumn + indent + } + c.mstate = mstate +} + +func (c *Context) setRawFolded(column int) { + mstate := &MultiLineState{ + isRawFolded: true, + } + mstate.updateIndentColumn(column) + c.mstate = mstate +} + +func firstLineIndentColumnByOpt(opt string) int { + opt = strings.TrimPrefix(opt, "-") + opt = strings.TrimPrefix(opt, "+") + opt = strings.TrimSuffix(opt, "-") + opt = strings.TrimSuffix(opt, "+") + i, _ := strconv.ParseInt(opt, 10, 64) + return int(i) +} + +func (s *MultiLineState) lastDelimColumn() int { + if s.firstLineIndentColumn == 0 { + return 0 + } + return s.firstLineIndentColumn - 1 +} + +func (s *MultiLineState) updateIndentColumn(column int) { + if s.firstLineIndentColumn == 0 { + s.firstLineIndentColumn = column + } + if s.lineIndentColumn == 0 { + s.lineIndentColumn = column + } +} + +func (s *MultiLineState) updateSpaceOnlyIndentColumn(column int) { + if s.firstLineIndentColumn != 0 { + return + } + s.spaceOnlyIndentColumn = column } -func (c *Context) breakLiteral() { - c.isLiteral = false - c.isRawFolded = false - c.isFolded = false - c.literalOpt = "" +func (s *MultiLineState) validateIndentAfterSpaceOnly(column int) error { + if s.firstLineIndentColumn != 0 { + return nil + } + if s.spaceOnlyIndentColumn > column { + return errors.New("invalid number of indent is specified after space only") + } + return nil +} + +func (s *MultiLineState) validateIndentColumn() error { + if firstLineIndentColumnByOpt(s.opt) == 0 { + return nil + } + if s.firstLineIndentColumn > s.lineIndentColumn { + return errors.New("invalid number of indent is specified in the multi-line header") + } + return nil +} + +func (s *MultiLineState) updateNewLineState() { + s.prevLineIndentColumn = s.lineIndentColumn + if s.lineIndentColumn != 0 { + s.lastNotSpaceOnlyLineIndentColumn = s.lineIndentColumn + } + s.foldedNewLine = true + s.lineIndentColumn = 0 +} + +func (s *MultiLineState) isIndentColumn(column int) bool { + if s.firstLineIndentColumn == 0 { + return column == 1 + } + return s.firstLineIndentColumn > column +} + +func (s *MultiLineState) addIndent(ctx *Context, column int) { + if s.firstLineIndentColumn == 0 { + return + } + + // If the first line of the document has already been evaluated, the number is treated as the threshold, since the `firstLineIndentColumn` is a positive number. + if column < s.firstLineIndentColumn { + return + } + + // `c.foldedNewLine` is a variable that is set to true for every newline. + if !s.isLiteral && s.foldedNewLine { + s.foldedNewLine = false + } + // Since addBuf ignore space character, add to the buffer directly. + ctx.buf = append(ctx.buf, ' ') + ctx.notSpaceCharPos = len(ctx.buf) +} + +// updateNewLineInFolded if Folded or RawFolded context and the content on the current line starts at the same column as the previous line, +// treat the new-line-char as a space. +func (s *MultiLineState) updateNewLineInFolded(ctx *Context, column int) { + if s.isLiteral { + return + } + + // Folded or RawFolded. + + if !s.foldedNewLine { + return + } + var ( + lastChar rune + prevLastChar rune + ) + if len(ctx.buf) != 0 { + lastChar = ctx.buf[len(ctx.buf)-1] + } + if len(ctx.buf) > 1 { + prevLastChar = ctx.buf[len(ctx.buf)-2] + } + if s.lineIndentColumn == s.prevLineIndentColumn { + // --- + // > + // a + // b + if lastChar == '\n' { + ctx.buf[len(ctx.buf)-1] = ' ' + } + } else if s.prevLineIndentColumn == 0 && s.lastNotSpaceOnlyLineIndentColumn == column { + // if previous line is indent-space and new-line-char only, prevLineIndentColumn is zero. + // In this case, last new-line-char is removed. + // --- + // > + // a + // + // b + if lastChar == '\n' && prevLastChar == '\n' { + ctx.buf = ctx.buf[:len(ctx.buf)-1] + ctx.notSpaceCharPos = len(ctx.buf) + } + } + s.foldedNewLine = false +} + +func (s *MultiLineState) hasTrimAllEndNewlineOpt() bool { + return strings.HasPrefix(s.opt, "-") || strings.HasSuffix(s.opt, "-") || s.isRawFolded +} + +func (s *MultiLineState) hasKeepAllEndNewlineOpt() bool { + return strings.HasPrefix(s.opt, "+") || strings.HasSuffix(s.opt, "+") } func (c *Context) addToken(tk *token.Token) { @@ -90,7 +270,7 @@ func (c *Context) addToken(tk *token.Token) { } func (c *Context) addBuf(r rune) { - if len(c.buf) == 0 && r == ' ' { + if len(c.buf) == 0 && (r == ' ' || r == '\t') { return } c.buf = append(c.buf, r) @@ -99,6 +279,16 @@ func (c *Context) addBuf(r rune) { } } +func (c *Context) addBufWithTab(r rune) { + if len(c.buf) == 0 && r == ' ' { + return + } + c.buf = append(c.buf, r) + if r != ' ' { + c.notSpaceCharPos = len(c.buf) + } +} + func (c *Context) addOriginBuf(r rune) { c.obuf = append(c.obuf, r) if r != ' ' && r != '\t' { @@ -106,7 +296,7 @@ func (c *Context) addOriginBuf(r rune) { } } -func (c *Context) removeRightSpaceFromBuf() int { +func (c *Context) removeRightSpaceFromBuf() { trimmedBuf := c.obuf[:c.notSpaceOrgCharPos] buflen := len(trimmedBuf) diff := len(c.obuf) - buflen @@ -114,11 +304,6 @@ func (c *Context) removeRightSpaceFromBuf() int { c.obuf = c.obuf[:buflen] c.buf = c.bufferedSrc() } - return diff -} - -func (c *Context) isDocument() bool { - return c.isLiteral || c.isFolded || c.isRawFolded } func (c *Context) isEOS() bool { @@ -126,7 +311,7 @@ func (c *Context) isEOS() bool { } func (c *Context) isNextEOS() bool { - return len(c.src)-1 <= c.idx+1 + return len(c.src) <= c.idx+1 } func (c *Context) next() bool { @@ -151,18 +336,6 @@ func (c *Context) currentChar() rune { return rune(0) } -func (c *Context) currentCharWithSkipWhitespace() rune { - idx := c.idx - for c.size > idx { - ch := c.src[idx] - if ch != whitespace { - return ch - } - idx++ - } - return rune(0) -} - func (c *Context) nextChar() rune { if c.size > c.idx+1 { return c.src[c.idx+1] @@ -186,25 +359,52 @@ func (c *Context) progress(num int) { c.idx += num } -func (c *Context) nextPos() int { - return c.idx + 1 -} - func (c *Context) existsBuffer() bool { return len(c.bufferedSrc()) != 0 } +func (c *Context) isMultiLine() bool { + return c.mstate != nil +} + func (c *Context) bufferedSrc() []rune { src := c.buf[:c.notSpaceCharPos] - if c.isDocument() && c.literalOpt == "-" { - // remove end '\n' character and trailing empty lines + if c.isMultiLine() { + mstate := c.getMultiLineState() + // remove end '\n' character and trailing empty lines. // https://yaml.org/spec/1.2.2/#8112-block-chomping-indicator - for { - if len(src) > 0 && src[len(src)-1] == '\n' { - src = src[:len(src)-1] - continue + if mstate.hasTrimAllEndNewlineOpt() { + // If the '-' flag is specified, all trailing newline characters will be removed. + src = []rune(strings.TrimRight(string(src), "\n")) + } else if !mstate.hasKeepAllEndNewlineOpt() { + // Normally, all but one of the trailing newline characters are removed. + var newLineCharCount int + for i := len(src) - 1; i >= 0; i-- { + if src[i] == '\n' { + newLineCharCount++ + continue + } + break } - break + removedNewLineCharCount := newLineCharCount - 1 + for removedNewLineCharCount > 0 { + src = []rune(strings.TrimSuffix(string(src), "\n")) + removedNewLineCharCount-- + } + } + + // If the text ends with a space character, remove all of them. + if mstate.hasTrimAllEndNewlineOpt() { + src = []rune(strings.TrimRight(string(src), " ")) + } + if string(src) == "\n" { + // If the content consists only of a newline, + // it can be considered as the document ending without any specified value, + // so it is treated as an empty string. + src = []rune{} + } + if mstate.hasKeepAllEndNewlineOpt() && len(src) == 0 { + src = []rune{'\n'} } } return src @@ -216,18 +416,34 @@ func (c *Context) bufferedToken(pos *token.Position) *token.Token { } source := c.bufferedSrc() if len(source) == 0 { + c.buf = c.buf[:0] // clear value's buffer only. return nil } var tk *token.Token - if c.isDocument() { + if c.isMultiLine() { tk = token.String(string(source), string(c.obuf), pos) } else { tk = token.New(string(source), string(c.obuf), pos) } + c.setTokenTypeByPrevTag(tk) c.resetBuffer() return tk } +func (c *Context) setTokenTypeByPrevTag(tk *token.Token) { + lastTk := c.lastToken() + if lastTk == nil { + return + } + if lastTk.Type != token.TagType { + return + } + tag := token.ReservedTagKeyword(lastTk.Value) + if _, exists := token.ReservedTagKeywordMap[tag]; !exists { + tk.Type = token.StringType + } +} + func (c *Context) lastToken() *token.Token { if len(c.tokens) != 0 { return c.tokens[len(c.tokens)-1] diff --git a/vendor/github.com/goccy/go-yaml/scanner/error.go b/vendor/github.com/goccy/go-yaml/scanner/error.go new file mode 100644 index 00000000..3f5419af --- /dev/null +++ b/vendor/github.com/goccy/go-yaml/scanner/error.go @@ -0,0 +1,17 @@ +package scanner + +import "github.com/goccy/go-yaml/token" + +type InvalidTokenError struct { + Token *token.Token +} + +func (e *InvalidTokenError) Error() string { + return e.Token.Error +} + +func ErrInvalidToken(tk *token.Token) *InvalidTokenError { + return &InvalidTokenError{ + Token: tk, + } +} diff --git a/vendor/github.com/goccy/go-yaml/scanner/scanner.go b/vendor/github.com/goccy/go-yaml/scanner/scanner.go index 77acb418..799f4694 100644 --- a/vendor/github.com/goccy/go-yaml/scanner/scanner.go +++ b/vendor/github.com/goccy/go-yaml/scanner/scanner.go @@ -1,11 +1,12 @@ package scanner import ( + "errors" + "fmt" "io" + "strconv" "strings" - "golang.org/x/xerrors" - "github.com/goccy/go-yaml/token" ) @@ -26,20 +27,27 @@ const ( // Scanner holds the scanner's internal state while processing a given text. // It can be allocated as part of another data structure but must be initialized via Init before use. type Scanner struct { - source []rune - sourcePos int - sourceSize int - line int - column int - offset int - prevIndentLevel int - prevIndentNum int - prevIndentColumn int - docStartColumn int + source []rune + sourcePos int + sourceSize int + // line number. This number starts from 1. + line int + // column number. This number starts from 1. + column int + // offset represents the offset from the beginning of the source. + offset int + // lastDelimColumn is the last column needed to compare indent is retained. + lastDelimColumn int + // indentNum indicates the number of spaces used for indentation. + indentNum int + // prevLineIndentNum indicates the number of spaces used for indentation at previous line. + prevLineIndentNum int + // indentLevel indicates the level of indent depth. This value does not match the column value. indentLevel int - indentNum int isFirstCharAtLine bool isAnchor bool + isAlias bool + isDirective bool startedFlowSequenceNum int startedFlowMapNum int indentState IndentState @@ -65,7 +73,7 @@ func (s *Scanner) bufferedToken(ctx *Context) *token.Token { line := s.line column := s.column - len(ctx.buf) level := s.indentLevel - if ctx.isSaveIndentMode() { + if ctx.isMultiLine() { line -= s.newLineCount(ctx.buf) column = strings.Index(string(ctx.obuf), string(ctx.buf)) + 1 // Since we are in a literal, folded or raw folded @@ -87,30 +95,30 @@ func (s *Scanner) bufferedToken(ctx *Context) *token.Token { func (s *Scanner) progressColumn(ctx *Context, num int) { s.column += num s.offset += num - ctx.progress(num) + s.progress(ctx, num) +} + +func (s *Scanner) progressOnly(ctx *Context, num int) { + s.offset += num + s.progress(ctx, num) } func (s *Scanner) progressLine(ctx *Context) { + s.prevLineIndentNum = s.indentNum s.column = 1 s.line++ s.offset++ s.indentNum = 0 s.isFirstCharAtLine = true s.isAnchor = false - ctx.progress(1) + s.isAlias = false + s.isDirective = false + s.progress(ctx, 1) } -func (s *Scanner) isNeededKeepPreviousIndentNum(ctx *Context, c rune) bool { - if !s.isChangedToIndentStateUp() { - return false - } - if ctx.isDocument() { - return true - } - if c == '-' && ctx.existsBuffer() { - return true - } - return false +func (s *Scanner) progress(ctx *Context, num int) { + ctx.progress(num) + s.sourcePos += num } func (s *Scanner) isNewLineChar(c rune) bool { @@ -141,67 +149,50 @@ func (s *Scanner) newLineCount(src []rune) int { return cnt } -func (s *Scanner) updateIndentState(ctx *Context) { - indentNumBasedIndentState := s.indentState - if s.prevIndentNum < s.indentNum { - s.indentLevel = s.prevIndentLevel + 1 - indentNumBasedIndentState = IndentStateUp - } else if s.prevIndentNum == s.indentNum { - s.indentLevel = s.prevIndentLevel - indentNumBasedIndentState = IndentStateEqual - } else { - indentNumBasedIndentState = IndentStateDown - if s.prevIndentLevel > 0 { - s.indentLevel = s.prevIndentLevel - 1 - } - } - - if s.prevIndentColumn > 0 { - if s.prevIndentColumn < s.column { - s.indentState = IndentStateUp - } else if s.prevIndentColumn != s.column || indentNumBasedIndentState != IndentStateEqual { - // The following case ( current position is 'd' ), some variables becomes like here - // - prevIndentColumn: 1 of 'a' - // - indentNumBasedIndentState: IndentStateDown because d's indentNum(1) is less than c's indentNum(3). - // Therefore, s.prevIndentColumn(1) == s.column(1) is true, but we want to treat this as IndentStateDown. - // So, we look also current indentState value by the above prevIndentNum based logic, and determins finally indentState. - // --- - // a: - // b - // c - // d: e - // ^ - s.indentState = IndentStateDown - } else { - s.indentState = IndentStateEqual +func (s *Scanner) updateIndentLevel() { + if s.prevLineIndentNum < s.indentNum { + s.indentLevel++ + } else if s.prevLineIndentNum > s.indentNum { + if s.indentLevel > 0 { + s.indentLevel-- } + } +} + +func (s *Scanner) updateIndentState(ctx *Context) { + if s.lastDelimColumn == 0 { + return + } + + if s.lastDelimColumn < s.column { + s.indentState = IndentStateUp } else { - s.indentState = indentNumBasedIndentState + // If lastDelimColumn and s.column are the same, + // treat as Down state since it is the same column as delimiter. + s.indentState = IndentStateDown } } func (s *Scanner) updateIndent(ctx *Context, c rune) { - if s.isFirstCharAtLine && s.isNewLineChar(c) && ctx.isDocument() { + if s.isFirstCharAtLine && s.isNewLineChar(c) { return } if s.isFirstCharAtLine && c == ' ' { s.indentNum++ return } + if s.isFirstCharAtLine && c == '\t' { + // found tab indent. + // In this case, scanTab returns error. + return + } if !s.isFirstCharAtLine { s.indentState = IndentStateKeep return } + s.updateIndentLevel() s.updateIndentState(ctx) s.isFirstCharAtLine = false - if s.isNeededKeepPreviousIndentNum(ctx, c) { - return - } - if s.indentState != IndentStateUp { - s.prevIndentColumn = 0 - } - s.prevIndentNum = s.indentNum - s.prevIndentLevel = s.indentLevel } func (s *Scanner) isChangedToIndentStateDown() bool { @@ -212,20 +203,15 @@ func (s *Scanner) isChangedToIndentStateUp() bool { return s.indentState == IndentStateUp } -func (s *Scanner) isChangedToIndentStateEqual() bool { - return s.indentState == IndentStateEqual -} - func (s *Scanner) addBufferedTokenIfExists(ctx *Context) { ctx.addToken(s.bufferedToken(ctx)) } -func (s *Scanner) breakLiteral(ctx *Context) { - s.docStartColumn = 0 - ctx.breakLiteral() +func (s *Scanner) breakMultiLine(ctx *Context) { + ctx.breakMultiLine() } -func (s *Scanner) scanSingleQuote(ctx *Context) (tk *token.Token, pos int) { +func (s *Scanner) scanSingleQuote(ctx *Context) (*token.Token, error) { ctx.addOriginBuf('\'') srcpos := s.pos() startIndex := ctx.idx + 1 @@ -234,6 +220,7 @@ func (s *Scanner) scanSingleQuote(ctx *Context) (tk *token.Token, pos int) { value := []rune{} isFirstLineChar := false isNewLine := false + for idx := startIndex; idx < size; idx++ { if !isNewLine { s.progressColumn(ctx, 1) @@ -241,34 +228,67 @@ func (s *Scanner) scanSingleQuote(ctx *Context) (tk *token.Token, pos int) { isNewLine = false } c := src[idx] - pos = idx + 1 ctx.addOriginBuf(c) if s.isNewLineChar(c) { - value = append(value, ' ') + notSpaceIdx := -1 + for i := len(value) - 1; i >= 0; i-- { + if value[i] == ' ' { + continue + } + notSpaceIdx = i + break + } + if len(value) > notSpaceIdx { + value = value[:notSpaceIdx+1] + } + if isFirstLineChar { + value = append(value, '\n') + } else { + value = append(value, ' ') + } isFirstLineChar = true isNewLine = true s.progressLine(ctx) + if idx+1 < size { + if err := s.validateDocumentSeparatorMarker(ctx, src[idx+1:]); err != nil { + return nil, err + } + } continue - } else if c == ' ' && isFirstLineChar { + } else if isFirstLineChar && c == ' ' { + continue + } else if isFirstLineChar && c == '\t' { + if s.lastDelimColumn >= s.column { + return nil, ErrInvalidToken( + token.Invalid( + "tab character cannot be used for indentation in single-quoted text", + string(ctx.obuf), s.pos(), + ), + ) + } continue } else if c != '\'' { value = append(value, c) isFirstLineChar = false continue - } - if idx+1 < len(ctx.src) && ctx.src[idx+1] == '\'' { + } else if idx+1 < len(ctx.src) && ctx.src[idx+1] == '\'' { // '' handle as ' character value = append(value, c) ctx.addOriginBuf(c) idx++ + s.progressColumn(ctx, 1) continue } s.progressColumn(ctx, 1) - tk = token.SingleQuote(string(value), string(ctx.obuf), srcpos) - pos = idx - startIndex + 1 - return + return token.SingleQuote(string(value), string(ctx.obuf), srcpos), nil } - return + s.progressColumn(ctx, 1) + return nil, ErrInvalidToken( + token.Invalid( + "could not find end character of single-quoted text", + string(ctx.obuf), srcpos, + ), + ) } func hexToInt(b rune) int { @@ -289,7 +309,7 @@ func hexRunesToInt(b []rune) int { return sum } -func (s *Scanner) scanDoubleQuote(ctx *Context) (tk *token.Token, pos int) { +func (s *Scanner) scanDoubleQuote(ctx *Context) (*token.Token, error) { ctx.addOriginBuf('"') srcpos := s.pos() startIndex := ctx.idx + 1 @@ -298,6 +318,7 @@ func (s *Scanner) scanDoubleQuote(ctx *Context) (tk *token.Token, pos int) { value := []rune{} isFirstLineChar := false isNewLine := false + for idx := startIndex; idx < size; idx++ { if !isNewLine { s.progressColumn(ctx, 1) @@ -305,15 +326,44 @@ func (s *Scanner) scanDoubleQuote(ctx *Context) (tk *token.Token, pos int) { isNewLine = false } c := src[idx] - pos = idx + 1 ctx.addOriginBuf(c) if s.isNewLineChar(c) { - value = append(value, ' ') + notSpaceIdx := -1 + for i := len(value) - 1; i >= 0; i-- { + if value[i] == ' ' { + continue + } + notSpaceIdx = i + break + } + if len(value) > notSpaceIdx { + value = value[:notSpaceIdx+1] + } + if isFirstLineChar { + value = append(value, '\n') + } else { + value = append(value, ' ') + } isFirstLineChar = true isNewLine = true s.progressLine(ctx) + if idx+1 < size { + if err := s.validateDocumentSeparatorMarker(ctx, src[idx+1:]); err != nil { + return nil, err + } + } + continue + } else if isFirstLineChar && c == ' ' { continue - } else if c == ' ' && isFirstLineChar { + } else if isFirstLineChar && c == '\t' { + if s.lastDelimColumn >= s.column { + return nil, ErrInvalidToken( + token.Invalid( + "tab character cannot be used for indentation in double-quoted text", + string(ctx.obuf), s.pos(), + ), + ) + } continue } else if c == '\\' { isFirstLineChar = false @@ -324,105 +374,292 @@ func (s *Scanner) scanDoubleQuote(ctx *Context) (tk *token.Token, pos int) { nextChar := src[idx+1] progress := 0 switch nextChar { - case 'b': + case '0': progress = 1 ctx.addOriginBuf(nextChar) - value = append(value, '\b') - case 'e': + value = append(value, 0x00) + case 'a': progress = 1 ctx.addOriginBuf(nextChar) - value = append(value, '\x1B') - case 'f': + value = append(value, 0x07) + case 'b': progress = 1 ctx.addOriginBuf(nextChar) - value = append(value, '\f') - case 'n': + value = append(value, 0x08) + case 't': progress = 1 ctx.addOriginBuf(nextChar) - value = append(value, '\n') - case 'r': + value = append(value, 0x09) + case 'n': progress = 1 ctx.addOriginBuf(nextChar) - value = append(value, '\r') + value = append(value, 0x0A) case 'v': progress = 1 ctx.addOriginBuf(nextChar) - value = append(value, '\v') - case 'L': // LS (#x2028) + value = append(value, 0x0B) + case 'f': progress = 1 ctx.addOriginBuf(nextChar) - value = append(value, []rune{'\xE2', '\x80', '\xA8'}...) - case 'N': // NEL (#x85) + value = append(value, 0x0C) + case 'r': progress = 1 ctx.addOriginBuf(nextChar) - value = append(value, []rune{'\xC2', '\x85'}...) - case 'P': // PS (#x2029) + value = append(value, 0x0D) + case 'e': progress = 1 ctx.addOriginBuf(nextChar) - value = append(value, []rune{'\xE2', '\x80', '\xA9'}...) - case '_': // #xA0 + value = append(value, 0x1B) + case ' ': progress = 1 ctx.addOriginBuf(nextChar) - value = append(value, []rune{'\xC2', '\xA0'}...) + value = append(value, 0x20) case '"': progress = 1 ctx.addOriginBuf(nextChar) - value = append(value, nextChar) + value = append(value, 0x22) + case '/': + progress = 1 + ctx.addOriginBuf(nextChar) + value = append(value, 0x2F) + case '\\': + progress = 1 + ctx.addOriginBuf(nextChar) + value = append(value, 0x5C) + case 'N': + progress = 1 + ctx.addOriginBuf(nextChar) + value = append(value, 0x85) + case '_': + progress = 1 + ctx.addOriginBuf(nextChar) + value = append(value, 0xA0) + case 'L': + progress = 1 + ctx.addOriginBuf(nextChar) + value = append(value, 0x2028) + case 'P': + progress = 1 + ctx.addOriginBuf(nextChar) + value = append(value, 0x2029) case 'x': - progress = 3 - if idx+progress >= size { - // TODO: need to return error - //err = xerrors.New("invalid escape character \\x") - return + if idx+3 >= size { + progress = 1 + ctx.addOriginBuf(nextChar) + value = append(value, nextChar) + } else { + progress = 3 + codeNum := hexRunesToInt(src[idx+2 : idx+progress+1]) + value = append(value, rune(codeNum)) } - codeNum := hexRunesToInt(src[idx+2 : idx+progress+1]) - value = append(value, rune(codeNum)) case 'u': + // \u0000 style must have 5 characters at least. + if idx+5 >= size { + return nil, ErrInvalidToken( + token.Invalid( + "not enough length for escaped UTF-16 character", + string(ctx.obuf), s.pos(), + ), + ) + } progress = 5 - if idx+progress >= size { - // TODO: need to return error - //err = xerrors.New("invalid escape character \\u") - return + codeNum := hexRunesToInt(src[idx+2 : idx+6]) + + // handle surrogate pairs. + if codeNum >= 0xD800 && codeNum <= 0xDBFF { + high := codeNum + + // \u0000\u0000 style must have 11 characters at least. + if idx+11 >= size { + return nil, ErrInvalidToken( + token.Invalid( + "not enough length for escaped UTF-16 surrogate pair", + string(ctx.obuf), s.pos(), + ), + ) + } + + if src[idx+6] != '\\' || src[idx+7] != 'u' { + return nil, ErrInvalidToken( + token.Invalid( + "found unexpected character after high surrogate for UTF-16 surrogate pair", + string(ctx.obuf), s.pos(), + ), + ) + } + + low := hexRunesToInt(src[idx+8 : idx+12]) + if low < 0xDC00 || low > 0xDFFF { + return nil, ErrInvalidToken( + token.Invalid( + "found unexpected low surrogate after high surrogate", + string(ctx.obuf), s.pos(), + ), + ) + } + codeNum = ((high - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000 + progress += 6 } - codeNum := hexRunesToInt(src[idx+2 : idx+progress+1]) value = append(value, rune(codeNum)) case 'U': - progress = 9 - if idx+progress >= size { - // TODO: need to return error - //err = xerrors.New("invalid escape character \\U") - return + // \U00000000 style must have 9 characters at least. + if idx+9 >= size { + return nil, ErrInvalidToken( + token.Invalid( + "not enough length for escaped UTF-32 character", + string(ctx.obuf), s.pos(), + ), + ) } - codeNum := hexRunesToInt(src[idx+2 : idx+progress+1]) + progress = 9 + codeNum := hexRunesToInt(src[idx+2 : idx+10]) value = append(value, rune(codeNum)) - case '\\': + case '\n': + isFirstLineChar = true + isNewLine = true + ctx.addOriginBuf(nextChar) + s.progressColumn(ctx, 1) + s.progressLine(ctx) + idx++ + continue + case '\r': + isFirstLineChar = true + isNewLine = true + ctx.addOriginBuf(nextChar) + s.progressLine(ctx) + progress = 1 + // Skip \n after \r in CRLF sequences + if idx+2 < size && src[idx+2] == '\n' { + ctx.addOriginBuf('\n') + progress = 2 + } + case '\t': progress = 1 ctx.addOriginBuf(nextChar) - value = append(value, c) + value = append(value, nextChar) default: - value = append(value, c) + s.progressColumn(ctx, 1) + return nil, ErrInvalidToken( + token.Invalid( + fmt.Sprintf("found unknown escape character %q", nextChar), + string(ctx.obuf), s.pos(), + ), + ) } idx += progress s.progressColumn(ctx, progress) continue + } else if c == '\t' { + var ( + foundNotSpaceChar bool + progress int + ) + for i := idx + 1; i < size; i++ { + if src[i] == ' ' || src[i] == '\t' { + progress++ + continue + } + if s.isNewLineChar(src[i]) { + break + } + foundNotSpaceChar = true + } + if foundNotSpaceChar { + value = append(value, c) + if src[idx+1] != '"' { + s.progressColumn(ctx, 1) + } + } else { + idx += progress + s.progressColumn(ctx, progress) + } + continue } else if c != '"' { value = append(value, c) isFirstLineChar = false continue } s.progressColumn(ctx, 1) - tk = token.DoubleQuote(string(value), string(ctx.obuf), srcpos) - pos = idx - startIndex + 1 - return + return token.DoubleQuote(string(value), string(ctx.obuf), srcpos), nil } - return + s.progressColumn(ctx, 1) + return nil, ErrInvalidToken( + token.Invalid( + "could not find end character of double-quoted text", + string(ctx.obuf), srcpos, + ), + ) } -func (s *Scanner) scanQuote(ctx *Context, ch rune) (tk *token.Token, pos int) { +func (s *Scanner) validateDocumentSeparatorMarker(ctx *Context, src []rune) error { + if s.foundDocumentSeparatorMarker(src) { + return ErrInvalidToken( + token.Invalid("found unexpected document separator", string(ctx.obuf), s.pos()), + ) + } + return nil +} + +func (s *Scanner) foundDocumentSeparatorMarker(src []rune) bool { + if len(src) < 3 { + return false + } + var marker string + if len(src) == 3 { + marker = string(src) + } else { + marker = strings.TrimRightFunc(string(src[:4]), func(r rune) bool { + return r == ' ' || r == '\t' || r == '\n' || r == '\r' + }) + } + return marker == "---" || marker == "..." +} + +func (s *Scanner) scanQuote(ctx *Context, ch rune) (bool, error) { + if ctx.existsBuffer() { + return false, nil + } if ch == '\'' { - return s.scanSingleQuote(ctx) + tk, err := s.scanSingleQuote(ctx) + if err != nil { + return false, err + } + ctx.addToken(tk) + } else { + tk, err := s.scanDoubleQuote(ctx) + if err != nil { + return false, err + } + ctx.addToken(tk) } - return s.scanDoubleQuote(ctx) + ctx.clear() + return true, nil +} + +func (s *Scanner) scanWhiteSpace(ctx *Context) bool { + if ctx.isMultiLine() { + return false + } + if !s.isAnchor && !s.isDirective && !s.isAlias && !s.isFirstCharAtLine { + return false + } + + if s.isFirstCharAtLine { + s.progressColumn(ctx, 1) + ctx.addOriginBuf(' ') + return true + } + if s.isDirective { + s.addBufferedTokenIfExists(ctx) + s.progressColumn(ctx, 1) + ctx.addOriginBuf(' ') + return true + } + + s.addBufferedTokenIfExists(ctx) + s.isAnchor = false + s.isAlias = false + return true } func (s *Scanner) isMergeKey(ctx *Context) bool { @@ -449,156 +686,178 @@ func (s *Scanner) isMergeKey(ctx *Context) bool { return false } -func (s *Scanner) scanTag(ctx *Context) (tk *token.Token, pos int) { +func (s *Scanner) scanTag(ctx *Context) (bool, error) { + if ctx.existsBuffer() || s.isDirective { + return false, nil + } + ctx.addOriginBuf('!') - ctx.progress(1) // skip '!' character + s.progress(ctx, 1) // skip '!' character + + var progress int for idx, c := range ctx.src[ctx.idx:] { - pos = idx + 1 - ctx.addOriginBuf(c) + progress = idx + 1 switch c { - case ' ', '\n', '\r': + case ' ': + ctx.addOriginBuf(c) value := ctx.source(ctx.idx-1, ctx.idx+idx) - tk = token.Tag(value, string(ctx.obuf), s.pos()) - pos = len([]rune(value)) - return + ctx.addToken(token.Tag(value, string(ctx.obuf), s.pos())) + s.progressColumn(ctx, len([]rune(value))) + ctx.clear() + return true, nil + case ',': + if s.startedFlowSequenceNum > 0 || s.startedFlowMapNum > 0 { + value := ctx.source(ctx.idx-1, ctx.idx+idx) + ctx.addToken(token.Tag(value, string(ctx.obuf), s.pos())) + s.progressColumn(ctx, len([]rune(value))-1) // progress column before collect-entry for scanning it at scanFlowEntry function. + ctx.clear() + return true, nil + } else { + ctx.addOriginBuf(c) + } + case '\n', '\r': + ctx.addOriginBuf(c) + value := ctx.source(ctx.idx-1, ctx.idx+idx) + ctx.addToken(token.Tag(value, string(ctx.obuf), s.pos())) + s.progressColumn(ctx, len([]rune(value))-1) // progress column before new-line-char for scanning new-line-char at scanNewLine function. + ctx.clear() + return true, nil + case '{', '}': + ctx.addOriginBuf(c) + s.progressColumn(ctx, progress) + invalidTk := token.Invalid(fmt.Sprintf("found invalid tag character %q", c), string(ctx.obuf), s.pos()) + return false, ErrInvalidToken(invalidTk) + default: + ctx.addOriginBuf(c) } } - return + s.progressColumn(ctx, progress) + ctx.clear() + return true, nil } -func (s *Scanner) scanComment(ctx *Context) (tk *token.Token, pos int) { +func (s *Scanner) scanComment(ctx *Context) bool { + if ctx.existsBuffer() { + c := ctx.previousChar() + if c != ' ' && c != '\t' && !s.isNewLineChar(c) { + return false + } + } + + s.addBufferedTokenIfExists(ctx) ctx.addOriginBuf('#') - ctx.progress(1) // skip '#' character + s.progress(ctx, 1) // skip '#' character + for idx, c := range ctx.src[ctx.idx:] { - pos = idx + 1 ctx.addOriginBuf(c) - switch c { - case '\n', '\r': - if ctx.previousChar() == '\\' { - continue - } - value := ctx.source(ctx.idx, ctx.idx+idx) - tk = token.Comment(value, string(ctx.obuf), s.pos()) - pos = len([]rune(value)) + 1 - return + if !s.isNewLineChar(c) { + continue } + if ctx.previousChar() == '\\' { + continue + } + value := ctx.source(ctx.idx, ctx.idx+idx) + progress := len([]rune(value)) + ctx.addToken(token.Comment(value, string(ctx.obuf), s.pos())) + s.progressColumn(ctx, progress) + s.progressLine(ctx) + ctx.clear() + return true } // document ends with comment. value := string(ctx.src[ctx.idx:]) - tk = token.Comment(value, string(ctx.obuf), s.pos()) - pos = len([]rune(value)) + 1 - return -} - -func trimCommentFromLiteralOpt(text string) (string, error) { - idx := strings.Index(text, "#") - if idx < 0 { - return text, nil - } - if idx == 0 { - return "", xerrors.New("invalid literal header") - } - return text[:idx-1], nil + ctx.addToken(token.Comment(value, string(ctx.obuf), s.pos())) + progress := len([]rune(value)) + s.progressColumn(ctx, progress) + s.progressLine(ctx) + ctx.clear() + return true } -func (s *Scanner) scanLiteral(ctx *Context, c rune) { +func (s *Scanner) scanMultiLine(ctx *Context, c rune) error { + state := ctx.getMultiLineState() ctx.addOriginBuf(c) + // normalize CR and CRLF to LF + if c == '\r' { + if ctx.nextChar() == '\n' { + ctx.addOriginBuf('\n') + s.progress(ctx, 1) + s.offset++ + } + c = '\n' + } if ctx.isEOS() { - if ctx.isLiteral { + if s.isFirstCharAtLine && c == ' ' { + state.addIndent(ctx, s.column) + } else { ctx.addBuf(c) } + state.updateIndentColumn(s.column) + if err := state.validateIndentColumn(); err != nil { + invalidTk := token.Invalid(err.Error(), string(ctx.obuf), s.pos()) + s.progressColumn(ctx, 1) + return ErrInvalidToken(invalidTk) + } value := ctx.bufferedSrc() ctx.addToken(token.String(string(value), string(ctx.obuf), s.pos())) - ctx.resetBuffer() + ctx.clear() s.progressColumn(ctx, 1) } else if s.isNewLineChar(c) { - if ctx.isLiteral { - ctx.addBuf(c) - } else { - ctx.addBuf(' ') - } + ctx.addBuf(c) + state.updateSpaceOnlyIndentColumn(s.column - 1) + state.updateNewLineState() s.progressLine(ctx) - } else if s.isFirstCharAtLine && c == ' ' { - if 0 < s.docStartColumn && s.docStartColumn <= s.column { - ctx.addBuf(c) + if ctx.next() { + if s.foundDocumentSeparatorMarker(ctx.src[ctx.idx:]) { + value := ctx.bufferedSrc() + ctx.addToken(token.String(string(value), string(ctx.obuf), s.pos())) + ctx.clear() + s.breakMultiLine(ctx) + } } + } else if s.isFirstCharAtLine && c == ' ' { + state.addIndent(ctx, s.column) + s.progressColumn(ctx, 1) + } else if s.isFirstCharAtLine && c == '\t' && state.isIndentColumn(s.column) { + err := ErrInvalidToken( + token.Invalid( + "found a tab character where an indentation space is expected", + string(ctx.obuf), s.pos(), + ), + ) + s.progressColumn(ctx, 1) + return err + } else if c == '\t' && !state.isIndentColumn(s.column) { + ctx.addBufWithTab(c) s.progressColumn(ctx, 1) } else { - if s.docStartColumn == 0 { - s.docStartColumn = s.column + if err := state.validateIndentAfterSpaceOnly(s.column); err != nil { + invalidTk := token.Invalid(err.Error(), string(ctx.obuf), s.pos()) + s.progressColumn(ctx, 1) + return ErrInvalidToken(invalidTk) } - ctx.addBuf(c) - s.progressColumn(ctx, 1) - } -} - -func (s *Scanner) scanLiteralHeader(ctx *Context) (pos int, err error) { - header := ctx.currentChar() - ctx.addOriginBuf(header) - ctx.progress(1) // skip '|' or '>' character - for idx, c := range ctx.src[ctx.idx:] { - pos = idx - ctx.addOriginBuf(c) - switch c { - case '\n', '\r': - value := ctx.source(ctx.idx, ctx.idx+idx) - opt := strings.TrimRight(value, " ") - orgOptLen := len(opt) - opt, err = trimCommentFromLiteralOpt(opt) - if err != nil { - return - } - switch opt { - case "", "+", "-", - "0", "1", "2", "3", "4", "5", "6", "7", "8", "9": - hasComment := len(opt) < orgOptLen - if header == '|' { - if hasComment { - commentLen := orgOptLen - len(opt) - headerPos := strings.Index(string(ctx.obuf), "|") - litBuf := ctx.obuf[:len(ctx.obuf)-commentLen-headerPos] - commentBuf := ctx.obuf[len(litBuf):] - ctx.addToken(token.Literal("|"+opt, string(litBuf), s.pos())) - s.column += len(litBuf) - s.offset += len(litBuf) - commentHeader := strings.Index(value, "#") - ctx.addToken(token.Comment(string(value[commentHeader+1:]), string(commentBuf), s.pos())) - } else { - ctx.addToken(token.Literal("|"+opt, string(ctx.obuf), s.pos())) - } - ctx.isLiteral = true - } else if header == '>' { - if hasComment { - commentLen := orgOptLen - len(opt) - headerPos := strings.Index(string(ctx.obuf), ">") - foldedBuf := ctx.obuf[:len(ctx.obuf)-commentLen-headerPos] - commentBuf := ctx.obuf[len(foldedBuf):] - ctx.addToken(token.Folded(">"+opt, string(foldedBuf), s.pos())) - s.column += len(foldedBuf) - s.offset += len(foldedBuf) - commentHeader := strings.Index(value, "#") - ctx.addToken(token.Comment(string(value[commentHeader+1:]), string(commentBuf), s.pos())) - } else { - ctx.addToken(token.Folded(">"+opt, string(ctx.obuf), s.pos())) - } - ctx.isFolded = true - } - s.indentState = IndentStateKeep - ctx.resetBuffer() - ctx.literalOpt = opt - return - } - break + state.updateIndentColumn(s.column) + if err := state.validateIndentColumn(); err != nil { + invalidTk := token.Invalid(err.Error(), string(ctx.obuf), s.pos()) + s.progressColumn(ctx, 1) + return ErrInvalidToken(invalidTk) } + if col := state.lastDelimColumn(); col > 0 { + s.lastDelimColumn = col + } + state.updateNewLineInFolded(ctx, s.column) + ctx.addBufWithTab(c) + s.progressColumn(ctx, 1) } - err = xerrors.New("invalid literal header") - return + return nil } func (s *Scanner) scanNewLine(ctx *Context, c rune) { if len(ctx.buf) > 0 && s.savedPos == nil { + bufLen := len(ctx.bufferedSrc()) s.savedPos = s.pos() - s.savedPos.Column -= len(ctx.bufferedSrc()) + s.savedPos.Column -= bufLen + s.savedPos.Offset -= bufLen } // if the following case, origin buffer has unnecessary two spaces. @@ -606,14 +865,7 @@ func (s *Scanner) scanNewLine(ctx *Context, c rune) { // --- // a:[space][space] // b: c - removedNum := ctx.removeRightSpaceFromBuf() - if removedNum > 0 { - s.column -= removedNum - s.offset -= removedNum - if s.savedPos != nil { - s.savedPos.Column -= removedNum - } - } + ctx.removeRightSpaceFromBuf() // There is no problem that we ignore CR which followed by LF and normalize it to LF, because of following YAML1.2 spec. // > Line breaks inside scalar content must be normalized by the YAML processor. Each such line break must be parsed into a single line feed character. @@ -621,254 +873,624 @@ func (s *Scanner) scanNewLine(ctx *Context, c rune) { // > -- https://yaml.org/spec/1.2/spec.html if c == '\r' && ctx.nextChar() == '\n' { ctx.addOriginBuf('\r') - ctx.progress(1) + s.progress(ctx, 1) + s.offset++ c = '\n' } if ctx.isEOS() { s.addBufferedTokenIfExists(ctx) - } else if s.isAnchor { + } else if s.isAnchor || s.isAlias || s.isDirective { s.addBufferedTokenIfExists(ctx) } - ctx.addBuf(' ') + if ctx.existsBuffer() && s.isFirstCharAtLine { + if ctx.buf[len(ctx.buf)-1] == ' ' { + ctx.buf[len(ctx.buf)-1] = '\n' + } else { + ctx.buf = append(ctx.buf, '\n') + } + } else { + ctx.addBuf(' ') + } + ctx.addOriginBuf(c) + s.progressLine(ctx) +} + +func (s *Scanner) isFlowMode() bool { + if s.startedFlowSequenceNum > 0 { + return true + } + if s.startedFlowMapNum > 0 { + return true + } + return false +} + +func (s *Scanner) scanFlowMapStart(ctx *Context) bool { + if ctx.existsBuffer() && !s.isFlowMode() { + return false + } + + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf('{') + ctx.addToken(token.MappingStart(string(ctx.obuf), s.pos())) + s.startedFlowMapNum++ + s.progressColumn(ctx, 1) + ctx.clear() + return true +} + +func (s *Scanner) scanFlowMapEnd(ctx *Context) bool { + if s.startedFlowMapNum <= 0 { + return false + } + + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf('}') + ctx.addToken(token.MappingEnd(string(ctx.obuf), s.pos())) + s.startedFlowMapNum-- + s.progressColumn(ctx, 1) + ctx.clear() + return true +} + +func (s *Scanner) scanFlowArrayStart(ctx *Context) bool { + if ctx.existsBuffer() && !s.isFlowMode() { + return false + } + + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf('[') + ctx.addToken(token.SequenceStart(string(ctx.obuf), s.pos())) + s.startedFlowSequenceNum++ + s.progressColumn(ctx, 1) + ctx.clear() + return true +} + +func (s *Scanner) scanFlowArrayEnd(ctx *Context) bool { + if ctx.existsBuffer() && s.startedFlowSequenceNum <= 0 { + return false + } + + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf(']') + ctx.addToken(token.SequenceEnd(string(ctx.obuf), s.pos())) + s.startedFlowSequenceNum-- + s.progressColumn(ctx, 1) + ctx.clear() + return true +} + +func (s *Scanner) scanFlowEntry(ctx *Context, c rune) bool { + if s.startedFlowSequenceNum <= 0 && s.startedFlowMapNum <= 0 { + return false + } + + s.addBufferedTokenIfExists(ctx) ctx.addOriginBuf(c) - ctx.isSingleLine = false + ctx.addToken(token.CollectEntry(string(ctx.obuf), s.pos())) + s.progressColumn(ctx, 1) + ctx.clear() + return true +} + +func (s *Scanner) scanMapDelim(ctx *Context) (bool, error) { + nc := ctx.nextChar() + if s.isDirective || s.isAnchor || s.isAlias { + return false, nil + } + if s.startedFlowMapNum <= 0 && nc != ' ' && nc != '\t' && !s.isNewLineChar(nc) && !ctx.isNextEOS() { + return false, nil + } + if s.startedFlowMapNum > 0 && nc == '/' { + // like http:// + return false, nil + } + if s.startedFlowMapNum > 0 { + tk := ctx.lastToken() + if tk != nil && tk.Type == token.MappingValueType { + return false, nil + } + } + + if strings.HasPrefix(strings.TrimPrefix(string(ctx.obuf), " "), "\t") && !strings.HasPrefix(string(ctx.buf), "\t") { + invalidTk := token.Invalid("tab character cannot use as a map key directly", string(ctx.obuf), s.pos()) + s.progressColumn(ctx, 1) + return false, ErrInvalidToken(invalidTk) + } + + // mapping value + tk := s.bufferedToken(ctx) + if tk != nil { + s.lastDelimColumn = tk.Position.Column + ctx.addToken(tk) + } else if tk := ctx.lastToken(); tk != nil { + // If the map key is quote, the buffer does not exist because it has already been cut into tokens. + // Therefore, we need to check the last token. + if tk.Indicator == token.QuotedScalarIndicator { + s.lastDelimColumn = tk.Position.Column + } + } + ctx.addToken(token.MappingValue(s.pos())) + s.progressColumn(ctx, 1) + ctx.clear() + return true, nil +} + +func (s *Scanner) scanDocumentStart(ctx *Context) bool { + if s.indentNum != 0 { + return false + } + if s.column != 1 { + return false + } + if ctx.repeatNum('-') != 3 { + return false + } + if ctx.size > ctx.idx+3 { + c := ctx.src[ctx.idx+3] + if c != ' ' && c != '\t' && c != '\n' && c != '\r' { + return false + } + } + + s.addBufferedTokenIfExists(ctx) + ctx.addToken(token.DocumentHeader(string(ctx.obuf)+"---", s.pos())) + s.progressColumn(ctx, 3) + ctx.clear() + s.clearState() + return true +} + +func (s *Scanner) scanDocumentEnd(ctx *Context) bool { + if s.indentNum != 0 { + return false + } + if s.column != 1 { + return false + } + if ctx.repeatNum('.') != 3 { + return false + } + + s.addBufferedTokenIfExists(ctx) + ctx.addToken(token.DocumentEnd(string(ctx.obuf)+"...", s.pos())) + s.progressColumn(ctx, 3) + ctx.clear() + return true +} + +func (s *Scanner) scanMergeKey(ctx *Context) bool { + if !s.isMergeKey(ctx) { + return false + } + + s.lastDelimColumn = s.column + ctx.addToken(token.MergeKey(string(ctx.obuf)+"<<", s.pos())) + s.progressColumn(ctx, 2) + ctx.clear() + return true +} + +func (s *Scanner) scanRawFoldedChar(ctx *Context) bool { + if !ctx.existsBuffer() { + return false + } + if !s.isChangedToIndentStateUp() { + return false + } + + ctx.setRawFolded(s.column) + ctx.addBuf('-') + ctx.addOriginBuf('-') + s.progressColumn(ctx, 1) + return true +} + +func (s *Scanner) scanSequence(ctx *Context) (bool, error) { + if ctx.existsBuffer() { + return false, nil + } + + nc := ctx.nextChar() + if nc != 0 && nc != ' ' && nc != '\t' && !s.isNewLineChar(nc) { + return false, nil + } + + if strings.HasPrefix(strings.TrimPrefix(string(ctx.obuf), " "), "\t") { + invalidTk := token.Invalid("tab character cannot use as a sequence delimiter", string(ctx.obuf), s.pos()) + s.progressColumn(ctx, 1) + return false, ErrInvalidToken(invalidTk) + } + + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf('-') + tk := token.SequenceEntry(string(ctx.obuf), s.pos()) + s.lastDelimColumn = tk.Position.Column + ctx.addToken(tk) + s.progressColumn(ctx, 1) + ctx.clear() + return true, nil +} + +func (s *Scanner) scanMultiLineHeader(ctx *Context) (bool, error) { + if ctx.existsBuffer() { + return false, nil + } + + if err := s.scanMultiLineHeaderOption(ctx); err != nil { + return false, err + } s.progressLine(ctx) + return true, nil } -func (s *Scanner) scan(ctx *Context) (pos int) { +func (s *Scanner) validateMultiLineHeaderOption(opt string) error { + if len(opt) == 0 { + return nil + } + orgOpt := opt + opt = strings.TrimPrefix(opt, "-") + opt = strings.TrimPrefix(opt, "+") + opt = strings.TrimSuffix(opt, "-") + opt = strings.TrimSuffix(opt, "+") + if len(opt) == 0 { + return nil + } + if opt == "0" { + return fmt.Errorf("invalid header option: %q", orgOpt) + } + i, err := strconv.ParseInt(opt, 10, 64) + if err != nil { + return fmt.Errorf("invalid header option: %q", orgOpt) + } + if i > 9 { + return fmt.Errorf("invalid header option: %q", orgOpt) + } + return nil +} + +func (s *Scanner) scanMultiLineHeaderOption(ctx *Context) error { + header := ctx.currentChar() + ctx.addOriginBuf(header) + s.progress(ctx, 1) // skip '|' or '>' character + + var progress int + var crlf bool + for idx, c := range ctx.src[ctx.idx:] { + progress = idx + ctx.addOriginBuf(c) + if s.isNewLineChar(c) { + nextIdx := ctx.idx + idx + 1 + if c == '\r' && nextIdx < len(ctx.src) && ctx.src[nextIdx] == '\n' { + crlf = true + continue // process \n in the next iteration + } + break + } + } + endPos := ctx.idx + progress + if crlf { + // Exclude \r + endPos = endPos - 1 + } + value := strings.TrimRight(ctx.source(ctx.idx, endPos), " ") + commentValueIndex := strings.Index(value, "#") + opt := value + if commentValueIndex > 0 { + opt = value[:commentValueIndex] + } + opt = strings.TrimRightFunc(opt, func(r rune) bool { + return r == ' ' || r == '\t' + }) + if len(opt) != 0 { + if err := s.validateMultiLineHeaderOption(opt); err != nil { + invalidTk := token.Invalid(err.Error(), string(ctx.obuf), s.pos()) + s.progressColumn(ctx, progress) + return ErrInvalidToken(invalidTk) + } + } + if s.column == 1 { + s.lastDelimColumn = 1 + } + + commentIndex := strings.Index(string(ctx.obuf), "#") + headerBuf := string(ctx.obuf) + if commentIndex > 0 { + headerBuf = headerBuf[:commentIndex] + } + switch header { + case '|': + ctx.addToken(token.Literal("|"+opt, headerBuf, s.pos())) + ctx.setLiteral(s.lastDelimColumn, opt) + case '>': + ctx.addToken(token.Folded(">"+opt, headerBuf, s.pos())) + ctx.setFolded(s.lastDelimColumn, opt) + } + if commentIndex > 0 { + comment := value[commentValueIndex+1:] + s.offset += len(headerBuf) + s.column += len(headerBuf) + ctx.addToken(token.Comment(comment, string(ctx.obuf[len(headerBuf):]), s.pos())) + } + s.indentState = IndentStateKeep + ctx.resetBuffer() + s.progressColumn(ctx, progress) + return nil +} + +func (s *Scanner) scanMapKey(ctx *Context) bool { + if ctx.existsBuffer() { + return false + } + + nc := ctx.nextChar() + if nc != ' ' && nc != '\t' { + return false + } + + tk := token.MappingKey(s.pos()) + s.lastDelimColumn = tk.Position.Column + ctx.addToken(tk) + s.progressColumn(ctx, 1) + ctx.clear() + return true +} + +func (s *Scanner) scanDirective(ctx *Context) bool { + if ctx.existsBuffer() { + return false + } + if s.indentNum != 0 { + return false + } + + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf('%') + ctx.addToken(token.Directive(string(ctx.obuf), s.pos())) + s.progressColumn(ctx, 1) + ctx.clear() + s.isDirective = true + return true +} + +func (s *Scanner) scanAnchor(ctx *Context) bool { + if ctx.existsBuffer() { + return false + } + + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf('&') + ctx.addToken(token.Anchor(string(ctx.obuf), s.pos())) + s.progressColumn(ctx, 1) + s.isAnchor = true + ctx.clear() + return true +} + +func (s *Scanner) scanAlias(ctx *Context) bool { + if ctx.existsBuffer() { + return false + } + + s.addBufferedTokenIfExists(ctx) + ctx.addOriginBuf('*') + ctx.addToken(token.Alias(string(ctx.obuf), s.pos())) + s.progressColumn(ctx, 1) + s.isAlias = true + ctx.clear() + return true +} + +func (s *Scanner) scanReservedChar(ctx *Context, c rune) error { + if ctx.existsBuffer() { + return nil + } + + ctx.addBuf(c) + ctx.addOriginBuf(c) + err := ErrInvalidToken( + token.Invalid( + fmt.Sprintf("%q is a reserved character", c), + string(ctx.obuf), s.pos(), + ), + ) + s.progressColumn(ctx, 1) + ctx.clear() + return err +} + +func (s *Scanner) scanTab(ctx *Context, c rune) error { + if s.startedFlowSequenceNum > 0 || s.startedFlowMapNum > 0 { + // tabs character is allowed in flow mode. + return nil + } + + if !s.isFirstCharAtLine { + return nil + } + + ctx.addBuf(c) + ctx.addOriginBuf(c) + err := ErrInvalidToken( + token.Invalid("found character '\t' that cannot start any token", + string(ctx.obuf), s.pos(), + ), + ) + s.progressColumn(ctx, 1) + ctx.clear() + return err +} + +func (s *Scanner) scan(ctx *Context) error { for ctx.next() { - pos = ctx.nextPos() c := ctx.currentChar() + // First, change the IndentState. + // If the target character is the first character in a line, IndentState is Up/Down/Equal state. + // The second and subsequent letters are Keep. s.updateIndent(ctx, c) - if ctx.isDocument() { - if s.isChangedToIndentStateEqual() || - s.isChangedToIndentStateDown() { - s.addBufferedTokenIfExists(ctx) - s.breakLiteral(ctx) + + // If IndentState is down, tokens are split, so the buffer accumulated until that point needs to be cutted as a token. + if s.isChangedToIndentStateDown() { + s.addBufferedTokenIfExists(ctx) + } + if ctx.isMultiLine() { + if s.isChangedToIndentStateDown() { + if tk := ctx.lastToken(); tk != nil { + // If literal/folded content is empty, no string token is added. + // Therefore, add an empty string token. + // But if literal/folded token column is 1, it is invalid at down state. + if tk.Position.Column == 1 { + return ErrInvalidToken( + token.Invalid( + "could not find multi-line content", + string(ctx.obuf), s.pos(), + ), + ) + } + if tk.Type != token.StringType { + ctx.addToken(token.String("", "", s.pos())) + } + } + s.breakMultiLine(ctx) } else { - s.scanLiteral(ctx, c) + if err := s.scanMultiLine(ctx, c); err != nil { + return err + } continue } - } else if s.isChangedToIndentStateDown() { - s.addBufferedTokenIfExists(ctx) - } else if s.isChangedToIndentStateEqual() { - // if first character is new line character, buffer expect to raw folded literal - if len(ctx.obuf) > 0 && s.newLineCount(ctx.obuf) <= 1 { - // doesn't raw folded literal - s.addBufferedTokenIfExists(ctx) - } } switch c { case '{': - if !ctx.existsBuffer() { - ctx.addOriginBuf(c) - ctx.addToken(token.MappingStart(string(ctx.obuf), s.pos())) - s.startedFlowMapNum++ - s.progressColumn(ctx, 1) - return + if s.scanFlowMapStart(ctx) { + continue } case '}': - if !ctx.existsBuffer() || s.startedFlowMapNum > 0 { - ctx.addToken(s.bufferedToken(ctx)) - ctx.addOriginBuf(c) - ctx.addToken(token.MappingEnd(string(ctx.obuf), s.pos())) - s.startedFlowMapNum-- - s.progressColumn(ctx, 1) - return + if s.scanFlowMapEnd(ctx) { + continue } case '.': - if s.indentNum == 0 && s.column == 1 && ctx.repeatNum('.') == 3 { - ctx.addToken(token.DocumentEnd(string(ctx.obuf)+"...", s.pos())) - s.progressColumn(ctx, 3) - pos += 2 - return + if s.scanDocumentEnd(ctx) { + continue } case '<': - if s.isMergeKey(ctx) { - s.prevIndentColumn = s.column - ctx.addToken(token.MergeKey(string(ctx.obuf)+"<<", s.pos())) - s.progressColumn(ctx, 1) - pos++ - return + if s.scanMergeKey(ctx) { + continue } case '-': - if s.indentNum == 0 && s.column == 1 && ctx.repeatNum('-') == 3 { - s.addBufferedTokenIfExists(ctx) - ctx.addToken(token.DocumentHeader(string(ctx.obuf)+"---", s.pos())) - s.progressColumn(ctx, 3) - pos += 2 - return - } - if ctx.existsBuffer() && s.isChangedToIndentStateUp() { - // raw folded - ctx.isRawFolded = true - ctx.addBuf(c) - ctx.addOriginBuf(c) - s.progressColumn(ctx, 1) + if s.scanDocumentStart(ctx) { continue } - if ctx.existsBuffer() { - // '-' is literal - ctx.addBuf(c) - ctx.addOriginBuf(c) - s.progressColumn(ctx, 1) + if s.scanRawFoldedChar(ctx) { continue } - nc := ctx.nextChar() - if nc == ' ' || s.isNewLineChar(nc) { - s.addBufferedTokenIfExists(ctx) - ctx.addOriginBuf(c) - tk := token.SequenceEntry(string(ctx.obuf), s.pos()) - s.prevIndentColumn = tk.Position.Column - ctx.addToken(tk) - s.progressColumn(ctx, 1) - return + scanned, err := s.scanSequence(ctx) + if err != nil { + return err + } + if scanned { + continue } case '[': - if !ctx.existsBuffer() { - ctx.addOriginBuf(c) - ctx.addToken(token.SequenceStart(string(ctx.obuf), s.pos())) - s.startedFlowSequenceNum++ - s.progressColumn(ctx, 1) - return + if s.scanFlowArrayStart(ctx) { + continue } case ']': - if !ctx.existsBuffer() || s.startedFlowSequenceNum > 0 { - s.addBufferedTokenIfExists(ctx) - ctx.addOriginBuf(c) - ctx.addToken(token.SequenceEnd(string(ctx.obuf), s.pos())) - s.startedFlowSequenceNum-- - s.progressColumn(ctx, 1) - return + if s.scanFlowArrayEnd(ctx) { + continue } case ',': - if s.startedFlowSequenceNum > 0 || s.startedFlowMapNum > 0 { - s.addBufferedTokenIfExists(ctx) - ctx.addOriginBuf(c) - ctx.addToken(token.CollectEntry(string(ctx.obuf), s.pos())) - s.progressColumn(ctx, 1) - return + if s.scanFlowEntry(ctx, c) { + continue } case ':': - nc := ctx.nextChar() - if s.startedFlowMapNum > 0 || nc == ' ' || s.isNewLineChar(nc) || ctx.isNextEOS() { - // mapping value - tk := s.bufferedToken(ctx) - if tk != nil { - s.prevIndentColumn = tk.Position.Column - ctx.addToken(tk) - } else if tk := ctx.lastToken(); tk != nil { - // If the map key is quote, the buffer does not exist because it has already been cut into tokens. - // Therefore, we need to check the last token. - if tk.Indicator == token.QuotedScalarIndicator { - s.prevIndentColumn = tk.Position.Column - } - } - ctx.addToken(token.MappingValue(s.pos())) - s.progressColumn(ctx, 1) - return + scanned, err := s.scanMapDelim(ctx) + if err != nil { + return err + } + if scanned { + continue } case '|', '>': - if !ctx.existsBuffer() { - progress, err := s.scanLiteralHeader(ctx) - if err != nil { - // TODO: returns syntax error object - return - } - s.progressColumn(ctx, progress) - s.progressLine(ctx) + scanned, err := s.scanMultiLineHeader(ctx) + if err != nil { + return err + } + if scanned { continue } case '!': - if !ctx.existsBuffer() { - token, progress := s.scanTag(ctx) - ctx.addToken(token) - s.progressColumn(ctx, progress) - if c := ctx.previousChar(); s.isNewLineChar(c) { - s.progressLine(ctx) - } - pos += progress - return + scanned, err := s.scanTag(ctx) + if err != nil { + return err + } + if scanned { + continue } case '%': - if !ctx.existsBuffer() && s.indentNum == 0 { - ctx.addToken(token.Directive(string(ctx.obuf)+"%", s.pos())) - s.progressColumn(ctx, 1) - return + if s.scanDirective(ctx) { + continue } case '?': - nc := ctx.nextChar() - if !ctx.existsBuffer() && nc == ' ' { - ctx.addToken(token.MappingKey(s.pos())) - s.progressColumn(ctx, 1) - return + if s.scanMapKey(ctx) { + continue } case '&': - if !ctx.existsBuffer() { - s.addBufferedTokenIfExists(ctx) - ctx.addOriginBuf(c) - ctx.addToken(token.Anchor(string(ctx.obuf), s.pos())) - s.progressColumn(ctx, 1) - s.isAnchor = true - return + if s.scanAnchor(ctx) { + continue } case '*': - if !ctx.existsBuffer() { - s.addBufferedTokenIfExists(ctx) - ctx.addOriginBuf(c) - ctx.addToken(token.Alias(string(ctx.obuf), s.pos())) - s.progressColumn(ctx, 1) - return + if s.scanAlias(ctx) { + continue } case '#': - if !ctx.existsBuffer() || ctx.previousChar() == ' ' { - s.addBufferedTokenIfExists(ctx) - token, progress := s.scanComment(ctx) - ctx.addToken(token) - s.progressColumn(ctx, progress) - s.progressLine(ctx) - pos += progress - return + if s.scanComment(ctx) { + continue } case '\'', '"': - if !ctx.existsBuffer() { - token, progress := s.scanQuote(ctx, c) - ctx.addToken(token) - pos += progress - // If the non-whitespace character immediately following the quote is ':', the quote should be treated as a map key. - // Therefore, do not return and continue processing as a normal map key. - if ctx.currentCharWithSkipWhitespace() == ':' { - continue - } - return + scanned, err := s.scanQuote(ctx, c) + if err != nil { + return err + } + if scanned { + continue } case '\r', '\n': s.scanNewLine(ctx, c) continue case ' ': - if ctx.isSaveIndentMode() || (!s.isAnchor && !s.isFirstCharAtLine) { - ctx.addBuf(c) + if s.scanWhiteSpace(ctx) { + continue + } + case '@', '`': + if err := s.scanReservedChar(ctx, c); err != nil { + return err + } + case '\t': + if ctx.existsBuffer() && s.lastDelimColumn == 0 { + // tab indent for plain text (yaml-test-suite's spec-example-7-12-plain-lines). + s.indentNum++ ctx.addOriginBuf(c) - s.progressColumn(ctx, 1) + s.progressOnly(ctx, 1) continue } - if s.isFirstCharAtLine { - s.progressColumn(ctx, 1) + if s.lastDelimColumn < s.column { + s.indentNum++ ctx.addOriginBuf(c) + s.progressOnly(ctx, 1) continue } - s.addBufferedTokenIfExists(ctx) - pos-- // to rescan white space at next scanning for adding white space to next buffer. - s.isAnchor = false - return + if err := s.scanTab(ctx, c); err != nil { + return err + } } ctx.addBuf(c) ctx.addOriginBuf(c) s.progressColumn(ctx, 1) } s.addBufferedTokenIfExists(ctx) - return + return nil } // Init prepares the scanner s to tokenize the text src by setting the scanner at the beginning of src. @@ -880,12 +1502,15 @@ func (s *Scanner) Init(text string) { s.line = 1 s.column = 1 s.offset = 1 - s.prevIndentLevel = 0 - s.prevIndentNum = 0 - s.prevIndentColumn = 0 + s.isFirstCharAtLine = true + s.clearState() +} + +func (s *Scanner) clearState() { + s.prevLineIndentNum = 0 + s.lastDelimColumn = 0 s.indentLevel = 0 s.indentNum = 0 - s.isFirstCharAtLine = true } // Scan scans the next token and returns the token collection. The source end is indicated by io.EOF. @@ -895,9 +1520,17 @@ func (s *Scanner) Scan() (token.Tokens, error) { } ctx := newContext(s.source[s.sourcePos:]) defer ctx.release() - progress := s.scan(ctx) - s.sourcePos += progress + var tokens token.Tokens + err := s.scan(ctx) tokens = append(tokens, ctx.tokens...) + + if err != nil { + var invalidTokenErr *InvalidTokenError + if errors.As(err, &invalidTokenErr) { + tokens = append(tokens, invalidTokenErr.Token) + } + return tokens, err + } return tokens, nil } diff --git a/vendor/github.com/goccy/go-yaml/stdlib_quote.go b/vendor/github.com/goccy/go-yaml/stdlib_quote.go index be50ae61..120448d3 100644 --- a/vendor/github.com/goccy/go-yaml/stdlib_quote.go +++ b/vendor/github.com/goccy/go-yaml/stdlib_quote.go @@ -53,8 +53,18 @@ func appendQuotedWith(buf []byte, s string, quote byte) []byte { func appendEscapedRune(buf []byte, r rune, quote byte) []byte { var runeTmp [utf8.UTFMax]byte - if r == rune(quote) || r == '\\' { // always backslashed - buf = append(buf, '\\') + // goccy/go-yaml patch on top of the standard library's appendEscapedRune function. + // + // We use this to implement the YAML single-quoted string, where the only escape sequence is '', which represents a single quote. + // The below snippet from the standard library is for escaping e.g. \ with \\, which is not what we want for the single-quoted string. + // + // if r == rune(quote) || r == '\\' { // always backslashed + // buf = append(buf, '\\') + // buf = append(buf, byte(r)) + // return buf + // } + if r == rune(quote) { + buf = append(buf, byte(r)) buf = append(buf, byte(r)) return buf } diff --git a/vendor/github.com/goccy/go-yaml/struct.go b/vendor/github.com/goccy/go-yaml/struct.go index a3da8ddd..ece77f56 100644 --- a/vendor/github.com/goccy/go-yaml/struct.go +++ b/vendor/github.com/goccy/go-yaml/struct.go @@ -1,10 +1,9 @@ package yaml import ( + "fmt" "reflect" "strings" - - "golang.org/x/xerrors" ) const ( @@ -21,6 +20,7 @@ type StructField struct { IsAutoAnchor bool IsAutoAlias bool IsOmitEmpty bool + IsOmitZero bool IsFlow bool IsInline bool } @@ -45,7 +45,7 @@ func structField(field reflect.StructField) *StructField { fieldName = options[0] } } - structField := &StructField{ + sf := &StructField{ FieldName: field.Name, RenderName: fieldName, } @@ -53,30 +53,32 @@ func structField(field reflect.StructField) *StructField { for _, opt := range options[1:] { switch { case opt == "omitempty": - structField.IsOmitEmpty = true + sf.IsOmitEmpty = true + case opt == "omitzero": + sf.IsOmitZero = true case opt == "flow": - structField.IsFlow = true + sf.IsFlow = true case opt == "inline": - structField.IsInline = true + sf.IsInline = true case strings.HasPrefix(opt, "anchor"): anchor := strings.Split(opt, "=") if len(anchor) > 1 { - structField.AnchorName = anchor[1] + sf.AnchorName = anchor[1] } else { - structField.IsAutoAnchor = true + sf.IsAutoAnchor = true } case strings.HasPrefix(opt, "alias"): alias := strings.Split(opt, "=") if len(alias) > 1 { - structField.AliasName = alias[1] + sf.AliasName = alias[1] } else { - structField.IsAutoAlias = true + sf.IsAutoAlias = true } default: } } } - return structField + return sf } func isIgnoredStructField(field reflect.StructField) bool { @@ -84,11 +86,7 @@ func isIgnoredStructField(field reflect.StructField) bool { // private field return true } - tag := getTag(field) - if tag == "-" { - return true - } - return false + return getTag(field) == "-" } type StructFieldMap map[string]*StructField @@ -112,19 +110,19 @@ func (m StructFieldMap) hasMergeProperty() bool { } func structFieldMap(structType reflect.Type) (StructFieldMap, error) { - structFieldMap := StructFieldMap{} + fieldMap := StructFieldMap{} renderNameMap := map[string]struct{}{} for i := 0; i < structType.NumField(); i++ { field := structType.Field(i) if isIgnoredStructField(field) { continue } - structField := structField(field) - if _, exists := renderNameMap[structField.RenderName]; exists { - return nil, xerrors.Errorf("duplicated struct field name %s", structField.RenderName) + sf := structField(field) + if _, exists := renderNameMap[sf.RenderName]; exists { + return nil, fmt.Errorf("duplicated struct field name %s", sf.RenderName) } - structFieldMap[structField.FieldName] = structField - renderNameMap[structField.RenderName] = struct{}{} + fieldMap[sf.FieldName] = sf + renderNameMap[sf.RenderName] = struct{}{} } - return structFieldMap, nil + return fieldMap, nil } diff --git a/vendor/github.com/goccy/go-yaml/token/token.go b/vendor/github.com/goccy/go-yaml/token/token.go index 14d76220..e8873562 100644 --- a/vendor/github.com/goccy/go-yaml/token/token.go +++ b/vendor/github.com/goccy/go-yaml/token/token.go @@ -1,8 +1,11 @@ package token import ( + "errors" "fmt" + "strconv" "strings" + "time" ) // Character type for character @@ -99,6 +102,10 @@ const ( SpaceType // NullType type for Null token NullType + // ImplicitNullType type for implicit Null token. + // This is used when explicit keywords such as null or ~ are not specified. + // It is distinguished during encoding and output as an empty string. + ImplicitNullType // InfinityType type for Infinity token InfinityType // NanType type for Nan token @@ -117,6 +124,8 @@ const ( StringType // BoolType type for Bool token BoolType + // InvalidType type for invalid token + InvalidType ) // String type identifier to text @@ -182,10 +191,14 @@ func (t Type) String() string { return "Float" case NullType: return "Null" + case ImplicitNullType: + return "ImplicitNull" case InfinityType: return "Infinity" case NanType: return "Nan" + case InvalidType: + return "Invalid" } return "" } @@ -202,6 +215,8 @@ const ( CharacterTypeMiscellaneous // CharacterTypeEscaped type of escaped character CharacterTypeEscaped + // CharacterTypeInvalid type for a invalid token. + CharacterTypeInvalid ) // String character type identifier to text @@ -210,7 +225,7 @@ func (c CharacterType) String() string { case CharacterTypeIndicator: return "Indicator" case CharacterTypeWhiteSpace: - return "WhiteSpcae" + return "WhiteSpace" case CharacterTypeMiscellaneous: return "Miscellaneous" case CharacterTypeEscaped: @@ -339,9 +354,12 @@ func reservedKeywordToken(typ Type, value, org string, pos *Position) *Token { func init() { for _, keyword := range reservedNullKeywords { - reservedKeywordMap[keyword] = func(value, org string, pos *Position) *Token { + f := func(value, org string, pos *Position) *Token { return reservedKeywordToken(NullType, value, org, pos) } + + reservedKeywordMap[keyword] = f + reservedEncKeywordMap[keyword] = f } for _, keyword := range reservedBoolKeywords { f := func(value, org string, pos *Position) *Token { @@ -391,6 +409,10 @@ const ( SetTag ReservedTagKeyword = "!!set" // TimestampTag `!!timestamp` tag TimestampTag ReservedTagKeyword = "!!timestamp" + // BooleanTag `!!bool` tag + BooleanTag ReservedTagKeyword = "!!bool" + // MergeTag `!!merge` tag + MergeTag ReservedTagKeyword = "!!merge" ) var ( @@ -496,121 +518,163 @@ var ( Position: pos, } }, + BooleanTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, + MergeTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, } ) -type numType int +type NumberType string const ( - numTypeNone numType = iota - numTypeBinary - numTypeOctet - numTypeHex - numTypeFloat + NumberTypeDecimal NumberType = "decimal" + NumberTypeBinary NumberType = "binary" + NumberTypeOctet NumberType = "octet" + NumberTypeHex NumberType = "hex" + NumberTypeFloat NumberType = "float" ) -type numStat struct { - isNum bool - typ numType +type NumberValue struct { + Type NumberType + Value any + Text string } -func getNumberStat(str string) *numStat { - stat := &numStat{} - if str == "" { - return stat +func ToNumber(value string) *NumberValue { + num, err := toNumber(value) + if err != nil { + return nil } - if str == "-" || str == "." || str == "+" || str == "_" { - return stat + return num +} + +func isNumber(value string) bool { + num, err := toNumber(value) + if err != nil { + var numErr *strconv.NumError + if errors.As(err, &numErr) && errors.Is(numErr.Err, strconv.ErrRange) { + return true + } + return false } - if str[0] == '_' { - return stat + return num != nil +} + +func toNumber(value string) (*NumberValue, error) { + if len(value) == 0 { + return nil, nil } - dotFound := false - isNegative := false - isExponent := false - if str[0] == '-' { - isNegative = true + if strings.HasPrefix(value, "_") { + return nil, nil } - for idx, c := range str { - switch c { - case 'x': - if (isNegative && idx == 2) || (!isNegative && idx == 1) { - continue - } - case 'o': - if (isNegative && idx == 2) || (!isNegative && idx == 1) { - continue - } - case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - continue - case 'a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F': - if (len(str) > 2 && str[0] == '0' && str[1] == 'x') || - (len(str) > 3 && isNegative && str[1] == '0' && str[2] == 'x') { - // hex number - continue - } - if c == 'b' && ((isNegative && idx == 2) || (!isNegative && idx == 1)) { - // binary number - continue - } - if (c == 'e' || c == 'E') && dotFound { - // exponent - isExponent = true - continue - } - case '.': - if dotFound { - // multiple dot - return stat - } - dotFound = true - continue - case '-': - if idx == 0 || isExponent { - continue - } - case '+': - if idx == 0 || isExponent { - continue - } - case '_': - continue - } - return stat + dotCount := strings.Count(value, ".") + if dotCount > 1 { + return nil, nil } - stat.isNum = true + + isNegative := strings.HasPrefix(value, "-") + normalized := strings.ReplaceAll(strings.TrimPrefix(strings.TrimPrefix(value, "+"), "-"), "_", "") + + var ( + typ NumberType + base int + ) switch { - case dotFound: - stat.typ = numTypeFloat - case strings.HasPrefix(str, "0b") || strings.HasPrefix(str, "-0b"): - stat.typ = numTypeBinary - case strings.HasPrefix(str, "0x") || strings.HasPrefix(str, "-0x"): - stat.typ = numTypeHex - case strings.HasPrefix(str, "0o") || strings.HasPrefix(str, "-0o"): - stat.typ = numTypeOctet - case (len(str) > 1 && str[0] == '0') || (len(str) > 1 && str[0] == '-' && str[1] == '0'): - stat.typ = numTypeOctet - } - return stat + case strings.HasPrefix(normalized, "0x"): + normalized = strings.TrimPrefix(normalized, "0x") + base = 16 + typ = NumberTypeHex + case strings.HasPrefix(normalized, "0o"): + normalized = strings.TrimPrefix(normalized, "0o") + base = 8 + typ = NumberTypeOctet + case strings.HasPrefix(normalized, "0b"): + normalized = strings.TrimPrefix(normalized, "0b") + base = 2 + typ = NumberTypeBinary + case strings.HasPrefix(normalized, "0") && len(normalized) > 1 && dotCount == 0: + base = 8 + typ = NumberTypeOctet + case dotCount == 1: + typ = NumberTypeFloat + default: + typ = NumberTypeDecimal + base = 10 + } + + text := normalized + if isNegative { + text = "-" + text + } + + var v any + if typ == NumberTypeFloat { + f, err := strconv.ParseFloat(text, 64) + if err != nil { + return nil, err + } + v = f + } else if isNegative { + i, err := strconv.ParseInt(text, base, 64) + if err != nil { + return nil, err + } + v = i + } else { + u, err := strconv.ParseUint(text, base, 64) + if err != nil { + return nil, err + } + v = u + } + + return &NumberValue{ + Type: typ, + Value: v, + Text: text, + }, nil } -func looksLikeTimeValue(value string) bool { - for i, c := range value { - switch c { - case ':', '1', '2', '3', '4', '5', '6', '7', '8', '9': - continue - case '0': - if i == 0 { - return false - } - continue +// This is a subset of the formats permitted by the regular expression +// defined at http://yaml.org/type/timestamp.html. Note that time.Parse +// cannot handle: "2001-12-14 21:59:43.10 -5" from the examples. +var timestampFormats = []string{ + time.RFC3339Nano, + "2006-01-02t15:04:05.999999999Z07:00", // RFC3339Nano with lower-case "t". + time.DateTime, + time.DateOnly, + + // Not in examples, but to preserve backward compatibility by quoting time values. + "15:4", +} + +func isTimestamp(value string) bool { + for _, format := range timestampFormats { + if _, err := time.Parse(format, value); err == nil { + return true } - return false } - return true + return false } -// IsNeedQuoted whether need quote for passed string or not +// IsNeedQuoted checks whether the value needs quote for passed string or not func IsNeedQuoted(value string) bool { if value == "" { return true @@ -618,7 +682,10 @@ func IsNeedQuoted(value string) bool { if _, exists := reservedEncKeywordMap[value]; exists { return true } - if stat := getNumberStat(value); stat.isNum { + if isNumber(value) { + return true + } + if value == "-" { return true } first := value[0] @@ -631,14 +698,14 @@ func IsNeedQuoted(value string) bool { case ':', ' ': return true } - if looksLikeTimeValue(value) { + if isTimestamp(value) { return true } for i, c := range value { switch c { case '#', '\\': return true - case ':': + case ':', '-': if i+1 < len(value) && value[i+1] == ' ' { return true } @@ -663,13 +730,13 @@ func LiteralBlockHeader(value string) string { } } -// New create reserved keyword token or number token and other string token +// New create reserved keyword token or number token and other string token. func New(value string, org string, pos *Position) *Token { fn := reservedKeywordMap[value] if fn != nil { return fn(value, org, pos) } - if stat := getNumberStat(value); stat.isNum { + if num := ToNumber(value); num != nil { tk := &Token{ Type: IntegerType, CharacterType: CharacterTypeMiscellaneous, @@ -678,14 +745,14 @@ func New(value string, org string, pos *Position) *Token { Origin: org, Position: pos, } - switch stat.typ { - case numTypeFloat: + switch num.Type { + case NumberTypeFloat: tk.Type = FloatType - case numTypeBinary: + case NumberTypeBinary: tk.Type = BinaryIntegerType - case numTypeOctet: + case NumberTypeOctet: tk.Type = OctetIntegerType - case numTypeHex: + case NumberTypeHex: tk.Type = HexIntegerType } return tk @@ -709,14 +776,24 @@ func (p *Position) String() string { // Token type for token type Token struct { - Type Type + // Type is a token type. + Type Type + // CharacterType is a character type. CharacterType CharacterType - Indicator Indicator - Value string - Origin string - Position *Position - Next *Token - Prev *Token + // Indicator is a indicator type. + Indicator Indicator + // Value is a string extracted with only meaningful characters, with spaces and such removed. + Value string + // Origin is a string that stores the original text as-is. + Origin string + // Error keeps error message for InvalidToken. + Error string + // Position is a token position. + Position *Position + // Next is a next token reference. + Next *Token + // Prev is a previous token reference. + Prev *Token } // PreviousType previous token type @@ -756,9 +833,26 @@ func (t *Token) Clone() *Token { return &copied } +// Dump outputs token information to stdout for debugging. +func (t *Token) Dump() { + fmt.Printf( + "[TYPE]:%q [CHARTYPE]:%q [INDICATOR]:%q [VALUE]:%q [ORG]:%q [POS(line:column:level:offset)]: %d:%d:%d:%d\n", + t.Type, t.CharacterType, t.Indicator, t.Value, t.Origin, t.Position.Line, t.Position.Column, t.Position.IndentLevel, t.Position.Offset, + ) +} + // Tokens type of token collection type Tokens []*Token +func (t Tokens) InvalidToken() *Token { + for _, tt := range t { + if tt.Type == InvalidType { + return tt + } + } + return nil +} + func (t *Tokens) add(tk *Token) { tokens := *t if len(tokens) == 0 { @@ -782,7 +876,8 @@ func (t *Tokens) Add(tks ...*Token) { // Dump dump all token structures for debugging func (t Tokens) Dump() { for _, tk := range t { - fmt.Printf("- %+v\n", tk) + fmt.Print("- ") + tk.Dump() } } @@ -1054,6 +1149,18 @@ func DocumentEnd(org string, pos *Position) *Token { } } +func Invalid(err string, org string, pos *Position) *Token { + return &Token{ + Type: InvalidType, + CharacterType: CharacterTypeInvalid, + Indicator: NotIndicator, + Value: org, + Origin: org, + Error: err, + Position: pos, + } +} + // DetectLineBreakCharacter detect line break character in only one inside scalar content scope. func DetectLineBreakCharacter(src string) string { nc := strings.Count(src, "\n") diff --git a/vendor/github.com/goccy/go-yaml/yaml.go b/vendor/github.com/goccy/go-yaml/yaml.go index 25b1056f..e1b5fbd9 100644 --- a/vendor/github.com/goccy/go-yaml/yaml.go +++ b/vendor/github.com/goccy/go-yaml/yaml.go @@ -9,7 +9,6 @@ import ( "github.com/goccy/go-yaml/ast" "github.com/goccy/go-yaml/internal/errors" - "golang.org/x/xerrors" ) // BytesMarshaler interface may be implemented by types to customize their @@ -58,6 +57,16 @@ type InterfaceUnmarshalerContext interface { UnmarshalYAML(context.Context, func(interface{}) error) error } +// NodeUnmarshaler interface is similar to BytesUnmarshaler but provide related AST node instead of raw YAML source. +type NodeUnmarshaler interface { + UnmarshalYAML(ast.Node) error +} + +// NodeUnmarshalerContext interface is similar to BytesUnmarshaler but provide related AST node instead of raw YAML source. +type NodeUnmarshalerContext interface { + UnmarshalYAML(context.Context, ast.Node) error +} + // MapItem is an item in a MapSlice. type MapItem struct { Key, Value interface{} @@ -80,11 +89,11 @@ func (s MapSlice) ToMap() map[interface{}]interface{} { // of the generated document will reflect the structure of the value itself. // Maps and pointers (to struct, string, int, etc) are accepted as the in value. // -// Struct fields are only marshalled if they are exported (have an upper case -// first letter), and are marshalled using the field name lowercased as the +// Struct fields are only marshaled if they are exported (have an upper case +// first letter), and are marshaled using the field name lowercased as the // default key. Custom keys may be defined via the "yaml" name in the field // tag: the content preceding the first comma is used as the key, and the -// following comma-separated options are used to tweak the marshalling process. +// following comma-separated options are used to tweak the marshaling process. // Conflicting names result in a runtime error. // // The field tag format accepted is: @@ -99,6 +108,13 @@ func (s MapSlice) ToMap() map[interface{}]interface{} { // fields are zero, unless they implement an IsZero // method (see the IsZeroer interface type), in which // case the field will be included if that method returns true. +// Note that this definition is slightly different from the Go's +// encoding/json 'omitempty' definition. It combines some elements +// of 'omitempty' and 'omitzero'. See https://github.com/goccy/go-yaml/issues/695. +// +// omitzero The omitzero tag behaves in the same way as the interpretation of the omitzero tag in the encoding/json library. +// 1) If the field type has an "IsZero() bool" method, that will be used to determine whether the value is zero. +// 2) Otherwise, the value is zero if it is the zero value for its type. // // flow Marshal using a flow style (useful for structs, // sequences and maps). @@ -138,7 +154,7 @@ func MarshalWithOptions(v interface{}, opts ...EncodeOption) ([]byte, error) { func MarshalContext(ctx context.Context, v interface{}, opts ...EncodeOption) ([]byte, error) { var buf bytes.Buffer if err := NewEncoder(&buf, opts...).EncodeContext(ctx, v); err != nil { - return nil, errors.Wrapf(err, "failed to marshal") + return nil, err } return buf.Bytes(), nil } @@ -148,7 +164,7 @@ func ValueToNode(v interface{}, opts ...EncodeOption) (ast.Node, error) { var buf bytes.Buffer node, err := NewEncoder(&buf, opts...).EncodeToNode(v) if err != nil { - return nil, errors.Wrapf(err, "failed to convert value to node") + return nil, err } return node, nil } @@ -161,7 +177,7 @@ func ValueToNode(v interface{}, opts ...EncodeOption) (ast.Node, error) { // lowercased as the default key. Custom keys may be defined via the // "yaml" name in the field tag: the content preceding the first comma // is used as the key, and the following comma-separated options are -// used to tweak the marshalling process (see Marshal). +// used to tweak the marshaling process (see Marshal). // Conflicting names result in a runtime error. // // For example: @@ -192,7 +208,7 @@ func UnmarshalContext(ctx context.Context, data []byte, v interface{}, opts ...D if err == io.EOF { return nil } - return errors.Wrapf(err, "failed to unmarshal") + return err } return nil } @@ -201,7 +217,7 @@ func UnmarshalContext(ctx context.Context, data []byte, v interface{}, opts ...D func NodeToValue(node ast.Node, v interface{}, opts ...DecodeOption) error { var buf bytes.Buffer if err := NewDecoder(&buf, opts...).DecodeFromNode(node, v); err != nil { - return errors.Wrapf(err, "failed to convert node to value") + return err } return nil } @@ -213,11 +229,9 @@ func NodeToValue(node ast.Node, v interface{}, opts ...DecodeOption) error { // If the third argument `inclSource` is true, the error message will // contain snippets of the YAML source that was used. func FormatError(e error, colored, inclSource bool) string { - var pp errors.PrettyPrinter - if xerrors.As(e, &pp) { - var buf bytes.Buffer - pp.PrettyPrint(&errors.Sink{&buf}, colored, inclSource) - return buf.String() + var yamlErr Error + if errors.As(e, &yamlErr) { + return yamlErr.FormatError(colored, inclSource) } return e.Error() @@ -227,11 +241,11 @@ func FormatError(e error, colored, inclSource bool) string { func YAMLToJSON(bytes []byte) ([]byte, error) { var v interface{} if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil { - return nil, errors.Wrapf(err, "failed to unmarshal") + return nil, err } out, err := MarshalWithOptions(v, JSON()) if err != nil { - return nil, errors.Wrapf(err, "failed to marshal with json option") + return nil, err } return out, nil } @@ -240,11 +254,11 @@ func YAMLToJSON(bytes []byte) ([]byte, error) { func JSONToYAML(bytes []byte) ([]byte, error) { var v interface{} if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil { - return nil, errors.Wrapf(err, "failed to unmarshal from json bytes") + return nil, err } out, err := Marshal(v) if err != nil { - return nil, errors.Wrapf(err, "failed to marshal") + return nil, err } return out, nil } @@ -252,8 +266,8 @@ func JSONToYAML(bytes []byte) ([]byte, error) { var ( globalCustomMarshalerMu sync.Mutex globalCustomUnmarshalerMu sync.Mutex - globalCustomMarshalerMap = map[reflect.Type]func(interface{}) ([]byte, error){} - globalCustomUnmarshalerMap = map[reflect.Type]func(interface{}, []byte) error{} + globalCustomMarshalerMap = map[reflect.Type]func(context.Context, interface{}) ([]byte, error){} + globalCustomUnmarshalerMap = map[reflect.Type]func(context.Context, interface{}, []byte) error{} ) // RegisterCustomMarshaler overrides any encoding process for the type specified in generics. @@ -267,11 +281,23 @@ func RegisterCustomMarshaler[T any](marshaler func(T) ([]byte, error)) { defer globalCustomMarshalerMu.Unlock() var typ T - globalCustomMarshalerMap[reflect.TypeOf(typ)] = func(v interface{}) ([]byte, error) { + globalCustomMarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}) ([]byte, error) { return marshaler(v.(T)) } } +// RegisterCustomMarshalerContext overrides any encoding process for the type specified in generics. +// Similar to RegisterCustomMarshalerContext, but allows passing a context to the unmarshaler function. +func RegisterCustomMarshalerContext[T any](marshaler func(context.Context, T) ([]byte, error)) { + globalCustomMarshalerMu.Lock() + defer globalCustomMarshalerMu.Unlock() + + var typ T + globalCustomMarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}) ([]byte, error) { + return marshaler(ctx, v.(T)) + } +} + // RegisterCustomUnmarshaler overrides any decoding process for the type specified in generics. // If you want to switch the behavior for each decoder, use `CustomUnmarshaler` defined as DecodeOption. // @@ -282,7 +308,50 @@ func RegisterCustomUnmarshaler[T any](unmarshaler func(*T, []byte) error) { defer globalCustomUnmarshalerMu.Unlock() var typ *T - globalCustomUnmarshalerMap[reflect.TypeOf(typ)] = func(v interface{}, b []byte) error { + globalCustomUnmarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}, b []byte) error { return unmarshaler(v.(*T), b) } } + +// RegisterCustomUnmarshalerContext overrides any decoding process for the type specified in generics. +// Similar to RegisterCustomUnmarshalerContext, but allows passing a context to the unmarshaler function. +func RegisterCustomUnmarshalerContext[T any](unmarshaler func(context.Context, *T, []byte) error) { + globalCustomUnmarshalerMu.Lock() + defer globalCustomUnmarshalerMu.Unlock() + + var typ *T + globalCustomUnmarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}, b []byte) error { + return unmarshaler(ctx, v.(*T), b) + } +} + +// RawMessage is a raw encoded YAML value. It implements [BytesMarshaler] and +// [BytesUnmarshaler] and can be used to delay YAML decoding or precompute a YAML +// encoding. +// It also implements [json.Marshaler] and [json.Unmarshaler]. +// +// This is similar to [json.RawMessage] in the stdlib. +type RawMessage []byte + +func (m RawMessage) MarshalYAML() ([]byte, error) { + if m == nil { + return []byte("null"), nil + } + return m, nil +} + +func (m *RawMessage) UnmarshalYAML(dt []byte) error { + if m == nil { + return errors.New("yaml.RawMessage: UnmarshalYAML on nil pointer") + } + *m = append((*m)[0:0], dt...) + return nil +} + +func (m *RawMessage) UnmarshalJSON(b []byte) error { + return m.UnmarshalYAML(b) +} + +func (m RawMessage) MarshalJSON() ([]byte, error) { + return YAMLToJSON(m) +} diff --git a/vendor/github.com/mattn/go-colorable/LICENSE b/vendor/github.com/mattn/go-colorable/LICENSE deleted file mode 100644 index 91b5cef3..00000000 --- a/vendor/github.com/mattn/go-colorable/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Yasuhiro Matsumoto - -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. diff --git a/vendor/github.com/mattn/go-colorable/README.md b/vendor/github.com/mattn/go-colorable/README.md deleted file mode 100644 index ca048371..00000000 --- a/vendor/github.com/mattn/go-colorable/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# go-colorable - -[![Build Status](https://github.com/mattn/go-colorable/workflows/test/badge.svg)](https://github.com/mattn/go-colorable/actions?query=workflow%3Atest) -[![Codecov](https://codecov.io/gh/mattn/go-colorable/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-colorable) -[![GoDoc](https://godoc.org/github.com/mattn/go-colorable?status.svg)](http://godoc.org/github.com/mattn/go-colorable) -[![Go Report Card](https://goreportcard.com/badge/mattn/go-colorable)](https://goreportcard.com/report/mattn/go-colorable) - -Colorable writer for windows. - -For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.) -This package is possible to handle escape sequence for ansi color on windows. - -## Too Bad! - -![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png) - - -## So Good! - -![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png) - -## Usage - -```go -logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true}) -logrus.SetOutput(colorable.NewColorableStdout()) - -logrus.Info("succeeded") -logrus.Warn("not correct") -logrus.Error("something error") -logrus.Fatal("panic") -``` - -You can compile above code on non-windows OSs. - -## Installation - -``` -$ go get github.com/mattn/go-colorable -``` - -# License - -MIT - -# Author - -Yasuhiro Matsumoto (a.k.a mattn) diff --git a/vendor/github.com/mattn/go-colorable/colorable_appengine.go b/vendor/github.com/mattn/go-colorable/colorable_appengine.go deleted file mode 100644 index 416d1bbb..00000000 --- a/vendor/github.com/mattn/go-colorable/colorable_appengine.go +++ /dev/null @@ -1,38 +0,0 @@ -//go:build appengine -// +build appengine - -package colorable - -import ( - "io" - "os" - - _ "github.com/mattn/go-isatty" -) - -// NewColorable returns new instance of Writer which handles escape sequence. -func NewColorable(file *os.File) io.Writer { - if file == nil { - panic("nil passed instead of *os.File to NewColorable()") - } - - return file -} - -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. -func NewColorableStdout() io.Writer { - return os.Stdout -} - -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. -func NewColorableStderr() io.Writer { - return os.Stderr -} - -// EnableColorsStdout enable colors if possible. -func EnableColorsStdout(enabled *bool) func() { - if enabled != nil { - *enabled = true - } - return func() {} -} diff --git a/vendor/github.com/mattn/go-colorable/colorable_others.go b/vendor/github.com/mattn/go-colorable/colorable_others.go deleted file mode 100644 index 766d9460..00000000 --- a/vendor/github.com/mattn/go-colorable/colorable_others.go +++ /dev/null @@ -1,38 +0,0 @@ -//go:build !windows && !appengine -// +build !windows,!appengine - -package colorable - -import ( - "io" - "os" - - _ "github.com/mattn/go-isatty" -) - -// NewColorable returns new instance of Writer which handles escape sequence. -func NewColorable(file *os.File) io.Writer { - if file == nil { - panic("nil passed instead of *os.File to NewColorable()") - } - - return file -} - -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. -func NewColorableStdout() io.Writer { - return os.Stdout -} - -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. -func NewColorableStderr() io.Writer { - return os.Stderr -} - -// EnableColorsStdout enable colors if possible. -func EnableColorsStdout(enabled *bool) func() { - if enabled != nil { - *enabled = true - } - return func() {} -} diff --git a/vendor/github.com/mattn/go-colorable/colorable_windows.go b/vendor/github.com/mattn/go-colorable/colorable_windows.go deleted file mode 100644 index 1846ad5a..00000000 --- a/vendor/github.com/mattn/go-colorable/colorable_windows.go +++ /dev/null @@ -1,1047 +0,0 @@ -//go:build windows && !appengine -// +build windows,!appengine - -package colorable - -import ( - "bytes" - "io" - "math" - "os" - "strconv" - "strings" - "sync" - "syscall" - "unsafe" - - "github.com/mattn/go-isatty" -) - -const ( - foregroundBlue = 0x1 - foregroundGreen = 0x2 - foregroundRed = 0x4 - foregroundIntensity = 0x8 - foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity) - backgroundBlue = 0x10 - backgroundGreen = 0x20 - backgroundRed = 0x40 - backgroundIntensity = 0x80 - backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity) - commonLvbUnderscore = 0x8000 - - cENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4 -) - -const ( - genericRead = 0x80000000 - genericWrite = 0x40000000 -) - -const ( - consoleTextmodeBuffer = 0x1 -) - -type wchar uint16 -type short int16 -type dword uint32 -type word uint16 - -type coord struct { - x short - y short -} - -type smallRect struct { - left short - top short - right short - bottom short -} - -type consoleScreenBufferInfo struct { - size coord - cursorPosition coord - attributes word - window smallRect - maximumWindowSize coord -} - -type consoleCursorInfo struct { - size dword - visible int32 -} - -var ( - kernel32 = syscall.NewLazyDLL("kernel32.dll") - procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") - procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute") - procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition") - procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") - procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute") - procGetConsoleCursorInfo = kernel32.NewProc("GetConsoleCursorInfo") - procSetConsoleCursorInfo = kernel32.NewProc("SetConsoleCursorInfo") - procSetConsoleTitle = kernel32.NewProc("SetConsoleTitleW") - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") - procSetConsoleMode = kernel32.NewProc("SetConsoleMode") - procCreateConsoleScreenBuffer = kernel32.NewProc("CreateConsoleScreenBuffer") -) - -// Writer provides colorable Writer to the console -type Writer struct { - out io.Writer - handle syscall.Handle - althandle syscall.Handle - oldattr word - oldpos coord - rest bytes.Buffer - mutex sync.Mutex -} - -// NewColorable returns new instance of Writer which handles escape sequence from File. -func NewColorable(file *os.File) io.Writer { - if file == nil { - panic("nil passed instead of *os.File to NewColorable()") - } - - if isatty.IsTerminal(file.Fd()) { - var mode uint32 - if r, _, _ := procGetConsoleMode.Call(file.Fd(), uintptr(unsafe.Pointer(&mode))); r != 0 && mode&cENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 { - return file - } - var csbi consoleScreenBufferInfo - handle := syscall.Handle(file.Fd()) - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - return &Writer{out: file, handle: handle, oldattr: csbi.attributes, oldpos: coord{0, 0}} - } - return file -} - -// NewColorableStdout returns new instance of Writer which handles escape sequence for stdout. -func NewColorableStdout() io.Writer { - return NewColorable(os.Stdout) -} - -// NewColorableStderr returns new instance of Writer which handles escape sequence for stderr. -func NewColorableStderr() io.Writer { - return NewColorable(os.Stderr) -} - -var color256 = map[int]int{ - 0: 0x000000, - 1: 0x800000, - 2: 0x008000, - 3: 0x808000, - 4: 0x000080, - 5: 0x800080, - 6: 0x008080, - 7: 0xc0c0c0, - 8: 0x808080, - 9: 0xff0000, - 10: 0x00ff00, - 11: 0xffff00, - 12: 0x0000ff, - 13: 0xff00ff, - 14: 0x00ffff, - 15: 0xffffff, - 16: 0x000000, - 17: 0x00005f, - 18: 0x000087, - 19: 0x0000af, - 20: 0x0000d7, - 21: 0x0000ff, - 22: 0x005f00, - 23: 0x005f5f, - 24: 0x005f87, - 25: 0x005faf, - 26: 0x005fd7, - 27: 0x005fff, - 28: 0x008700, - 29: 0x00875f, - 30: 0x008787, - 31: 0x0087af, - 32: 0x0087d7, - 33: 0x0087ff, - 34: 0x00af00, - 35: 0x00af5f, - 36: 0x00af87, - 37: 0x00afaf, - 38: 0x00afd7, - 39: 0x00afff, - 40: 0x00d700, - 41: 0x00d75f, - 42: 0x00d787, - 43: 0x00d7af, - 44: 0x00d7d7, - 45: 0x00d7ff, - 46: 0x00ff00, - 47: 0x00ff5f, - 48: 0x00ff87, - 49: 0x00ffaf, - 50: 0x00ffd7, - 51: 0x00ffff, - 52: 0x5f0000, - 53: 0x5f005f, - 54: 0x5f0087, - 55: 0x5f00af, - 56: 0x5f00d7, - 57: 0x5f00ff, - 58: 0x5f5f00, - 59: 0x5f5f5f, - 60: 0x5f5f87, - 61: 0x5f5faf, - 62: 0x5f5fd7, - 63: 0x5f5fff, - 64: 0x5f8700, - 65: 0x5f875f, - 66: 0x5f8787, - 67: 0x5f87af, - 68: 0x5f87d7, - 69: 0x5f87ff, - 70: 0x5faf00, - 71: 0x5faf5f, - 72: 0x5faf87, - 73: 0x5fafaf, - 74: 0x5fafd7, - 75: 0x5fafff, - 76: 0x5fd700, - 77: 0x5fd75f, - 78: 0x5fd787, - 79: 0x5fd7af, - 80: 0x5fd7d7, - 81: 0x5fd7ff, - 82: 0x5fff00, - 83: 0x5fff5f, - 84: 0x5fff87, - 85: 0x5fffaf, - 86: 0x5fffd7, - 87: 0x5fffff, - 88: 0x870000, - 89: 0x87005f, - 90: 0x870087, - 91: 0x8700af, - 92: 0x8700d7, - 93: 0x8700ff, - 94: 0x875f00, - 95: 0x875f5f, - 96: 0x875f87, - 97: 0x875faf, - 98: 0x875fd7, - 99: 0x875fff, - 100: 0x878700, - 101: 0x87875f, - 102: 0x878787, - 103: 0x8787af, - 104: 0x8787d7, - 105: 0x8787ff, - 106: 0x87af00, - 107: 0x87af5f, - 108: 0x87af87, - 109: 0x87afaf, - 110: 0x87afd7, - 111: 0x87afff, - 112: 0x87d700, - 113: 0x87d75f, - 114: 0x87d787, - 115: 0x87d7af, - 116: 0x87d7d7, - 117: 0x87d7ff, - 118: 0x87ff00, - 119: 0x87ff5f, - 120: 0x87ff87, - 121: 0x87ffaf, - 122: 0x87ffd7, - 123: 0x87ffff, - 124: 0xaf0000, - 125: 0xaf005f, - 126: 0xaf0087, - 127: 0xaf00af, - 128: 0xaf00d7, - 129: 0xaf00ff, - 130: 0xaf5f00, - 131: 0xaf5f5f, - 132: 0xaf5f87, - 133: 0xaf5faf, - 134: 0xaf5fd7, - 135: 0xaf5fff, - 136: 0xaf8700, - 137: 0xaf875f, - 138: 0xaf8787, - 139: 0xaf87af, - 140: 0xaf87d7, - 141: 0xaf87ff, - 142: 0xafaf00, - 143: 0xafaf5f, - 144: 0xafaf87, - 145: 0xafafaf, - 146: 0xafafd7, - 147: 0xafafff, - 148: 0xafd700, - 149: 0xafd75f, - 150: 0xafd787, - 151: 0xafd7af, - 152: 0xafd7d7, - 153: 0xafd7ff, - 154: 0xafff00, - 155: 0xafff5f, - 156: 0xafff87, - 157: 0xafffaf, - 158: 0xafffd7, - 159: 0xafffff, - 160: 0xd70000, - 161: 0xd7005f, - 162: 0xd70087, - 163: 0xd700af, - 164: 0xd700d7, - 165: 0xd700ff, - 166: 0xd75f00, - 167: 0xd75f5f, - 168: 0xd75f87, - 169: 0xd75faf, - 170: 0xd75fd7, - 171: 0xd75fff, - 172: 0xd78700, - 173: 0xd7875f, - 174: 0xd78787, - 175: 0xd787af, - 176: 0xd787d7, - 177: 0xd787ff, - 178: 0xd7af00, - 179: 0xd7af5f, - 180: 0xd7af87, - 181: 0xd7afaf, - 182: 0xd7afd7, - 183: 0xd7afff, - 184: 0xd7d700, - 185: 0xd7d75f, - 186: 0xd7d787, - 187: 0xd7d7af, - 188: 0xd7d7d7, - 189: 0xd7d7ff, - 190: 0xd7ff00, - 191: 0xd7ff5f, - 192: 0xd7ff87, - 193: 0xd7ffaf, - 194: 0xd7ffd7, - 195: 0xd7ffff, - 196: 0xff0000, - 197: 0xff005f, - 198: 0xff0087, - 199: 0xff00af, - 200: 0xff00d7, - 201: 0xff00ff, - 202: 0xff5f00, - 203: 0xff5f5f, - 204: 0xff5f87, - 205: 0xff5faf, - 206: 0xff5fd7, - 207: 0xff5fff, - 208: 0xff8700, - 209: 0xff875f, - 210: 0xff8787, - 211: 0xff87af, - 212: 0xff87d7, - 213: 0xff87ff, - 214: 0xffaf00, - 215: 0xffaf5f, - 216: 0xffaf87, - 217: 0xffafaf, - 218: 0xffafd7, - 219: 0xffafff, - 220: 0xffd700, - 221: 0xffd75f, - 222: 0xffd787, - 223: 0xffd7af, - 224: 0xffd7d7, - 225: 0xffd7ff, - 226: 0xffff00, - 227: 0xffff5f, - 228: 0xffff87, - 229: 0xffffaf, - 230: 0xffffd7, - 231: 0xffffff, - 232: 0x080808, - 233: 0x121212, - 234: 0x1c1c1c, - 235: 0x262626, - 236: 0x303030, - 237: 0x3a3a3a, - 238: 0x444444, - 239: 0x4e4e4e, - 240: 0x585858, - 241: 0x626262, - 242: 0x6c6c6c, - 243: 0x767676, - 244: 0x808080, - 245: 0x8a8a8a, - 246: 0x949494, - 247: 0x9e9e9e, - 248: 0xa8a8a8, - 249: 0xb2b2b2, - 250: 0xbcbcbc, - 251: 0xc6c6c6, - 252: 0xd0d0d0, - 253: 0xdadada, - 254: 0xe4e4e4, - 255: 0xeeeeee, -} - -// `\033]0;TITLESTR\007` -func doTitleSequence(er *bytes.Reader) error { - var c byte - var err error - - c, err = er.ReadByte() - if err != nil { - return err - } - if c != '0' && c != '2' { - return nil - } - c, err = er.ReadByte() - if err != nil { - return err - } - if c != ';' { - return nil - } - title := make([]byte, 0, 80) - for { - c, err = er.ReadByte() - if err != nil { - return err - } - if c == 0x07 || c == '\n' { - break - } - title = append(title, c) - } - if len(title) > 0 { - title8, err := syscall.UTF16PtrFromString(string(title)) - if err == nil { - procSetConsoleTitle.Call(uintptr(unsafe.Pointer(title8))) - } - } - return nil -} - -// returns Atoi(s) unless s == "" in which case it returns def -func atoiWithDefault(s string, def int) (int, error) { - if s == "" { - return def, nil - } - return strconv.Atoi(s) -} - -// Write writes data on console -func (w *Writer) Write(data []byte) (n int, err error) { - w.mutex.Lock() - defer w.mutex.Unlock() - var csbi consoleScreenBufferInfo - procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi))) - - handle := w.handle - - var er *bytes.Reader - if w.rest.Len() > 0 { - var rest bytes.Buffer - w.rest.WriteTo(&rest) - w.rest.Reset() - rest.Write(data) - er = bytes.NewReader(rest.Bytes()) - } else { - er = bytes.NewReader(data) - } - var plaintext bytes.Buffer -loop: - for { - c1, err := er.ReadByte() - if err != nil { - plaintext.WriteTo(w.out) - break loop - } - if c1 != 0x1b { - plaintext.WriteByte(c1) - continue - } - _, err = plaintext.WriteTo(w.out) - if err != nil { - break loop - } - c2, err := er.ReadByte() - if err != nil { - break loop - } - - switch c2 { - case '>': - continue - case ']': - w.rest.WriteByte(c1) - w.rest.WriteByte(c2) - er.WriteTo(&w.rest) - if bytes.IndexByte(w.rest.Bytes(), 0x07) == -1 { - break loop - } - er = bytes.NewReader(w.rest.Bytes()[2:]) - err := doTitleSequence(er) - if err != nil { - break loop - } - w.rest.Reset() - continue - // https://github.com/mattn/go-colorable/issues/27 - case '7': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - w.oldpos = csbi.cursorPosition - continue - case '8': - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) - continue - case 0x5b: - // execute part after switch - default: - continue - } - - w.rest.WriteByte(c1) - w.rest.WriteByte(c2) - er.WriteTo(&w.rest) - - var buf bytes.Buffer - var m byte - for i, c := range w.rest.Bytes()[2:] { - if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { - m = c - er = bytes.NewReader(w.rest.Bytes()[2+i+1:]) - w.rest.Reset() - break - } - buf.Write([]byte(string(c))) - } - if m == 0 { - break loop - } - - switch m { - case 'A': - n, err = atoiWithDefault(buf.String(), 1) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.y -= short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'B': - n, err = atoiWithDefault(buf.String(), 1) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.y += short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'C': - n, err = atoiWithDefault(buf.String(), 1) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x += short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'D': - n, err = atoiWithDefault(buf.String(), 1) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x -= short(n) - if csbi.cursorPosition.x < 0 { - csbi.cursorPosition.x = 0 - } - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'E': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x = 0 - csbi.cursorPosition.y += short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'F': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x = 0 - csbi.cursorPosition.y -= short(n) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'G': - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - if n < 1 { - n = 1 - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - csbi.cursorPosition.x = short(n - 1) - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'H', 'f': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - if buf.Len() > 0 { - token := strings.Split(buf.String(), ";") - switch len(token) { - case 1: - n1, err := strconv.Atoi(token[0]) - if err != nil { - continue - } - csbi.cursorPosition.y = short(n1 - 1) - case 2: - n1, err := strconv.Atoi(token[0]) - if err != nil { - continue - } - n2, err := strconv.Atoi(token[1]) - if err != nil { - continue - } - csbi.cursorPosition.x = short(n2 - 1) - csbi.cursorPosition.y = short(n1 - 1) - } - } else { - csbi.cursorPosition.y = 0 - } - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition))) - case 'J': - n := 0 - if buf.Len() > 0 { - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - } - var count, written dword - var cursor coord - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - switch n { - case 0: - cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} - count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) - case 1: - cursor = coord{x: csbi.window.left, y: csbi.window.top} - count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.window.top-csbi.cursorPosition.y)*dword(csbi.size.x) - case 2: - cursor = coord{x: csbi.window.left, y: csbi.window.top} - count = dword(csbi.size.x) - dword(csbi.cursorPosition.x) + dword(csbi.size.y-csbi.cursorPosition.y)*dword(csbi.size.x) - } - procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - case 'K': - n := 0 - if buf.Len() > 0 { - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - var cursor coord - var count, written dword - switch n { - case 0: - cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} - count = dword(csbi.size.x - csbi.cursorPosition.x) - case 1: - cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} - count = dword(csbi.size.x - csbi.cursorPosition.x) - case 2: - cursor = coord{x: csbi.window.left, y: csbi.cursorPosition.y} - count = dword(csbi.size.x) - } - procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - case 'X': - n := 0 - if buf.Len() > 0 { - n, err = strconv.Atoi(buf.String()) - if err != nil { - continue - } - } - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - var cursor coord - var written dword - cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y} - procFillConsoleOutputCharacter.Call(uintptr(handle), uintptr(' '), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - procFillConsoleOutputAttribute.Call(uintptr(handle), uintptr(csbi.attributes), uintptr(n), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written))) - case 'm': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - attr := csbi.attributes - cs := buf.String() - if cs == "" { - procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(w.oldattr)) - continue - } - token := strings.Split(cs, ";") - for i := 0; i < len(token); i++ { - ns := token[i] - if n, err = strconv.Atoi(ns); err == nil { - switch { - case n == 0 || n == 100: - attr = w.oldattr - case n == 4: - attr |= commonLvbUnderscore - case (1 <= n && n <= 3) || n == 5: - attr |= foregroundIntensity - case n == 7 || n == 27: - attr = - (attr &^ (foregroundMask | backgroundMask)) | - ((attr & foregroundMask) << 4) | - ((attr & backgroundMask) >> 4) - case n == 22: - attr &^= foregroundIntensity - case n == 24: - attr &^= commonLvbUnderscore - case 30 <= n && n <= 37: - attr &= backgroundMask - if (n-30)&1 != 0 { - attr |= foregroundRed - } - if (n-30)&2 != 0 { - attr |= foregroundGreen - } - if (n-30)&4 != 0 { - attr |= foregroundBlue - } - case n == 38: // set foreground color. - if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") { - if n256, err := strconv.Atoi(token[i+2]); err == nil { - if n256foreAttr == nil { - n256setup() - } - attr &= backgroundMask - attr |= n256foreAttr[n256%len(n256foreAttr)] - i += 2 - } - } else if len(token) == 5 && token[i+1] == "2" { - var r, g, b int - r, _ = strconv.Atoi(token[i+2]) - g, _ = strconv.Atoi(token[i+3]) - b, _ = strconv.Atoi(token[i+4]) - i += 4 - if r > 127 { - attr |= foregroundRed - } - if g > 127 { - attr |= foregroundGreen - } - if b > 127 { - attr |= foregroundBlue - } - } else { - attr = attr & (w.oldattr & backgroundMask) - } - case n == 39: // reset foreground color. - attr &= backgroundMask - attr |= w.oldattr & foregroundMask - case 40 <= n && n <= 47: - attr &= foregroundMask - if (n-40)&1 != 0 { - attr |= backgroundRed - } - if (n-40)&2 != 0 { - attr |= backgroundGreen - } - if (n-40)&4 != 0 { - attr |= backgroundBlue - } - case n == 48: // set background color. - if i < len(token)-2 && token[i+1] == "5" { - if n256, err := strconv.Atoi(token[i+2]); err == nil { - if n256backAttr == nil { - n256setup() - } - attr &= foregroundMask - attr |= n256backAttr[n256%len(n256backAttr)] - i += 2 - } - } else if len(token) == 5 && token[i+1] == "2" { - var r, g, b int - r, _ = strconv.Atoi(token[i+2]) - g, _ = strconv.Atoi(token[i+3]) - b, _ = strconv.Atoi(token[i+4]) - i += 4 - if r > 127 { - attr |= backgroundRed - } - if g > 127 { - attr |= backgroundGreen - } - if b > 127 { - attr |= backgroundBlue - } - } else { - attr = attr & (w.oldattr & foregroundMask) - } - case n == 49: // reset foreground color. - attr &= foregroundMask - attr |= w.oldattr & backgroundMask - case 90 <= n && n <= 97: - attr = (attr & backgroundMask) - attr |= foregroundIntensity - if (n-90)&1 != 0 { - attr |= foregroundRed - } - if (n-90)&2 != 0 { - attr |= foregroundGreen - } - if (n-90)&4 != 0 { - attr |= foregroundBlue - } - case 100 <= n && n <= 107: - attr = (attr & foregroundMask) - attr |= backgroundIntensity - if (n-100)&1 != 0 { - attr |= backgroundRed - } - if (n-100)&2 != 0 { - attr |= backgroundGreen - } - if (n-100)&4 != 0 { - attr |= backgroundBlue - } - } - procSetConsoleTextAttribute.Call(uintptr(handle), uintptr(attr)) - } - } - case 'h': - var ci consoleCursorInfo - cs := buf.String() - if cs == "5>" { - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - ci.visible = 0 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - } else if cs == "?25" { - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - ci.visible = 1 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - } else if cs == "?1049" { - if w.althandle == 0 { - h, _, _ := procCreateConsoleScreenBuffer.Call(uintptr(genericRead|genericWrite), 0, 0, uintptr(consoleTextmodeBuffer), 0, 0) - w.althandle = syscall.Handle(h) - if w.althandle != 0 { - handle = w.althandle - } - } - } - case 'l': - var ci consoleCursorInfo - cs := buf.String() - if cs == "5>" { - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - ci.visible = 1 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - } else if cs == "?25" { - procGetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - ci.visible = 0 - procSetConsoleCursorInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&ci))) - } else if cs == "?1049" { - if w.althandle != 0 { - syscall.CloseHandle(w.althandle) - w.althandle = 0 - handle = w.handle - } - } - case 's': - procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi))) - w.oldpos = csbi.cursorPosition - case 'u': - procSetConsoleCursorPosition.Call(uintptr(handle), *(*uintptr)(unsafe.Pointer(&w.oldpos))) - } - } - - return len(data), nil -} - -type consoleColor struct { - rgb int - red bool - green bool - blue bool - intensity bool -} - -func (c consoleColor) foregroundAttr() (attr word) { - if c.red { - attr |= foregroundRed - } - if c.green { - attr |= foregroundGreen - } - if c.blue { - attr |= foregroundBlue - } - if c.intensity { - attr |= foregroundIntensity - } - return -} - -func (c consoleColor) backgroundAttr() (attr word) { - if c.red { - attr |= backgroundRed - } - if c.green { - attr |= backgroundGreen - } - if c.blue { - attr |= backgroundBlue - } - if c.intensity { - attr |= backgroundIntensity - } - return -} - -var color16 = []consoleColor{ - {0x000000, false, false, false, false}, - {0x000080, false, false, true, false}, - {0x008000, false, true, false, false}, - {0x008080, false, true, true, false}, - {0x800000, true, false, false, false}, - {0x800080, true, false, true, false}, - {0x808000, true, true, false, false}, - {0xc0c0c0, true, true, true, false}, - {0x808080, false, false, false, true}, - {0x0000ff, false, false, true, true}, - {0x00ff00, false, true, false, true}, - {0x00ffff, false, true, true, true}, - {0xff0000, true, false, false, true}, - {0xff00ff, true, false, true, true}, - {0xffff00, true, true, false, true}, - {0xffffff, true, true, true, true}, -} - -type hsv struct { - h, s, v float32 -} - -func (a hsv) dist(b hsv) float32 { - dh := a.h - b.h - switch { - case dh > 0.5: - dh = 1 - dh - case dh < -0.5: - dh = -1 - dh - } - ds := a.s - b.s - dv := a.v - b.v - return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv))) -} - -func toHSV(rgb int) hsv { - r, g, b := float32((rgb&0xFF0000)>>16)/256.0, - float32((rgb&0x00FF00)>>8)/256.0, - float32(rgb&0x0000FF)/256.0 - min, max := minmax3f(r, g, b) - h := max - min - if h > 0 { - if max == r { - h = (g - b) / h - if h < 0 { - h += 6 - } - } else if max == g { - h = 2 + (b-r)/h - } else { - h = 4 + (r-g)/h - } - } - h /= 6.0 - s := max - min - if max != 0 { - s /= max - } - v := max - return hsv{h: h, s: s, v: v} -} - -type hsvTable []hsv - -func toHSVTable(rgbTable []consoleColor) hsvTable { - t := make(hsvTable, len(rgbTable)) - for i, c := range rgbTable { - t[i] = toHSV(c.rgb) - } - return t -} - -func (t hsvTable) find(rgb int) consoleColor { - hsv := toHSV(rgb) - n := 7 - l := float32(5.0) - for i, p := range t { - d := hsv.dist(p) - if d < l { - l, n = d, i - } - } - return color16[n] -} - -func minmax3f(a, b, c float32) (min, max float32) { - if a < b { - if b < c { - return a, c - } else if a < c { - return a, b - } else { - return c, b - } - } else { - if a < c { - return b, c - } else if b < c { - return b, a - } else { - return c, a - } - } -} - -var n256foreAttr []word -var n256backAttr []word - -func n256setup() { - n256foreAttr = make([]word, 256) - n256backAttr = make([]word, 256) - t := toHSVTable(color16) - for i, rgb := range color256 { - c := t.find(rgb) - n256foreAttr[i] = c.foregroundAttr() - n256backAttr[i] = c.backgroundAttr() - } -} - -// EnableColorsStdout enable colors if possible. -func EnableColorsStdout(enabled *bool) func() { - var mode uint32 - h := os.Stdout.Fd() - if r, _, _ := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&mode))); r != 0 { - if r, _, _ = procSetConsoleMode.Call(h, uintptr(mode|cENABLE_VIRTUAL_TERMINAL_PROCESSING)); r != 0 { - if enabled != nil { - *enabled = true - } - return func() { - procSetConsoleMode.Call(h, uintptr(mode)) - } - } - } - if enabled != nil { - *enabled = true - } - return func() {} -} diff --git a/vendor/github.com/mattn/go-colorable/go.test.sh b/vendor/github.com/mattn/go-colorable/go.test.sh deleted file mode 100644 index 012162b0..00000000 --- a/vendor/github.com/mattn/go-colorable/go.test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -e -echo "" > coverage.txt - -for d in $(go list ./... | grep -v vendor); do - go test -race -coverprofile=profile.out -covermode=atomic "$d" - if [ -f profile.out ]; then - cat profile.out >> coverage.txt - rm profile.out - fi -done diff --git a/vendor/github.com/mattn/go-colorable/noncolorable.go b/vendor/github.com/mattn/go-colorable/noncolorable.go deleted file mode 100644 index 05d6f74b..00000000 --- a/vendor/github.com/mattn/go-colorable/noncolorable.go +++ /dev/null @@ -1,57 +0,0 @@ -package colorable - -import ( - "bytes" - "io" -) - -// NonColorable holds writer but removes escape sequence. -type NonColorable struct { - out io.Writer -} - -// NewNonColorable returns new instance of Writer which removes escape sequence from Writer. -func NewNonColorable(w io.Writer) io.Writer { - return &NonColorable{out: w} -} - -// Write writes data on console -func (w *NonColorable) Write(data []byte) (n int, err error) { - er := bytes.NewReader(data) - var plaintext bytes.Buffer -loop: - for { - c1, err := er.ReadByte() - if err != nil { - plaintext.WriteTo(w.out) - break loop - } - if c1 != 0x1b { - plaintext.WriteByte(c1) - continue - } - _, err = plaintext.WriteTo(w.out) - if err != nil { - break loop - } - c2, err := er.ReadByte() - if err != nil { - break loop - } - if c2 != 0x5b { - continue - } - - for { - c, err := er.ReadByte() - if err != nil { - break loop - } - if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' { - break - } - } - } - - return len(data), nil -} diff --git a/vendor/github.com/mattn/go-isatty/LICENSE b/vendor/github.com/mattn/go-isatty/LICENSE deleted file mode 100644 index 65dc692b..00000000 --- a/vendor/github.com/mattn/go-isatty/LICENSE +++ /dev/null @@ -1,9 +0,0 @@ -Copyright (c) Yasuhiro MATSUMOTO - -MIT License (Expat) - -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. diff --git a/vendor/github.com/mattn/go-isatty/README.md b/vendor/github.com/mattn/go-isatty/README.md deleted file mode 100644 index 38418353..00000000 --- a/vendor/github.com/mattn/go-isatty/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# go-isatty - -[![Godoc Reference](https://godoc.org/github.com/mattn/go-isatty?status.svg)](http://godoc.org/github.com/mattn/go-isatty) -[![Codecov](https://codecov.io/gh/mattn/go-isatty/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-isatty) -[![Coverage Status](https://coveralls.io/repos/github/mattn/go-isatty/badge.svg?branch=master)](https://coveralls.io/github/mattn/go-isatty?branch=master) -[![Go Report Card](https://goreportcard.com/badge/mattn/go-isatty)](https://goreportcard.com/report/mattn/go-isatty) - -isatty for golang - -## Usage - -```go -package main - -import ( - "fmt" - "github.com/mattn/go-isatty" - "os" -) - -func main() { - if isatty.IsTerminal(os.Stdout.Fd()) { - fmt.Println("Is Terminal") - } else if isatty.IsCygwinTerminal(os.Stdout.Fd()) { - fmt.Println("Is Cygwin/MSYS2 Terminal") - } else { - fmt.Println("Is Not Terminal") - } -} -``` - -## Installation - -``` -$ go get github.com/mattn/go-isatty -``` - -## License - -MIT - -## Author - -Yasuhiro Matsumoto (a.k.a mattn) - -## Thanks - -* k-takata: base idea for IsCygwinTerminal - - https://github.com/k-takata/go-iscygpty diff --git a/vendor/github.com/mattn/go-isatty/doc.go b/vendor/github.com/mattn/go-isatty/doc.go deleted file mode 100644 index 17d4f90e..00000000 --- a/vendor/github.com/mattn/go-isatty/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package isatty implements interface to isatty -package isatty diff --git a/vendor/github.com/mattn/go-isatty/go.test.sh b/vendor/github.com/mattn/go-isatty/go.test.sh deleted file mode 100644 index 012162b0..00000000 --- a/vendor/github.com/mattn/go-isatty/go.test.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/usr/bin/env bash - -set -e -echo "" > coverage.txt - -for d in $(go list ./... | grep -v vendor); do - go test -race -coverprofile=profile.out -covermode=atomic "$d" - if [ -f profile.out ]; then - cat profile.out >> coverage.txt - rm profile.out - fi -done diff --git a/vendor/github.com/mattn/go-isatty/isatty_bsd.go b/vendor/github.com/mattn/go-isatty/isatty_bsd.go deleted file mode 100644 index d0ea68f4..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_bsd.go +++ /dev/null @@ -1,20 +0,0 @@ -//go:build (darwin || freebsd || openbsd || netbsd || dragonfly || hurd) && !appengine && !tinygo -// +build darwin freebsd openbsd netbsd dragonfly hurd -// +build !appengine -// +build !tinygo - -package isatty - -import "golang.org/x/sys/unix" - -// IsTerminal return true if the file descriptor is terminal. -func IsTerminal(fd uintptr) bool { - _, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA) - return err == nil -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/vendor/github.com/mattn/go-isatty/isatty_others.go b/vendor/github.com/mattn/go-isatty/isatty_others.go deleted file mode 100644 index 7402e061..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_others.go +++ /dev/null @@ -1,17 +0,0 @@ -//go:build (appengine || js || nacl || tinygo || wasm) && !windows -// +build appengine js nacl tinygo wasm -// +build !windows - -package isatty - -// IsTerminal returns true if the file descriptor is terminal which -// is always false on js and appengine classic which is a sandboxed PaaS. -func IsTerminal(fd uintptr) bool { - return false -} - -// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/vendor/github.com/mattn/go-isatty/isatty_plan9.go b/vendor/github.com/mattn/go-isatty/isatty_plan9.go deleted file mode 100644 index bae7f9bb..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_plan9.go +++ /dev/null @@ -1,23 +0,0 @@ -//go:build plan9 -// +build plan9 - -package isatty - -import ( - "syscall" -) - -// IsTerminal returns true if the given file descriptor is a terminal. -func IsTerminal(fd uintptr) bool { - path, err := syscall.Fd2path(int(fd)) - if err != nil { - return false - } - return path == "/dev/cons" || path == "/mnt/term/dev/cons" -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/vendor/github.com/mattn/go-isatty/isatty_solaris.go b/vendor/github.com/mattn/go-isatty/isatty_solaris.go deleted file mode 100644 index 0c3acf2d..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_solaris.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build solaris && !appengine -// +build solaris,!appengine - -package isatty - -import ( - "golang.org/x/sys/unix" -) - -// IsTerminal returns true if the given file descriptor is a terminal. -// see: https://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/isatty.c -func IsTerminal(fd uintptr) bool { - _, err := unix.IoctlGetTermio(int(fd), unix.TCGETA) - return err == nil -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go b/vendor/github.com/mattn/go-isatty/isatty_tcgets.go deleted file mode 100644 index 0337d8cf..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_tcgets.go +++ /dev/null @@ -1,20 +0,0 @@ -//go:build (linux || aix || zos) && !appengine && !tinygo -// +build linux aix zos -// +build !appengine -// +build !tinygo - -package isatty - -import "golang.org/x/sys/unix" - -// IsTerminal return true if the file descriptor is terminal. -func IsTerminal(fd uintptr) bool { - _, err := unix.IoctlGetTermios(int(fd), unix.TCGETS) - return err == nil -} - -// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2 -// terminal. This is also always false on this environment. -func IsCygwinTerminal(fd uintptr) bool { - return false -} diff --git a/vendor/github.com/mattn/go-isatty/isatty_windows.go b/vendor/github.com/mattn/go-isatty/isatty_windows.go deleted file mode 100644 index 8e3c9917..00000000 --- a/vendor/github.com/mattn/go-isatty/isatty_windows.go +++ /dev/null @@ -1,125 +0,0 @@ -//go:build windows && !appengine -// +build windows,!appengine - -package isatty - -import ( - "errors" - "strings" - "syscall" - "unicode/utf16" - "unsafe" -) - -const ( - objectNameInfo uintptr = 1 - fileNameInfo = 2 - fileTypePipe = 3 -) - -var ( - kernel32 = syscall.NewLazyDLL("kernel32.dll") - ntdll = syscall.NewLazyDLL("ntdll.dll") - procGetConsoleMode = kernel32.NewProc("GetConsoleMode") - procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx") - procGetFileType = kernel32.NewProc("GetFileType") - procNtQueryObject = ntdll.NewProc("NtQueryObject") -) - -func init() { - // Check if GetFileInformationByHandleEx is available. - if procGetFileInformationByHandleEx.Find() != nil { - procGetFileInformationByHandleEx = nil - } -} - -// IsTerminal return true if the file descriptor is terminal. -func IsTerminal(fd uintptr) bool { - var st uint32 - r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0) - return r != 0 && e == 0 -} - -// Check pipe name is used for cygwin/msys2 pty. -// Cygwin/MSYS2 PTY has a name like: -// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master -func isCygwinPipeName(name string) bool { - token := strings.Split(name, "-") - if len(token) < 5 { - return false - } - - if token[0] != `\msys` && - token[0] != `\cygwin` && - token[0] != `\Device\NamedPipe\msys` && - token[0] != `\Device\NamedPipe\cygwin` { - return false - } - - if token[1] == "" { - return false - } - - if !strings.HasPrefix(token[2], "pty") { - return false - } - - if token[3] != `from` && token[3] != `to` { - return false - } - - if token[4] != "master" { - return false - } - - return true -} - -// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler -// since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion -// guys are using Windows XP, this is a workaround for those guys, it will also work on system from -// Windows vista to 10 -// see https://stackoverflow.com/a/18792477 for details -func getFileNameByHandle(fd uintptr) (string, error) { - if procNtQueryObject == nil { - return "", errors.New("ntdll.dll: NtQueryObject not supported") - } - - var buf [4 + syscall.MAX_PATH]uint16 - var result int - r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5, - fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0) - if r != 0 { - return "", e - } - return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil -} - -// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2 -// terminal. -func IsCygwinTerminal(fd uintptr) bool { - if procGetFileInformationByHandleEx == nil { - name, err := getFileNameByHandle(fd) - if err != nil { - return false - } - return isCygwinPipeName(name) - } - - // Cygwin/msys's pty is a pipe. - ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0) - if ft != fileTypePipe || e != 0 { - return false - } - - var buf [2 + syscall.MAX_PATH]uint16 - r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(), - 4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)), - uintptr(len(buf)*2), 0, 0) - if r == 0 || e != 0 { - return false - } - - l := *(*uint32)(unsafe.Pointer(&buf)) - return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2]))) -} diff --git a/vendor/github.com/spf13/cobra/.golangci.yml b/vendor/github.com/spf13/cobra/.golangci.yml index 2c8f4808..104dc244 100644 --- a/vendor/github.com/spf13/cobra/.golangci.yml +++ b/vendor/github.com/spf13/cobra/.golangci.yml @@ -12,14 +12,20 @@ # See the License for the specific language governing permissions and # limitations under the License. +version: "2" + run: - deadline: 5m + timeout: 5m + +formatters: + enable: + - gofmt + - goimports linters: - disable-all: true + default: none enable: #- bodyclose - # - deadcode ! deprecated since v1.49.0; replaced by 'unused' #- depguard #- dogsled #- dupl @@ -30,28 +36,31 @@ linters: - goconst - gocritic #- gocyclo - - gofmt - - goimports - #- gomnd #- goprintffuncname - gosec - - gosimple - govet - ineffassign #- lll - misspell + #- mnd #- nakedret #- noctx - nolintlint #- rowserrcheck - #- scopelint - staticcheck - #- structcheck ! deprecated since v1.49.0; replaced by 'unused' - - stylecheck - #- typecheck - unconvert #- unparam - unused - # - varcheck ! deprecated since v1.49.0; replaced by 'unused' #- whitespace - fast: false + exclusions: + presets: + - common-false-positives + - legacy + - std-error-handling + settings: + govet: + # Disable buildtag check to allow dual build tag syntax (both //go:build and // +build). + # This is necessary for Go 1.15 compatibility since //go:build was introduced in Go 1.17. + # This can be removed once Cobra requires Go 1.17 or higher. + disable: + - buildtag diff --git a/vendor/github.com/spf13/cobra/README.md b/vendor/github.com/spf13/cobra/README.md index 6444f4b7..8416275f 100644 --- a/vendor/github.com/spf13/cobra/README.md +++ b/vendor/github.com/spf13/cobra/README.md @@ -1,7 +1,14 @@ -![cobra logo](assets/CobraMain.png) +
+ +cobra-logo + +
Cobra is a library for creating powerful modern CLI applications. +Visit Cobra.dev for extensive documentation + + Cobra is used in many Go projects such as [Kubernetes](https://kubernetes.io/), [Hugo](https://gohugo.io), and [GitHub CLI](https://github.com/cli/cli) to name a few. [This list](site/content/projects_using_cobra.md) contains a more extensive list of projects using Cobra. @@ -10,6 +17,20 @@ name a few. [This list](site/content/projects_using_cobra.md) contains a more ex [![Go Reference](https://pkg.go.dev/badge/github.com/spf13/cobra.svg)](https://pkg.go.dev/github.com/spf13/cobra) [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cobra)](https://goreportcard.com/report/github.com/spf13/cobra) [![Slack](https://img.shields.io/badge/Slack-cobra-brightgreen)](https://gophers.slack.com/archives/CD3LP1199) +
+
+ Supported by: +
+
+ + Warp sponsorship + + +### [Warp, the AI terminal for devs](https://www.warp.dev/cobra) +[Try Cobra in Warp today](https://www.warp.dev/cobra)
+ +
+
# Overview @@ -105,7 +126,7 @@ go install github.com/spf13/cobra-cli@latest For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md) -For complete details on using the Cobra library, please read the [The Cobra User Guide](site/content/user_guide.md). +For complete details on using the Cobra library, please read [The Cobra User Guide](site/content/user_guide.md). # License diff --git a/vendor/github.com/spf13/cobra/SECURITY.md b/vendor/github.com/spf13/cobra/SECURITY.md new file mode 100644 index 00000000..54e60c28 --- /dev/null +++ b/vendor/github.com/spf13/cobra/SECURITY.md @@ -0,0 +1,105 @@ +# Security Policy + +## Reporting a Vulnerability + +The `cobra` maintainers take security issues seriously and +we appreciate your efforts to _**responsibly**_ disclose your findings. +We will make every effort to swiftly respond and address concerns. + +To report a security vulnerability: + +1. **DO NOT** create a public GitHub issue for the vulnerability! +2. **DO NOT** create a public GitHub Pull Request with a fix for the vulnerability! +3. Send an email to `cobra-security@googlegroups.com`. +4. Include the following details in your report: + - Description of the vulnerability + - Steps to reproduce + - Potential impact of the vulnerability (to your downstream project, to the Go ecosystem, etc.) + - Any potential mitigations you've already identified +5. Allow up to 7 days for an initial response. + You should receive an acknowledgment of your report and an estimated timeline for a fix. +6. (Optional) If you have a fix and would like to contribute your patch, please work + directly with the maintainers via `cobra-security@googlegroups.com` to + coordinate pushing the patch to GitHub, cutting a new release, and disclosing the change. + +## Response Process + +When a security vulnerability report is received, the `cobra` maintainers will: + +1. Confirm receipt of the vulnerability report within 7 days. +2. Assess the report to determine if it constitutes a security vulnerability. +3. If confirmed, assign the vulnerability a severity level and create a timeline for addressing it. +4. Develop and test a fix. +5. Patch the vulnerability and make a new GitHub release: the maintainers will coordinate disclosure with the reporter. +6. Create a new GitHub Security Advisory to inform the broader Go ecosystem + +## Disclosure Policy + +The `cobra` maintainers follow a coordinated disclosure process: + +1. Security vulnerabilities will be addressed as quickly as possible. +2. A CVE (Common Vulnerabilities and Exposures) identifier will be requested for significant vulnerabilities + that are within `cobra` itself. +3. Once a fix is ready, the maintainers will: + - Release a new version containing the fix. + - Update the security advisory with details about the vulnerability. + - Credit the reporter (unless they wish to remain anonymous). + - Credit the fixer (unless they wish to remain anonymous, this may be the same as the reporter). + - Announce the vulnerability through appropriate channels + (GitHub Security Advisory, mailing lists, GitHub Releases, etc.) + +## Supported Versions + +Security fixes will typically only be released for the most recent major release. + +## Upstream Security Issues + +`cobra` generally will not accept vulnerability reports that originate in upstream +dependencies. I.e., if there is a problem in Go code that `cobra` depends on, +it is best to engage that project's maintainers and owners. + +This security policy primarily pertains only to `cobra` itself but if you believe you've +identified a problem that originates in an upstream dependency and is being widely +distributed by `cobra`, please follow the disclosure procedure above: the `cobra` +maintainers will work with you to determine the severity and ecosystem impact. + +## Security Updates and CVEs + +Information about known security vulnerabilities and CVEs affecting `cobra` will +be published as GitHub Security Advisories at +https://github.com/spf13/cobra/security/advisories. + +All users are encouraged to watch the repository and upgrade promptly when +security releases are published. + +## `cobra` Security Best Practices for Users + +When using `cobra` in your CLIs, the `cobra` maintainers recommend the following: + +1. Always use the latest version of `cobra`. +2. [Use Go modules](https://go.dev/blog/using-go-modules) for dependency management. +3. Always use the latest possible version of Go. + +## Security Best Practices for Contributors + +When contributing to `cobra`: + +1. Be mindful of security implications when adding new features or modifying existing ones. +2. Be aware of `cobra`'s extremely large reach: it is used in nearly every Go CLI + (like Kubernetes, Docker, Prometheus, etc. etc.) +3. Write tests that explicitly cover edge cases and potential issues. +4. If you discover a security issue while working on `cobra`, please report it + following the process above rather than opening a public pull request or issue that + addresses the vulnerability. +5. Take personal sec-ops seriously and secure your GitHub account: use [two-factor authentication](https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa), + [sign your commits with a GPG or SSH key](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification), + etc. + +## Acknowledgments + +The `cobra` maintainers would like to thank all security researchers and +community members who help keep cobra, its users, and the entire Go ecosystem secure through responsible disclosures!! + +--- + +*This security policy is inspired by the [Open Web Application Security Project (OWASP)](https://owasp.org/) guidelines and security best practices.* diff --git a/vendor/github.com/spf13/cobra/active_help.go b/vendor/github.com/spf13/cobra/active_help.go index 25c30e3c..b3e2dadf 100644 --- a/vendor/github.com/spf13/cobra/active_help.go +++ b/vendor/github.com/spf13/cobra/active_help.go @@ -35,7 +35,7 @@ const ( // This function can be called multiple times before and/or after completions are added to // the array. Each time this function is called with the same array, the new // ActiveHelp line will be shown below the previous ones when completion is triggered. -func AppendActiveHelp(compArray []string, activeHelpStr string) []string { +func AppendActiveHelp(compArray []Completion, activeHelpStr string) []Completion { return append(compArray, fmt.Sprintf("%s%s", activeHelpMarker, activeHelpStr)) } diff --git a/vendor/github.com/spf13/cobra/bash_completionsV2.go b/vendor/github.com/spf13/cobra/bash_completionsV2.go index 1cce5c32..d2397aa3 100644 --- a/vendor/github.com/spf13/cobra/bash_completionsV2.go +++ b/vendor/github.com/spf13/cobra/bash_completionsV2.go @@ -146,7 +146,7 @@ __%[1]s_process_completion_results() { if (((directive & shellCompDirectiveFilterFileExt) != 0)); then # File extension filtering - local fullFilter filter filteringCmd + local fullFilter="" filter filteringCmd # Do not use quotes around the $completions variable or else newline # characters will be kept. @@ -177,20 +177,71 @@ __%[1]s_process_completion_results() { __%[1]s_handle_special_char "$cur" = # Print the activeHelp statements before we finish + __%[1]s_handle_activeHelp +} + +__%[1]s_handle_activeHelp() { + # Print the activeHelp statements if ((${#activeHelp[*]} != 0)); then - printf "\n"; - printf "%%s\n" "${activeHelp[@]}" - printf "\n" - - # The prompt format is only available from bash 4.4. - # We test if it is available before using it. - if (x=${PS1@P}) 2> /dev/null; then - printf "%%s" "${PS1@P}${COMP_LINE[@]}" - else - # Can't print the prompt. Just print the - # text the user had typed, it is workable enough. - printf "%%s" "${COMP_LINE[@]}" + if [ -z $COMP_TYPE ]; then + # Bash v3 does not set the COMP_TYPE variable. + printf "\n"; + printf "%%s\n" "${activeHelp[@]}" + printf "\n" + __%[1]s_reprint_commandLine + return fi + + # Only print ActiveHelp on the second TAB press + if [ $COMP_TYPE -eq 63 ]; then + printf "\n" + printf "%%s\n" "${activeHelp[@]}" + + if ((${#COMPREPLY[*]} == 0)); then + # When there are no completion choices from the program, file completion + # may kick in if the program has not disabled it; in such a case, we want + # to know if any files will match what the user typed, so that we know if + # there will be completions presented, so that we know how to handle ActiveHelp. + # To find out, we actually trigger the file completion ourselves; + # the call to _filedir will fill COMPREPLY if files match. + if (((directive & shellCompDirectiveNoFileComp) == 0)); then + __%[1]s_debug "Listing files" + _filedir + fi + fi + + if ((${#COMPREPLY[*]} != 0)); then + # If there are completion choices to be shown, print a delimiter. + # Re-printing the command-line will automatically be done + # by the shell when it prints the completion choices. + printf -- "--" + else + # When there are no completion choices at all, we need + # to re-print the command-line since the shell will + # not be doing it itself. + __%[1]s_reprint_commandLine + fi + elif [ $COMP_TYPE -eq 37 ] || [ $COMP_TYPE -eq 42 ]; then + # For completion type: menu-complete/menu-complete-backward and insert-completions + # the completions are immediately inserted into the command-line, so we first + # print the activeHelp message and reprint the command-line since the shell won't. + printf "\n" + printf "%%s\n" "${activeHelp[@]}" + + __%[1]s_reprint_commandLine + fi + fi +} + +__%[1]s_reprint_commandLine() { + # The prompt format is only available from bash 4.4. + # We test if it is available before using it. + if (x=${PS1@P}) 2> /dev/null; then + printf "%%s" "${PS1@P}${COMP_LINE[@]}" + else + # Can't print the prompt. Just print the + # text the user had typed, it is workable enough. + printf "%%s" "${COMP_LINE[@]}" fi } @@ -201,6 +252,8 @@ __%[1]s_extract_activeHelp() { local endIndex=${#activeHelpMarker} while IFS='' read -r comp; do + [[ -z $comp ]] && continue + if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then comp=${comp:endIndex} __%[1]s_debug "ActiveHelp found: $comp" @@ -223,16 +276,21 @@ __%[1]s_handle_completion_types() { # If the user requested inserting one completion at a time, or all # completions at once on the command-line we must remove the descriptions. # https://github.com/spf13/cobra/issues/1508 - local tab=$'\t' comp - while IFS='' read -r comp; do - [[ -z $comp ]] && continue - # Strip any description - comp=${comp%%%%$tab*} - # Only consider the completions that match - if [[ $comp == "$cur"* ]]; then - COMPREPLY+=("$comp") - fi - done < <(printf "%%s\n" "${completions[@]}") + + # If there are no completions, we don't need to do anything + (( ${#completions[@]} == 0 )) && return 0 + + local tab=$'\t' + + # Strip any description and escape the completion to handled special characters + IFS=$'\n' read -ra completions -d '' < <(printf "%%q\n" "${completions[@]%%%%$tab*}") + + # Only consider the completions that match + IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${completions[*]}" -- "${cur}") + + # compgen looses the escaping so we need to escape all completions again since they will + # all be inserted on the command-line. + IFS=$'\n' read -ra COMPREPLY -d '' < <(printf "%%q\n" "${COMPREPLY[@]}") ;; *) @@ -243,11 +301,25 @@ __%[1]s_handle_completion_types() { } __%[1]s_handle_standard_completion_case() { - local tab=$'\t' comp + local tab=$'\t' + + # If there are no completions, we don't need to do anything + (( ${#completions[@]} == 0 )) && return 0 # Short circuit to optimize if we don't have descriptions if [[ "${completions[*]}" != *$tab* ]]; then - IFS=$'\n' read -ra COMPREPLY -d '' < <(compgen -W "${completions[*]}" -- "$cur") + # First, escape the completions to handle special characters + IFS=$'\n' read -ra completions -d '' < <(printf "%%q\n" "${completions[@]}") + # Only consider the completions that match what the user typed + IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${completions[*]}" -- "${cur}") + + # compgen looses the escaping so, if there is only a single completion, we need to + # escape it again because it will be inserted on the command-line. If there are multiple + # completions, we don't want to escape them because they will be printed in a list + # and we don't want to show escape characters in that list. + if (( ${#COMPREPLY[@]} == 1 )); then + COMPREPLY[0]=$(printf "%%q" "${COMPREPLY[0]}") + fi return 0 fi @@ -256,23 +328,39 @@ __%[1]s_handle_standard_completion_case() { # Look for the longest completion so that we can format things nicely while IFS='' read -r compline; do [[ -z $compline ]] && continue - # Strip any description before checking the length - comp=${compline%%%%$tab*} + + # Before checking if the completion matches what the user typed, + # we need to strip any description and escape the completion to handle special + # characters because those escape characters are part of what the user typed. + # Don't call "printf" in a sub-shell because it will be much slower + # since we are in a loop. + printf -v comp "%%q" "${compline%%%%$tab*}" &>/dev/null || comp=$(printf "%%q" "${compline%%%%$tab*}") + # Only consider the completions that match [[ $comp == "$cur"* ]] || continue + + # The completions matches. Add it to the list of full completions including + # its description. We don't escape the completion because it may get printed + # in a list if there are more than one and we don't want show escape characters + # in that list. COMPREPLY+=("$compline") + + # Strip any description before checking the length, and again, don't escape + # the completion because this length is only used when printing the completions + # in a list and we don't want show escape characters in that list. + comp=${compline%%%%$tab*} if ((${#comp}>longest)); then longest=${#comp} fi done < <(printf "%%s\n" "${completions[@]}") - # If there is a single completion left, remove the description text + # If there is a single completion left, remove the description text and escape any special characters if ((${#COMPREPLY[*]} == 1)); then __%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}" - comp="${COMPREPLY[0]%%%%$tab*}" - __%[1]s_debug "Removed description from single completion, which is now: ${comp}" - COMPREPLY[0]=$comp - else # Format the descriptions + COMPREPLY[0]=$(printf "%%q" "${COMPREPLY[0]%%%%$tab*}") + __%[1]s_debug "Removed description from single completion, which is now: ${COMPREPLY[0]}" + else + # Format the descriptions __%[1]s_format_comp_descriptions $longest fi } diff --git a/vendor/github.com/spf13/cobra/cobra.go b/vendor/github.com/spf13/cobra/cobra.go index e0b0947b..d9cd2414 100644 --- a/vendor/github.com/spf13/cobra/cobra.go +++ b/vendor/github.com/spf13/cobra/cobra.go @@ -176,12 +176,16 @@ func rpad(s string, padding int) string { return fmt.Sprintf(formattedString, s) } -// tmpl executes the given template text on data, writing the result to w. -func tmpl(w io.Writer, text string, data interface{}) error { - t := template.New("top") - t.Funcs(templateFuncs) - template.Must(t.Parse(text)) - return t.Execute(w, data) +func tmpl(text string) *tmplFunc { + return &tmplFunc{ + tmpl: text, + fn: func(w io.Writer, data interface{}) error { + t := template.New("top") + t.Funcs(templateFuncs) + template.Must(t.Parse(text)) + return t.Execute(w, data) + }, + } } // ld compares two strings and returns the levenshtein distance between them. diff --git a/vendor/github.com/spf13/cobra/command.go b/vendor/github.com/spf13/cobra/command.go index 54748fc6..c05fed45 100644 --- a/vendor/github.com/spf13/cobra/command.go +++ b/vendor/github.com/spf13/cobra/command.go @@ -33,10 +33,13 @@ import ( const ( FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra" CommandDisplayNameAnnotation = "cobra_annotation_command_display_name" + + helpFlagName = "help" + helpCommandName = "help" ) // FParseErrWhitelist configures Flag parse errors to be ignored -type FParseErrWhitelist flag.ParseErrorsWhitelist +type FParseErrWhitelist flag.ParseErrorsAllowlist // Group Structure to manage groups for commands type Group struct { @@ -80,11 +83,11 @@ type Command struct { Example string // ValidArgs is list of all valid non-flag arguments that are accepted in shell completions - ValidArgs []string + ValidArgs []Completion // ValidArgsFunction is an optional function that provides valid non-flag arguments for shell completion. // It is a dynamic version of using ValidArgs. // Only one of ValidArgs and ValidArgsFunction can be used for a command. - ValidArgsFunction func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) + ValidArgsFunction CompletionFunc // Expected arguments Args PositionalArgs @@ -168,12 +171,12 @@ type Command struct { // usageFunc is usage func defined by user. usageFunc func(*Command) error // usageTemplate is usage template defined by user. - usageTemplate string + usageTemplate *tmplFunc // flagErrorFunc is func defined by user and it's called when the parsing of // flags returns an error. flagErrorFunc func(*Command, error) error // helpTemplate is help template defined by user. - helpTemplate string + helpTemplate *tmplFunc // helpFunc is help func defined by user. helpFunc func(*Command, []string) // helpCommand is command with usage 'help'. If it's not defined by user, @@ -186,7 +189,7 @@ type Command struct { completionCommandGroupID string // versionTemplate is the version template defined by user. - versionTemplate string + versionTemplate *tmplFunc // errPrefix is the error message prefix defined by user. errPrefix string @@ -281,6 +284,7 @@ func (c *Command) SetArgs(a []string) { // SetOutput sets the destination for usage and error messages. // If output is nil, os.Stderr is used. +// // Deprecated: Use SetOut and/or SetErr instead func (c *Command) SetOutput(output io.Writer) { c.outWriter = output @@ -312,7 +316,11 @@ func (c *Command) SetUsageFunc(f func(*Command) error) { // SetUsageTemplate sets usage template. Can be defined by Application. func (c *Command) SetUsageTemplate(s string) { - c.usageTemplate = s + if s == "" { + c.usageTemplate = nil + return + } + c.usageTemplate = tmpl(s) } // SetFlagErrorFunc sets a function to generate an error when flag parsing @@ -348,12 +356,20 @@ func (c *Command) SetCompletionCommandGroupID(groupID string) { // SetHelpTemplate sets help template to be used. Application can use it to set custom template. func (c *Command) SetHelpTemplate(s string) { - c.helpTemplate = s + if s == "" { + c.helpTemplate = nil + return + } + c.helpTemplate = tmpl(s) } // SetVersionTemplate sets version template to be used. Application can use it to set custom template. func (c *Command) SetVersionTemplate(s string) { - c.versionTemplate = s + if s == "" { + c.versionTemplate = nil + return + } + c.versionTemplate = tmpl(s) } // SetErrPrefix sets error message prefix to be used. Application can use it to set custom prefix. @@ -434,7 +450,8 @@ func (c *Command) UsageFunc() (f func(*Command) error) { } return func(c *Command) error { c.mergePersistentFlags() - err := tmpl(c.OutOrStderr(), c.UsageTemplate(), c) + fn := c.getUsageTemplateFunc() + err := fn(c.OutOrStderr(), c) if err != nil { c.PrintErrln(err) } @@ -442,6 +459,19 @@ func (c *Command) UsageFunc() (f func(*Command) error) { } } +// getUsageTemplateFunc returns the usage template function for the command +// going up the command tree if necessary. +func (c *Command) getUsageTemplateFunc() func(w io.Writer, data interface{}) error { + if c.usageTemplate != nil { + return c.usageTemplate.fn + } + + if c.HasParent() { + return c.parent.getUsageTemplateFunc() + } + return defaultUsageFunc +} + // Usage puts out the usage for the command. // Used when a user provides invalid input. // Can be defined by user by overriding UsageFunc. @@ -460,15 +490,30 @@ func (c *Command) HelpFunc() func(*Command, []string) { } return func(c *Command, a []string) { c.mergePersistentFlags() + fn := c.getHelpTemplateFunc() // The help should be sent to stdout // See https://github.com/spf13/cobra/issues/1002 - err := tmpl(c.OutOrStdout(), c.HelpTemplate(), c) + err := fn(c.OutOrStdout(), c) if err != nil { c.PrintErrln(err) } } } +// getHelpTemplateFunc returns the help template function for the command +// going up the command tree if necessary. +func (c *Command) getHelpTemplateFunc() func(w io.Writer, data interface{}) error { + if c.helpTemplate != nil { + return c.helpTemplate.fn + } + + if c.HasParent() { + return c.parent.getHelpTemplateFunc() + } + + return defaultHelpFunc +} + // Help puts out the help for the command. // Used when a user calls help [command]. // Can be defined by user by overriding HelpFunc. @@ -512,7 +557,7 @@ func (c *Command) FlagErrorFunc() (f func(*Command, error) error) { } } -var minUsagePadding = 25 +const minUsagePadding = 25 // UsagePadding return padding for the usage. func (c *Command) UsagePadding() int { @@ -522,7 +567,7 @@ func (c *Command) UsagePadding() int { return c.parent.commandsMaxUseLen } -var minCommandPathPadding = 11 +const minCommandPathPadding = 11 // CommandPathPadding return padding for the command path. func (c *Command) CommandPathPadding() int { @@ -532,7 +577,7 @@ func (c *Command) CommandPathPadding() int { return c.parent.commandsMaxCommandPathLen } -var minNamePadding = 11 +const minNamePadding = 11 // NamePadding returns padding for the name. func (c *Command) NamePadding() int { @@ -543,71 +588,55 @@ func (c *Command) NamePadding() int { } // UsageTemplate returns usage template for the command. +// This function is kept for backwards-compatibility reasons. func (c *Command) UsageTemplate() string { - if c.usageTemplate != "" { - return c.usageTemplate + if c.usageTemplate != nil { + return c.usageTemplate.tmpl } if c.HasParent() { return c.parent.UsageTemplate() } - return `Usage:{{if .Runnable}} - {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} - {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} - -Aliases: - {{.NameAndAliases}}{{end}}{{if .HasExample}} - -Examples: -{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}} - -Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}} - -{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}} - -Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} - -Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} - {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} - -Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} -` + return defaultUsageTemplate } // HelpTemplate return help template for the command. +// This function is kept for backwards-compatibility reasons. func (c *Command) HelpTemplate() string { - if c.helpTemplate != "" { - return c.helpTemplate + if c.helpTemplate != nil { + return c.helpTemplate.tmpl } if c.HasParent() { return c.parent.HelpTemplate() } - return `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}} - -{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` + return defaultHelpTemplate } // VersionTemplate return version template for the command. +// This function is kept for backwards-compatibility reasons. func (c *Command) VersionTemplate() string { - if c.versionTemplate != "" { - return c.versionTemplate + if c.versionTemplate != nil { + return c.versionTemplate.tmpl } if c.HasParent() { return c.parent.VersionTemplate() } - return `{{with .Name}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}} -` + return defaultVersionTemplate +} + +// getVersionTemplateFunc returns the version template function for the command +// going up the command tree if necessary. +func (c *Command) getVersionTemplateFunc() func(w io.Writer, data interface{}) error { + if c.versionTemplate != nil { + return c.versionTemplate.fn + } + + if c.HasParent() { + return c.parent.getVersionTemplateFunc() + } + return defaultVersionFunc } // ErrPrefix return error message prefix for the command @@ -894,7 +923,7 @@ func (c *Command) execute(a []string) (err error) { // If help is called, regardless of other flags, return we want help. // Also say we need help if the command isn't runnable. - helpVal, err := c.Flags().GetBool("help") + helpVal, err := c.Flags().GetBool(helpFlagName) if err != nil { // should be impossible to get here as we always declare a help // flag in InitDefaultHelpFlag() @@ -914,7 +943,8 @@ func (c *Command) execute(a []string) (err error) { return err } if versionVal { - err := tmpl(c.OutOrStdout(), c.VersionTemplate(), c) + fn := c.getVersionTemplateFunc() + err := fn(c.OutOrStdout(), c) if err != nil { c.Println(err) } @@ -1068,12 +1098,6 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { // initialize help at the last point to allow for user overriding c.InitDefaultHelpCmd() - // initialize completion at the last point to allow for user overriding - c.InitDefaultCompletionCmd() - - // Now that all commands have been created, let's make sure all groups - // are properly created also - c.checkCommandGroups() args := c.args @@ -1082,9 +1106,16 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { args = os.Args[1:] } - // initialize the hidden command to be used for shell completion + // initialize the __complete command to be used for shell completion c.initCompleteCmd(args) + // initialize the default completion command + c.InitDefaultCompletionCmd(args...) + + // Now that all commands have been created, let's make sure all groups + // are properly created also + c.checkCommandGroups() + var flags []string if c.TraverseChildren { cmd, flags, err = c.Traverse(args) @@ -1187,16 +1218,16 @@ func (c *Command) checkCommandGroups() { // If c already has help flag, it will do nothing. func (c *Command) InitDefaultHelpFlag() { c.mergePersistentFlags() - if c.Flags().Lookup("help") == nil { + if c.Flags().Lookup(helpFlagName) == nil { usage := "help for " - name := c.displayName() + name := c.DisplayName() if name == "" { usage += "this command" } else { usage += name } - c.Flags().BoolP("help", "h", false, usage) - _ = c.Flags().SetAnnotation("help", FlagSetByCobraAnnotation, []string{"true"}) + c.Flags().BoolP(helpFlagName, "h", false, usage) + _ = c.Flags().SetAnnotation(helpFlagName, FlagSetByCobraAnnotation, []string{"true"}) } } @@ -1215,7 +1246,7 @@ func (c *Command) InitDefaultVersionFlag() { if c.Name() == "" { usage += "this command" } else { - usage += c.Name() + usage += c.DisplayName() } if c.Flags().ShorthandLookup("v") == nil { c.Flags().BoolP("version", "v", false, usage) @@ -1239,9 +1270,9 @@ func (c *Command) InitDefaultHelpCmd() { Use: "help [command]", Short: "Help about any command", Long: `Help provides help for any command in the application. -Simply type ` + c.displayName() + ` help [path to command] for full details.`, - ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]string, ShellCompDirective) { - var completions []string +Simply type ` + c.DisplayName() + ` help [path to command] for full details.`, + ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) { + var completions []Completion cmd, _, e := c.Root().Find(args) if e != nil { return nil, ShellCompDirectiveNoFileComp @@ -1253,7 +1284,7 @@ Simply type ` + c.displayName() + ` help [path to command] for full details.`, for _, subCmd := range cmd.Commands() { if subCmd.IsAvailableCommand() || subCmd == cmd.helpCommand { if strings.HasPrefix(subCmd.Name(), toComplete) { - completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short)) + completions = append(completions, CompletionWithDesc(subCmd.Name(), subCmd.Short)) } } } @@ -1265,6 +1296,11 @@ Simply type ` + c.displayName() + ` help [path to command] for full details.`, c.Printf("Unknown help topic %#q\n", args) CheckErr(c.Root().Usage()) } else { + // FLow the context down to be used in help text + if cmd.ctx == nil { + cmd.ctx = c.ctx + } + cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown cmd.InitDefaultVersionFlag() // make possible 'version' flag to be shown CheckErr(cmd.Help()) @@ -1430,10 +1466,12 @@ func (c *Command) CommandPath() string { if c.HasParent() { return c.Parent().CommandPath() + " " + c.Name() } - return c.displayName() + return c.DisplayName() } -func (c *Command) displayName() string { +// DisplayName returns the name to display in help text. Returns command Name() +// If CommandDisplayNameAnnoation is not set +func (c *Command) DisplayName() string { if displayName, ok := c.Annotations[CommandDisplayNameAnnotation]; ok { return displayName } @@ -1443,7 +1481,7 @@ func (c *Command) displayName() string { // UseLine puts out the full usage for a given command (including parents). func (c *Command) UseLine() string { var useline string - use := strings.Replace(c.Use, c.Name(), c.displayName(), 1) + use := strings.Replace(c.Use, c.Name(), c.DisplayName(), 1) if c.HasParent() { useline = c.parent.CommandPath() + " " + use } else { @@ -1649,7 +1687,7 @@ func (c *Command) GlobalNormalizationFunc() func(f *flag.FlagSet, name string) f // to this command (local and persistent declared here and by all parents). func (c *Command) Flags() *flag.FlagSet { if c.flags == nil { - c.flags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.flags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } @@ -1664,7 +1702,7 @@ func (c *Command) Flags() *flag.FlagSet { func (c *Command) LocalNonPersistentFlags() *flag.FlagSet { persistentFlags := c.PersistentFlags() - out := flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + out := flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) c.LocalFlags().VisitAll(func(f *flag.Flag) { if persistentFlags.Lookup(f.Name) == nil { out.AddFlag(f) @@ -1679,7 +1717,7 @@ func (c *Command) LocalFlags() *flag.FlagSet { c.mergePersistentFlags() if c.lflags == nil { - c.lflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.lflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } @@ -1707,7 +1745,7 @@ func (c *Command) InheritedFlags() *flag.FlagSet { c.mergePersistentFlags() if c.iflags == nil { - c.iflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.iflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } @@ -1736,7 +1774,7 @@ func (c *Command) NonInheritedFlags() *flag.FlagSet { // PersistentFlags returns the persistent FlagSet specifically set in the current command. func (c *Command) PersistentFlags() *flag.FlagSet { if c.pflags == nil { - c.pflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.pflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } @@ -1749,9 +1787,9 @@ func (c *Command) PersistentFlags() *flag.FlagSet { func (c *Command) ResetFlags() { c.flagErrorBuf = new(bytes.Buffer) c.flagErrorBuf.Reset() - c.flags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.flags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) c.flags.SetOutput(c.flagErrorBuf) - c.pflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.pflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) c.pflags.SetOutput(c.flagErrorBuf) c.lflags = nil @@ -1839,7 +1877,7 @@ func (c *Command) ParseFlags(args []string) error { c.mergePersistentFlags() // do it here after merging all flags and just before parse - c.Flags().ParseErrorsWhitelist = flag.ParseErrorsWhitelist(c.FParseErrWhitelist) + c.Flags().ParseErrorsAllowlist = flag.ParseErrorsAllowlist(c.FParseErrWhitelist) err := c.Flags().Parse(args) // Print warnings if they occurred (e.g. deprecated flag messages). @@ -1868,7 +1906,7 @@ func (c *Command) mergePersistentFlags() { // If c.parentsPflags == nil, it makes new. func (c *Command) updateParentsPflags() { if c.parentsPflags == nil { - c.parentsPflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.parentsPflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) c.parentsPflags.SetOutput(c.flagErrorBuf) c.parentsPflags.SortFlags = false } @@ -1894,3 +1932,141 @@ func commandNameMatches(s string, t string) bool { return s == t } + +// tmplFunc holds a template and a function that will execute said template. +type tmplFunc struct { + tmpl string + fn func(io.Writer, interface{}) error +} + +const defaultUsageTemplate = `Usage:{{if .Runnable}} + {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} + {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} + +Aliases: + {{.NameAndAliases}}{{end}}{{if .HasExample}} + +Examples: +{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}} + +Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}} + +{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}} + +Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} + +Flags: +{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} + +Global Flags: +{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} + +Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} + {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} + +Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} +` + +// defaultUsageFunc is equivalent to executing defaultUsageTemplate. The two should be changed in sync. +func defaultUsageFunc(w io.Writer, in interface{}) error { + c := in.(*Command) + fmt.Fprint(w, "Usage:") + if c.Runnable() { + fmt.Fprintf(w, "\n %s", c.UseLine()) + } + if c.HasAvailableSubCommands() { + fmt.Fprintf(w, "\n %s [command]", c.CommandPath()) + } + if len(c.Aliases) > 0 { + fmt.Fprintf(w, "\n\nAliases:\n") + fmt.Fprintf(w, " %s", c.NameAndAliases()) + } + if c.HasExample() { + fmt.Fprintf(w, "\n\nExamples:\n") + fmt.Fprintf(w, "%s", c.Example) + } + if c.HasAvailableSubCommands() { + cmds := c.Commands() + if len(c.Groups()) == 0 { + fmt.Fprintf(w, "\n\nAvailable Commands:") + for _, subcmd := range cmds { + if subcmd.IsAvailableCommand() || subcmd.Name() == helpCommandName { + fmt.Fprintf(w, "\n %s %s", rpad(subcmd.Name(), subcmd.NamePadding()), subcmd.Short) + } + } + } else { + for _, group := range c.Groups() { + fmt.Fprintf(w, "\n\n%s", group.Title) + for _, subcmd := range cmds { + if subcmd.GroupID == group.ID && (subcmd.IsAvailableCommand() || subcmd.Name() == helpCommandName) { + fmt.Fprintf(w, "\n %s %s", rpad(subcmd.Name(), subcmd.NamePadding()), subcmd.Short) + } + } + } + if !c.AllChildCommandsHaveGroup() { + fmt.Fprintf(w, "\n\nAdditional Commands:") + for _, subcmd := range cmds { + if subcmd.GroupID == "" && (subcmd.IsAvailableCommand() || subcmd.Name() == helpCommandName) { + fmt.Fprintf(w, "\n %s %s", rpad(subcmd.Name(), subcmd.NamePadding()), subcmd.Short) + } + } + } + } + } + if c.HasAvailableLocalFlags() { + fmt.Fprintf(w, "\n\nFlags:\n") + fmt.Fprint(w, trimRightSpace(c.LocalFlags().FlagUsages())) + } + if c.HasAvailableInheritedFlags() { + fmt.Fprintf(w, "\n\nGlobal Flags:\n") + fmt.Fprint(w, trimRightSpace(c.InheritedFlags().FlagUsages())) + } + if c.HasHelpSubCommands() { + fmt.Fprintf(w, "\n\nAdditional help topics:") + for _, subcmd := range c.Commands() { + if subcmd.IsAdditionalHelpTopicCommand() { + fmt.Fprintf(w, "\n %s %s", rpad(subcmd.CommandPath(), subcmd.CommandPathPadding()), subcmd.Short) + } + } + } + if c.HasAvailableSubCommands() { + fmt.Fprintf(w, "\n\nUse \"%s [command] --help\" for more information about a command.", c.CommandPath()) + } + fmt.Fprintln(w) + return nil +} + +const defaultHelpTemplate = `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}} + +{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` + +// defaultHelpFunc is equivalent to executing defaultHelpTemplate. The two should be changed in sync. +func defaultHelpFunc(w io.Writer, in interface{}) error { + c := in.(*Command) + usage := c.Long + if usage == "" { + usage = c.Short + } + usage = trimRightSpace(usage) + if usage != "" { + fmt.Fprintln(w, usage) + fmt.Fprintln(w) + } + if c.Runnable() || c.HasSubCommands() { + fmt.Fprint(w, c.UsageString()) + } + return nil +} + +const defaultVersionTemplate = `{{with .DisplayName}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}} +` + +// defaultVersionFunc is equivalent to executing defaultVersionTemplate. The two should be changed in sync. +func defaultVersionFunc(w io.Writer, in interface{}) error { + c := in.(*Command) + _, err := fmt.Fprintf(w, "%s version %s\n", c.DisplayName(), c.Version) + return err +} diff --git a/vendor/github.com/spf13/cobra/completions.go b/vendor/github.com/spf13/cobra/completions.go index c0c08b05..d3607c2d 100644 --- a/vendor/github.com/spf13/cobra/completions.go +++ b/vendor/github.com/spf13/cobra/completions.go @@ -35,7 +35,7 @@ const ( ) // Global map of flag completion functions. Make sure to use flagCompletionMutex before you try to read and write from it. -var flagCompletionFunctions = map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){} +var flagCompletionFunctions = map[*pflag.Flag]CompletionFunc{} // lock for reading and writing from flagCompletionFunctions var flagCompletionMutex = &sync.RWMutex{} @@ -115,24 +115,59 @@ type CompletionOptions struct { DisableDescriptions bool // HiddenDefaultCmd makes the default 'completion' command hidden HiddenDefaultCmd bool + // DefaultShellCompDirective sets the ShellCompDirective that is returned + // if no special directive can be determined + DefaultShellCompDirective *ShellCompDirective +} + +func (receiver *CompletionOptions) SetDefaultShellCompDirective(directive ShellCompDirective) { + receiver.DefaultShellCompDirective = &directive +} + +// Completion is a string that can be used for completions +// +// two formats are supported: +// - the completion choice +// - the completion choice with a textual description (separated by a TAB). +// +// [CompletionWithDesc] can be used to create a completion string with a textual description. +// +// Note: Go type alias is used to provide a more descriptive name in the documentation, but any string can be used. +type Completion = string + +// CompletionFunc is a function that provides completion results. +type CompletionFunc = func(cmd *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) + +// CompletionWithDesc returns a [Completion] with a description by using the TAB delimited format. +func CompletionWithDesc(choice string, description string) Completion { + return choice + "\t" + description } // NoFileCompletions can be used to disable file completion for commands that should // not trigger file completions. -func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { +// +// This method satisfies [CompletionFunc]. +// It can be used with [Command.RegisterFlagCompletionFunc] and for [Command.ValidArgsFunction]. +func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) { return nil, ShellCompDirectiveNoFileComp } // FixedCompletions can be used to create a completion function which always // returns the same results. -func FixedCompletions(choices []string, directive ShellCompDirective) func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { - return func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { +// +// This method returns a function that satisfies [CompletionFunc] +// It can be used with [Command.RegisterFlagCompletionFunc] and for [Command.ValidArgsFunction]. +func FixedCompletions(choices []Completion, directive ShellCompDirective) CompletionFunc { + return func(cmd *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) { return choices, directive } } // RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag. -func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)) error { +// +// You can use pre-defined completion functions such as [FixedCompletions] or [NoFileCompletions], +// or you can define your own. +func (c *Command) RegisterFlagCompletionFunc(flagName string, f CompletionFunc) error { flag := c.Flag(flagName) if flag == nil { return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName) @@ -148,7 +183,7 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman } // GetFlagCompletionFunc returns the completion function for the given flag of the command, if available. -func (c *Command) GetFlagCompletionFunc(flagName string) (func(*Command, []string, string) ([]string, ShellCompDirective), bool) { +func (c *Command) GetFlagCompletionFunc(flagName string) (CompletionFunc, bool) { flag := c.Flag(flagName) if flag == nil { return nil, false @@ -270,7 +305,15 @@ func (c *Command) initCompleteCmd(args []string) { } } -func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDirective, error) { +// SliceValue is a reduced version of [pflag.SliceValue]. It is used to detect +// flags that accept multiple values and therefore can provide completion +// multiple times. +type SliceValue interface { + // GetSlice returns the flag value list as an array of strings. + GetSlice() []string +} + +func (c *Command) getCompletions(args []string) (*Command, []Completion, ShellCompDirective, error) { // The last argument, which is not completely typed by the user, // should not be part of the list of arguments toComplete := args[len(args)-1] @@ -298,7 +341,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi } if err != nil { // Unable to find the real command. E.g., someInvalidCmd - return c, []string{}, ShellCompDirectiveDefault, fmt.Errorf("unable to find a command for arguments: %v", trimmedArgs) + return c, []Completion{}, ShellCompDirectiveDefault, fmt.Errorf("unable to find a command for arguments: %v", trimmedArgs) } finalCmd.ctx = c.ctx @@ -328,7 +371,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi // Parse the flags early so we can check if required flags are set if err = finalCmd.ParseFlags(finalArgs); err != nil { - return finalCmd, []string{}, ShellCompDirectiveDefault, fmt.Errorf("Error while parsing flags from args %v: %s", finalArgs, err.Error()) + return finalCmd, []Completion{}, ShellCompDirectiveDefault, fmt.Errorf("Error while parsing flags from args %v: %s", finalArgs, err.Error()) } realArgCount := finalCmd.Flags().NArg() @@ -339,15 +382,15 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi // Error while attempting to parse flags if flagErr != nil { // If error type is flagCompError and we don't want flagCompletion we should ignore the error - if _, ok := flagErr.(*flagCompError); !(ok && !flagCompletion) { - return finalCmd, []string{}, ShellCompDirectiveDefault, flagErr + if _, ok := flagErr.(*flagCompError); !ok || flagCompletion { + return finalCmd, []Completion{}, ShellCompDirectiveDefault, flagErr } } // Look for the --help or --version flags. If they are present, // there should be no further completions. if helpOrVersionFlagPresent(finalCmd) { - return finalCmd, []string{}, ShellCompDirectiveNoFileComp, nil + return finalCmd, []Completion{}, ShellCompDirectiveNoFileComp, nil } // We only remove the flags from the arguments if DisableFlagParsing is not set. @@ -376,11 +419,11 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi return finalCmd, subDir, ShellCompDirectiveFilterDirs, nil } // Directory completion - return finalCmd, []string{}, ShellCompDirectiveFilterDirs, nil + return finalCmd, []Completion{}, ShellCompDirectiveFilterDirs, nil } } - var completions []string + var completions []Completion var directive ShellCompDirective // Enforce flag groups before doing flag completions @@ -399,10 +442,14 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi // If we have not found any required flags, only then can we show regular flags if len(completions) == 0 { doCompleteFlags := func(flag *pflag.Flag) { - if !flag.Changed || + _, acceptsMultiple := flag.Value.(SliceValue) + acceptsMultiple = acceptsMultiple || strings.Contains(flag.Value.Type(), "Slice") || - strings.Contains(flag.Value.Type(), "Array") { - // If the flag is not already present, or if it can be specified multiple times (Array or Slice) + strings.Contains(flag.Value.Type(), "Array") || + strings.HasPrefix(flag.Value.Type(), "stringTo") + + if !flag.Changed || acceptsMultiple { + // If the flag is not already present, or if it can be specified multiple times (Array, Slice, or stringTo) // we suggest it as a completion completions = append(completions, getFlagNameCompletions(flag, toComplete)...) } @@ -440,6 +487,14 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi } } else { directive = ShellCompDirectiveDefault + // check current and parent commands for a custom DefaultShellCompDirective + for cmd := finalCmd; cmd != nil; cmd = cmd.parent { + if cmd.CompletionOptions.DefaultShellCompDirective != nil { + directive = *cmd.CompletionOptions.DefaultShellCompDirective + break + } + } + if flag == nil { foundLocalNonPersistentFlag := false // If TraverseChildren is true on the root command we don't check for @@ -462,7 +517,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi for _, subCmd := range finalCmd.Commands() { if subCmd.IsAvailableCommand() || subCmd == finalCmd.helpCommand { if strings.HasPrefix(subCmd.Name(), toComplete) { - completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short)) + completions = append(completions, CompletionWithDesc(subCmd.Name(), subCmd.Short)) } directive = ShellCompDirectiveNoFileComp } @@ -507,7 +562,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi } // Find the completion function for the flag or command - var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) + var completionFn CompletionFunc if flag != nil && flagCompletion { flagCompletionMutex.RLock() completionFn = flagCompletionFunctions[flag] @@ -518,7 +573,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi if completionFn != nil { // Go custom completion defined for this flag or command. // Call the registered completion function to get the completions. - var comps []string + var comps []Completion comps, directive = completionFn(finalCmd, finalArgs, toComplete) completions = append(completions, comps...) } @@ -531,23 +586,23 @@ func helpOrVersionFlagPresent(cmd *Command) bool { len(versionFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && versionFlag.Changed { return true } - if helpFlag := cmd.Flags().Lookup("help"); helpFlag != nil && + if helpFlag := cmd.Flags().Lookup(helpFlagName); helpFlag != nil && len(helpFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && helpFlag.Changed { return true } return false } -func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []string { +func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []Completion { if nonCompletableFlag(flag) { - return []string{} + return []Completion{} } - var completions []string + var completions []Completion flagName := "--" + flag.Name if strings.HasPrefix(flagName, toComplete) { // Flag without the = - completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage)) + completions = append(completions, CompletionWithDesc(flagName, flag.Usage)) // Why suggest both long forms: --flag and --flag= ? // This forces the user to *always* have to type either an = or a space after the flag name. @@ -559,20 +614,20 @@ func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []string { // if len(flag.NoOptDefVal) == 0 { // // Flag requires a value, so it can be suffixed with = // flagName += "=" - // completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage)) + // completions = append(completions, CompletionWithDesc(flagName, flag.Usage)) // } } flagName = "-" + flag.Shorthand if len(flag.Shorthand) > 0 && strings.HasPrefix(flagName, toComplete) { - completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage)) + completions = append(completions, CompletionWithDesc(flagName, flag.Usage)) } return completions } -func completeRequireFlags(finalCmd *Command, toComplete string) []string { - var completions []string +func completeRequireFlags(finalCmd *Command, toComplete string) []Completion { + var completions []Completion doCompleteRequiredFlags := func(flag *pflag.Flag) { if _, present := flag.Annotations[BashCompOneRequiredFlag]; present { @@ -687,8 +742,8 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p // 1- the feature has been explicitly disabled by the program, // 2- c has no subcommands (to avoid creating one), // 3- c already has a 'completion' command provided by the program. -func (c *Command) InitDefaultCompletionCmd() { - if c.CompletionOptions.DisableDefaultCmd || !c.HasSubCommands() { +func (c *Command) InitDefaultCompletionCmd(args ...string) { + if c.CompletionOptions.DisableDefaultCmd { return } @@ -701,6 +756,16 @@ func (c *Command) InitDefaultCompletionCmd() { haveNoDescFlag := !c.CompletionOptions.DisableNoDescFlag && !c.CompletionOptions.DisableDescriptions + // Special case to know if there are sub-commands or not. + hasSubCommands := false + for _, cmd := range c.commands { + if cmd.Name() != ShellCompRequestCmd && cmd.Name() != helpCommandName { + // We found a real sub-command (not 'help' or '__complete') + hasSubCommands = true + break + } + } + completionCmd := &Command{ Use: compCmdName, Short: "Generate the autocompletion script for the specified shell", @@ -714,6 +779,22 @@ See each sub-command's help for details on how to use the generated script. } c.AddCommand(completionCmd) + if !hasSubCommands { + // If the 'completion' command will be the only sub-command, + // we only create it if it is actually being called. + // This avoids breaking programs that would suddenly find themselves with + // a subcommand, which would prevent them from accepting arguments. + // We also create the 'completion' command if the user is triggering + // shell completion for it (prog __complete completion '') + subCmd, cmdArgs, err := c.Find(args) + if err != nil || subCmd.Name() != compCmdName && + (subCmd.Name() != ShellCompRequestCmd || len(cmdArgs) <= 1 || cmdArgs[0] != compCmdName) { + // The completion command is not being called or being completed so we remove it. + c.RemoveCommand(completionCmd) + return + } + } + out := c.OutOrStdout() noDesc := c.CompletionOptions.DisableDescriptions shortDesc := "Generate the autocompletion script for %s" diff --git a/vendor/github.com/spf13/cobra/powershell_completions.go b/vendor/github.com/spf13/cobra/powershell_completions.go index a830b7bc..746dcb92 100644 --- a/vendor/github.com/spf13/cobra/powershell_completions.go +++ b/vendor/github.com/spf13/cobra/powershell_completions.go @@ -162,7 +162,10 @@ filter __%[1]s_escapeStringWithSpecialChars { if (-Not $Description) { $Description = " " } - @{Name="$Name";Description="$Description"} + New-Object -TypeName PSCustomObject -Property @{ + Name = "$Name" + Description = "$Description" + } } @@ -240,7 +243,12 @@ filter __%[1]s_escapeStringWithSpecialChars { __%[1]s_debug "Only one completion left" # insert space after value - [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + $CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space + if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ + [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + } else { + $CompletionText + } } else { # Add the proper number of spaces to align the descriptions @@ -255,7 +263,12 @@ filter __%[1]s_escapeStringWithSpecialChars { $Description = " ($($comp.Description))" } - [System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)") + $CompletionText = "$($comp.Name)$Description" + if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ + [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)") + } else { + $CompletionText + } } } @@ -264,7 +277,13 @@ filter __%[1]s_escapeStringWithSpecialChars { # insert space after value # MenuComplete will automatically show the ToolTip of # the highlighted value at the bottom of the suggestions. - [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + + $CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space + if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ + [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + } else { + $CompletionText + } } # TabCompleteNext and in case we get something unknown @@ -272,7 +291,13 @@ filter __%[1]s_escapeStringWithSpecialChars { # Like MenuComplete but we don't want to add a space here because # the user need to press space anyway to get the completion. # Description will not be shown because that's not possible with TabCompleteNext - [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + + $CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars) + if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ + [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + } else { + $CompletionText + } } } diff --git a/vendor/github.com/spf13/pflag/.editorconfig b/vendor/github.com/spf13/pflag/.editorconfig new file mode 100644 index 00000000..4492e9f9 --- /dev/null +++ b/vendor/github.com/spf13/pflag/.editorconfig @@ -0,0 +1,12 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.go] +indent_style = tab diff --git a/vendor/github.com/spf13/pflag/.golangci.yaml b/vendor/github.com/spf13/pflag/.golangci.yaml new file mode 100644 index 00000000..b274f248 --- /dev/null +++ b/vendor/github.com/spf13/pflag/.golangci.yaml @@ -0,0 +1,4 @@ +linters: + disable-all: true + enable: + - nolintlint diff --git a/vendor/github.com/spf13/pflag/README.md b/vendor/github.com/spf13/pflag/README.md index 7eacc5bd..388c4e5e 100644 --- a/vendor/github.com/spf13/pflag/README.md +++ b/vendor/github.com/spf13/pflag/README.md @@ -284,6 +284,33 @@ func main() { } ``` +### Using pflag with go test +`pflag` does not parse the shorthand versions of go test's built-in flags (i.e., those starting with `-test.`). +For more context, see issues [#63](https://github.com/spf13/pflag/issues/63) and [#238](https://github.com/spf13/pflag/issues/238) for more details. + +For example, if you use pflag in your `TestMain` function and call `pflag.Parse()` after defining your custom flags, running a test like this: +```bash +go test /your/tests -run ^YourTest -v --your-test-pflags +``` +will result in the `-v` flag being ignored. This happens because of the way pflag handles flag parsing, skipping over go test's built-in shorthand flags. +To work around this, you can use the `ParseSkippedFlags` function, which ensures that go test's flags are parsed separately using the standard flag package. + +**Example**: You want to parse go test flags that are otherwise ignore by `pflag.Parse()` +```go +import ( + goflag "flag" + flag "github.com/spf13/pflag" +) + +var ip *int = flag.Int("flagname", 1234, "help message for flagname") + +func main() { + flag.CommandLine.AddGoFlagSet(goflag.CommandLine) + flag.ParseSkippedFlags(os.Args[1:], goflag.CommandLine) + flag.Parse() +} +``` + ## More info You can see the full reference documentation of the pflag package diff --git a/vendor/github.com/spf13/pflag/bool_func.go b/vendor/github.com/spf13/pflag/bool_func.go new file mode 100644 index 00000000..83d77afa --- /dev/null +++ b/vendor/github.com/spf13/pflag/bool_func.go @@ -0,0 +1,40 @@ +package pflag + +// -- func Value +type boolfuncValue func(string) error + +func (f boolfuncValue) Set(s string) error { return f(s) } + +func (f boolfuncValue) Type() string { return "boolfunc" } + +func (f boolfuncValue) String() string { return "" } // same behavior as stdlib 'flag' package + +func (f boolfuncValue) IsBoolFlag() bool { return true } + +// BoolFunc defines a func flag with specified name, callback function and usage string. +// +// The callback function will be called every time "--{name}" (or any form that matches the flag) is parsed +// on the command line. +func (f *FlagSet) BoolFunc(name string, usage string, fn func(string) error) { + f.BoolFuncP(name, "", usage, fn) +} + +// BoolFuncP is like BoolFunc, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) BoolFuncP(name, shorthand string, usage string, fn func(string) error) { + var val Value = boolfuncValue(fn) + flag := f.VarPF(val, name, shorthand, usage) + flag.NoOptDefVal = "true" +} + +// BoolFunc defines a func flag with specified name, callback function and usage string. +// +// The callback function will be called every time "--{name}" (or any form that matches the flag) is parsed +// on the command line. +func BoolFunc(name string, usage string, fn func(string) error) { + CommandLine.BoolFuncP(name, "", usage, fn) +} + +// BoolFuncP is like BoolFunc, but accepts a shorthand letter that can be used after a single dash. +func BoolFuncP(name, shorthand string, usage string, fn func(string) error) { + CommandLine.BoolFuncP(name, shorthand, usage, fn) +} diff --git a/vendor/github.com/spf13/pflag/count.go b/vendor/github.com/spf13/pflag/count.go index a0b2679f..d49c0143 100644 --- a/vendor/github.com/spf13/pflag/count.go +++ b/vendor/github.com/spf13/pflag/count.go @@ -85,7 +85,7 @@ func (f *FlagSet) CountP(name, shorthand string, usage string) *int { // Count defines a count flag with specified name, default value, and usage string. // The return value is the address of an int variable that stores the value of the flag. -// A count flag will add 1 to its value evey time it is found on the command line +// A count flag will add 1 to its value every time it is found on the command line func Count(name string, usage string) *int { return CommandLine.CountP(name, "", usage) } diff --git a/vendor/github.com/spf13/pflag/errors.go b/vendor/github.com/spf13/pflag/errors.go new file mode 100644 index 00000000..ff11b66b --- /dev/null +++ b/vendor/github.com/spf13/pflag/errors.go @@ -0,0 +1,149 @@ +package pflag + +import "fmt" + +// notExistErrorMessageType specifies which flavor of "flag does not exist" +// is printed by NotExistError. This allows the related errors to be grouped +// under a single NotExistError struct without making a breaking change to +// the error message text. +type notExistErrorMessageType int + +const ( + flagNotExistMessage notExistErrorMessageType = iota + flagNotDefinedMessage + flagNoSuchFlagMessage + flagUnknownFlagMessage + flagUnknownShorthandFlagMessage +) + +// NotExistError is the error returned when trying to access a flag that +// does not exist in the FlagSet. +type NotExistError struct { + name string + specifiedShorthands string + messageType notExistErrorMessageType +} + +// Error implements error. +func (e *NotExistError) Error() string { + switch e.messageType { + case flagNotExistMessage: + return fmt.Sprintf("flag %q does not exist", e.name) + + case flagNotDefinedMessage: + return fmt.Sprintf("flag accessed but not defined: %s", e.name) + + case flagNoSuchFlagMessage: + return fmt.Sprintf("no such flag -%v", e.name) + + case flagUnknownFlagMessage: + return fmt.Sprintf("unknown flag: --%s", e.name) + + case flagUnknownShorthandFlagMessage: + c := rune(e.name[0]) + return fmt.Sprintf("unknown shorthand flag: %q in -%s", c, e.specifiedShorthands) + } + + panic(fmt.Errorf("unknown flagNotExistErrorMessageType: %v", e.messageType)) +} + +// GetSpecifiedName returns the name of the flag (without dashes) as it +// appeared in the parsed arguments. +func (e *NotExistError) GetSpecifiedName() string { + return e.name +} + +// GetSpecifiedShortnames returns the group of shorthand arguments +// (without dashes) that the flag appeared within. If the flag was not in a +// shorthand group, this will return an empty string. +func (e *NotExistError) GetSpecifiedShortnames() string { + return e.specifiedShorthands +} + +// ValueRequiredError is the error returned when a flag needs an argument but +// no argument was provided. +type ValueRequiredError struct { + flag *Flag + specifiedName string + specifiedShorthands string +} + +// Error implements error. +func (e *ValueRequiredError) Error() string { + if len(e.specifiedShorthands) > 0 { + c := rune(e.specifiedName[0]) + return fmt.Sprintf("flag needs an argument: %q in -%s", c, e.specifiedShorthands) + } + + return fmt.Sprintf("flag needs an argument: --%s", e.specifiedName) +} + +// GetFlag returns the flag for which the error occurred. +func (e *ValueRequiredError) GetFlag() *Flag { + return e.flag +} + +// GetSpecifiedName returns the name of the flag (without dashes) as it +// appeared in the parsed arguments. +func (e *ValueRequiredError) GetSpecifiedName() string { + return e.specifiedName +} + +// GetSpecifiedShortnames returns the group of shorthand arguments +// (without dashes) that the flag appeared within. If the flag was not in a +// shorthand group, this will return an empty string. +func (e *ValueRequiredError) GetSpecifiedShortnames() string { + return e.specifiedShorthands +} + +// InvalidValueError is the error returned when an invalid value is used +// for a flag. +type InvalidValueError struct { + flag *Flag + value string + cause error +} + +// Error implements error. +func (e *InvalidValueError) Error() string { + flag := e.flag + var flagName string + if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { + flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name) + } else { + flagName = fmt.Sprintf("--%s", flag.Name) + } + return fmt.Sprintf("invalid argument %q for %q flag: %v", e.value, flagName, e.cause) +} + +// Unwrap implements errors.Unwrap. +func (e *InvalidValueError) Unwrap() error { + return e.cause +} + +// GetFlag returns the flag for which the error occurred. +func (e *InvalidValueError) GetFlag() *Flag { + return e.flag +} + +// GetValue returns the invalid value that was provided. +func (e *InvalidValueError) GetValue() string { + return e.value +} + +// InvalidSyntaxError is the error returned when a bad flag name is passed on +// the command line. +type InvalidSyntaxError struct { + specifiedFlag string +} + +// Error implements error. +func (e *InvalidSyntaxError) Error() string { + return fmt.Sprintf("bad flag syntax: %s", e.specifiedFlag) +} + +// GetSpecifiedName returns the exact flag (with dashes) as it +// appeared in the parsed arguments. +func (e *InvalidSyntaxError) GetSpecifiedFlag() string { + return e.specifiedFlag +} diff --git a/vendor/github.com/spf13/pflag/flag.go b/vendor/github.com/spf13/pflag/flag.go index 24a5036e..eeed1e92 100644 --- a/vendor/github.com/spf13/pflag/flag.go +++ b/vendor/github.com/spf13/pflag/flag.go @@ -27,23 +27,32 @@ unaffected. Define flags using flag.String(), Bool(), Int(), etc. This declares an integer flag, -flagname, stored in the pointer ip, with type *int. + var ip = flag.Int("flagname", 1234, "help message for flagname") + If you like, you can bind the flag to a variable using the Var() functions. + var flagvar int func init() { flag.IntVar(&flagvar, "flagname", 1234, "help message for flagname") } + Or you can create custom flags that satisfy the Value interface (with pointer receivers) and couple them to flag parsing by + flag.Var(&flagVal, "name", "help message for flagname") + For such flags, the default value is just the initial value of the variable. After all flags are defined, call + flag.Parse() + to parse the command line into the defined flags. Flags may then be used directly. If you're using the flags themselves, they are all pointers; if you bind to variables, they're values. + fmt.Println("ip has value ", *ip) fmt.Println("flagvar has value ", flagvar) @@ -54,22 +63,26 @@ The arguments are indexed from 0 through flag.NArg()-1. The pflag package also defines some new functions that are not in flag, that give one-letter shorthands for flags. You can use these by appending 'P' to the name of any function that defines a flag. + var ip = flag.IntP("flagname", "f", 1234, "help message") var flagvar bool func init() { flag.BoolVarP(&flagvar, "boolname", "b", true, "help message") } flag.VarP(&flagval, "varname", "v", "help message") + Shorthand letters can be used with single dashes on the command line. Boolean shorthand flags can be combined with other shorthand flags. Command line flag syntax: + --flag // boolean flags only --flag=x Unlike the flag package, a single dash before an option means something different than a double dash. Single dashes signify a series of shorthand letters for flags. All but the last shorthand letter must be boolean flags. + // boolean flags -f -abc @@ -124,12 +137,16 @@ const ( PanicOnError ) -// ParseErrorsWhitelist defines the parsing errors that can be ignored -type ParseErrorsWhitelist struct { +// ParseErrorsAllowlist defines the parsing errors that can be ignored +type ParseErrorsAllowlist struct { // UnknownFlags will ignore unknown flags errors and continue parsing rest of the flags UnknownFlags bool } +// DEPRECATED: please use ParseErrorsAllowlist instead +// This type will be removed in a future release +type ParseErrorsWhitelist = ParseErrorsAllowlist + // NormalizedName is a flag name that has been normalized according to rules // for the FlagSet (e.g. making '-' and '_' equivalent). type NormalizedName string @@ -145,8 +162,12 @@ type FlagSet struct { // help/usage messages. SortFlags bool - // ParseErrorsWhitelist is used to configure a whitelist of errors - ParseErrorsWhitelist ParseErrorsWhitelist + // ParseErrorsAllowlist is used to configure an allowlist of errors + ParseErrorsAllowlist ParseErrorsAllowlist + + // DEPRECATED: please use ParseErrorsAllowlist instead + // This field will be removed in a future release + ParseErrorsWhitelist ParseErrorsAllowlist name string parsed bool @@ -160,7 +181,7 @@ type FlagSet struct { args []string // arguments after flags argsLenAtDash int // len(args) when a '--' was located when parsing, or -1 if no -- errorHandling ErrorHandling - output io.Writer // nil means stderr; use out() accessor + output io.Writer // nil means stderr; use Output() accessor interspersed bool // allow interspersed option/non-option args normalizeNameFunc func(f *FlagSet, name string) NormalizedName @@ -255,13 +276,20 @@ func (f *FlagSet) normalizeFlagName(name string) NormalizedName { return n(f, name) } -func (f *FlagSet) out() io.Writer { +// Output returns the destination for usage and error messages. os.Stderr is returned if +// output was not set or was set to nil. +func (f *FlagSet) Output() io.Writer { if f.output == nil { return os.Stderr } return f.output } +// Name returns the name of the flag set. +func (f *FlagSet) Name() string { + return f.name +} + // SetOutput sets the destination for usage and error messages. // If output is nil, os.Stderr is used. func (f *FlagSet) SetOutput(output io.Writer) { @@ -358,7 +386,7 @@ func (f *FlagSet) ShorthandLookup(name string) *Flag { } if len(name) > 1 { msg := fmt.Sprintf("can not look up shorthand which is more than one ASCII character: %q", name) - fmt.Fprintf(f.out(), msg) + fmt.Fprintf(f.Output(), msg) panic(msg) } c := name[0] @@ -374,7 +402,7 @@ func (f *FlagSet) lookup(name NormalizedName) *Flag { func (f *FlagSet) getFlagType(name string, ftype string, convFunc func(sval string) (interface{}, error)) (interface{}, error) { flag := f.Lookup(name) if flag == nil { - err := fmt.Errorf("flag accessed but not defined: %s", name) + err := &NotExistError{name: name, messageType: flagNotDefinedMessage} return nil, err } @@ -404,7 +432,7 @@ func (f *FlagSet) ArgsLenAtDash() int { func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error { flag := f.Lookup(name) if flag == nil { - return fmt.Errorf("flag %q does not exist", name) + return &NotExistError{name: name, messageType: flagNotExistMessage} } if usageMessage == "" { return fmt.Errorf("deprecated message for flag %q must be set", name) @@ -420,7 +448,7 @@ func (f *FlagSet) MarkDeprecated(name string, usageMessage string) error { func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) error { flag := f.Lookup(name) if flag == nil { - return fmt.Errorf("flag %q does not exist", name) + return &NotExistError{name: name, messageType: flagNotExistMessage} } if usageMessage == "" { return fmt.Errorf("deprecated message for flag %q must be set", name) @@ -434,7 +462,7 @@ func (f *FlagSet) MarkShorthandDeprecated(name string, usageMessage string) erro func (f *FlagSet) MarkHidden(name string) error { flag := f.Lookup(name) if flag == nil { - return fmt.Errorf("flag %q does not exist", name) + return &NotExistError{name: name, messageType: flagNotExistMessage} } flag.Hidden = true return nil @@ -457,18 +485,16 @@ func (f *FlagSet) Set(name, value string) error { normalName := f.normalizeFlagName(name) flag, ok := f.formal[normalName] if !ok { - return fmt.Errorf("no such flag -%v", name) + return &NotExistError{name: name, messageType: flagNoSuchFlagMessage} } err := flag.Value.Set(value) if err != nil { - var flagName string - if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { - flagName = fmt.Sprintf("-%s, --%s", flag.Shorthand, flag.Name) - } else { - flagName = fmt.Sprintf("--%s", flag.Name) + return &InvalidValueError{ + flag: flag, + value: value, + cause: err, } - return fmt.Errorf("invalid argument %q for %q flag: %v", value, flagName, err) } if !flag.Changed { @@ -482,7 +508,7 @@ func (f *FlagSet) Set(name, value string) error { } if flag.Deprecated != "" { - fmt.Fprintf(f.out(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) + fmt.Fprintf(f.Output(), "Flag --%s has been deprecated, %s\n", flag.Name, flag.Deprecated) } return nil } @@ -494,7 +520,7 @@ func (f *FlagSet) SetAnnotation(name, key string, values []string) error { normalName := f.normalizeFlagName(name) flag, ok := f.formal[normalName] if !ok { - return fmt.Errorf("no such flag -%v", name) + return &NotExistError{name: name, messageType: flagNoSuchFlagMessage} } if flag.Annotations == nil { flag.Annotations = map[string][]string{} @@ -523,7 +549,7 @@ func Set(name, value string) error { // otherwise, the default values of all defined flags in the set. func (f *FlagSet) PrintDefaults() { usages := f.FlagUsages() - fmt.Fprint(f.out(), usages) + fmt.Fprint(f.Output(), usages) } // defaultIsZeroValue returns true if the default value for this flag represents @@ -531,7 +557,7 @@ func (f *FlagSet) PrintDefaults() { func (f *Flag) defaultIsZeroValue() bool { switch f.Value.(type) { case boolFlag: - return f.DefValue == "false" + return f.DefValue == "false" || f.DefValue == "" case *durationValue: // Beginning in Go 1.7, duration zero values are "0s" return f.DefValue == "0" || f.DefValue == "0s" @@ -544,7 +570,7 @@ func (f *Flag) defaultIsZeroValue() bool { case *intSliceValue, *stringSliceValue, *stringArrayValue: return f.DefValue == "[]" default: - switch f.Value.String() { + switch f.DefValue { case "false": return true case "": @@ -581,8 +607,10 @@ func UnquoteUsage(flag *Flag) (name string, usage string) { name = flag.Value.Type() switch name { - case "bool": + case "bool", "boolfunc": name = "" + case "func": + name = "value" case "float64": name = "float" case "int64": @@ -700,7 +728,7 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string { switch flag.Value.Type() { case "string": line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal) - case "bool": + case "bool", "boolfunc": if flag.NoOptDefVal != "true" { line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) } @@ -758,7 +786,7 @@ func PrintDefaults() { // defaultUsage is the default function to print a usage message. func defaultUsage(f *FlagSet) { - fmt.Fprintf(f.out(), "Usage of %s:\n", f.name) + fmt.Fprintf(f.Output(), "Usage of %s:\n", f.name) f.PrintDefaults() } @@ -844,7 +872,7 @@ func (f *FlagSet) AddFlag(flag *Flag) { _, alreadyThere := f.formal[normalizedFlagName] if alreadyThere { msg := fmt.Sprintf("%s flag redefined: %s", f.name, flag.Name) - fmt.Fprintln(f.out(), msg) + fmt.Fprintln(f.Output(), msg) panic(msg) // Happens only if flags are declared with identical names } if f.formal == nil { @@ -860,7 +888,7 @@ func (f *FlagSet) AddFlag(flag *Flag) { } if len(flag.Shorthand) > 1 { msg := fmt.Sprintf("%q shorthand is more than one ASCII character", flag.Shorthand) - fmt.Fprintf(f.out(), msg) + fmt.Fprintf(f.Output(), msg) panic(msg) } if f.shorthands == nil { @@ -870,7 +898,7 @@ func (f *FlagSet) AddFlag(flag *Flag) { used, alreadyThere := f.shorthands[c] if alreadyThere { msg := fmt.Sprintf("unable to redefine %q shorthand in %q flagset: it's already used for %q flag", c, f.name, used.Name) - fmt.Fprintf(f.out(), msg) + fmt.Fprintf(f.Output(), msg) panic(msg) } f.shorthands[c] = flag @@ -904,12 +932,10 @@ func VarP(value Value, name, shorthand, usage string) { CommandLine.VarP(value, name, shorthand, usage) } -// failf prints to standard error a formatted error and usage message and +// fail prints an error message and usage message to standard error and // returns the error. -func (f *FlagSet) failf(format string, a ...interface{}) error { - err := fmt.Errorf(format, a...) +func (f *FlagSet) fail(err error) error { if f.errorHandling != ContinueOnError { - fmt.Fprintln(f.out(), err) f.usage() } return err @@ -927,9 +953,9 @@ func (f *FlagSet) usage() { } } -//--unknown (args will be empty) -//--unknown --next-flag ... (args will be --next-flag ...) -//--unknown arg ... (args will be arg ...) +// --unknown (args will be empty) +// --unknown --next-flag ... (args will be --next-flag ...) +// --unknown arg ... (args will be arg ...) func stripUnknownFlagValue(args []string) []string { if len(args) == 0 { //--unknown @@ -953,7 +979,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin a = args name := s[2:] if len(name) == 0 || name[0] == '-' || name[0] == '=' { - err = f.failf("bad flag syntax: %s", s) + err = f.fail(&InvalidSyntaxError{specifiedFlag: s}) return } @@ -967,6 +993,8 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin f.usage() return a, ErrHelp case f.ParseErrorsWhitelist.UnknownFlags: + fallthrough + case f.ParseErrorsAllowlist.UnknownFlags: // --unknown=unknownval arg ... // we do not want to lose arg in this case if len(split) >= 2 { @@ -975,7 +1003,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin return stripUnknownFlagValue(a), nil default: - err = f.failf("unknown flag: --%s", name) + err = f.fail(&NotExistError{name: name, messageType: flagUnknownFlagMessage}) return } } @@ -993,13 +1021,16 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin a = a[1:] } else { // '--flag' (arg was required) - err = f.failf("flag needs an argument: %s", s) + err = f.fail(&ValueRequiredError{ + flag: flag, + specifiedName: name, + }) return } err = fn(flag, value) if err != nil { - f.failf(err.Error()) + f.fail(err) } return } @@ -1007,7 +1038,7 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parseFunc) (outShorts string, outArgs []string, err error) { outArgs = args - if strings.HasPrefix(shorthands, "test.") { + if isGotestShorthandFlag(shorthands) { return } @@ -1022,6 +1053,8 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse err = ErrHelp return case f.ParseErrorsWhitelist.UnknownFlags: + fallthrough + case f.ParseErrorsAllowlist.UnknownFlags: // '-f=arg arg ...' // we do not want to lose arg in this case if len(shorthands) > 2 && shorthands[1] == '=' { @@ -1032,7 +1065,11 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse outArgs = stripUnknownFlagValue(outArgs) return default: - err = f.failf("unknown shorthand flag: %q in -%s", c, shorthands) + err = f.fail(&NotExistError{ + name: string(c), + specifiedShorthands: shorthands, + messageType: flagUnknownShorthandFlagMessage, + }) return } } @@ -1055,17 +1092,21 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse outArgs = args[1:] } else { // '-f' (arg was required) - err = f.failf("flag needs an argument: %q in -%s", c, shorthands) + err = f.fail(&ValueRequiredError{ + flag: flag, + specifiedName: string(c), + specifiedShorthands: shorthands, + }) return } if flag.ShorthandDeprecated != "" { - fmt.Fprintf(f.out(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) + fmt.Fprintf(f.Output(), "Flag shorthand -%s has been deprecated, %s\n", flag.Shorthand, flag.ShorthandDeprecated) } err = fn(flag, value) if err != nil { - f.failf(err.Error()) + f.fail(err) } return } @@ -1128,12 +1169,12 @@ func (f *FlagSet) Parse(arguments []string) error { } f.parsed = true - if len(arguments) < 0 { + f.args = make([]string, 0, len(arguments)) + + if len(arguments) == 0 { return nil } - f.args = make([]string, 0, len(arguments)) - set := func(flag *Flag, value string) error { return f.Set(flag.Name, value) } @@ -1144,7 +1185,10 @@ func (f *FlagSet) Parse(arguments []string) error { case ContinueOnError: return err case ExitOnError: - fmt.Println(err) + if errors.Is(err, ErrHelp) { + os.Exit(0) + } + fmt.Fprintln(f.Output(), err) os.Exit(2) case PanicOnError: panic(err) @@ -1170,6 +1214,10 @@ func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string) case ContinueOnError: return err case ExitOnError: + if errors.Is(err, ErrHelp) { + os.Exit(0) + } + fmt.Fprintln(f.Output(), err) os.Exit(2) case PanicOnError: panic(err) diff --git a/vendor/github.com/spf13/pflag/func.go b/vendor/github.com/spf13/pflag/func.go new file mode 100644 index 00000000..9f4d88f2 --- /dev/null +++ b/vendor/github.com/spf13/pflag/func.go @@ -0,0 +1,37 @@ +package pflag + +// -- func Value +type funcValue func(string) error + +func (f funcValue) Set(s string) error { return f(s) } + +func (f funcValue) Type() string { return "func" } + +func (f funcValue) String() string { return "" } // same behavior as stdlib 'flag' package + +// Func defines a func flag with specified name, callback function and usage string. +// +// The callback function will be called every time "--{name}={value}" (or equivalent) is +// parsed on the command line, with "{value}" as an argument. +func (f *FlagSet) Func(name string, usage string, fn func(string) error) { + f.FuncP(name, "", usage, fn) +} + +// FuncP is like Func, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) FuncP(name string, shorthand string, usage string, fn func(string) error) { + var val Value = funcValue(fn) + f.VarP(val, name, shorthand, usage) +} + +// Func defines a func flag with specified name, callback function and usage string. +// +// The callback function will be called every time "--{name}={value}" (or equivalent) is +// parsed on the command line, with "{value}" as an argument. +func Func(name string, usage string, fn func(string) error) { + CommandLine.FuncP(name, "", usage, fn) +} + +// FuncP is like Func, but accepts a shorthand letter that can be used after a single dash. +func FuncP(name, shorthand string, usage string, fn func(string) error) { + CommandLine.FuncP(name, shorthand, usage, fn) +} diff --git a/vendor/github.com/spf13/pflag/golangflag.go b/vendor/github.com/spf13/pflag/golangflag.go index d3dd72b7..e62eab53 100644 --- a/vendor/github.com/spf13/pflag/golangflag.go +++ b/vendor/github.com/spf13/pflag/golangflag.go @@ -8,8 +8,18 @@ import ( goflag "flag" "reflect" "strings" + "time" ) +// go test flags prefixes +func isGotestFlag(flag string) bool { + return strings.HasPrefix(flag, "-test.") +} + +func isGotestShorthandFlag(flag string) bool { + return strings.HasPrefix(flag, "test.") +} + // flagValueWrapper implements pflag.Value around a flag.Value. The main // difference here is the addition of the Type method that returns a string // name of the type. As this is generally unknown, we approximate that with @@ -103,3 +113,49 @@ func (f *FlagSet) AddGoFlagSet(newSet *goflag.FlagSet) { } f.addedGoFlagSets = append(f.addedGoFlagSets, newSet) } + +// CopyToGoFlagSet will add all current flags to the given Go flag set. +// Deprecation remarks get copied into the usage description. +// Whenever possible, a flag gets added for which Go flags shows +// a proper type in the help message. +func (f *FlagSet) CopyToGoFlagSet(newSet *goflag.FlagSet) { + f.VisitAll(func(flag *Flag) { + usage := flag.Usage + if flag.Deprecated != "" { + usage += " (DEPRECATED: " + flag.Deprecated + ")" + } + + switch value := flag.Value.(type) { + case *stringValue: + newSet.StringVar((*string)(value), flag.Name, flag.DefValue, usage) + case *intValue: + newSet.IntVar((*int)(value), flag.Name, *(*int)(value), usage) + case *int64Value: + newSet.Int64Var((*int64)(value), flag.Name, *(*int64)(value), usage) + case *uintValue: + newSet.UintVar((*uint)(value), flag.Name, *(*uint)(value), usage) + case *uint64Value: + newSet.Uint64Var((*uint64)(value), flag.Name, *(*uint64)(value), usage) + case *durationValue: + newSet.DurationVar((*time.Duration)(value), flag.Name, *(*time.Duration)(value), usage) + case *float64Value: + newSet.Float64Var((*float64)(value), flag.Name, *(*float64)(value), usage) + default: + newSet.Var(flag.Value, flag.Name, usage) + } + }) +} + +// ParseSkippedFlags explicitly Parses go test flags (i.e. the one starting with '-test.') with goflag.Parse(), +// since by default those are skipped by pflag.Parse(). +// Typical usage example: `ParseGoTestFlags(os.Args[1:], goflag.CommandLine)` +func ParseSkippedFlags(osArgs []string, goFlagSet *goflag.FlagSet) error { + var skippedFlags []string + for _, f := range osArgs { + if isGotestFlag(f) { + skippedFlags = append(skippedFlags, f) + } + } + return goFlagSet.Parse(skippedFlags) +} + diff --git a/vendor/github.com/spf13/pflag/ip.go b/vendor/github.com/spf13/pflag/ip.go index 3d414ba6..06b8bcb5 100644 --- a/vendor/github.com/spf13/pflag/ip.go +++ b/vendor/github.com/spf13/pflag/ip.go @@ -16,6 +16,9 @@ func newIPValue(val net.IP, p *net.IP) *ipValue { func (i *ipValue) String() string { return net.IP(*i).String() } func (i *ipValue) Set(s string) error { + if s == "" { + return nil + } ip := net.ParseIP(strings.TrimSpace(s)) if ip == nil { return fmt.Errorf("failed to parse IP: %q", s) diff --git a/vendor/github.com/spf13/pflag/ipnet_slice.go b/vendor/github.com/spf13/pflag/ipnet_slice.go new file mode 100644 index 00000000..c6e89da1 --- /dev/null +++ b/vendor/github.com/spf13/pflag/ipnet_slice.go @@ -0,0 +1,147 @@ +package pflag + +import ( + "fmt" + "io" + "net" + "strings" +) + +// -- ipNetSlice Value +type ipNetSliceValue struct { + value *[]net.IPNet + changed bool +} + +func newIPNetSliceValue(val []net.IPNet, p *[]net.IPNet) *ipNetSliceValue { + ipnsv := new(ipNetSliceValue) + ipnsv.value = p + *ipnsv.value = val + return ipnsv +} + +// Set converts, and assigns, the comma-separated IPNet argument string representation as the []net.IPNet value of this flag. +// If Set is called on a flag that already has a []net.IPNet assigned, the newly converted values will be appended. +func (s *ipNetSliceValue) Set(val string) error { + + // remove all quote characters + rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "") + + // read flag arguments with CSV parser + ipNetStrSlice, err := readAsCSV(rmQuote.Replace(val)) + if err != nil && err != io.EOF { + return err + } + + // parse ip values into slice + out := make([]net.IPNet, 0, len(ipNetStrSlice)) + for _, ipNetStr := range ipNetStrSlice { + _, n, err := net.ParseCIDR(strings.TrimSpace(ipNetStr)) + if err != nil { + return fmt.Errorf("invalid string being converted to CIDR: %s", ipNetStr) + } + out = append(out, *n) + } + + if !s.changed { + *s.value = out + } else { + *s.value = append(*s.value, out...) + } + + s.changed = true + + return nil +} + +// Type returns a string that uniquely represents this flag's type. +func (s *ipNetSliceValue) Type() string { + return "ipNetSlice" +} + +// String defines a "native" format for this net.IPNet slice flag value. +func (s *ipNetSliceValue) String() string { + + ipNetStrSlice := make([]string, len(*s.value)) + for i, n := range *s.value { + ipNetStrSlice[i] = n.String() + } + + out, _ := writeAsCSV(ipNetStrSlice) + return "[" + out + "]" +} + +func ipNetSliceConv(val string) (interface{}, error) { + val = strings.Trim(val, "[]") + // Empty string would cause a slice with one (empty) entry + if len(val) == 0 { + return []net.IPNet{}, nil + } + ss := strings.Split(val, ",") + out := make([]net.IPNet, len(ss)) + for i, sval := range ss { + _, n, err := net.ParseCIDR(strings.TrimSpace(sval)) + if err != nil { + return nil, fmt.Errorf("invalid string being converted to CIDR: %s", sval) + } + out[i] = *n + } + return out, nil +} + +// GetIPNetSlice returns the []net.IPNet value of a flag with the given name +func (f *FlagSet) GetIPNetSlice(name string) ([]net.IPNet, error) { + val, err := f.getFlagType(name, "ipNetSlice", ipNetSliceConv) + if err != nil { + return []net.IPNet{}, err + } + return val.([]net.IPNet), nil +} + +// IPNetSliceVar defines a ipNetSlice flag with specified name, default value, and usage string. +// The argument p points to a []net.IPNet variable in which to store the value of the flag. +func (f *FlagSet) IPNetSliceVar(p *[]net.IPNet, name string, value []net.IPNet, usage string) { + f.VarP(newIPNetSliceValue(value, p), name, "", usage) +} + +// IPNetSliceVarP is like IPNetSliceVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IPNetSliceVarP(p *[]net.IPNet, name, shorthand string, value []net.IPNet, usage string) { + f.VarP(newIPNetSliceValue(value, p), name, shorthand, usage) +} + +// IPNetSliceVar defines a []net.IPNet flag with specified name, default value, and usage string. +// The argument p points to a []net.IPNet variable in which to store the value of the flag. +func IPNetSliceVar(p *[]net.IPNet, name string, value []net.IPNet, usage string) { + CommandLine.VarP(newIPNetSliceValue(value, p), name, "", usage) +} + +// IPNetSliceVarP is like IPNetSliceVar, but accepts a shorthand letter that can be used after a single dash. +func IPNetSliceVarP(p *[]net.IPNet, name, shorthand string, value []net.IPNet, usage string) { + CommandLine.VarP(newIPNetSliceValue(value, p), name, shorthand, usage) +} + +// IPNetSlice defines a []net.IPNet flag with specified name, default value, and usage string. +// The return value is the address of a []net.IPNet variable that stores the value of that flag. +func (f *FlagSet) IPNetSlice(name string, value []net.IPNet, usage string) *[]net.IPNet { + p := []net.IPNet{} + f.IPNetSliceVarP(&p, name, "", value, usage) + return &p +} + +// IPNetSliceP is like IPNetSlice, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) IPNetSliceP(name, shorthand string, value []net.IPNet, usage string) *[]net.IPNet { + p := []net.IPNet{} + f.IPNetSliceVarP(&p, name, shorthand, value, usage) + return &p +} + +// IPNetSlice defines a []net.IPNet flag with specified name, default value, and usage string. +// The return value is the address of a []net.IP variable that stores the value of the flag. +func IPNetSlice(name string, value []net.IPNet, usage string) *[]net.IPNet { + return CommandLine.IPNetSliceP(name, "", value, usage) +} + +// IPNetSliceP is like IPNetSlice, but accepts a shorthand letter that can be used after a single dash. +func IPNetSliceP(name, shorthand string, value []net.IPNet, usage string) *[]net.IPNet { + return CommandLine.IPNetSliceP(name, shorthand, value, usage) +} diff --git a/vendor/github.com/spf13/pflag/string_array.go b/vendor/github.com/spf13/pflag/string_array.go index 4894af81..d1ff0a96 100644 --- a/vendor/github.com/spf13/pflag/string_array.go +++ b/vendor/github.com/spf13/pflag/string_array.go @@ -31,11 +31,7 @@ func (s *stringArrayValue) Append(val string) error { func (s *stringArrayValue) Replace(val []string) error { out := make([]string, len(val)) for i, d := range val { - var err error out[i] = d - if err != nil { - return err - } } *s.value = out return nil diff --git a/vendor/github.com/spf13/pflag/string_to_string.go b/vendor/github.com/spf13/pflag/string_to_string.go index 890a01af..1d1e3bf9 100644 --- a/vendor/github.com/spf13/pflag/string_to_string.go +++ b/vendor/github.com/spf13/pflag/string_to_string.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/csv" "fmt" + "sort" "strings" ) @@ -62,8 +63,15 @@ func (s *stringToStringValue) Type() string { } func (s *stringToStringValue) String() string { + keys := make([]string, 0, len(*s.value)) + for k := range *s.value { + keys = append(keys, k) + } + sort.Strings(keys) + records := make([]string, 0, len(*s.value)>>1) - for k, v := range *s.value { + for _, k := range keys { + v := (*s.value)[k] records = append(records, k+"="+v) } diff --git a/vendor/github.com/spf13/pflag/text.go b/vendor/github.com/spf13/pflag/text.go new file mode 100644 index 00000000..886d5a3d --- /dev/null +++ b/vendor/github.com/spf13/pflag/text.go @@ -0,0 +1,81 @@ +package pflag + +import ( + "encoding" + "fmt" + "reflect" +) + +// following is copied from go 1.23.4 flag.go +type textValue struct{ p encoding.TextUnmarshaler } + +func newTextValue(val encoding.TextMarshaler, p encoding.TextUnmarshaler) textValue { + ptrVal := reflect.ValueOf(p) + if ptrVal.Kind() != reflect.Ptr { + panic("variable value type must be a pointer") + } + defVal := reflect.ValueOf(val) + if defVal.Kind() == reflect.Ptr { + defVal = defVal.Elem() + } + if defVal.Type() != ptrVal.Type().Elem() { + panic(fmt.Sprintf("default type does not match variable type: %v != %v", defVal.Type(), ptrVal.Type().Elem())) + } + ptrVal.Elem().Set(defVal) + return textValue{p} +} + +func (v textValue) Set(s string) error { + return v.p.UnmarshalText([]byte(s)) +} + +func (v textValue) Get() interface{} { + return v.p +} + +func (v textValue) String() string { + if m, ok := v.p.(encoding.TextMarshaler); ok { + if b, err := m.MarshalText(); err == nil { + return string(b) + } + } + return "" +} + +//end of copy + +func (v textValue) Type() string { + return reflect.ValueOf(v.p).Type().Name() +} + +// GetText set out, which implements encoding.UnmarshalText, to the value of a flag with given name +func (f *FlagSet) GetText(name string, out encoding.TextUnmarshaler) error { + flag := f.Lookup(name) + if flag == nil { + return fmt.Errorf("flag accessed but not defined: %s", name) + } + if flag.Value.Type() != reflect.TypeOf(out).Name() { + return fmt.Errorf("trying to get %s value of flag of type %s", reflect.TypeOf(out).Name(), flag.Value.Type()) + } + return out.UnmarshalText([]byte(flag.Value.String())) +} + +// TextVar defines a flag with a specified name, default value, and usage string. The argument p must be a pointer to a variable that will hold the value of the flag, and p must implement encoding.TextUnmarshaler. If the flag is used, the flag value will be passed to p's UnmarshalText method. The type of the default value must be the same as the type of p. +func (f *FlagSet) TextVar(p encoding.TextUnmarshaler, name string, value encoding.TextMarshaler, usage string) { + f.VarP(newTextValue(value, p), name, "", usage) +} + +// TextVarP is like TextVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) TextVarP(p encoding.TextUnmarshaler, name, shorthand string, value encoding.TextMarshaler, usage string) { + f.VarP(newTextValue(value, p), name, shorthand, usage) +} + +// TextVar defines a flag with a specified name, default value, and usage string. The argument p must be a pointer to a variable that will hold the value of the flag, and p must implement encoding.TextUnmarshaler. If the flag is used, the flag value will be passed to p's UnmarshalText method. The type of the default value must be the same as the type of p. +func TextVar(p encoding.TextUnmarshaler, name string, value encoding.TextMarshaler, usage string) { + CommandLine.VarP(newTextValue(value, p), name, "", usage) +} + +// TextVarP is like TextVar, but accepts a shorthand letter that can be used after a single dash. +func TextVarP(p encoding.TextUnmarshaler, name, shorthand string, value encoding.TextMarshaler, usage string) { + CommandLine.VarP(newTextValue(value, p), name, shorthand, usage) +} diff --git a/vendor/github.com/spf13/pflag/time.go b/vendor/github.com/spf13/pflag/time.go new file mode 100644 index 00000000..3dee4247 --- /dev/null +++ b/vendor/github.com/spf13/pflag/time.go @@ -0,0 +1,124 @@ +package pflag + +import ( + "fmt" + "strings" + "time" +) + +// TimeValue adapts time.Time for use as a flag. +type timeValue struct { + *time.Time + formats []string +} + +func newTimeValue(val time.Time, p *time.Time, formats []string) *timeValue { + *p = val + return &timeValue{ + Time: p, + formats: formats, + } +} + +// Set time.Time value from string based on accepted formats. +func (d *timeValue) Set(s string) error { + s = strings.TrimSpace(s) + for _, f := range d.formats { + v, err := time.Parse(f, s) + if err != nil { + continue + } + *d.Time = v + return nil + } + + formatsString := "" + for i, f := range d.formats { + if i > 0 { + formatsString += ", " + } + formatsString += fmt.Sprintf("`%s`", f) + } + + return fmt.Errorf("invalid time format `%s` must be one of: %s", s, formatsString) +} + +// Type name for time.Time flags. +func (d *timeValue) Type() string { + return "time" +} + +func (d *timeValue) String() string { + if d.Time.IsZero() { + return "" + } else { + return d.Time.Format(time.RFC3339Nano) + } +} + +// GetTime return the time value of a flag with the given name +func (f *FlagSet) GetTime(name string) (time.Time, error) { + flag := f.Lookup(name) + if flag == nil { + err := fmt.Errorf("flag accessed but not defined: %s", name) + return time.Time{}, err + } + + if flag.Value.Type() != "time" { + err := fmt.Errorf("trying to get %s value of flag of type %s", "time", flag.Value.Type()) + return time.Time{}, err + } + + val, ok := flag.Value.(*timeValue) + if !ok { + return time.Time{}, fmt.Errorf("value %s is not a time", flag.Value) + } + + return *val.Time, nil +} + +// TimeVar defines a time.Time flag with specified name, default value, and usage string. +// The argument p points to a time.Time variable in which to store the value of the flag. +func (f *FlagSet) TimeVar(p *time.Time, name string, value time.Time, formats []string, usage string) { + f.TimeVarP(p, name, "", value, formats, usage) +} + +// TimeVarP is like TimeVar, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) TimeVarP(p *time.Time, name, shorthand string, value time.Time, formats []string, usage string) { + f.VarP(newTimeValue(value, p, formats), name, shorthand, usage) +} + +// TimeVar defines a time.Time flag with specified name, default value, and usage string. +// The argument p points to a time.Time variable in which to store the value of the flag. +func TimeVar(p *time.Time, name string, value time.Time, formats []string, usage string) { + CommandLine.TimeVarP(p, name, "", value, formats, usage) +} + +// TimeVarP is like TimeVar, but accepts a shorthand letter that can be used after a single dash. +func TimeVarP(p *time.Time, name, shorthand string, value time.Time, formats []string, usage string) { + CommandLine.VarP(newTimeValue(value, p, formats), name, shorthand, usage) +} + +// Time defines a time.Time flag with specified name, default value, and usage string. +// The return value is the address of a time.Time variable that stores the value of the flag. +func (f *FlagSet) Time(name string, value time.Time, formats []string, usage string) *time.Time { + return f.TimeP(name, "", value, formats, usage) +} + +// TimeP is like Time, but accepts a shorthand letter that can be used after a single dash. +func (f *FlagSet) TimeP(name, shorthand string, value time.Time, formats []string, usage string) *time.Time { + p := new(time.Time) + f.TimeVarP(p, name, shorthand, value, formats, usage) + return p +} + +// Time defines a time.Time flag with specified name, default value, and usage string. +// The return value is the address of a time.Time variable that stores the value of the flag. +func Time(name string, value time.Time, formats []string, usage string) *time.Time { + return CommandLine.TimeP(name, "", value, formats, usage) +} + +// TimeP is like Time, but accepts a shorthand letter that can be used after a single dash. +func TimeP(name, shorthand string, value time.Time, formats []string, usage string) *time.Time { + return CommandLine.TimeP(name, shorthand, value, formats, usage) +} diff --git a/vendor/golang.org/x/xerrors/LICENSE b/vendor/golang.org/x/xerrors/LICENSE deleted file mode 100644 index e4a47e17..00000000 --- a/vendor/golang.org/x/xerrors/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2019 The Go 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. diff --git a/vendor/golang.org/x/xerrors/PATENTS b/vendor/golang.org/x/xerrors/PATENTS deleted file mode 100644 index 73309904..00000000 --- a/vendor/golang.org/x/xerrors/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google 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, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/xerrors/README b/vendor/golang.org/x/xerrors/README deleted file mode 100644 index aac7867a..00000000 --- a/vendor/golang.org/x/xerrors/README +++ /dev/null @@ -1,2 +0,0 @@ -This repository holds the transition packages for the new Go 1.13 error values. -See golang.org/design/29934-error-values. diff --git a/vendor/golang.org/x/xerrors/adaptor.go b/vendor/golang.org/x/xerrors/adaptor.go deleted file mode 100644 index 4317f248..00000000 --- a/vendor/golang.org/x/xerrors/adaptor.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2018 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. - -package xerrors - -import ( - "bytes" - "fmt" - "io" - "reflect" - "strconv" -) - -// FormatError calls the FormatError method of f with an errors.Printer -// configured according to s and verb, and writes the result to s. -func FormatError(f Formatter, s fmt.State, verb rune) { - // Assuming this function is only called from the Format method, and given - // that FormatError takes precedence over Format, it cannot be called from - // any package that supports errors.Formatter. It is therefore safe to - // disregard that State may be a specific printer implementation and use one - // of our choice instead. - - // limitations: does not support printing error as Go struct. - - var ( - sep = " " // separator before next error - p = &state{State: s} - direct = true - ) - - var err error = f - - switch verb { - // Note that this switch must match the preference order - // for ordinary string printing (%#v before %+v, and so on). - - case 'v': - if s.Flag('#') { - if stringer, ok := err.(fmt.GoStringer); ok { - io.WriteString(&p.buf, stringer.GoString()) - goto exit - } - // proceed as if it were %v - } else if s.Flag('+') { - p.printDetail = true - sep = "\n - " - } - case 's': - case 'q', 'x', 'X': - // Use an intermediate buffer in the rare cases that precision, - // truncation, or one of the alternative verbs (q, x, and X) are - // specified. - direct = false - - default: - p.buf.WriteString("%!") - p.buf.WriteRune(verb) - p.buf.WriteByte('(') - switch { - case err != nil: - p.buf.WriteString(reflect.TypeOf(f).String()) - default: - p.buf.WriteString("") - } - p.buf.WriteByte(')') - io.Copy(s, &p.buf) - return - } - -loop: - for { - switch v := err.(type) { - case Formatter: - err = v.FormatError((*printer)(p)) - case fmt.Formatter: - v.Format(p, 'v') - break loop - default: - io.WriteString(&p.buf, v.Error()) - break loop - } - if err == nil { - break - } - if p.needColon || !p.printDetail { - p.buf.WriteByte(':') - p.needColon = false - } - p.buf.WriteString(sep) - p.inDetail = false - p.needNewline = false - } - -exit: - width, okW := s.Width() - prec, okP := s.Precision() - - if !direct || (okW && width > 0) || okP { - // Construct format string from State s. - format := []byte{'%'} - if s.Flag('-') { - format = append(format, '-') - } - if s.Flag('+') { - format = append(format, '+') - } - if s.Flag(' ') { - format = append(format, ' ') - } - if okW { - format = strconv.AppendInt(format, int64(width), 10) - } - if okP { - format = append(format, '.') - format = strconv.AppendInt(format, int64(prec), 10) - } - format = append(format, string(verb)...) - fmt.Fprintf(s, string(format), p.buf.String()) - } else { - io.Copy(s, &p.buf) - } -} - -var detailSep = []byte("\n ") - -// state tracks error printing state. It implements fmt.State. -type state struct { - fmt.State - buf bytes.Buffer - - printDetail bool - inDetail bool - needColon bool - needNewline bool -} - -func (s *state) Write(b []byte) (n int, err error) { - if s.printDetail { - if len(b) == 0 { - return 0, nil - } - if s.inDetail && s.needColon { - s.needNewline = true - if b[0] == '\n' { - b = b[1:] - } - } - k := 0 - for i, c := range b { - if s.needNewline { - if s.inDetail && s.needColon { - s.buf.WriteByte(':') - s.needColon = false - } - s.buf.Write(detailSep) - s.needNewline = false - } - if c == '\n' { - s.buf.Write(b[k:i]) - k = i + 1 - s.needNewline = true - } - } - s.buf.Write(b[k:]) - if !s.inDetail { - s.needColon = true - } - } else if !s.inDetail { - s.buf.Write(b) - } - return len(b), nil -} - -// printer wraps a state to implement an xerrors.Printer. -type printer state - -func (s *printer) Print(args ...interface{}) { - if !s.inDetail || s.printDetail { - fmt.Fprint((*state)(s), args...) - } -} - -func (s *printer) Printf(format string, args ...interface{}) { - if !s.inDetail || s.printDetail { - fmt.Fprintf((*state)(s), format, args...) - } -} - -func (s *printer) Detail() bool { - s.inDetail = true - return s.printDetail -} diff --git a/vendor/golang.org/x/xerrors/codereview.cfg b/vendor/golang.org/x/xerrors/codereview.cfg deleted file mode 100644 index 3f8b14b6..00000000 --- a/vendor/golang.org/x/xerrors/codereview.cfg +++ /dev/null @@ -1 +0,0 @@ -issuerepo: golang/go diff --git a/vendor/golang.org/x/xerrors/doc.go b/vendor/golang.org/x/xerrors/doc.go deleted file mode 100644 index eef99d9d..00000000 --- a/vendor/golang.org/x/xerrors/doc.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019 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. - -// Package xerrors implements functions to manipulate errors. -// -// This package is based on the Go 2 proposal for error values: -// https://golang.org/design/29934-error-values -// -// These functions were incorporated into the standard library's errors package -// in Go 1.13: -// - Is -// - As -// - Unwrap -// -// Also, Errorf's %w verb was incorporated into fmt.Errorf. -// -// Use this package to get equivalent behavior in all supported Go versions. -// -// No other features of this package were included in Go 1.13, and at present -// there are no plans to include any of them. -package xerrors // import "golang.org/x/xerrors" diff --git a/vendor/golang.org/x/xerrors/errors.go b/vendor/golang.org/x/xerrors/errors.go deleted file mode 100644 index e88d3772..00000000 --- a/vendor/golang.org/x/xerrors/errors.go +++ /dev/null @@ -1,33 +0,0 @@ -// 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. - -package xerrors - -import "fmt" - -// errorString is a trivial implementation of error. -type errorString struct { - s string - frame Frame -} - -// New returns an error that formats as the given text. -// -// The returned error contains a Frame set to the caller's location and -// implements Formatter to show this information when printed with details. -func New(text string) error { - return &errorString{text, Caller(1)} -} - -func (e *errorString) Error() string { - return e.s -} - -func (e *errorString) Format(s fmt.State, v rune) { FormatError(e, s, v) } - -func (e *errorString) FormatError(p Printer) (next error) { - p.Print(e.s) - e.frame.Format(p) - return nil -} diff --git a/vendor/golang.org/x/xerrors/fmt.go b/vendor/golang.org/x/xerrors/fmt.go deleted file mode 100644 index 829862dd..00000000 --- a/vendor/golang.org/x/xerrors/fmt.go +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2018 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. - -package xerrors - -import ( - "fmt" - "strings" - "unicode" - "unicode/utf8" - - "golang.org/x/xerrors/internal" -) - -const percentBangString = "%!" - -// Errorf formats according to a format specifier and returns the string as a -// value that satisfies error. -// -// The returned error includes the file and line number of the caller when -// formatted with additional detail enabled. If the last argument is an error -// the returned error's Format method will return it if the format string ends -// with ": %s", ": %v", or ": %w". If the last argument is an error and the -// format string ends with ": %w", the returned error implements an Unwrap -// method returning it. -// -// If the format specifier includes a %w verb with an error operand in a -// position other than at the end, the returned error will still implement an -// Unwrap method returning the operand, but the error's Format method will not -// return the wrapped error. -// -// It is invalid to include more than one %w verb or to supply it with an -// operand that does not implement the error interface. The %w verb is otherwise -// a synonym for %v. -func Errorf(format string, a ...interface{}) error { - format = formatPlusW(format) - // Support a ": %[wsv]" suffix, which works well with xerrors.Formatter. - wrap := strings.HasSuffix(format, ": %w") - idx, format2, ok := parsePercentW(format) - percentWElsewhere := !wrap && idx >= 0 - if !percentWElsewhere && (wrap || strings.HasSuffix(format, ": %s") || strings.HasSuffix(format, ": %v")) { - err := errorAt(a, len(a)-1) - if err == nil { - return &noWrapError{fmt.Sprintf(format, a...), nil, Caller(1)} - } - // TODO: this is not entirely correct. The error value could be - // printed elsewhere in format if it mixes numbered with unnumbered - // substitutions. With relatively small changes to doPrintf we can - // have it optionally ignore extra arguments and pass the argument - // list in its entirety. - msg := fmt.Sprintf(format[:len(format)-len(": %s")], a[:len(a)-1]...) - frame := Frame{} - if internal.EnableTrace { - frame = Caller(1) - } - if wrap { - return &wrapError{msg, err, frame} - } - return &noWrapError{msg, err, frame} - } - // Support %w anywhere. - // TODO: don't repeat the wrapped error's message when %w occurs in the middle. - msg := fmt.Sprintf(format2, a...) - if idx < 0 { - return &noWrapError{msg, nil, Caller(1)} - } - err := errorAt(a, idx) - if !ok || err == nil { - // Too many %ws or argument of %w is not an error. Approximate the Go - // 1.13 fmt.Errorf message. - return &noWrapError{fmt.Sprintf("%sw(%s)", percentBangString, msg), nil, Caller(1)} - } - frame := Frame{} - if internal.EnableTrace { - frame = Caller(1) - } - return &wrapError{msg, err, frame} -} - -func errorAt(args []interface{}, i int) error { - if i < 0 || i >= len(args) { - return nil - } - err, ok := args[i].(error) - if !ok { - return nil - } - return err -} - -// formatPlusW is used to avoid the vet check that will barf at %w. -func formatPlusW(s string) string { - return s -} - -// Return the index of the only %w in format, or -1 if none. -// Also return a rewritten format string with %w replaced by %v, and -// false if there is more than one %w. -// TODO: handle "%[N]w". -func parsePercentW(format string) (idx int, newFormat string, ok bool) { - // Loosely copied from golang.org/x/tools/go/analysis/passes/printf/printf.go. - idx = -1 - ok = true - n := 0 - sz := 0 - var isW bool - for i := 0; i < len(format); i += sz { - if format[i] != '%' { - sz = 1 - continue - } - // "%%" is not a format directive. - if i+1 < len(format) && format[i+1] == '%' { - sz = 2 - continue - } - sz, isW = parsePrintfVerb(format[i:]) - if isW { - if idx >= 0 { - ok = false - } else { - idx = n - } - // "Replace" the last character, the 'w', with a 'v'. - p := i + sz - 1 - format = format[:p] + "v" + format[p+1:] - } - n++ - } - return idx, format, ok -} - -// Parse the printf verb starting with a % at s[0]. -// Return how many bytes it occupies and whether the verb is 'w'. -func parsePrintfVerb(s string) (int, bool) { - // Assume only that the directive is a sequence of non-letters followed by a single letter. - sz := 0 - var r rune - for i := 1; i < len(s); i += sz { - r, sz = utf8.DecodeRuneInString(s[i:]) - if unicode.IsLetter(r) { - return i + sz, r == 'w' - } - } - return len(s), false -} - -type noWrapError struct { - msg string - err error - frame Frame -} - -func (e *noWrapError) Error() string { - return fmt.Sprint(e) -} - -func (e *noWrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } - -func (e *noWrapError) FormatError(p Printer) (next error) { - p.Print(e.msg) - e.frame.Format(p) - return e.err -} - -type wrapError struct { - msg string - err error - frame Frame -} - -func (e *wrapError) Error() string { - return fmt.Sprint(e) -} - -func (e *wrapError) Format(s fmt.State, v rune) { FormatError(e, s, v) } - -func (e *wrapError) FormatError(p Printer) (next error) { - p.Print(e.msg) - e.frame.Format(p) - return e.err -} - -func (e *wrapError) Unwrap() error { - return e.err -} diff --git a/vendor/golang.org/x/xerrors/format.go b/vendor/golang.org/x/xerrors/format.go deleted file mode 100644 index 1bc9c26b..00000000 --- a/vendor/golang.org/x/xerrors/format.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018 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. - -package xerrors - -// A Formatter formats error messages. -type Formatter interface { - error - - // FormatError prints the receiver's first error and returns the next error in - // the error chain, if any. - FormatError(p Printer) (next error) -} - -// A Printer formats error messages. -// -// The most common implementation of Printer is the one provided by package fmt -// during Printf (as of Go 1.13). Localization packages such as golang.org/x/text/message -// typically provide their own implementations. -type Printer interface { - // Print appends args to the message output. - Print(args ...interface{}) - - // Printf writes a formatted string. - Printf(format string, args ...interface{}) - - // Detail reports whether error detail is requested. - // After the first call to Detail, all text written to the Printer - // is formatted as additional detail, or ignored when - // detail has not been requested. - // If Detail returns false, the caller can avoid printing the detail at all. - Detail() bool -} diff --git a/vendor/golang.org/x/xerrors/frame.go b/vendor/golang.org/x/xerrors/frame.go deleted file mode 100644 index 0de628ec..00000000 --- a/vendor/golang.org/x/xerrors/frame.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2018 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. - -package xerrors - -import ( - "runtime" -) - -// A Frame contains part of a call stack. -type Frame struct { - // Make room for three PCs: the one we were asked for, what it called, - // and possibly a PC for skipPleaseUseCallersFrames. See: - // https://go.googlesource.com/go/+/032678e0fb/src/runtime/extern.go#169 - frames [3]uintptr -} - -// Caller returns a Frame that describes a frame on the caller's stack. -// The argument skip is the number of frames to skip over. -// Caller(0) returns the frame for the caller of Caller. -func Caller(skip int) Frame { - var s Frame - runtime.Callers(skip+1, s.frames[:]) - return s -} - -// location reports the file, line, and function of a frame. -// -// The returned function may be "" even if file and line are not. -func (f Frame) location() (function, file string, line int) { - frames := runtime.CallersFrames(f.frames[:]) - if _, ok := frames.Next(); !ok { - return "", "", 0 - } - fr, ok := frames.Next() - if !ok { - return "", "", 0 - } - return fr.Function, fr.File, fr.Line -} - -// Format prints the stack as error detail. -// It should be called from an error's Format implementation -// after printing any other error detail. -func (f Frame) Format(p Printer) { - if p.Detail() { - function, file, line := f.location() - if function != "" { - p.Printf("%s\n ", function) - } - if file != "" { - p.Printf("%s:%d\n", file, line) - } - } -} diff --git a/vendor/golang.org/x/xerrors/internal/internal.go b/vendor/golang.org/x/xerrors/internal/internal.go deleted file mode 100644 index 89f4eca5..00000000 --- a/vendor/golang.org/x/xerrors/internal/internal.go +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2018 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. - -package internal - -// EnableTrace indicates whether stack information should be recorded in errors. -var EnableTrace = true diff --git a/vendor/golang.org/x/xerrors/wrap.go b/vendor/golang.org/x/xerrors/wrap.go deleted file mode 100644 index 9a3b5103..00000000 --- a/vendor/golang.org/x/xerrors/wrap.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2018 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. - -package xerrors - -import ( - "reflect" -) - -// A Wrapper provides context around another error. -type Wrapper interface { - // Unwrap returns the next error in the error chain. - // If there is no next error, Unwrap returns nil. - Unwrap() error -} - -// Opaque returns an error with the same error formatting as err -// but that does not match err and cannot be unwrapped. -func Opaque(err error) error { - return noWrapper{err} -} - -type noWrapper struct { - error -} - -func (e noWrapper) FormatError(p Printer) (next error) { - if f, ok := e.error.(Formatter); ok { - return f.FormatError(p) - } - p.Print(e.error) - return nil -} - -// Unwrap returns the result of calling the Unwrap method on err, if err implements -// Unwrap. Otherwise, Unwrap returns nil. -func Unwrap(err error) error { - u, ok := err.(Wrapper) - if !ok { - return nil - } - return u.Unwrap() -} - -// Is reports whether any error in err's chain matches target. -// -// An error is considered to match a target if it is equal to that target or if -// it implements a method Is(error) bool such that Is(target) returns true. -func Is(err, target error) bool { - if target == nil { - return err == target - } - - isComparable := reflect.TypeOf(target).Comparable() - for { - if isComparable && err == target { - return true - } - if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) { - return true - } - // TODO: consider supporing target.Is(err). This would allow - // user-definable predicates, but also may allow for coping with sloppy - // APIs, thereby making it easier to get away with them. - if err = Unwrap(err); err == nil { - return false - } - } -} - -// As finds the first error in err's chain that matches the type to which target -// points, and if so, sets the target to its value and returns true. An error -// matches a type if it is assignable to the target type, or if it has a method -// As(interface{}) bool such that As(target) returns true. As will panic if target -// is not a non-nil pointer to a type which implements error or is of interface type. -// -// The As method should set the target to its value and return true if err -// matches the type to which target points. -func As(err error, target interface{}) bool { - if target == nil { - panic("errors: target cannot be nil") - } - val := reflect.ValueOf(target) - typ := val.Type() - if typ.Kind() != reflect.Ptr || val.IsNil() { - panic("errors: target must be a non-nil pointer") - } - if e := typ.Elem(); e.Kind() != reflect.Interface && !e.Implements(errorType) { - panic("errors: *target must be interface or implement error") - } - targetType := typ.Elem() - for err != nil { - if reflect.TypeOf(err).AssignableTo(targetType) { - val.Elem().Set(reflect.ValueOf(err)) - return true - } - if x, ok := err.(interface{ As(interface{}) bool }); ok && x.As(target) { - return true - } - err = Unwrap(err) - } - return false -} - -var errorType = reflect.TypeOf((*error)(nil)).Elem() diff --git a/vendor/modules.txt b/vendor/modules.txt index 65efc59e..b9b91461 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -9,9 +9,6 @@ github.com/SovereignCloudStack/csctl/pkg/hash # github.com/dustin/go-humanize v1.0.1 ## explicit; go 1.16 github.com/dustin/go-humanize -# github.com/fatih/color v1.13.0 -## explicit; go 1.13 -github.com/fatih/color # github.com/go-ini/ini v1.67.0 ## explicit github.com/go-ini/ini @@ -26,11 +23,12 @@ github.com/goccy/go-json/internal/encoder/vm_color_indent github.com/goccy/go-json/internal/encoder/vm_indent github.com/goccy/go-json/internal/errors github.com/goccy/go-json/internal/runtime -# github.com/goccy/go-yaml v1.12.0 -## explicit; go 1.19 +# github.com/goccy/go-yaml v1.19.0 +## explicit; go 1.21.0 github.com/goccy/go-yaml github.com/goccy/go-yaml/ast github.com/goccy/go-yaml/internal/errors +github.com/goccy/go-yaml/internal/format github.com/goccy/go-yaml/lexer github.com/goccy/go-yaml/parser github.com/goccy/go-yaml/printer @@ -57,12 +55,6 @@ github.com/klauspost/compress/s2 github.com/klauspost/cpuid/v2 # github.com/kr/pretty v0.3.1 ## explicit; go 1.12 -# github.com/mattn/go-colorable v0.1.13 -## explicit; go 1.15 -github.com/mattn/go-colorable -# github.com/mattn/go-isatty v0.0.20 -## explicit; go 1.15 -github.com/mattn/go-isatty # github.com/minio/md5-simd v1.1.2 ## explicit; go 1.14 github.com/minio/md5-simd @@ -83,10 +75,10 @@ github.com/minio/minio-go/v7/pkg/tags # github.com/rs/xid v1.6.0 ## explicit; go 1.16 github.com/rs/xid -# github.com/spf13/cobra v1.8.1 +# github.com/spf13/cobra v1.10.2 ## explicit; go 1.15 github.com/spf13/cobra -# github.com/spf13/pflag v1.0.5 +# github.com/spf13/pflag v1.0.9 ## explicit; go 1.12 github.com/spf13/pflag # golang.org/x/crypto v0.26.0 @@ -111,10 +103,6 @@ golang.org/x/text/secure/bidirule golang.org/x/text/transform golang.org/x/text/unicode/bidi golang.org/x/text/unicode/norm -# golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 -## explicit; go 1.11 -golang.org/x/xerrors -golang.org/x/xerrors/internal # gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c ## explicit; go 1.11 # gopkg.in/yaml.v3 v3.0.1