feat: Support multiple configuration files (#396)
* Revert "Revert "feat: Support multiple configuration files" (#395)"
This reverts commit 87740e74a6.
* feat: Properly implement support for config directory
			
			
This commit is contained in:
		
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							| @ -7,7 +7,7 @@ install: | ||||
| 	go build -mod vendor -o $(BINARY) . | ||||
|  | ||||
| run: | ||||
| 	GATUS_CONFIG_FILE=./config.yaml ./$(BINARY) | ||||
| 	GATUS_CONFIG_PATH=./config.yaml ./$(BINARY) | ||||
|  | ||||
| clean: | ||||
| 	rm $(BINARY) | ||||
|  | ||||
							
								
								
									
										17
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								README.md
									
									
									
									
									
								
							| @ -147,10 +147,6 @@ docker run -p 8080:8080 --name gatus ghcr.io/twin/gatus | ||||
| If you want to create your own configuration, see [Docker](#docker) for information on how to mount a configuration file. | ||||
| </details> | ||||
|  | ||||
| By default, the configuration file is expected to be at `config/config.yaml`. | ||||
|  | ||||
| You can specify a custom path by setting the `GATUS_CONFIG_FILE` environment variable. | ||||
|  | ||||
| Here's a simple example: | ||||
| ```yaml | ||||
| endpoints: | ||||
| @ -174,7 +170,18 @@ This example would look similar to this: | ||||
|  | ||||
|  | ||||
|  | ||||
| Note that you can also use environment variables in the configuration file (e.g. `$DOMAIN`, `${DOMAIN}`) | ||||
| By default, the configuration file is expected to be at `config/config.yaml`. | ||||
|  | ||||
| You can specify a custom path by setting the `GATUS_CONFIG_PATH` environment variable. | ||||
|  | ||||
| If `GATUS_CONFIG_PATH` points to a directory, all `*.yaml` and `*.yml` files inside said directory and its | ||||
| subdirectories are merged like so: | ||||
| - All maps/objects are deep merged (i.e. you could define `alerting.slack` in one file and `alerting.pagerduty` in another file) | ||||
| - All slices/arrays are appended (i.e. you can define `endpoints` in multiple files and each endpoint will be added to the final list of endpoints) | ||||
| - Parameters with a primitive value (e.g. `debug`, `metrics`, `alerting.slack.webhook-url`, etc.) may only be defined once to forcefully avoid any ambiguity | ||||
|     - To clarify, this also means that you could not define `alerting.slack.webhook-url` in two files with different values. All files are merged into one before they are processed. This is by design. | ||||
|  | ||||
| > 💡 You can also use environment variables in the configuration file (e.g. `$DOMAIN`, `${DOMAIN}`) | ||||
|  | ||||
| If you want to test it locally, see [Docker](#docker). | ||||
|  | ||||
|  | ||||
| @ -36,6 +36,7 @@ func TestGetHTTPClient(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestGetDomainExpiration(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 	if domainExpiration, err := GetDomainExpiration("example.com"); err != nil { | ||||
| 		t.Fatalf("expected error to be nil, but got: `%s`", err) | ||||
| 	} else if domainExpiration <= 0 { | ||||
| @ -63,6 +64,7 @@ func TestGetDomainExpiration(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestPing(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 	if success, rtt := Ping("127.0.0.1", &Config{Timeout: 500 * time.Millisecond}); !success { | ||||
| 		t.Error("expected true") | ||||
| 		if rtt == 0 { | ||||
| @ -121,6 +123,7 @@ func TestCanPerformStartTLS(t *testing.T) { | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			t.Parallel() | ||||
| 			connected, _, err := CanPerformStartTLS(tt.args.address, &Config{Insecure: tt.args.insecure, Timeout: 5 * time.Second}) | ||||
| 			if (err != nil) != tt.wantErr { | ||||
| 				t.Errorf("CanPerformStartTLS() err=%v, wantErr=%v", err, tt.wantErr) | ||||
| @ -171,6 +174,7 @@ func TestCanPerformTLS(t *testing.T) { | ||||
| 	} | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			t.Parallel() | ||||
| 			connected, _, err := CanPerformTLS(tt.args.address, &Config{Insecure: tt.args.insecure, Timeout: 5 * time.Second}) | ||||
| 			if (err != nil) != tt.wantErr { | ||||
| 				t.Errorf("CanPerformTLS() err=%v, wantErr=%v", err, tt.wantErr) | ||||
|  | ||||
							
								
								
									
										153
									
								
								config/config.go
									
									
									
									
									
								
							
							
						
						
									
										153
									
								
								config/config.go
									
									
									
									
									
								
							| @ -3,10 +3,13 @@ package config | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io/fs" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/TwiN/deepmerge" | ||||
| 	"github.com/TwiN/gatus/v5/alerting" | ||||
| 	"github.com/TwiN/gatus/v5/alerting/alert" | ||||
| 	"github.com/TwiN/gatus/v5/alerting/provider" | ||||
| @ -18,12 +21,12 @@ import ( | ||||
| 	"github.com/TwiN/gatus/v5/security" | ||||
| 	"github.com/TwiN/gatus/v5/storage" | ||||
| 	"github.com/TwiN/gatus/v5/util" | ||||
| 	"gopkg.in/yaml.v2" | ||||
| 	"gopkg.in/yaml.v3" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// DefaultConfigurationFilePath is the default path that will be used to search for the configuration file | ||||
| 	// if a custom path isn't configured through the GATUS_CONFIG_FILE environment variable | ||||
| 	// if a custom path isn't configured through the GATUS_CONFIG_PATH environment variable | ||||
| 	DefaultConfigurationFilePath = "config/config.yaml" | ||||
|  | ||||
| 	// DefaultFallbackConfigurationFilePath is the default fallback path that will be used to search for the | ||||
| @ -32,14 +35,17 @@ const ( | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// ErrNoEndpointInConfig is an error returned when a configuration file has no endpoints configured | ||||
| 	ErrNoEndpointInConfig = errors.New("configuration file should contain at least 1 endpoint") | ||||
| 	// ErrNoEndpointInConfig is an error returned when a configuration file or directory has no endpoints configured | ||||
| 	ErrNoEndpointInConfig = errors.New("configuration should contain at least 1 endpoint") | ||||
|  | ||||
| 	// ErrConfigFileNotFound is an error returned when the configuration file could not be found | ||||
| 	// ErrConfigFileNotFound is an error returned when a configuration file could not be found | ||||
| 	ErrConfigFileNotFound = errors.New("configuration file not found") | ||||
|  | ||||
| 	// ErrInvalidSecurityConfig is an error returned when the security configuration is invalid | ||||
| 	ErrInvalidSecurityConfig = errors.New("invalid security configuration") | ||||
|  | ||||
| 	// errEarlyReturn is returned to break out of a loop from a callback early | ||||
| 	errEarlyReturn = errors.New("early escape") | ||||
| ) | ||||
|  | ||||
| // Config is the main configuration structure | ||||
| @ -84,11 +90,12 @@ type Config struct { | ||||
| 	// WARNING: This is in ALPHA and may change or be completely removed in the future | ||||
| 	Remote *remote.Config `yaml:"remote,omitempty"` | ||||
|  | ||||
| 	filePath        string    // path to the file from which config was loaded from | ||||
| 	configPath      string    // path to the file or directory from which config was loaded | ||||
| 	lastFileModTime time.Time // last modification time | ||||
| } | ||||
|  | ||||
| func (config *Config) GetEndpointByKey(key string) *core.Endpoint { | ||||
| 	// TODO: Should probably add a mutex here to prevent concurrent access | ||||
| 	for i := 0; i < len(config.Endpoints); i++ { | ||||
| 		ep := config.Endpoints[i] | ||||
| 		if util.ConvertGroupAndEndpointNameToKey(ep.Group, ep.Name) == key { | ||||
| @ -98,63 +105,111 @@ func (config *Config) GetEndpointByKey(key string) *core.Endpoint { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // HasLoadedConfigurationFileBeenModified returns whether the file that the | ||||
| // HasLoadedConfigurationBeenModified returns whether one of the file that the | ||||
| // configuration has been loaded from has been modified since it was last read | ||||
| func (config Config) HasLoadedConfigurationFileBeenModified() bool { | ||||
| 	if fileInfo, err := os.Stat(config.filePath); err == nil { | ||||
| 		if !fileInfo.ModTime().IsZero() { | ||||
| 			return config.lastFileModTime.Unix() != fileInfo.ModTime().Unix() | ||||
| 		} | ||||
| func (config Config) HasLoadedConfigurationBeenModified() bool { | ||||
| 	lastMod := config.lastFileModTime.Unix() | ||||
| 	fileInfo, err := os.Stat(config.configPath) | ||||
| 	if err != nil { | ||||
| 		return false | ||||
| 	} | ||||
| 	return false | ||||
| 	if fileInfo.IsDir() { | ||||
| 		err = walkConfigDir(config.configPath, func(path string, d fs.DirEntry, err error) error { | ||||
| 			if info, err := d.Info(); err == nil && lastMod < info.ModTime().Unix() { | ||||
| 				return errEarlyReturn | ||||
| 			} | ||||
| 			return nil | ||||
| 		}) | ||||
| 		return err == errEarlyReturn | ||||
| 	} | ||||
| 	return !fileInfo.ModTime().IsZero() && config.lastFileModTime.Unix() < fileInfo.ModTime().Unix() | ||||
| } | ||||
|  | ||||
| // UpdateLastFileModTime refreshes Config.lastFileModTime | ||||
| func (config *Config) UpdateLastFileModTime() { | ||||
| 	if fileInfo, err := os.Stat(config.filePath); err == nil { | ||||
| 		if !fileInfo.ModTime().IsZero() { | ||||
| 			config.lastFileModTime = fileInfo.ModTime() | ||||
| 	config.lastFileModTime = time.Now() | ||||
| } | ||||
|  | ||||
| // LoadConfiguration loads the full configuration composed from the main configuration file | ||||
| // and all composed configuration files | ||||
| func LoadConfiguration(configPath string) (*Config, error) { | ||||
| 	var configBytes []byte | ||||
| 	var fileInfo os.FileInfo | ||||
| 	var usedConfigPath string | ||||
| 	// Figure out what config path we'll use (either configPath or the default config path) | ||||
| 	for _, configurationPath := range []string{configPath, DefaultConfigurationFilePath, DefaultFallbackConfigurationFilePath} { | ||||
| 		if len(configurationPath) == 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		var err error | ||||
| 		fileInfo, err = os.Stat(configurationPath) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		usedConfigPath = configPath | ||||
| 		break | ||||
| 	} | ||||
| 	if len(usedConfigPath) == 0 { | ||||
| 		return nil, ErrConfigFileNotFound | ||||
| 	} | ||||
| 	var config *Config | ||||
| 	if fileInfo.IsDir() { | ||||
| 		err := walkConfigDir(configPath, func(path string, d fs.DirEntry, err error) error { | ||||
| 			if err != nil { | ||||
| 				log.Printf("[config][LoadConfiguration] Error walking path=%s: %s", path, err) | ||||
| 				return err | ||||
| 			} | ||||
| 			log.Printf("[config][LoadConfiguration] Reading configuration from %s", path) | ||||
| 			data, err := os.ReadFile(path) | ||||
| 			if err != nil { | ||||
| 				log.Printf("[config][LoadConfiguration] Error reading configuration from %s: %s", path, err) | ||||
| 				return fmt.Errorf("error reading configuration from file %s: %w", path, err) | ||||
| 			} | ||||
| 			configBytes, err = deepmerge.YAML(configBytes, data) | ||||
| 			return err | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			return nil, fmt.Errorf("error reading configuration from directory %s: %w", usedConfigPath, err) | ||||
| 		} | ||||
| 	} else { | ||||
| 		log.Println("[config][UpdateLastFileModTime] Ran into error updating lastFileModTime:", err.Error()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Load loads a custom configuration file | ||||
| // Note that the misconfiguration of some fields may lead to panics. This is on purpose. | ||||
| func Load(configFile string) (*Config, error) { | ||||
| 	log.Printf("[config][Load] Reading configuration from configFile=%s", configFile) | ||||
| 	cfg, err := readConfigurationFile(configFile) | ||||
| 	if err != nil { | ||||
| 		if os.IsNotExist(err) { | ||||
| 			return nil, ErrConfigFileNotFound | ||||
| 		log.Printf("[config][LoadConfiguration] Reading configuration from configFile=%s", configPath) | ||||
| 		if data, err := os.ReadFile(usedConfigPath); err != nil { | ||||
| 			return nil, err | ||||
| 		} else { | ||||
| 			configBytes = data | ||||
| 		} | ||||
| 	} | ||||
| 	if len(configBytes) == 0 { | ||||
| 		return nil, ErrConfigFileNotFound | ||||
| 	} | ||||
| 	config, err := parseAndValidateConfigBytes(configBytes) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	cfg.filePath = configFile | ||||
| 	cfg.UpdateLastFileModTime() | ||||
| 	return cfg, nil | ||||
| 	config.configPath = usedConfigPath | ||||
| 	config.UpdateLastFileModTime() | ||||
| 	return config, err | ||||
| } | ||||
|  | ||||
| // LoadDefaultConfiguration loads the default configuration file | ||||
| func LoadDefaultConfiguration() (*Config, error) { | ||||
| 	cfg, err := Load(DefaultConfigurationFilePath) | ||||
| 	if err != nil { | ||||
| 		if err == ErrConfigFileNotFound { | ||||
| 			return Load(DefaultFallbackConfigurationFilePath) | ||||
| // walkConfigDir is a wrapper for filepath.WalkDir that strips directories and non-config files | ||||
| func walkConfigDir(path string, fn fs.WalkDirFunc) error { | ||||
| 	if len(path) == 0 { | ||||
| 		// If the user didn't provide a directory, we'll just use the default config file, so we can return nil now. | ||||
| 		return nil | ||||
| 	} | ||||
| 	return filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error { | ||||
| 		if err != nil { | ||||
| 			return nil | ||||
| 		} | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return cfg, nil | ||||
| } | ||||
|  | ||||
| func readConfigurationFile(fileName string) (config *Config, err error) { | ||||
| 	var bytes []byte | ||||
| 	if bytes, err = os.ReadFile(fileName); err == nil { | ||||
| 		// file exists, so we'll parse it and return it | ||||
| 		return parseAndValidateConfigBytes(bytes) | ||||
| 	} | ||||
| 	return | ||||
| 		if d == nil || d.IsDir() { | ||||
| 			return nil | ||||
| 		} | ||||
| 		ext := filepath.Ext(path) | ||||
| 		if ext != ".yml" && ext != ".yaml" { | ||||
| 			return nil | ||||
| 		} | ||||
| 		return fn(path, d, err) | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| // parseAndValidateConfigBytes parses a Gatus configuration file into a Config struct and validates its parameters | ||||
|  | ||||
| @ -1,7 +1,10 @@ | ||||
| package config | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| @ -11,6 +14,7 @@ import ( | ||||
| 	"github.com/TwiN/gatus/v5/alerting/provider/custom" | ||||
| 	"github.com/TwiN/gatus/v5/alerting/provider/discord" | ||||
| 	"github.com/TwiN/gatus/v5/alerting/provider/email" | ||||
| 	"github.com/TwiN/gatus/v5/alerting/provider/github" | ||||
| 	"github.com/TwiN/gatus/v5/alerting/provider/googlechat" | ||||
| 	"github.com/TwiN/gatus/v5/alerting/provider/matrix" | ||||
| 	"github.com/TwiN/gatus/v5/alerting/provider/mattermost" | ||||
| @ -26,20 +30,270 @@ import ( | ||||
| 	"github.com/TwiN/gatus/v5/config/web" | ||||
| 	"github.com/TwiN/gatus/v5/core" | ||||
| 	"github.com/TwiN/gatus/v5/storage" | ||||
| 	"gopkg.in/yaml.v3" | ||||
| ) | ||||
|  | ||||
| func TestLoadFileThatDoesNotExist(t *testing.T) { | ||||
| 	_, err := Load("file-that-does-not-exist.yaml") | ||||
| 	if err == nil { | ||||
| 		t.Error("Should've returned an error, because the file specified doesn't exist") | ||||
| func TestLoadConfiguration(t *testing.T) { | ||||
| 	dir := t.TempDir() | ||||
| 	scenarios := []struct { | ||||
| 		name           string | ||||
| 		configPath     string            // value to pass as the configPath parameter in LoadConfiguration | ||||
| 		pathAndFiles   map[string]string // files to create in dir | ||||
| 		expectedConfig *Config | ||||
| 		expectedError  error | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:       "empty-config-file", | ||||
| 			configPath: filepath.Join(dir, "config.yaml"), | ||||
| 			pathAndFiles: map[string]string{ | ||||
| 				"config.yaml": "", | ||||
| 			}, | ||||
| 			expectedError: ErrConfigFileNotFound, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:          "config-file-that-does-not-exist", | ||||
| 			configPath:    filepath.Join(dir, "config.yaml"), | ||||
| 			expectedError: ErrConfigFileNotFound, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:       "config-file-with-endpoint-that-has-no-url", | ||||
| 			configPath: filepath.Join(dir, "config.yaml"), | ||||
| 			pathAndFiles: map[string]string{ | ||||
| 				"config.yaml": ` | ||||
| endpoints: | ||||
|   - name: website`, | ||||
| 			}, | ||||
| 			expectedError: core.ErrEndpointWithNoURL, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:       "config-file-with-endpoint-that-has-no-conditions", | ||||
| 			configPath: filepath.Join(dir, "config.yaml"), | ||||
| 			pathAndFiles: map[string]string{ | ||||
| 				"config.yaml": ` | ||||
| endpoints: | ||||
|   - name: website | ||||
|     url: https://twin.sh/health`, | ||||
| 			}, | ||||
| 			expectedError: core.ErrEndpointWithNoCondition, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:       "config-file", | ||||
| 			configPath: filepath.Join(dir, "config.yaml"), | ||||
| 			pathAndFiles: map[string]string{ | ||||
| 				"config.yaml": ` | ||||
| endpoints: | ||||
|   - name: website | ||||
|     url: https://twin.sh/health | ||||
|     conditions: | ||||
|       - "[STATUS] == 200"`, | ||||
| 			}, | ||||
| 			expectedConfig: &Config{ | ||||
| 				Endpoints: []*core.Endpoint{ | ||||
| 					{ | ||||
| 						Name:       "website", | ||||
| 						URL:        "https://twin.sh/health", | ||||
| 						Conditions: []core.Condition{"[STATUS] == 200"}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:          "empty-dir", | ||||
| 			configPath:    dir, | ||||
| 			pathAndFiles:  map[string]string{}, | ||||
| 			expectedError: ErrConfigFileNotFound, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:       "dir-with-empty-config-file", | ||||
| 			configPath: dir, | ||||
| 			pathAndFiles: map[string]string{ | ||||
| 				"config.yaml": "", | ||||
| 			}, | ||||
| 			expectedError: ErrNoEndpointInConfig, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:       "dir-with-two-config-files", | ||||
| 			configPath: dir, | ||||
| 			pathAndFiles: map[string]string{ | ||||
| 				"config.yaml": `endpoints:  | ||||
|   - name: one | ||||
|     url: https://example.com | ||||
|     conditions: | ||||
|       - "[CONNECTED] == true" | ||||
|       - "[STATUS] == 200" | ||||
|  | ||||
|   - name: two | ||||
|     url: https://example.org | ||||
|     conditions: | ||||
|       - "len([BODY]) > 0"`, | ||||
| 				"config.yml": `endpoints:  | ||||
|   - name: three | ||||
|     url: https://twin.sh/health | ||||
|     conditions: | ||||
|       - "[STATUS] == 200" | ||||
|       - "[BODY].status == UP"`, | ||||
| 			}, | ||||
| 			expectedConfig: &Config{ | ||||
| 				Endpoints: []*core.Endpoint{ | ||||
| 					{ | ||||
| 						Name:       "one", | ||||
| 						URL:        "https://example.com", | ||||
| 						Conditions: []core.Condition{"[CONNECTED] == true", "[STATUS] == 200"}, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:       "two", | ||||
| 						URL:        "https://example.org", | ||||
| 						Conditions: []core.Condition{"len([BODY]) > 0"}, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:       "three", | ||||
| 						URL:        "https://twin.sh/health", | ||||
| 						Conditions: []core.Condition{"[STATUS] == 200", "[BODY].status == UP"}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:       "dir-with-2-config-files-deep-merge-with-map-slice-and-primitive", | ||||
| 			configPath: dir, | ||||
| 			pathAndFiles: map[string]string{ | ||||
| 				"a.yaml": ` | ||||
| metrics: true | ||||
|  | ||||
| alerting: | ||||
|   slack: | ||||
|     webhook-url: https://hooks.slack.com/services/xxx/yyy/zzz | ||||
|  | ||||
| endpoints: | ||||
|   - name: example | ||||
|     url: https://example.org | ||||
|     interval: 5s | ||||
|     conditions: | ||||
|       - "[STATUS] == 200"`, | ||||
| 				"b.yaml": ` | ||||
| debug: true | ||||
|  | ||||
| alerting: | ||||
|   discord: | ||||
|     webhook-url: https://discord.com/api/webhooks/xxx/yyy | ||||
|  | ||||
| endpoints: | ||||
|   - name: frontend | ||||
|     url: https://example.com | ||||
|     conditions: | ||||
|       - "[STATUS] == 200"`, | ||||
| 			}, | ||||
| 			expectedConfig: &Config{ | ||||
| 				Debug:   true, | ||||
| 				Metrics: true, | ||||
| 				Alerting: &alerting.Config{ | ||||
| 					Discord: &discord.AlertProvider{WebhookURL: "https://discord.com/api/webhooks/xxx/yyy"}, | ||||
| 					Slack:   &slack.AlertProvider{WebhookURL: "https://hooks.slack.com/services/xxx/yyy/zzz"}, | ||||
| 				}, | ||||
| 				Endpoints: []*core.Endpoint{ | ||||
| 					{ | ||||
| 						Name:       "example", | ||||
| 						URL:        "https://example.org", | ||||
| 						Interval:   5 * time.Second, | ||||
| 						Conditions: []core.Condition{"[STATUS] == 200"}, | ||||
| 					}, | ||||
| 					{ | ||||
| 						Name:       "frontend", | ||||
| 						URL:        "https://example.com", | ||||
| 						Conditions: []core.Condition{"[STATUS] == 200"}, | ||||
| 					}, | ||||
| 				}, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	for _, scenario := range scenarios { | ||||
| 		t.Run(scenario.name, func(t *testing.T) { | ||||
| 			for path, content := range scenario.pathAndFiles { | ||||
| 				if err := os.WriteFile(filepath.Join(dir, path), []byte(content), 0644); err != nil { | ||||
| 					t.Fatalf("[%s] failed to write file: %v", scenario.name, err) | ||||
| 				} | ||||
| 			} | ||||
| 			defer func(pathAndFiles map[string]string) { | ||||
| 				for path := range pathAndFiles { | ||||
| 					_ = os.Remove(filepath.Join(dir, path)) | ||||
| 				} | ||||
| 			}(scenario.pathAndFiles) | ||||
| 			config, err := LoadConfiguration(scenario.configPath) | ||||
| 			if !errors.Is(err, scenario.expectedError) { | ||||
| 				t.Errorf("[%s] expected error %v, got %v", scenario.name, scenario.expectedError, err) | ||||
| 				return | ||||
| 			} else if err != nil && errors.Is(err, scenario.expectedError) { | ||||
| 				return | ||||
| 			} | ||||
| 			// parse the expected output so that expectations are closer to reality (under the right circumstances, even I can be poetic) | ||||
| 			expectedConfigAsYAML, _ := yaml.Marshal(scenario.expectedConfig) | ||||
| 			expectedConfigAfterBeingParsedAndValidated, err := parseAndValidateConfigBytes(expectedConfigAsYAML) | ||||
| 			if err != nil { | ||||
| 				t.Fatalf("[%s] failed to parse expected config: %v", scenario.name, err) | ||||
| 			} | ||||
| 			// Marshal em' before comparing em' so that we don't have to deal with formatting and ordering | ||||
| 			actualConfigAsYAML, err := yaml.Marshal(config) | ||||
| 			if err != nil { | ||||
| 				t.Fatalf("[%s] failed to marshal actual config: %v", scenario.name, err) | ||||
| 			} | ||||
| 			expectedConfigAfterBeingParsedAndValidatedAsYAML, _ := yaml.Marshal(expectedConfigAfterBeingParsedAndValidated) | ||||
| 			if string(actualConfigAsYAML) != string(expectedConfigAfterBeingParsedAndValidatedAsYAML) { | ||||
| 				t.Errorf("[%s] expected config %s, got %s", scenario.name, string(expectedConfigAfterBeingParsedAndValidatedAsYAML), string(actualConfigAsYAML)) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestLoadDefaultConfigurationFile(t *testing.T) { | ||||
| 	_, err := LoadDefaultConfiguration() | ||||
| 	if err == nil { | ||||
| 		t.Error("Should've returned an error, because there's no configuration files at the default path nor the default fallback path") | ||||
| 	} | ||||
| func TestConfig_HasLoadedConfigurationBeenModified(t *testing.T) { | ||||
| 	t.Parallel() | ||||
| 	dir := t.TempDir() | ||||
|  | ||||
| 	configFilePath := filepath.Join(dir, "config.yaml") | ||||
| 	_ = os.WriteFile(configFilePath, []byte(`endpoints: | ||||
|   - name: website | ||||
|     url: https://twin.sh/health | ||||
|     conditions: | ||||
|       - "[STATUS] == 200" | ||||
| `), 0644) | ||||
|  | ||||
| 	t.Run("config-file-as-config-path", func(t *testing.T) { | ||||
| 		config, err := LoadConfiguration(configFilePath) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("failed to load configuration: %v", err) | ||||
| 		} | ||||
| 		if config.HasLoadedConfigurationBeenModified() { | ||||
| 			t.Errorf("expected config.HasLoadedConfigurationBeenModified() to return false because nothing has happened since it was created") | ||||
| 		} | ||||
| 		time.Sleep(time.Second) // Because the file mod time only has second precision, we have to wait for a second | ||||
| 		// Update the config file | ||||
| 		if err = os.WriteFile(filepath.Join(dir, "config.yaml"), []byte(`endpoints: | ||||
|   - name: website | ||||
|     url: https://twin.sh/health | ||||
|     conditions: | ||||
|       - "[STATUS] == 200"`), 0644); err != nil { | ||||
| 			t.Fatalf("failed to overwrite config file: %v", err) | ||||
| 		} | ||||
| 		if !config.HasLoadedConfigurationBeenModified() { | ||||
| 			t.Errorf("expected config.HasLoadedConfigurationBeenModified() to return true because a new file has been added in the directory") | ||||
| 		} | ||||
| 	}) | ||||
| 	t.Run("config-directory-as-config-path", func(t *testing.T) { | ||||
| 		config, err := LoadConfiguration(dir) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("failed to load configuration: %v", err) | ||||
| 		} | ||||
| 		if config.HasLoadedConfigurationBeenModified() { | ||||
| 			t.Errorf("expected config.HasLoadedConfigurationBeenModified() to return false because nothing has happened since it was created") | ||||
| 		} | ||||
| 		time.Sleep(time.Second) // Because the file mod time only has second precision, we have to wait for a second | ||||
| 		// Update the config file | ||||
| 		if err = os.WriteFile(filepath.Join(dir, "metrics.yaml"), []byte(`metrics: true`), 0644); err != nil { | ||||
| 			t.Fatalf("failed to overwrite config file: %v", err) | ||||
| 		} | ||||
| 		if !config.HasLoadedConfigurationBeenModified() { | ||||
| 			t.Errorf("expected config.HasLoadedConfigurationBeenModified() to return true because a new file has been added in the directory") | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func TestParseAndValidateConfigBytes(t *testing.T) { | ||||
| @ -1219,6 +1473,7 @@ func TestGetAlertingProviderByAlertType(t *testing.T) { | ||||
| 		Custom:      &custom.AlertProvider{}, | ||||
| 		Discord:     &discord.AlertProvider{}, | ||||
| 		Email:       &email.AlertProvider{}, | ||||
| 		GitHub:      &github.AlertProvider{}, | ||||
| 		GoogleChat:  &googlechat.AlertProvider{}, | ||||
| 		Matrix:      &matrix.AlertProvider{}, | ||||
| 		Mattermost:  &mattermost.AlertProvider{}, | ||||
| @ -1238,6 +1493,7 @@ func TestGetAlertingProviderByAlertType(t *testing.T) { | ||||
| 		{alertType: alert.TypeCustom, expected: alertingConfig.Custom}, | ||||
| 		{alertType: alert.TypeDiscord, expected: alertingConfig.Discord}, | ||||
| 		{alertType: alert.TypeEmail, expected: alertingConfig.Email}, | ||||
| 		{alertType: alert.TypeGitHub, expected: alertingConfig.GitHub}, | ||||
| 		{alertType: alert.TypeGoogleChat, expected: alertingConfig.GoogleChat}, | ||||
| 		{alertType: alert.TypeMatrix, expected: alertingConfig.Matrix}, | ||||
| 		{alertType: alert.TypeMattermost, expected: alertingConfig.Mattermost}, | ||||
|  | ||||
							
								
								
									
										12
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								go.mod
									
									
									
									
									
								
							| @ -3,14 +3,17 @@ module github.com/TwiN/gatus/v5 | ||||
| go 1.19 | ||||
|  | ||||
| require ( | ||||
| 	github.com/TwiN/deepmerge v0.1.0 | ||||
| 	github.com/TwiN/g8 v1.4.0 | ||||
| 	github.com/TwiN/gocache/v2 v2.2.0 | ||||
| 	github.com/TwiN/health v1.6.0 | ||||
| 	github.com/TwiN/whois v1.1.0 | ||||
| 	github.com/coreos/go-oidc/v3 v3.4.0 | ||||
| 	github.com/go-ping/ping v0.0.0-20210911151512-381826476871 | ||||
| 	github.com/google/go-github/v48 v48.2.0 | ||||
| 	github.com/google/uuid v1.3.0 | ||||
| 	github.com/gorilla/mux v1.8.0 | ||||
| 	github.com/ishidawataru/sctp v0.0.0-20210707070123-9a39160e9062 | ||||
| 	github.com/lib/pq v1.10.7 | ||||
| 	github.com/miekg/dns v1.1.50 | ||||
| 	github.com/prometheus/client_golang v1.14.0 | ||||
| @ -18,22 +21,17 @@ require ( | ||||
| 	golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 | ||||
| 	golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 | ||||
| 	gopkg.in/mail.v2 v2.3.1 | ||||
| 	gopkg.in/yaml.v2 v2.4.0 | ||||
| 	gopkg.in/yaml.v3 v3.0.1 | ||||
| 	modernc.org/sqlite v1.19.5 | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| 	github.com/google/go-github/v48 v48.2.0 | ||||
| 	github.com/google/go-querystring v1.1.0 // indirect | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| 	github.com/beorn7/perks v1.0.1 // indirect | ||||
| 	github.com/cespare/xxhash/v2 v2.1.2 // indirect | ||||
| 	github.com/davecgh/go-spew v1.1.1 // indirect | ||||
| 	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect | ||||
| 	github.com/golang/protobuf v1.5.2 // indirect | ||||
| 	github.com/ishidawataru/sctp v0.0.0-20210707070123-9a39160e9062 | ||||
| 	github.com/google/go-querystring v1.1.0 // indirect | ||||
| 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect | ||||
| 	github.com/mattn/go-isatty v0.0.16 // indirect | ||||
| 	github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect | ||||
|  | ||||
							
								
								
									
										6
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										6
									
								
								go.sum
									
									
									
									
									
								
							| @ -57,6 +57,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | ||||
| github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= | ||||
| github.com/TwiN/deepmerge v0.1.0 h1:xVFKkF0WCcIoJANSVf102NZIorVIAldT/G+Jv/uYn8A= | ||||
| github.com/TwiN/deepmerge v0.1.0/go.mod h1:cR9OWsvI13y+FxrbnPLuF6BX2tbYeOjkiI6JWjEGqAE= | ||||
| github.com/TwiN/g8 v1.4.0 h1:RUk5xTtxKCdMo0GGSbBVyjtAAfi2nqVbA9E0C4u5Cxo= | ||||
| github.com/TwiN/g8 v1.4.0/go.mod h1:ECyGJsoIb99klUfvVQoS1StgRLte9yvvPigGrHdy284= | ||||
| github.com/TwiN/gocache/v2 v2.2.0 h1:M3B36KyH24BntxLrLaUb2kgTdq8DzCnfod0IekLG57w= | ||||
| @ -790,10 +792,10 @@ gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | ||||
| gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
|  | ||||
							
								
								
									
										17
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								main.go
									
									
									
									
									
								
							| @ -52,14 +52,13 @@ func save() { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func loadConfiguration() (cfg *config.Config, err error) { | ||||
| 	customConfigFile := os.Getenv("GATUS_CONFIG_FILE") | ||||
| 	if len(customConfigFile) > 0 { | ||||
| 		cfg, err = config.Load(customConfigFile) | ||||
| 	} else { | ||||
| 		cfg, err = config.LoadDefaultConfiguration() | ||||
| func loadConfiguration() (*config.Config, error) { | ||||
| 	configPath := os.Getenv("GATUS_CONFIG_PATH") | ||||
| 	// Backwards compatibility | ||||
| 	if len(configPath) == 0 { | ||||
| 		configPath = os.Getenv("GATUS_CONFIG_FILE") | ||||
| 	} | ||||
| 	return | ||||
| 	return config.LoadConfiguration(configPath) | ||||
| } | ||||
|  | ||||
| // initializeStorage initializes the storage provider | ||||
| @ -79,14 +78,14 @@ func initializeStorage(cfg *config.Config) { | ||||
| 	} | ||||
| 	numberOfEndpointStatusesDeleted := store.Get().DeleteAllEndpointStatusesNotInKeys(keys) | ||||
| 	if numberOfEndpointStatusesDeleted > 0 { | ||||
| 		log.Printf("[config][validateStorageConfig] Deleted %d endpoint statuses because their matching endpoints no longer existed", numberOfEndpointStatusesDeleted) | ||||
| 		log.Printf("[main][initializeStorage] Deleted %d endpoint statuses because their matching endpoints no longer existed", numberOfEndpointStatusesDeleted) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func listenToConfigurationFileChanges(cfg *config.Config) { | ||||
| 	for { | ||||
| 		time.Sleep(30 * time.Second) | ||||
| 		if cfg.HasLoadedConfigurationFileBeenModified() { | ||||
| 		if cfg.HasLoadedConfigurationBeenModified() { | ||||
| 			log.Println("[main][listenToConfigurationFileChanges] Configuration file has been modified") | ||||
| 			stop() | ||||
| 			time.Sleep(time.Second) // Wait a bit to make sure everything is done. | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| package store | ||||
|  | ||||
| import ( | ||||
| 	"path/filepath" | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| @ -566,6 +567,7 @@ func TestGet(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestInitialize(t *testing.T) { | ||||
| 	dir := t.TempDir() | ||||
| 	type Scenario struct { | ||||
| 		Name        string | ||||
| 		Cfg         *storage.Config | ||||
| @ -594,7 +596,7 @@ func TestInitialize(t *testing.T) { | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:        "sqlite-with-path", | ||||
| 			Cfg:         &storage.Config{Type: storage.TypeSQLite, Path: t.TempDir() + "/TestInitialize_sqlite-with-path.db"}, | ||||
| 			Cfg:         &storage.Config{Type: storage.TypeSQLite, Path: filepath.Join(dir, "TestInitialize_sqlite-with-path.db")}, | ||||
| 			ExpectedErr: nil, | ||||
| 		}, | ||||
| 	} | ||||
| @ -629,7 +631,7 @@ func TestInitialize(t *testing.T) { | ||||
| } | ||||
|  | ||||
| func TestAutoSave(t *testing.T) { | ||||
| 	file := t.TempDir() + "/TestAutoSave.db" | ||||
| 	file := filepath.Join(t.TempDir(), "/TestAutoSave.db") | ||||
| 	if err := Initialize(&storage.Config{Path: file}); err != nil { | ||||
| 		t.Fatal("shouldn't have returned an error") | ||||
| 	} | ||||
|  | ||||
							
								
								
									
										1
									
								
								vendor/github.com/TwiN/deepmerge/.gitattributes
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								vendor/github.com/TwiN/deepmerge/.gitattributes
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | ||||
| * text=auto eol=lf | ||||
							
								
								
									
										13
									
								
								vendor/github.com/TwiN/deepmerge/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/TwiN/deepmerge/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| # IDE | ||||
| *.iml | ||||
| .idea | ||||
| .vscode | ||||
|  | ||||
| # OS | ||||
| .DS_Store | ||||
|  | ||||
| # JS | ||||
| node_modules | ||||
|  | ||||
| # Go | ||||
| /vendor | ||||
							
								
								
									
										21
									
								
								vendor/github.com/TwiN/deepmerge/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/TwiN/deepmerge/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | ||||
| MIT License | ||||
|  | ||||
| Copyright (c) 2023 TwiN | ||||
|  | ||||
| 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. | ||||
							
								
								
									
										59
									
								
								vendor/github.com/TwiN/deepmerge/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								vendor/github.com/TwiN/deepmerge/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | ||||
| # deepmerge | ||||
|  | ||||
|  | ||||
| Go library for deep merging YAML files. | ||||
|  | ||||
|  | ||||
| ## Usage | ||||
| ```go | ||||
| package main | ||||
|  | ||||
| import ( | ||||
| 	"github.com/TwiN/deepmerge" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| 	dst := ` | ||||
| debug: true | ||||
| client: | ||||
|   insecure: true | ||||
| users: | ||||
|   - id: 1 | ||||
|     firstName: John | ||||
|     lastName: Doe | ||||
|   - id: 2 | ||||
|     firstName: Jane | ||||
|     lastName: Doe` | ||||
| 	src := ` | ||||
| client: | ||||
|   timeout: 5s | ||||
| users: | ||||
|   - id: 3 | ||||
|     firstName: Bob | ||||
|     lastName: Smith` | ||||
|  | ||||
| 	output, err := deepmerge.YAML([]byte(dst), []byte(src)) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	println(string(output)) | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Output: | ||||
| ```yaml | ||||
| client: | ||||
|     insecure: true | ||||
|     timeout: 5s | ||||
| debug: true | ||||
| users: | ||||
|     - firstName: John | ||||
|       id: 1 | ||||
|       lastName: Doe | ||||
|     - firstName: Jane | ||||
|       id: 2 | ||||
|       lastName: Doe | ||||
|     - firstName: Bob | ||||
|       id: 3 | ||||
|       lastName: Smith | ||||
| ``` | ||||
							
								
								
									
										83
									
								
								vendor/github.com/TwiN/deepmerge/yaml.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								vendor/github.com/TwiN/deepmerge/yaml.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,83 @@ | ||||
| package deepmerge | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
|  | ||||
| 	"gopkg.in/yaml.v3" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	ErrDeepMergeDuplicatePrimitiveKey = errors.New("error deep merging YAML files due to duplicate primitive key: only maps and slices/arrays can be merged, which means you cannot have define the same key twice for parameters that are not maps or slices/arrays") | ||||
| ) | ||||
|  | ||||
| type Config struct { | ||||
| 	// PreventDuplicateKeysWithPrimitiveValue causes YAML to return an error if dst and src define the same key if | ||||
| 	// said key has a value with a primitive type | ||||
| 	// This does not apply to slices or maps. Defaults to true | ||||
| 	PreventDuplicateKeysWithPrimitiveValue bool | ||||
| } | ||||
|  | ||||
| // YAML merges the contents of src into dst | ||||
| func YAML(dst, src []byte, optionalConfig ...Config) ([]byte, error) { | ||||
| 	var cfg Config | ||||
| 	if len(optionalConfig) > 0 { | ||||
| 		cfg = optionalConfig[0] | ||||
| 	} else { | ||||
| 		cfg = Config{PreventDuplicateKeysWithPrimitiveValue: true} | ||||
| 	} | ||||
| 	var dstMap, srcMap map[string]interface{} | ||||
| 	err := yaml.Unmarshal(dst, &dstMap) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	err = yaml.Unmarshal(src, &srcMap) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if dstMap == nil { | ||||
| 		dstMap = make(map[string]interface{}) | ||||
| 	} | ||||
| 	if err = deepMerge(dstMap, srcMap, cfg); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return yaml.Marshal(dstMap) | ||||
| } | ||||
|  | ||||
| func deepMerge(dst, src map[string]interface{}, config Config) error { | ||||
| 	for srcKey, srcValue := range src { | ||||
| 		if srcValueAsMap, ok := srcValue.(map[string]interface{}); ok { // handle maps | ||||
| 			if dstValue, ok := dst[srcKey]; ok { | ||||
| 				if dstValueAsMap, ok := dstValue.(map[string]interface{}); ok { | ||||
| 					err := deepMerge(dstValueAsMap, srcValueAsMap, config) | ||||
| 					if err != nil { | ||||
| 						return err | ||||
| 					} | ||||
| 					continue | ||||
| 				} | ||||
| 			} else { | ||||
| 				dst[srcKey] = make(map[string]interface{}) | ||||
| 			} | ||||
| 			err := deepMerge(dst[srcKey].(map[string]interface{}), srcValueAsMap, config) | ||||
| 			if err != nil { | ||||
| 				return err | ||||
| 			} | ||||
| 		} else if srcValueAsSlice, ok := srcValue.([]interface{}); ok { // handle slices | ||||
| 			if dstValue, ok := dst[srcKey]; ok { | ||||
| 				if dstValueAsSlice, ok := dstValue.([]interface{}); ok { | ||||
| 					// If both src and dst are slices, we'll copy the elements from that src slice over to the dst slice | ||||
| 					dst[srcKey] = append(dstValueAsSlice, srcValueAsSlice...) | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
| 			dst[srcKey] = srcValueAsSlice | ||||
| 		} else { // handle primitives | ||||
| 			if config.PreventDuplicateKeysWithPrimitiveValue { | ||||
| 				if _, ok := dst[srcKey]; ok { | ||||
| 					return ErrDeepMergeDuplicatePrimitiveKey | ||||
| 				} | ||||
| 			} | ||||
| 			dst[srcKey] = srcValue | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										17
									
								
								vendor/gopkg.in/yaml.v2/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								vendor/gopkg.in/yaml.v2/.travis.yml
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,17 +0,0 @@ | ||||
| language: go | ||||
|  | ||||
| go: | ||||
|     - "1.4.x" | ||||
|     - "1.5.x" | ||||
|     - "1.6.x" | ||||
|     - "1.7.x" | ||||
|     - "1.8.x" | ||||
|     - "1.9.x" | ||||
|     - "1.10.x" | ||||
|     - "1.11.x" | ||||
|     - "1.12.x" | ||||
|     - "1.13.x" | ||||
|     - "1.14.x" | ||||
|     - "tip" | ||||
|  | ||||
| go_import_path: gopkg.in/yaml.v2 | ||||
							
								
								
									
										201
									
								
								vendor/gopkg.in/yaml.v2/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										201
									
								
								vendor/gopkg.in/yaml.v2/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,201 +0,0 @@ | ||||
|                                  Apache License | ||||
|                            Version 2.0, January 2004 | ||||
|                         http://www.apache.org/licenses/ | ||||
|  | ||||
|    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||||
|  | ||||
|    1. Definitions. | ||||
|  | ||||
|       "License" shall mean the terms and conditions for use, reproduction, | ||||
|       and distribution as defined by Sections 1 through 9 of this document. | ||||
|  | ||||
|       "Licensor" shall mean the copyright owner or entity authorized by | ||||
|       the copyright owner that is granting the License. | ||||
|  | ||||
|       "Legal Entity" shall mean the union of the acting entity and all | ||||
|       other entities that control, are controlled by, or are under common | ||||
|       control with that entity. For the purposes of this definition, | ||||
|       "control" means (i) the power, direct or indirect, to cause the | ||||
|       direction or management of such entity, whether by contract or | ||||
|       otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||||
|       outstanding shares, or (iii) beneficial ownership of such entity. | ||||
|  | ||||
|       "You" (or "Your") shall mean an individual or Legal Entity | ||||
|       exercising permissions granted by this License. | ||||
|  | ||||
|       "Source" form shall mean the preferred form for making modifications, | ||||
|       including but not limited to software source code, documentation | ||||
|       source, and configuration files. | ||||
|  | ||||
|       "Object" form shall mean any form resulting from mechanical | ||||
|       transformation or translation of a Source form, including but | ||||
|       not limited to compiled object code, generated documentation, | ||||
|       and conversions to other media types. | ||||
|  | ||||
|       "Work" shall mean the work of authorship, whether in Source or | ||||
|       Object form, made available under the License, as indicated by a | ||||
|       copyright notice that is included in or attached to the work | ||||
|       (an example is provided in the Appendix below). | ||||
|  | ||||
|       "Derivative Works" shall mean any work, whether in Source or Object | ||||
|       form, that is based on (or derived from) the Work and for which the | ||||
|       editorial revisions, annotations, elaborations, or other modifications | ||||
|       represent, as a whole, an original work of authorship. For the purposes | ||||
|       of this License, Derivative Works shall not include works that remain | ||||
|       separable from, or merely link (or bind by name) to the interfaces of, | ||||
|       the Work and Derivative Works thereof. | ||||
|  | ||||
|       "Contribution" shall mean any work of authorship, including | ||||
|       the original version of the Work and any modifications or additions | ||||
|       to that Work or Derivative Works thereof, that is intentionally | ||||
|       submitted to Licensor for inclusion in the Work by the copyright owner | ||||
|       or by an individual or Legal Entity authorized to submit on behalf of | ||||
|       the copyright owner. For the purposes of this definition, "submitted" | ||||
|       means any form of electronic, verbal, or written communication sent | ||||
|       to the Licensor or its representatives, including but not limited to | ||||
|       communication on electronic mailing lists, source code control systems, | ||||
|       and issue tracking systems that are managed by, or on behalf of, the | ||||
|       Licensor for the purpose of discussing and improving the Work, but | ||||
|       excluding communication that is conspicuously marked or otherwise | ||||
|       designated in writing by the copyright owner as "Not a Contribution." | ||||
|  | ||||
|       "Contributor" shall mean Licensor and any individual or Legal Entity | ||||
|       on behalf of whom a Contribution has been received by Licensor and | ||||
|       subsequently incorporated within the Work. | ||||
|  | ||||
|    2. Grant of Copyright License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       copyright license to reproduce, prepare Derivative Works of, | ||||
|       publicly display, publicly perform, sublicense, and distribute the | ||||
|       Work and such Derivative Works in Source or Object form. | ||||
|  | ||||
|    3. Grant of Patent License. Subject to the terms and conditions of | ||||
|       this License, each Contributor hereby grants to You a perpetual, | ||||
|       worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||||
|       (except as stated in this section) patent license to make, have made, | ||||
|       use, offer to sell, sell, import, and otherwise transfer the Work, | ||||
|       where such license applies only to those patent claims licensable | ||||
|       by such Contributor that are necessarily infringed by their | ||||
|       Contribution(s) alone or by combination of their Contribution(s) | ||||
|       with the Work to which such Contribution(s) was submitted. If You | ||||
|       institute patent litigation against any entity (including a | ||||
|       cross-claim or counterclaim in a lawsuit) alleging that the Work | ||||
|       or a Contribution incorporated within the Work constitutes direct | ||||
|       or contributory patent infringement, then any patent licenses | ||||
|       granted to You under this License for that Work shall terminate | ||||
|       as of the date such litigation is filed. | ||||
|  | ||||
|    4. Redistribution. You may reproduce and distribute copies of the | ||||
|       Work or Derivative Works thereof in any medium, with or without | ||||
|       modifications, and in Source or Object form, provided that You | ||||
|       meet the following conditions: | ||||
|  | ||||
|       (a) You must give any other recipients of the Work or | ||||
|           Derivative Works a copy of this License; and | ||||
|  | ||||
|       (b) You must cause any modified files to carry prominent notices | ||||
|           stating that You changed the files; and | ||||
|  | ||||
|       (c) You must retain, in the Source form of any Derivative Works | ||||
|           that You distribute, all copyright, patent, trademark, and | ||||
|           attribution notices from the Source form of the Work, | ||||
|           excluding those notices that do not pertain to any part of | ||||
|           the Derivative Works; and | ||||
|  | ||||
|       (d) If the Work includes a "NOTICE" text file as part of its | ||||
|           distribution, then any Derivative Works that You distribute must | ||||
|           include a readable copy of the attribution notices contained | ||||
|           within such NOTICE file, excluding those notices that do not | ||||
|           pertain to any part of the Derivative Works, in at least one | ||||
|           of the following places: within a NOTICE text file distributed | ||||
|           as part of the Derivative Works; within the Source form or | ||||
|           documentation, if provided along with the Derivative Works; or, | ||||
|           within a display generated by the Derivative Works, if and | ||||
|           wherever such third-party notices normally appear. The contents | ||||
|           of the NOTICE file are for informational purposes only and | ||||
|           do not modify the License. You may add Your own attribution | ||||
|           notices within Derivative Works that You distribute, alongside | ||||
|           or as an addendum to the NOTICE text from the Work, provided | ||||
|           that such additional attribution notices cannot be construed | ||||
|           as modifying the License. | ||||
|  | ||||
|       You may add Your own copyright statement to Your modifications and | ||||
|       may provide additional or different license terms and conditions | ||||
|       for use, reproduction, or distribution of Your modifications, or | ||||
|       for any such Derivative Works as a whole, provided Your use, | ||||
|       reproduction, and distribution of the Work otherwise complies with | ||||
|       the conditions stated in this License. | ||||
|  | ||||
|    5. Submission of Contributions. Unless You explicitly state otherwise, | ||||
|       any Contribution intentionally submitted for inclusion in the Work | ||||
|       by You to the Licensor shall be under the terms and conditions of | ||||
|       this License, without any additional terms or conditions. | ||||
|       Notwithstanding the above, nothing herein shall supersede or modify | ||||
|       the terms of any separate license agreement you may have executed | ||||
|       with Licensor regarding such Contributions. | ||||
|  | ||||
|    6. Trademarks. This License does not grant permission to use the trade | ||||
|       names, trademarks, service marks, or product names of the Licensor, | ||||
|       except as required for reasonable and customary use in describing the | ||||
|       origin of the Work and reproducing the content of the NOTICE file. | ||||
|  | ||||
|    7. Disclaimer of Warranty. Unless required by applicable law or | ||||
|       agreed to in writing, Licensor provides the Work (and each | ||||
|       Contributor provides its Contributions) on an "AS IS" BASIS, | ||||
|       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||||
|       implied, including, without limitation, any warranties or conditions | ||||
|       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||||
|       PARTICULAR PURPOSE. You are solely responsible for determining the | ||||
|       appropriateness of using or redistributing the Work and assume any | ||||
|       risks associated with Your exercise of permissions under this License. | ||||
|  | ||||
|    8. Limitation of Liability. In no event and under no legal theory, | ||||
|       whether in tort (including negligence), contract, or otherwise, | ||||
|       unless required by applicable law (such as deliberate and grossly | ||||
|       negligent acts) or agreed to in writing, shall any Contributor be | ||||
|       liable to You for damages, including any direct, indirect, special, | ||||
|       incidental, or consequential damages of any character arising as a | ||||
|       result of this License or out of the use or inability to use the | ||||
|       Work (including but not limited to damages for loss of goodwill, | ||||
|       work stoppage, computer failure or malfunction, or any and all | ||||
|       other commercial damages or losses), even if such Contributor | ||||
|       has been advised of the possibility of such damages. | ||||
|  | ||||
|    9. Accepting Warranty or Additional Liability. While redistributing | ||||
|       the Work or Derivative Works thereof, You may choose to offer, | ||||
|       and charge a fee for, acceptance of support, warranty, indemnity, | ||||
|       or other liability obligations and/or rights consistent with this | ||||
|       License. However, in accepting such obligations, You may act only | ||||
|       on Your own behalf and on Your sole responsibility, not on behalf | ||||
|       of any other Contributor, and only if You agree to indemnify, | ||||
|       defend, and hold each Contributor harmless for any liability | ||||
|       incurred by, or claims asserted against, such Contributor by reason | ||||
|       of your accepting any such warranty or additional liability. | ||||
|  | ||||
|    END OF TERMS AND CONDITIONS | ||||
|  | ||||
|    APPENDIX: How to apply the Apache License to your work. | ||||
|  | ||||
|       To apply the Apache License to your work, attach the following | ||||
|       boilerplate notice, with the fields enclosed by brackets "{}" | ||||
|       replaced with your own identifying information. (Don't include | ||||
|       the brackets!)  The text should be enclosed in the appropriate | ||||
|       comment syntax for the file format. We also recommend that a | ||||
|       file or class name and description of purpose be included on the | ||||
|       same "printed page" as the copyright notice for easier | ||||
|       identification within third-party archives. | ||||
|  | ||||
|    Copyright {yyyy} {name of copyright owner} | ||||
|  | ||||
|    Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|    you may not use this file except in compliance with the License. | ||||
|    You may obtain a copy of the License at | ||||
|  | ||||
|        http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  | ||||
|    Unless required by applicable law or agreed to in writing, software | ||||
|    distributed under the License is distributed on an "AS IS" BASIS, | ||||
|    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|    See the License for the specific language governing permissions and | ||||
|    limitations under the License. | ||||
							
								
								
									
										390
									
								
								vendor/gopkg.in/yaml.v2/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										390
									
								
								vendor/gopkg.in/yaml.v2/encode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,390 +0,0 @@ | ||||
| package yaml | ||||
|  | ||||
| import ( | ||||
| 	"encoding" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"reflect" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| // jsonNumber is the interface of the encoding/json.Number datatype. | ||||
| // Repeating the interface here avoids a dependency on encoding/json, and also | ||||
| // supports other libraries like jsoniter, which use a similar datatype with | ||||
| // the same interface. Detecting this interface is useful when dealing with | ||||
| // structures containing json.Number, which is a string under the hood. The | ||||
| // encoder should prefer the use of Int64(), Float64() and string(), in that | ||||
| // order, when encoding this type. | ||||
| type jsonNumber interface { | ||||
| 	Float64() (float64, error) | ||||
| 	Int64() (int64, error) | ||||
| 	String() string | ||||
| } | ||||
|  | ||||
| type encoder struct { | ||||
| 	emitter yaml_emitter_t | ||||
| 	event   yaml_event_t | ||||
| 	out     []byte | ||||
| 	flow    bool | ||||
| 	// doneInit holds whether the initial stream_start_event has been | ||||
| 	// emitted. | ||||
| 	doneInit bool | ||||
| } | ||||
|  | ||||
| func newEncoder() *encoder { | ||||
| 	e := &encoder{} | ||||
| 	yaml_emitter_initialize(&e.emitter) | ||||
| 	yaml_emitter_set_output_string(&e.emitter, &e.out) | ||||
| 	yaml_emitter_set_unicode(&e.emitter, true) | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| func newEncoderWithWriter(w io.Writer) *encoder { | ||||
| 	e := &encoder{} | ||||
| 	yaml_emitter_initialize(&e.emitter) | ||||
| 	yaml_emitter_set_output_writer(&e.emitter, w) | ||||
| 	yaml_emitter_set_unicode(&e.emitter, true) | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| func (e *encoder) init() { | ||||
| 	if e.doneInit { | ||||
| 		return | ||||
| 	} | ||||
| 	yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) | ||||
| 	e.emit() | ||||
| 	e.doneInit = true | ||||
| } | ||||
|  | ||||
| func (e *encoder) finish() { | ||||
| 	e.emitter.open_ended = false | ||||
| 	yaml_stream_end_event_initialize(&e.event) | ||||
| 	e.emit() | ||||
| } | ||||
|  | ||||
| func (e *encoder) destroy() { | ||||
| 	yaml_emitter_delete(&e.emitter) | ||||
| } | ||||
|  | ||||
| func (e *encoder) emit() { | ||||
| 	// This will internally delete the e.event value. | ||||
| 	e.must(yaml_emitter_emit(&e.emitter, &e.event)) | ||||
| } | ||||
|  | ||||
| func (e *encoder) must(ok bool) { | ||||
| 	if !ok { | ||||
| 		msg := e.emitter.problem | ||||
| 		if msg == "" { | ||||
| 			msg = "unknown problem generating YAML content" | ||||
| 		} | ||||
| 		failf("%s", msg) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (e *encoder) marshalDoc(tag string, in reflect.Value) { | ||||
| 	e.init() | ||||
| 	yaml_document_start_event_initialize(&e.event, nil, nil, true) | ||||
| 	e.emit() | ||||
| 	e.marshal(tag, in) | ||||
| 	yaml_document_end_event_initialize(&e.event, true) | ||||
| 	e.emit() | ||||
| } | ||||
|  | ||||
| func (e *encoder) marshal(tag string, in reflect.Value) { | ||||
| 	if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { | ||||
| 		e.nilv() | ||||
| 		return | ||||
| 	} | ||||
| 	iface := in.Interface() | ||||
| 	switch m := iface.(type) { | ||||
| 	case jsonNumber: | ||||
| 		integer, err := m.Int64() | ||||
| 		if err == nil { | ||||
| 			// In this case the json.Number is a valid int64 | ||||
| 			in = reflect.ValueOf(integer) | ||||
| 			break | ||||
| 		} | ||||
| 		float, err := m.Float64() | ||||
| 		if err == nil { | ||||
| 			// In this case the json.Number is a valid float64 | ||||
| 			in = reflect.ValueOf(float) | ||||
| 			break | ||||
| 		} | ||||
| 		// fallback case - no number could be obtained | ||||
| 		in = reflect.ValueOf(m.String()) | ||||
| 	case time.Time, *time.Time: | ||||
| 		// Although time.Time implements TextMarshaler, | ||||
| 		// we don't want to treat it as a string for YAML | ||||
| 		// purposes because YAML has special support for | ||||
| 		// timestamps. | ||||
| 	case Marshaler: | ||||
| 		v, err := m.MarshalYAML() | ||||
| 		if err != nil { | ||||
| 			fail(err) | ||||
| 		} | ||||
| 		if v == nil { | ||||
| 			e.nilv() | ||||
| 			return | ||||
| 		} | ||||
| 		in = reflect.ValueOf(v) | ||||
| 	case encoding.TextMarshaler: | ||||
| 		text, err := m.MarshalText() | ||||
| 		if err != nil { | ||||
| 			fail(err) | ||||
| 		} | ||||
| 		in = reflect.ValueOf(string(text)) | ||||
| 	case nil: | ||||
| 		e.nilv() | ||||
| 		return | ||||
| 	} | ||||
| 	switch in.Kind() { | ||||
| 	case reflect.Interface: | ||||
| 		e.marshal(tag, in.Elem()) | ||||
| 	case reflect.Map: | ||||
| 		e.mapv(tag, in) | ||||
| 	case reflect.Ptr: | ||||
| 		if in.Type() == ptrTimeType { | ||||
| 			e.timev(tag, in.Elem()) | ||||
| 		} else { | ||||
| 			e.marshal(tag, in.Elem()) | ||||
| 		} | ||||
| 	case reflect.Struct: | ||||
| 		if in.Type() == timeType { | ||||
| 			e.timev(tag, in) | ||||
| 		} else { | ||||
| 			e.structv(tag, in) | ||||
| 		} | ||||
| 	case reflect.Slice, reflect.Array: | ||||
| 		if in.Type().Elem() == mapItemType { | ||||
| 			e.itemsv(tag, in) | ||||
| 		} else { | ||||
| 			e.slicev(tag, in) | ||||
| 		} | ||||
| 	case reflect.String: | ||||
| 		e.stringv(tag, in) | ||||
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||
| 		if in.Type() == durationType { | ||||
| 			e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String())) | ||||
| 		} else { | ||||
| 			e.intv(tag, in) | ||||
| 		} | ||||
| 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||||
| 		e.uintv(tag, in) | ||||
| 	case reflect.Float32, reflect.Float64: | ||||
| 		e.floatv(tag, in) | ||||
| 	case reflect.Bool: | ||||
| 		e.boolv(tag, in) | ||||
| 	default: | ||||
| 		panic("cannot marshal type: " + in.Type().String()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (e *encoder) mapv(tag string, in reflect.Value) { | ||||
| 	e.mappingv(tag, func() { | ||||
| 		keys := keyList(in.MapKeys()) | ||||
| 		sort.Sort(keys) | ||||
| 		for _, k := range keys { | ||||
| 			e.marshal("", k) | ||||
| 			e.marshal("", in.MapIndex(k)) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (e *encoder) itemsv(tag string, in reflect.Value) { | ||||
| 	e.mappingv(tag, func() { | ||||
| 		slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem) | ||||
| 		for _, item := range slice { | ||||
| 			e.marshal("", reflect.ValueOf(item.Key)) | ||||
| 			e.marshal("", reflect.ValueOf(item.Value)) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (e *encoder) structv(tag string, in reflect.Value) { | ||||
| 	sinfo, err := getStructInfo(in.Type()) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	e.mappingv(tag, func() { | ||||
| 		for _, info := range sinfo.FieldsList { | ||||
| 			var value reflect.Value | ||||
| 			if info.Inline == nil { | ||||
| 				value = in.Field(info.Num) | ||||
| 			} else { | ||||
| 				value = in.FieldByIndex(info.Inline) | ||||
| 			} | ||||
| 			if info.OmitEmpty && isZero(value) { | ||||
| 				continue | ||||
| 			} | ||||
| 			e.marshal("", reflect.ValueOf(info.Key)) | ||||
| 			e.flow = info.Flow | ||||
| 			e.marshal("", value) | ||||
| 		} | ||||
| 		if sinfo.InlineMap >= 0 { | ||||
| 			m := in.Field(sinfo.InlineMap) | ||||
| 			if m.Len() > 0 { | ||||
| 				e.flow = false | ||||
| 				keys := keyList(m.MapKeys()) | ||||
| 				sort.Sort(keys) | ||||
| 				for _, k := range keys { | ||||
| 					if _, found := sinfo.FieldsMap[k.String()]; found { | ||||
| 						panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String())) | ||||
| 					} | ||||
| 					e.marshal("", k) | ||||
| 					e.flow = false | ||||
| 					e.marshal("", m.MapIndex(k)) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (e *encoder) mappingv(tag string, f func()) { | ||||
| 	implicit := tag == "" | ||||
| 	style := yaml_BLOCK_MAPPING_STYLE | ||||
| 	if e.flow { | ||||
| 		e.flow = false | ||||
| 		style = yaml_FLOW_MAPPING_STYLE | ||||
| 	} | ||||
| 	yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) | ||||
| 	e.emit() | ||||
| 	f() | ||||
| 	yaml_mapping_end_event_initialize(&e.event) | ||||
| 	e.emit() | ||||
| } | ||||
|  | ||||
| func (e *encoder) slicev(tag string, in reflect.Value) { | ||||
| 	implicit := tag == "" | ||||
| 	style := yaml_BLOCK_SEQUENCE_STYLE | ||||
| 	if e.flow { | ||||
| 		e.flow = false | ||||
| 		style = yaml_FLOW_SEQUENCE_STYLE | ||||
| 	} | ||||
| 	e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) | ||||
| 	e.emit() | ||||
| 	n := in.Len() | ||||
| 	for i := 0; i < n; i++ { | ||||
| 		e.marshal("", in.Index(i)) | ||||
| 	} | ||||
| 	e.must(yaml_sequence_end_event_initialize(&e.event)) | ||||
| 	e.emit() | ||||
| } | ||||
|  | ||||
| // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. | ||||
| // | ||||
| // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported | ||||
| // in YAML 1.2 and by this package, but these should be marshalled quoted for | ||||
| // the time being for compatibility with other parsers. | ||||
| func isBase60Float(s string) (result bool) { | ||||
| 	// Fast path. | ||||
| 	if s == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 	c := s[0] | ||||
| 	if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	// Do the full match. | ||||
| 	return base60float.MatchString(s) | ||||
| } | ||||
|  | ||||
| // From http://yaml.org/type/float.html, except the regular expression there | ||||
| // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. | ||||
| var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) | ||||
|  | ||||
| func (e *encoder) stringv(tag string, in reflect.Value) { | ||||
| 	var style yaml_scalar_style_t | ||||
| 	s := in.String() | ||||
| 	canUsePlain := true | ||||
| 	switch { | ||||
| 	case !utf8.ValidString(s): | ||||
| 		if tag == yaml_BINARY_TAG { | ||||
| 			failf("explicitly tagged !!binary data must be base64-encoded") | ||||
| 		} | ||||
| 		if tag != "" { | ||||
| 			failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) | ||||
| 		} | ||||
| 		// It can't be encoded directly as YAML so use a binary tag | ||||
| 		// and encode it as base64. | ||||
| 		tag = yaml_BINARY_TAG | ||||
| 		s = encodeBase64(s) | ||||
| 	case tag == "": | ||||
| 		// Check to see if it would resolve to a specific | ||||
| 		// tag when encoded unquoted. If it doesn't, | ||||
| 		// there's no need to quote it. | ||||
| 		rtag, _ := resolve("", s) | ||||
| 		canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s) | ||||
| 	} | ||||
| 	// Note: it's possible for user code to emit invalid YAML | ||||
| 	// if they explicitly specify a tag and a string containing | ||||
| 	// text that's incompatible with that tag. | ||||
| 	switch { | ||||
| 	case strings.Contains(s, "\n"): | ||||
| 		style = yaml_LITERAL_SCALAR_STYLE | ||||
| 	case canUsePlain: | ||||
| 		style = yaml_PLAIN_SCALAR_STYLE | ||||
| 	default: | ||||
| 		style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | ||||
| 	} | ||||
| 	e.emitScalar(s, "", tag, style) | ||||
| } | ||||
|  | ||||
| func (e *encoder) boolv(tag string, in reflect.Value) { | ||||
| 	var s string | ||||
| 	if in.Bool() { | ||||
| 		s = "true" | ||||
| 	} else { | ||||
| 		s = "false" | ||||
| 	} | ||||
| 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) | ||||
| } | ||||
|  | ||||
| func (e *encoder) intv(tag string, in reflect.Value) { | ||||
| 	s := strconv.FormatInt(in.Int(), 10) | ||||
| 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) | ||||
| } | ||||
|  | ||||
| func (e *encoder) uintv(tag string, in reflect.Value) { | ||||
| 	s := strconv.FormatUint(in.Uint(), 10) | ||||
| 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) | ||||
| } | ||||
|  | ||||
| func (e *encoder) timev(tag string, in reflect.Value) { | ||||
| 	t := in.Interface().(time.Time) | ||||
| 	s := t.Format(time.RFC3339Nano) | ||||
| 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) | ||||
| } | ||||
|  | ||||
| func (e *encoder) floatv(tag string, in reflect.Value) { | ||||
| 	// Issue #352: When formatting, use the precision of the underlying value | ||||
| 	precision := 64 | ||||
| 	if in.Kind() == reflect.Float32 { | ||||
| 		precision = 32 | ||||
| 	} | ||||
|  | ||||
| 	s := strconv.FormatFloat(in.Float(), 'g', -1, precision) | ||||
| 	switch s { | ||||
| 	case "+Inf": | ||||
| 		s = ".inf" | ||||
| 	case "-Inf": | ||||
| 		s = "-.inf" | ||||
| 	case "NaN": | ||||
| 		s = ".nan" | ||||
| 	} | ||||
| 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE) | ||||
| } | ||||
|  | ||||
| func (e *encoder) nilv() { | ||||
| 	e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE) | ||||
| } | ||||
|  | ||||
| func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) { | ||||
| 	implicit := tag == "" | ||||
| 	e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) | ||||
| 	e.emit() | ||||
| } | ||||
							
								
								
									
										26
									
								
								vendor/gopkg.in/yaml.v2/writerc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								vendor/gopkg.in/yaml.v2/writerc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,26 +0,0 @@ | ||||
| package yaml | ||||
|  | ||||
| // Set the writer error and return false. | ||||
| func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { | ||||
| 	emitter.error = yaml_WRITER_ERROR | ||||
| 	emitter.problem = problem | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // Flush the output buffer. | ||||
| func yaml_emitter_flush(emitter *yaml_emitter_t) bool { | ||||
| 	if emitter.write_handler == nil { | ||||
| 		panic("write handler not set") | ||||
| 	} | ||||
|  | ||||
| 	// Check if the buffer is empty. | ||||
| 	if emitter.buffer_pos == 0 { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { | ||||
| 		return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) | ||||
| 	} | ||||
| 	emitter.buffer_pos = 0 | ||||
| 	return true | ||||
| } | ||||
							
								
								
									
										39
									
								
								vendor/gopkg.in/yaml.v2/LICENSE.libyaml → vendor/gopkg.in/yaml.v3/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										39
									
								
								vendor/gopkg.in/yaml.v2/LICENSE.libyaml → vendor/gopkg.in/yaml.v3/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,16 +1,17 @@ | ||||
| 
 | ||||
| This project is covered by two different licenses: MIT and Apache. | ||||
| 
 | ||||
| #### MIT License #### | ||||
| 
 | ||||
| The following files were ported to Go from C files of libyaml, and thus | ||||
| are still covered by their original copyright and license: | ||||
| are still covered by their original MIT license, with the additional | ||||
| copyright staring in 2011 when the project was ported over: | ||||
| 
 | ||||
|     apic.go | ||||
|     emitterc.go | ||||
|     parserc.go | ||||
|     readerc.go | ||||
|     scannerc.go | ||||
|     writerc.go | ||||
|     yamlh.go | ||||
|     yamlprivateh.go | ||||
|     apic.go emitterc.go parserc.go readerc.go scannerc.go | ||||
|     writerc.go yamlh.go yamlprivateh.go | ||||
| 
 | ||||
| Copyright (c) 2006 Kirill Simonov | ||||
| Copyright (c) 2006-2010 Kirill Simonov | ||||
| Copyright (c) 2006-2011 Kirill Simonov | ||||
| 
 | ||||
| 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 | ||||
| @ -29,3 +30,21 @@ 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. | ||||
| 
 | ||||
| ### Apache License ### | ||||
| 
 | ||||
| All the remaining project files are covered by the Apache license: | ||||
| 
 | ||||
| Copyright (c) 2011-2019 Canonical Ltd | ||||
| 
 | ||||
| Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| you may not use this file except in compliance with the License. | ||||
| You may obtain a copy of the License at | ||||
| 
 | ||||
|     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| 
 | ||||
| Unless required by applicable law or agreed to in writing, software | ||||
| distributed under the License is distributed on an "AS IS" BASIS, | ||||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| See the License for the specific language governing permissions and | ||||
| limitations under the License. | ||||
							
								
								
									
										0
									
								
								vendor/gopkg.in/yaml.v2/NOTICE → vendor/gopkg.in/yaml.v3/NOTICE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										0
									
								
								vendor/gopkg.in/yaml.v2/NOTICE → vendor/gopkg.in/yaml.v3/NOTICE
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
								
								
									
										31
									
								
								vendor/gopkg.in/yaml.v2/README.md → vendor/gopkg.in/yaml.v3/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								vendor/gopkg.in/yaml.v2/README.md → vendor/gopkg.in/yaml.v3/README.md
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -12,7 +12,23 @@ C library to parse and generate YAML data quickly and reliably. | ||||
| Compatibility | ||||
| ------------- | ||||
| 
 | ||||
| The yaml package supports most of YAML 1.1 and 1.2, including support for | ||||
| The yaml package supports most of YAML 1.2, but preserves some behavior | ||||
| from 1.1 for backwards compatibility. | ||||
| 
 | ||||
| Specifically, as of v3 of the yaml package: | ||||
| 
 | ||||
|  - YAML 1.1 bools (_yes/no, on/off_) are supported as long as they are being | ||||
|    decoded into a typed bool value. Otherwise they behave as a string. Booleans | ||||
|    in YAML 1.2 are _true/false_ only. | ||||
|  - Octals encode and decode as _0777_ per YAML 1.1, rather than _0o777_ | ||||
|    as specified in YAML 1.2, because most parsers still use the old format. | ||||
|    Octals in the  _0o777_ format are supported though, so new files work. | ||||
|  - Does not support base-60 floats. These are gone from YAML 1.2, and were | ||||
|    actually never supported by this package as it's clearly a poor choice. | ||||
| 
 | ||||
| and offers backwards | ||||
| compatibility with YAML 1.1 in some cases. | ||||
| 1.2, including support for | ||||
| anchors, tags, map merging, etc. Multi-document unmarshalling is not yet | ||||
| implemented, and base-60 floats from YAML 1.1 are purposefully not | ||||
| supported since they're a poor design and are gone in YAML 1.2. | ||||
| @ -20,29 +36,30 @@ supported since they're a poor design and are gone in YAML 1.2. | ||||
| Installation and usage | ||||
| ---------------------- | ||||
| 
 | ||||
| The import path for the package is *gopkg.in/yaml.v2*. | ||||
| The import path for the package is *gopkg.in/yaml.v3*. | ||||
| 
 | ||||
| To install it, run: | ||||
| 
 | ||||
|     go get gopkg.in/yaml.v2 | ||||
|     go get gopkg.in/yaml.v3 | ||||
| 
 | ||||
| API documentation | ||||
| ----------------- | ||||
| 
 | ||||
| If opened in a browser, the import path itself leads to the API documentation: | ||||
| 
 | ||||
|   * [https://gopkg.in/yaml.v2](https://gopkg.in/yaml.v2) | ||||
|   - [https://gopkg.in/yaml.v3](https://gopkg.in/yaml.v3) | ||||
| 
 | ||||
| API stability | ||||
| ------------- | ||||
| 
 | ||||
| The package API for yaml v2 will remain stable as described in [gopkg.in](https://gopkg.in). | ||||
| The package API for yaml v3 will remain stable as described in [gopkg.in](https://gopkg.in). | ||||
| 
 | ||||
| 
 | ||||
| License | ||||
| ------- | ||||
| 
 | ||||
| The yaml package is licensed under the Apache License 2.0. Please see the LICENSE file for details. | ||||
| The yaml package is licensed under the MIT and Apache License 2.0 licenses. | ||||
| Please see the LICENSE file for details. | ||||
| 
 | ||||
| 
 | ||||
| Example | ||||
| @ -55,7 +72,7 @@ import ( | ||||
|         "fmt" | ||||
|         "log" | ||||
| 
 | ||||
|         "gopkg.in/yaml.v2" | ||||
|         "gopkg.in/yaml.v3" | ||||
| ) | ||||
| 
 | ||||
| var data = ` | ||||
							
								
								
									
										61
									
								
								vendor/gopkg.in/yaml.v2/apic.go → vendor/gopkg.in/yaml.v3/apic.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										61
									
								
								vendor/gopkg.in/yaml.v2/apic.go → vendor/gopkg.in/yaml.v3/apic.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +1,25 @@ | ||||
| //  | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // Copyright (c) 2006-2010 Kirill Simonov | ||||
| //  | ||||
| // 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. | ||||
| 
 | ||||
| package yaml | ||||
| 
 | ||||
| import ( | ||||
| @ -79,8 +101,6 @@ func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { | ||||
| 	parser.encoding = encoding | ||||
| } | ||||
| 
 | ||||
| var disableLineWrapping = false | ||||
| 
 | ||||
| // Create a new emitter object. | ||||
| func yaml_emitter_initialize(emitter *yaml_emitter_t) { | ||||
| 	*emitter = yaml_emitter_t{ | ||||
| @ -88,9 +108,7 @@ func yaml_emitter_initialize(emitter *yaml_emitter_t) { | ||||
| 		raw_buffer: make([]byte, 0, output_raw_buffer_size), | ||||
| 		states:     make([]yaml_emitter_state_t, 0, initial_stack_size), | ||||
| 		events:     make([]yaml_event_t, 0, initial_queue_size), | ||||
| 	} | ||||
| 	if disableLineWrapping { | ||||
| 		emitter.best_width = -1 | ||||
| 		best_width: -1, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -143,7 +161,7 @@ func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { | ||||
| 	emitter.canonical = canonical | ||||
| } | ||||
| 
 | ||||
| //// Set the indentation increment. | ||||
| // Set the indentation increment. | ||||
| func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { | ||||
| 	if indent < 2 || indent > 9 { | ||||
| 		indent = 2 | ||||
| @ -293,29 +311,14 @@ func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| ///* | ||||
| // * Create ALIAS. | ||||
| // */ | ||||
| // | ||||
| //YAML_DECLARE(int) | ||||
| //yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t) | ||||
| //{ | ||||
| //    mark yaml_mark_t = { 0, 0, 0 } | ||||
| //    anchor_copy *yaml_char_t = NULL | ||||
| // | ||||
| //    assert(event) // Non-NULL event object is expected. | ||||
| //    assert(anchor) // Non-NULL anchor is expected. | ||||
| // | ||||
| //    if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0 | ||||
| // | ||||
| //    anchor_copy = yaml_strdup(anchor) | ||||
| //    if (!anchor_copy) | ||||
| //        return 0 | ||||
| // | ||||
| //    ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark) | ||||
| // | ||||
| //    return 1 | ||||
| //} | ||||
| // Create ALIAS. | ||||
| func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool { | ||||
| 	*event = yaml_event_t{ | ||||
| 		typ:    yaml_ALIAS_EVENT, | ||||
| 		anchor: anchor, | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Create SCALAR. | ||||
| func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { | ||||
							
								
								
									
										631
									
								
								vendor/gopkg.in/yaml.v2/decode.go → vendor/gopkg.in/yaml.v3/decode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										631
									
								
								vendor/gopkg.in/yaml.v2/decode.go → vendor/gopkg.in/yaml.v3/decode.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +1,18 @@ | ||||
| // | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
| 
 | ||||
| package yaml | ||||
| 
 | ||||
| import ( | ||||
| @ -11,34 +26,16 @@ import ( | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	documentNode = 1 << iota | ||||
| 	mappingNode | ||||
| 	sequenceNode | ||||
| 	scalarNode | ||||
| 	aliasNode | ||||
| ) | ||||
| 
 | ||||
| type node struct { | ||||
| 	kind         int | ||||
| 	line, column int | ||||
| 	tag          string | ||||
| 	// For an alias node, alias holds the resolved alias. | ||||
| 	alias    *node | ||||
| 	value    string | ||||
| 	implicit bool | ||||
| 	children []*node | ||||
| 	anchors  map[string]*node | ||||
| } | ||||
| 
 | ||||
| // ---------------------------------------------------------------------------- | ||||
| // Parser, produces a node tree out of a libyaml event stream. | ||||
| 
 | ||||
| type parser struct { | ||||
| 	parser   yaml_parser_t | ||||
| 	event    yaml_event_t | ||||
| 	doc      *node | ||||
| 	doc      *Node | ||||
| 	anchors  map[string]*Node | ||||
| 	doneInit bool | ||||
| 	textless bool | ||||
| } | ||||
| 
 | ||||
| func newParser(b []byte) *parser { | ||||
| @ -66,6 +63,7 @@ func (p *parser) init() { | ||||
| 	if p.doneInit { | ||||
| 		return | ||||
| 	} | ||||
| 	p.anchors = make(map[string]*Node) | ||||
| 	p.expect(yaml_STREAM_START_EVENT) | ||||
| 	p.doneInit = true | ||||
| } | ||||
| @ -102,7 +100,10 @@ func (p *parser) peek() yaml_event_type_t { | ||||
| 	if p.event.typ != yaml_NO_EVENT { | ||||
| 		return p.event.typ | ||||
| 	} | ||||
| 	if !yaml_parser_parse(&p.parser, &p.event) { | ||||
| 	// It's curious choice from the underlying API to generally return a | ||||
| 	// positive result on success, but on this case return true in an error | ||||
| 	// scenario. This was the source of bugs in the past (issue #666). | ||||
| 	if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR { | ||||
| 		p.fail() | ||||
| 	} | ||||
| 	return p.event.typ | ||||
| @ -111,14 +112,18 @@ func (p *parser) peek() yaml_event_type_t { | ||||
| func (p *parser) fail() { | ||||
| 	var where string | ||||
| 	var line int | ||||
| 	if p.parser.problem_mark.line != 0 { | ||||
| 	if p.parser.context_mark.line != 0 { | ||||
| 		line = p.parser.context_mark.line | ||||
| 		// Scanner errors don't iterate line before returning error | ||||
| 		if p.parser.error == yaml_SCANNER_ERROR { | ||||
| 			line++ | ||||
| 		} | ||||
| 	} else if p.parser.problem_mark.line != 0 { | ||||
| 		line = p.parser.problem_mark.line | ||||
| 		// Scanner errors don't iterate line before returning error | ||||
| 		if p.parser.error == yaml_SCANNER_ERROR { | ||||
| 			line++ | ||||
| 		} | ||||
| 	} else if p.parser.context_mark.line != 0 { | ||||
| 		line = p.parser.context_mark.line | ||||
| 	} | ||||
| 	if line != 0 { | ||||
| 		where = "line " + strconv.Itoa(line) + ": " | ||||
| @ -132,13 +137,14 @@ func (p *parser) fail() { | ||||
| 	failf("%s%s", where, msg) | ||||
| } | ||||
| 
 | ||||
| func (p *parser) anchor(n *node, anchor []byte) { | ||||
| func (p *parser) anchor(n *Node, anchor []byte) { | ||||
| 	if anchor != nil { | ||||
| 		p.doc.anchors[string(anchor)] = n | ||||
| 		n.Anchor = string(anchor) | ||||
| 		p.anchors[n.Anchor] = n | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (p *parser) parse() *node { | ||||
| func (p *parser) parse() *Node { | ||||
| 	p.init() | ||||
| 	switch p.peek() { | ||||
| 	case yaml_SCALAR_EVENT: | ||||
| @ -154,67 +160,148 @@ func (p *parser) parse() *node { | ||||
| 	case yaml_STREAM_END_EVENT: | ||||
| 		// Happens when attempting to decode an empty buffer. | ||||
| 		return nil | ||||
| 	case yaml_TAIL_COMMENT_EVENT: | ||||
| 		panic("internal error: unexpected tail comment event (please report)") | ||||
| 	default: | ||||
| 		panic("attempted to parse unknown event: " + p.event.typ.String()) | ||||
| 		panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String()) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (p *parser) node(kind int) *node { | ||||
| 	return &node{ | ||||
| 		kind:   kind, | ||||
| 		line:   p.event.start_mark.line, | ||||
| 		column: p.event.start_mark.column, | ||||
| func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { | ||||
| 	var style Style | ||||
| 	if tag != "" && tag != "!" { | ||||
| 		tag = shortTag(tag) | ||||
| 		style = TaggedStyle | ||||
| 	} else if defaultTag != "" { | ||||
| 		tag = defaultTag | ||||
| 	} else if kind == ScalarNode { | ||||
| 		tag, _ = resolve("", value) | ||||
| 	} | ||||
| 	n := &Node{ | ||||
| 		Kind:  kind, | ||||
| 		Tag:   tag, | ||||
| 		Value: value, | ||||
| 		Style: style, | ||||
| 	} | ||||
| 	if !p.textless { | ||||
| 		n.Line = p.event.start_mark.line + 1 | ||||
| 		n.Column = p.event.start_mark.column + 1 | ||||
| 		n.HeadComment = string(p.event.head_comment) | ||||
| 		n.LineComment = string(p.event.line_comment) | ||||
| 		n.FootComment = string(p.event.foot_comment) | ||||
| 	} | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func (p *parser) document() *node { | ||||
| 	n := p.node(documentNode) | ||||
| 	n.anchors = make(map[string]*node) | ||||
| func (p *parser) parseChild(parent *Node) *Node { | ||||
| 	child := p.parse() | ||||
| 	parent.Content = append(parent.Content, child) | ||||
| 	return child | ||||
| } | ||||
| 
 | ||||
| func (p *parser) document() *Node { | ||||
| 	n := p.node(DocumentNode, "", "", "") | ||||
| 	p.doc = n | ||||
| 	p.expect(yaml_DOCUMENT_START_EVENT) | ||||
| 	n.children = append(n.children, p.parse()) | ||||
| 	p.parseChild(n) | ||||
| 	if p.peek() == yaml_DOCUMENT_END_EVENT { | ||||
| 		n.FootComment = string(p.event.foot_comment) | ||||
| 	} | ||||
| 	p.expect(yaml_DOCUMENT_END_EVENT) | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func (p *parser) alias() *node { | ||||
| 	n := p.node(aliasNode) | ||||
| 	n.value = string(p.event.anchor) | ||||
| 	n.alias = p.doc.anchors[n.value] | ||||
| 	if n.alias == nil { | ||||
| 		failf("unknown anchor '%s' referenced", n.value) | ||||
| func (p *parser) alias() *Node { | ||||
| 	n := p.node(AliasNode, "", "", string(p.event.anchor)) | ||||
| 	n.Alias = p.anchors[n.Value] | ||||
| 	if n.Alias == nil { | ||||
| 		failf("unknown anchor '%s' referenced", n.Value) | ||||
| 	} | ||||
| 	p.expect(yaml_ALIAS_EVENT) | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func (p *parser) scalar() *node { | ||||
| 	n := p.node(scalarNode) | ||||
| 	n.value = string(p.event.value) | ||||
| 	n.tag = string(p.event.tag) | ||||
| 	n.implicit = p.event.implicit | ||||
| func (p *parser) scalar() *Node { | ||||
| 	var parsedStyle = p.event.scalar_style() | ||||
| 	var nodeStyle Style | ||||
| 	switch { | ||||
| 	case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0: | ||||
| 		nodeStyle = DoubleQuotedStyle | ||||
| 	case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0: | ||||
| 		nodeStyle = SingleQuotedStyle | ||||
| 	case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0: | ||||
| 		nodeStyle = LiteralStyle | ||||
| 	case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0: | ||||
| 		nodeStyle = FoldedStyle | ||||
| 	} | ||||
| 	var nodeValue = string(p.event.value) | ||||
| 	var nodeTag = string(p.event.tag) | ||||
| 	var defaultTag string | ||||
| 	if nodeStyle == 0 { | ||||
| 		if nodeValue == "<<" { | ||||
| 			defaultTag = mergeTag | ||||
| 		} | ||||
| 	} else { | ||||
| 		defaultTag = strTag | ||||
| 	} | ||||
| 	n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue) | ||||
| 	n.Style |= nodeStyle | ||||
| 	p.anchor(n, p.event.anchor) | ||||
| 	p.expect(yaml_SCALAR_EVENT) | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func (p *parser) sequence() *node { | ||||
| 	n := p.node(sequenceNode) | ||||
| func (p *parser) sequence() *Node { | ||||
| 	n := p.node(SequenceNode, seqTag, string(p.event.tag), "") | ||||
| 	if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 { | ||||
| 		n.Style |= FlowStyle | ||||
| 	} | ||||
| 	p.anchor(n, p.event.anchor) | ||||
| 	p.expect(yaml_SEQUENCE_START_EVENT) | ||||
| 	for p.peek() != yaml_SEQUENCE_END_EVENT { | ||||
| 		n.children = append(n.children, p.parse()) | ||||
| 		p.parseChild(n) | ||||
| 	} | ||||
| 	n.LineComment = string(p.event.line_comment) | ||||
| 	n.FootComment = string(p.event.foot_comment) | ||||
| 	p.expect(yaml_SEQUENCE_END_EVENT) | ||||
| 	return n | ||||
| } | ||||
| 
 | ||||
| func (p *parser) mapping() *node { | ||||
| 	n := p.node(mappingNode) | ||||
| func (p *parser) mapping() *Node { | ||||
| 	n := p.node(MappingNode, mapTag, string(p.event.tag), "") | ||||
| 	block := true | ||||
| 	if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 { | ||||
| 		block = false | ||||
| 		n.Style |= FlowStyle | ||||
| 	} | ||||
| 	p.anchor(n, p.event.anchor) | ||||
| 	p.expect(yaml_MAPPING_START_EVENT) | ||||
| 	for p.peek() != yaml_MAPPING_END_EVENT { | ||||
| 		n.children = append(n.children, p.parse(), p.parse()) | ||||
| 		k := p.parseChild(n) | ||||
| 		if block && k.FootComment != "" { | ||||
| 			// Must be a foot comment for the prior value when being dedented. | ||||
| 			if len(n.Content) > 2 { | ||||
| 				n.Content[len(n.Content)-3].FootComment = k.FootComment | ||||
| 				k.FootComment = "" | ||||
| 			} | ||||
| 		} | ||||
| 		v := p.parseChild(n) | ||||
| 		if k.FootComment == "" && v.FootComment != "" { | ||||
| 			k.FootComment = v.FootComment | ||||
| 			v.FootComment = "" | ||||
| 		} | ||||
| 		if p.peek() == yaml_TAIL_COMMENT_EVENT { | ||||
| 			if k.FootComment == "" { | ||||
| 				k.FootComment = string(p.event.foot_comment) | ||||
| 			} | ||||
| 			p.expect(yaml_TAIL_COMMENT_EVENT) | ||||
| 		} | ||||
| 	} | ||||
| 	n.LineComment = string(p.event.line_comment) | ||||
| 	n.FootComment = string(p.event.foot_comment) | ||||
| 	if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 { | ||||
| 		n.Content[len(n.Content)-2].FootComment = n.FootComment | ||||
| 		n.FootComment = "" | ||||
| 	} | ||||
| 	p.expect(yaml_MAPPING_END_EVENT) | ||||
| 	return n | ||||
| @ -224,48 +311,70 @@ func (p *parser) mapping() *node { | ||||
| // Decoder, unmarshals a node into a provided value. | ||||
| 
 | ||||
| type decoder struct { | ||||
| 	doc     *node | ||||
| 	aliases map[*node]bool | ||||
| 	mapType reflect.Type | ||||
| 	doc     *Node | ||||
| 	aliases map[*Node]bool | ||||
| 	terrors []string | ||||
| 	strict  bool | ||||
| 
 | ||||
| 	stringMapType  reflect.Type | ||||
| 	generalMapType reflect.Type | ||||
| 
 | ||||
| 	knownFields bool | ||||
| 	uniqueKeys  bool | ||||
| 	decodeCount int | ||||
| 	aliasCount  int | ||||
| 	aliasDepth  int | ||||
| 
 | ||||
| 	mergedFields map[interface{}]bool | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	mapItemType    = reflect.TypeOf(MapItem{}) | ||||
| 	nodeType       = reflect.TypeOf(Node{}) | ||||
| 	durationType   = reflect.TypeOf(time.Duration(0)) | ||||
| 	defaultMapType = reflect.TypeOf(map[interface{}]interface{}{}) | ||||
| 	ifaceType      = defaultMapType.Elem() | ||||
| 	stringMapType  = reflect.TypeOf(map[string]interface{}{}) | ||||
| 	generalMapType = reflect.TypeOf(map[interface{}]interface{}{}) | ||||
| 	ifaceType      = generalMapType.Elem() | ||||
| 	timeType       = reflect.TypeOf(time.Time{}) | ||||
| 	ptrTimeType    = reflect.TypeOf(&time.Time{}) | ||||
| ) | ||||
| 
 | ||||
| func newDecoder(strict bool) *decoder { | ||||
| 	d := &decoder{mapType: defaultMapType, strict: strict} | ||||
| 	d.aliases = make(map[*node]bool) | ||||
| func newDecoder() *decoder { | ||||
| 	d := &decoder{ | ||||
| 		stringMapType:  stringMapType, | ||||
| 		generalMapType: generalMapType, | ||||
| 		uniqueKeys:     true, | ||||
| 	} | ||||
| 	d.aliases = make(map[*Node]bool) | ||||
| 	return d | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) terror(n *node, tag string, out reflect.Value) { | ||||
| 	if n.tag != "" { | ||||
| 		tag = n.tag | ||||
| func (d *decoder) terror(n *Node, tag string, out reflect.Value) { | ||||
| 	if n.Tag != "" { | ||||
| 		tag = n.Tag | ||||
| 	} | ||||
| 	value := n.value | ||||
| 	if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG { | ||||
| 	value := n.Value | ||||
| 	if tag != seqTag && tag != mapTag { | ||||
| 		if len(value) > 10 { | ||||
| 			value = " `" + value[:7] + "...`" | ||||
| 		} else { | ||||
| 			value = " `" + value + "`" | ||||
| 		} | ||||
| 	} | ||||
| 	d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type())) | ||||
| 	d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type())) | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { | ||||
| func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) { | ||||
| 	err := u.UnmarshalYAML(n) | ||||
| 	if e, ok := err.(*TypeError); ok { | ||||
| 		d.terrors = append(d.terrors, e.Errors...) | ||||
| 		return false | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		fail(err) | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) { | ||||
| 	terrlen := len(d.terrors) | ||||
| 	err := u.UnmarshalYAML(func(v interface{}) (err error) { | ||||
| 		defer handleErr(&err) | ||||
| @ -294,8 +403,8 @@ func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) { | ||||
| // its types unmarshalled appropriately. | ||||
| // | ||||
| // If n holds a null value, prepare returns before doing anything. | ||||
| func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { | ||||
| 	if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "~" || n.value == "" && n.implicit) { | ||||
| func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { | ||||
| 	if n.ShortTag() == nullTag { | ||||
| 		return out, false, false | ||||
| 	} | ||||
| 	again := true | ||||
| @ -309,15 +418,40 @@ func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unm | ||||
| 			again = true | ||||
| 		} | ||||
| 		if out.CanAddr() { | ||||
| 			if u, ok := out.Addr().Interface().(Unmarshaler); ok { | ||||
| 			outi := out.Addr().Interface() | ||||
| 			if u, ok := outi.(Unmarshaler); ok { | ||||
| 				good = d.callUnmarshaler(n, u) | ||||
| 				return out, true, good | ||||
| 			} | ||||
| 			if u, ok := outi.(obsoleteUnmarshaler); ok { | ||||
| 				good = d.callObsoleteUnmarshaler(n, u) | ||||
| 				return out, true, good | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return out, false, false | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) { | ||||
| 	if n.ShortTag() == nullTag { | ||||
| 		return reflect.Value{} | ||||
| 	} | ||||
| 	for _, num := range index { | ||||
| 		for { | ||||
| 			if v.Kind() == reflect.Ptr { | ||||
| 				if v.IsNil() { | ||||
| 					v.Set(reflect.New(v.Type().Elem())) | ||||
| 				} | ||||
| 				v = v.Elem() | ||||
| 				continue | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
| 		v = v.Field(num) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	// 400,000 decode operations is ~500kb of dense object declarations, or | ||||
| 	// ~5kb of dense object declarations with 10000% alias expansion | ||||
| @ -347,7 +481,7 @@ func allowedAliasRatio(decodeCount int) float64 { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { | ||||
| func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) { | ||||
| 	d.decodeCount++ | ||||
| 	if d.aliasDepth > 0 { | ||||
| 		d.aliasCount++ | ||||
| @ -355,46 +489,55 @@ func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { | ||||
| 	if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { | ||||
| 		failf("document contains excessive aliasing") | ||||
| 	} | ||||
| 	switch n.kind { | ||||
| 	case documentNode: | ||||
| 	if out.Type() == nodeType { | ||||
| 		out.Set(reflect.ValueOf(n).Elem()) | ||||
| 		return true | ||||
| 	} | ||||
| 	switch n.Kind { | ||||
| 	case DocumentNode: | ||||
| 		return d.document(n, out) | ||||
| 	case aliasNode: | ||||
| 	case AliasNode: | ||||
| 		return d.alias(n, out) | ||||
| 	} | ||||
| 	out, unmarshaled, good := d.prepare(n, out) | ||||
| 	if unmarshaled { | ||||
| 		return good | ||||
| 	} | ||||
| 	switch n.kind { | ||||
| 	case scalarNode: | ||||
| 	switch n.Kind { | ||||
| 	case ScalarNode: | ||||
| 		good = d.scalar(n, out) | ||||
| 	case mappingNode: | ||||
| 	case MappingNode: | ||||
| 		good = d.mapping(n, out) | ||||
| 	case sequenceNode: | ||||
| 	case SequenceNode: | ||||
| 		good = d.sequence(n, out) | ||||
| 	case 0: | ||||
| 		if n.IsZero() { | ||||
| 			return d.null(out) | ||||
| 		} | ||||
| 		fallthrough | ||||
| 	default: | ||||
| 		panic("internal error: unknown node kind: " + strconv.Itoa(n.kind)) | ||||
| 		failf("cannot decode node with unknown kind %d", n.Kind) | ||||
| 	} | ||||
| 	return good | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) document(n *node, out reflect.Value) (good bool) { | ||||
| 	if len(n.children) == 1 { | ||||
| func (d *decoder) document(n *Node, out reflect.Value) (good bool) { | ||||
| 	if len(n.Content) == 1 { | ||||
| 		d.doc = n | ||||
| 		d.unmarshal(n.children[0], out) | ||||
| 		d.unmarshal(n.Content[0], out) | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) alias(n *node, out reflect.Value) (good bool) { | ||||
| func (d *decoder) alias(n *Node, out reflect.Value) (good bool) { | ||||
| 	if d.aliases[n] { | ||||
| 		// TODO this could actually be allowed in some circumstances. | ||||
| 		failf("anchor '%s' value contains itself", n.value) | ||||
| 		failf("anchor '%s' value contains itself", n.Value) | ||||
| 	} | ||||
| 	d.aliases[n] = true | ||||
| 	d.aliasDepth++ | ||||
| 	good = d.unmarshal(n.alias, out) | ||||
| 	good = d.unmarshal(n.Alias, out) | ||||
| 	d.aliasDepth-- | ||||
| 	delete(d.aliases, n) | ||||
| 	return good | ||||
| @ -408,15 +551,26 @@ func resetMap(out reflect.Value) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) scalar(n *node, out reflect.Value) bool { | ||||
| func (d *decoder) null(out reflect.Value) bool { | ||||
| 	if out.CanAddr() { | ||||
| 		switch out.Kind() { | ||||
| 		case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: | ||||
| 			out.Set(reflect.Zero(out.Type())) | ||||
| 			return true | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) scalar(n *Node, out reflect.Value) bool { | ||||
| 	var tag string | ||||
| 	var resolved interface{} | ||||
| 	if n.tag == "" && !n.implicit { | ||||
| 		tag = yaml_STR_TAG | ||||
| 		resolved = n.value | ||||
| 	if n.indicatedString() { | ||||
| 		tag = strTag | ||||
| 		resolved = n.Value | ||||
| 	} else { | ||||
| 		tag, resolved = resolve(n.tag, n.value) | ||||
| 		if tag == yaml_BINARY_TAG { | ||||
| 		tag, resolved = resolve(n.Tag, n.Value) | ||||
| 		if tag == binaryTag { | ||||
| 			data, err := base64.StdEncoding.DecodeString(resolved.(string)) | ||||
| 			if err != nil { | ||||
| 				failf("!!binary value contains invalid base64 data") | ||||
| @ -425,12 +579,7 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool { | ||||
| 		} | ||||
| 	} | ||||
| 	if resolved == nil { | ||||
| 		if out.Kind() == reflect.Map && !out.CanAddr() { | ||||
| 			resetMap(out) | ||||
| 		} else { | ||||
| 			out.Set(reflect.Zero(out.Type())) | ||||
| 		} | ||||
| 		return true | ||||
| 		return d.null(out) | ||||
| 	} | ||||
| 	if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { | ||||
| 		// We've resolved to exactly the type we want, so use that. | ||||
| @ -443,13 +592,13 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool { | ||||
| 		u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) | ||||
| 		if ok { | ||||
| 			var text []byte | ||||
| 			if tag == yaml_BINARY_TAG { | ||||
| 			if tag == binaryTag { | ||||
| 				text = []byte(resolved.(string)) | ||||
| 			} else { | ||||
| 				// We let any value be unmarshaled into TextUnmarshaler. | ||||
| 				// That might be more lax than we'd like, but the | ||||
| 				// TextUnmarshaler itself should bowl out any dubious values. | ||||
| 				text = []byte(n.value) | ||||
| 				text = []byte(n.Value) | ||||
| 			} | ||||
| 			err := u.UnmarshalText(text) | ||||
| 			if err != nil { | ||||
| @ -460,47 +609,37 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool { | ||||
| 	} | ||||
| 	switch out.Kind() { | ||||
| 	case reflect.String: | ||||
| 		if tag == yaml_BINARY_TAG { | ||||
| 		if tag == binaryTag { | ||||
| 			out.SetString(resolved.(string)) | ||||
| 			return true | ||||
| 		} | ||||
| 		if resolved != nil { | ||||
| 			out.SetString(n.value) | ||||
| 			return true | ||||
| 		} | ||||
| 		out.SetString(n.Value) | ||||
| 		return true | ||||
| 	case reflect.Interface: | ||||
| 		if resolved == nil { | ||||
| 			out.Set(reflect.Zero(out.Type())) | ||||
| 		} else if tag == yaml_TIMESTAMP_TAG { | ||||
| 			// It looks like a timestamp but for backward compatibility | ||||
| 			// reasons we set it as a string, so that code that unmarshals | ||||
| 			// timestamp-like values into interface{} will continue to | ||||
| 			// see a string and not a time.Time. | ||||
| 			// TODO(v3) Drop this. | ||||
| 			out.Set(reflect.ValueOf(n.value)) | ||||
| 		} else { | ||||
| 			out.Set(reflect.ValueOf(resolved)) | ||||
| 		} | ||||
| 		out.Set(reflect.ValueOf(resolved)) | ||||
| 		return true | ||||
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||
| 		// This used to work in v2, but it's very unfriendly. | ||||
| 		isDuration := out.Type() == durationType | ||||
| 
 | ||||
| 		switch resolved := resolved.(type) { | ||||
| 		case int: | ||||
| 			if !out.OverflowInt(int64(resolved)) { | ||||
| 			if !isDuration && !out.OverflowInt(int64(resolved)) { | ||||
| 				out.SetInt(int64(resolved)) | ||||
| 				return true | ||||
| 			} | ||||
| 		case int64: | ||||
| 			if !out.OverflowInt(resolved) { | ||||
| 			if !isDuration && !out.OverflowInt(resolved) { | ||||
| 				out.SetInt(resolved) | ||||
| 				return true | ||||
| 			} | ||||
| 		case uint64: | ||||
| 			if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { | ||||
| 			if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { | ||||
| 				out.SetInt(int64(resolved)) | ||||
| 				return true | ||||
| 			} | ||||
| 		case float64: | ||||
| 			if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { | ||||
| 			if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { | ||||
| 				out.SetInt(int64(resolved)) | ||||
| 				return true | ||||
| 			} | ||||
| @ -541,6 +680,17 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool { | ||||
| 		case bool: | ||||
| 			out.SetBool(resolved) | ||||
| 			return true | ||||
| 		case string: | ||||
| 			// This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html). | ||||
| 			// It only works if explicitly attempting to unmarshal into a typed bool value. | ||||
| 			switch resolved { | ||||
| 			case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON": | ||||
| 				out.SetBool(true) | ||||
| 				return true | ||||
| 			case "n", "N", "no", "No", "NO", "off", "Off", "OFF": | ||||
| 				out.SetBool(false) | ||||
| 				return true | ||||
| 			} | ||||
| 		} | ||||
| 	case reflect.Float32, reflect.Float64: | ||||
| 		switch resolved := resolved.(type) { | ||||
| @ -563,13 +713,7 @@ func (d *decoder) scalar(n *node, out reflect.Value) bool { | ||||
| 			return true | ||||
| 		} | ||||
| 	case reflect.Ptr: | ||||
| 		if out.Type().Elem() == reflect.TypeOf(resolved) { | ||||
| 			// TODO DOes this make sense? When is out a Ptr except when decoding a nil value? | ||||
| 			elem := reflect.New(out.Type().Elem()) | ||||
| 			elem.Elem().Set(reflect.ValueOf(resolved)) | ||||
| 			out.Set(elem) | ||||
| 			return true | ||||
| 		} | ||||
| 		panic("yaml internal error: please report the issue") | ||||
| 	} | ||||
| 	d.terror(n, tag, out) | ||||
| 	return false | ||||
| @ -582,8 +726,8 @@ func settableValueOf(i interface{}) reflect.Value { | ||||
| 	return sv | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { | ||||
| 	l := len(n.children) | ||||
| func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) { | ||||
| 	l := len(n.Content) | ||||
| 
 | ||||
| 	var iface reflect.Value | ||||
| 	switch out.Kind() { | ||||
| @ -598,7 +742,7 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { | ||||
| 		iface = out | ||||
| 		out = settableValueOf(make([]interface{}, l)) | ||||
| 	default: | ||||
| 		d.terror(n, yaml_SEQ_TAG, out) | ||||
| 		d.terror(n, seqTag, out) | ||||
| 		return false | ||||
| 	} | ||||
| 	et := out.Type().Elem() | ||||
| @ -606,7 +750,7 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { | ||||
| 	j := 0 | ||||
| 	for i := 0; i < l; i++ { | ||||
| 		e := reflect.New(et).Elem() | ||||
| 		if ok := d.unmarshal(n.children[i], e); ok { | ||||
| 		if ok := d.unmarshal(n.Content[i], e); ok { | ||||
| 			out.Index(j).Set(e) | ||||
| 			j++ | ||||
| 		} | ||||
| @ -620,51 +764,79 @@ func (d *decoder) sequence(n *node, out reflect.Value) (good bool) { | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { | ||||
| func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { | ||||
| 	l := len(n.Content) | ||||
| 	if d.uniqueKeys { | ||||
| 		nerrs := len(d.terrors) | ||||
| 		for i := 0; i < l; i += 2 { | ||||
| 			ni := n.Content[i] | ||||
| 			for j := i + 2; j < l; j += 2 { | ||||
| 				nj := n.Content[j] | ||||
| 				if ni.Kind == nj.Kind && ni.Value == nj.Value { | ||||
| 					d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line)) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		if len(d.terrors) > nerrs { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	switch out.Kind() { | ||||
| 	case reflect.Struct: | ||||
| 		return d.mappingStruct(n, out) | ||||
| 	case reflect.Slice: | ||||
| 		return d.mappingSlice(n, out) | ||||
| 	case reflect.Map: | ||||
| 		// okay | ||||
| 	case reflect.Interface: | ||||
| 		if d.mapType.Kind() == reflect.Map { | ||||
| 			iface := out | ||||
| 			out = reflect.MakeMap(d.mapType) | ||||
| 			iface.Set(out) | ||||
| 		iface := out | ||||
| 		if isStringMap(n) { | ||||
| 			out = reflect.MakeMap(d.stringMapType) | ||||
| 		} else { | ||||
| 			slicev := reflect.New(d.mapType).Elem() | ||||
| 			if !d.mappingSlice(n, slicev) { | ||||
| 				return false | ||||
| 			} | ||||
| 			out.Set(slicev) | ||||
| 			return true | ||||
| 			out = reflect.MakeMap(d.generalMapType) | ||||
| 		} | ||||
| 		iface.Set(out) | ||||
| 	default: | ||||
| 		d.terror(n, yaml_MAP_TAG, out) | ||||
| 		d.terror(n, mapTag, out) | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	outt := out.Type() | ||||
| 	kt := outt.Key() | ||||
| 	et := outt.Elem() | ||||
| 
 | ||||
| 	mapType := d.mapType | ||||
| 	if outt.Key() == ifaceType && outt.Elem() == ifaceType { | ||||
| 		d.mapType = outt | ||||
| 	stringMapType := d.stringMapType | ||||
| 	generalMapType := d.generalMapType | ||||
| 	if outt.Elem() == ifaceType { | ||||
| 		if outt.Key().Kind() == reflect.String { | ||||
| 			d.stringMapType = outt | ||||
| 		} else if outt.Key() == ifaceType { | ||||
| 			d.generalMapType = outt | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	mergedFields := d.mergedFields | ||||
| 	d.mergedFields = nil | ||||
| 
 | ||||
| 	var mergeNode *Node | ||||
| 
 | ||||
| 	mapIsNew := false | ||||
| 	if out.IsNil() { | ||||
| 		out.Set(reflect.MakeMap(outt)) | ||||
| 		mapIsNew = true | ||||
| 	} | ||||
| 	l := len(n.children) | ||||
| 	for i := 0; i < l; i += 2 { | ||||
| 		if isMerge(n.children[i]) { | ||||
| 			d.merge(n.children[i+1], out) | ||||
| 		if isMerge(n.Content[i]) { | ||||
| 			mergeNode = n.Content[i+1] | ||||
| 			continue | ||||
| 		} | ||||
| 		k := reflect.New(kt).Elem() | ||||
| 		if d.unmarshal(n.children[i], k) { | ||||
| 		if d.unmarshal(n.Content[i], k) { | ||||
| 			if mergedFields != nil { | ||||
| 				ki := k.Interface() | ||||
| 				if mergedFields[ki] { | ||||
| 					continue | ||||
| 				} | ||||
| 				mergedFields[ki] = true | ||||
| 			} | ||||
| 			kkind := k.Kind() | ||||
| 			if kkind == reflect.Interface { | ||||
| 				kkind = k.Elem().Kind() | ||||
| @ -673,87 +845,83 @@ func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { | ||||
| 				failf("invalid map key: %#v", k.Interface()) | ||||
| 			} | ||||
| 			e := reflect.New(et).Elem() | ||||
| 			if d.unmarshal(n.children[i+1], e) { | ||||
| 				d.setMapIndex(n.children[i+1], out, k, e) | ||||
| 			if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) { | ||||
| 				out.SetMapIndex(k, e) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	d.mapType = mapType | ||||
| 
 | ||||
| 	d.mergedFields = mergedFields | ||||
| 	if mergeNode != nil { | ||||
| 		d.merge(n, mergeNode, out) | ||||
| 	} | ||||
| 
 | ||||
| 	d.stringMapType = stringMapType | ||||
| 	d.generalMapType = generalMapType | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) setMapIndex(n *node, out, k, v reflect.Value) { | ||||
| 	if d.strict && out.MapIndex(k) != zeroValue { | ||||
| 		d.terrors = append(d.terrors, fmt.Sprintf("line %d: key %#v already set in map", n.line+1, k.Interface())) | ||||
| 		return | ||||
| 	} | ||||
| 	out.SetMapIndex(k, v) | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) { | ||||
| 	outt := out.Type() | ||||
| 	if outt.Elem() != mapItemType { | ||||
| 		d.terror(n, yaml_MAP_TAG, out) | ||||
| func isStringMap(n *Node) bool { | ||||
| 	if n.Kind != MappingNode { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	mapType := d.mapType | ||||
| 	d.mapType = outt | ||||
| 
 | ||||
| 	var slice []MapItem | ||||
| 	var l = len(n.children) | ||||
| 	l := len(n.Content) | ||||
| 	for i := 0; i < l; i += 2 { | ||||
| 		if isMerge(n.children[i]) { | ||||
| 			d.merge(n.children[i+1], out) | ||||
| 			continue | ||||
| 		} | ||||
| 		item := MapItem{} | ||||
| 		k := reflect.ValueOf(&item.Key).Elem() | ||||
| 		if d.unmarshal(n.children[i], k) { | ||||
| 			v := reflect.ValueOf(&item.Value).Elem() | ||||
| 			if d.unmarshal(n.children[i+1], v) { | ||||
| 				slice = append(slice, item) | ||||
| 			} | ||||
| 		shortTag := n.Content[i].ShortTag() | ||||
| 		if shortTag != strTag && shortTag != mergeTag { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	out.Set(reflect.ValueOf(slice)) | ||||
| 	d.mapType = mapType | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { | ||||
| func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { | ||||
| 	sinfo, err := getStructInfo(out.Type()) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	name := settableValueOf("") | ||||
| 	l := len(n.children) | ||||
| 
 | ||||
| 	var inlineMap reflect.Value | ||||
| 	var elemType reflect.Type | ||||
| 	if sinfo.InlineMap != -1 { | ||||
| 		inlineMap = out.Field(sinfo.InlineMap) | ||||
| 		inlineMap.Set(reflect.New(inlineMap.Type()).Elem()) | ||||
| 		elemType = inlineMap.Type().Elem() | ||||
| 	} | ||||
| 
 | ||||
| 	for _, index := range sinfo.InlineUnmarshalers { | ||||
| 		field := d.fieldByIndex(n, out, index) | ||||
| 		d.prepare(n, field) | ||||
| 	} | ||||
| 
 | ||||
| 	mergedFields := d.mergedFields | ||||
| 	d.mergedFields = nil | ||||
| 	var mergeNode *Node | ||||
| 	var doneFields []bool | ||||
| 	if d.strict { | ||||
| 	if d.uniqueKeys { | ||||
| 		doneFields = make([]bool, len(sinfo.FieldsList)) | ||||
| 	} | ||||
| 	name := settableValueOf("") | ||||
| 	l := len(n.Content) | ||||
| 	for i := 0; i < l; i += 2 { | ||||
| 		ni := n.children[i] | ||||
| 		ni := n.Content[i] | ||||
| 		if isMerge(ni) { | ||||
| 			d.merge(n.children[i+1], out) | ||||
| 			mergeNode = n.Content[i+1] | ||||
| 			continue | ||||
| 		} | ||||
| 		if !d.unmarshal(ni, name) { | ||||
| 			continue | ||||
| 		} | ||||
| 		if info, ok := sinfo.FieldsMap[name.String()]; ok { | ||||
| 			if d.strict { | ||||
| 		sname := name.String() | ||||
| 		if mergedFields != nil { | ||||
| 			if mergedFields[sname] { | ||||
| 				continue | ||||
| 			} | ||||
| 			mergedFields[sname] = true | ||||
| 		} | ||||
| 		if info, ok := sinfo.FieldsMap[sname]; ok { | ||||
| 			if d.uniqueKeys { | ||||
| 				if doneFields[info.Id] { | ||||
| 					d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.line+1, name.String(), out.Type())) | ||||
| 					d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) | ||||
| 					continue | ||||
| 				} | ||||
| 				doneFields[info.Id] = true | ||||
| @ -762,20 +930,25 @@ func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { | ||||
| 			if info.Inline == nil { | ||||
| 				field = out.Field(info.Num) | ||||
| 			} else { | ||||
| 				field = out.FieldByIndex(info.Inline) | ||||
| 				field = d.fieldByIndex(n, out, info.Inline) | ||||
| 			} | ||||
| 			d.unmarshal(n.children[i+1], field) | ||||
| 			d.unmarshal(n.Content[i+1], field) | ||||
| 		} else if sinfo.InlineMap != -1 { | ||||
| 			if inlineMap.IsNil() { | ||||
| 				inlineMap.Set(reflect.MakeMap(inlineMap.Type())) | ||||
| 			} | ||||
| 			value := reflect.New(elemType).Elem() | ||||
| 			d.unmarshal(n.children[i+1], value) | ||||
| 			d.setMapIndex(n.children[i+1], inlineMap, name, value) | ||||
| 		} else if d.strict { | ||||
| 			d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.line+1, name.String(), out.Type())) | ||||
| 			d.unmarshal(n.Content[i+1], value) | ||||
| 			inlineMap.SetMapIndex(name, value) | ||||
| 		} else if d.knownFields { | ||||
| 			d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	d.mergedFields = mergedFields | ||||
| 	if mergeNode != nil { | ||||
| 		d.merge(n, mergeNode, out) | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| @ -783,24 +956,34 @@ func failWantMap() { | ||||
| 	failf("map merge requires map or sequence of maps as the value") | ||||
| } | ||||
| 
 | ||||
| func (d *decoder) merge(n *node, out reflect.Value) { | ||||
| 	switch n.kind { | ||||
| 	case mappingNode: | ||||
| 		d.unmarshal(n, out) | ||||
| 	case aliasNode: | ||||
| 		if n.alias != nil && n.alias.kind != mappingNode { | ||||
| func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) { | ||||
| 	mergedFields := d.mergedFields | ||||
| 	if mergedFields == nil { | ||||
| 		d.mergedFields = make(map[interface{}]bool) | ||||
| 		for i := 0; i < len(parent.Content); i += 2 { | ||||
| 			k := reflect.New(ifaceType).Elem() | ||||
| 			if d.unmarshal(parent.Content[i], k) { | ||||
| 				d.mergedFields[k.Interface()] = true | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	switch merge.Kind { | ||||
| 	case MappingNode: | ||||
| 		d.unmarshal(merge, out) | ||||
| 	case AliasNode: | ||||
| 		if merge.Alias != nil && merge.Alias.Kind != MappingNode { | ||||
| 			failWantMap() | ||||
| 		} | ||||
| 		d.unmarshal(n, out) | ||||
| 	case sequenceNode: | ||||
| 		// Step backwards as earlier nodes take precedence. | ||||
| 		for i := len(n.children) - 1; i >= 0; i-- { | ||||
| 			ni := n.children[i] | ||||
| 			if ni.kind == aliasNode { | ||||
| 				if ni.alias != nil && ni.alias.kind != mappingNode { | ||||
| 		d.unmarshal(merge, out) | ||||
| 	case SequenceNode: | ||||
| 		for i := 0; i < len(merge.Content); i++ { | ||||
| 			ni := merge.Content[i] | ||||
| 			if ni.Kind == AliasNode { | ||||
| 				if ni.Alias != nil && ni.Alias.Kind != MappingNode { | ||||
| 					failWantMap() | ||||
| 				} | ||||
| 			} else if ni.kind != mappingNode { | ||||
| 			} else if ni.Kind != MappingNode { | ||||
| 				failWantMap() | ||||
| 			} | ||||
| 			d.unmarshal(ni, out) | ||||
| @ -808,8 +991,10 @@ func (d *decoder) merge(n *node, out reflect.Value) { | ||||
| 	default: | ||||
| 		failWantMap() | ||||
| 	} | ||||
| 
 | ||||
| 	d.mergedFields = mergedFields | ||||
| } | ||||
| 
 | ||||
| func isMerge(n *node) bool { | ||||
| 	return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG) | ||||
| func isMerge(n *Node) bool { | ||||
| 	return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag) | ||||
| } | ||||
							
								
								
									
										413
									
								
								vendor/gopkg.in/yaml.v2/emitterc.go → vendor/gopkg.in/yaml.v3/emitterc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										413
									
								
								vendor/gopkg.in/yaml.v2/emitterc.go → vendor/gopkg.in/yaml.v3/emitterc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +1,25 @@ | ||||
| // | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // Copyright (c) 2006-2010 Kirill Simonov | ||||
| // | ||||
| // 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. | ||||
| 
 | ||||
| package yaml | ||||
| 
 | ||||
| import ( | ||||
| @ -43,8 +65,13 @@ func put_break(emitter *yaml_emitter_t) bool { | ||||
| 	default: | ||||
| 		panic("unknown line break setting") | ||||
| 	} | ||||
| 	if emitter.column == 0 { | ||||
| 		emitter.space_above = true | ||||
| 	} | ||||
| 	emitter.column = 0 | ||||
| 	emitter.line++ | ||||
| 	// [Go] Do this here and below and drop from everywhere else (see commented lines). | ||||
| 	emitter.indention = true | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| @ -97,8 +124,13 @@ func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool { | ||||
| 		if !write(emitter, s, i) { | ||||
| 			return false | ||||
| 		} | ||||
| 		if emitter.column == 0 { | ||||
| 			emitter.space_above = true | ||||
| 		} | ||||
| 		emitter.column = 0 | ||||
| 		emitter.line++ | ||||
| 		// [Go] Do this here and above and drop from everywhere else (see commented lines). | ||||
| 		emitter.indention = true | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| @ -203,7 +235,14 @@ func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool | ||||
| 			emitter.indent = 0 | ||||
| 		} | ||||
| 	} else if !indentless { | ||||
| 		emitter.indent += emitter.best_indent | ||||
| 		// [Go] This was changed so that indentations are more regular. | ||||
| 		if emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE { | ||||
| 			// The first indent inside a sequence will just skip the "- " indicator. | ||||
| 			emitter.indent += 2 | ||||
| 		} else { | ||||
| 			// Everything else aligns to the chosen indentation. | ||||
| 			emitter.indent = emitter.best_indent*((emitter.indent+emitter.best_indent)/emitter.best_indent) | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| @ -228,16 +267,22 @@ func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bo | ||||
| 		return yaml_emitter_emit_document_end(emitter, event) | ||||
| 
 | ||||
| 	case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: | ||||
| 		return yaml_emitter_emit_flow_sequence_item(emitter, event, true) | ||||
| 		return yaml_emitter_emit_flow_sequence_item(emitter, event, true, false) | ||||
| 
 | ||||
| 	case yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE: | ||||
| 		return yaml_emitter_emit_flow_sequence_item(emitter, event, false, true) | ||||
| 
 | ||||
| 	case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: | ||||
| 		return yaml_emitter_emit_flow_sequence_item(emitter, event, false) | ||||
| 		return yaml_emitter_emit_flow_sequence_item(emitter, event, false, false) | ||||
| 
 | ||||
| 	case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: | ||||
| 		return yaml_emitter_emit_flow_mapping_key(emitter, event, true) | ||||
| 		return yaml_emitter_emit_flow_mapping_key(emitter, event, true, false) | ||||
| 
 | ||||
| 	case yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE: | ||||
| 		return yaml_emitter_emit_flow_mapping_key(emitter, event, false, true) | ||||
| 
 | ||||
| 	case yaml_EMIT_FLOW_MAPPING_KEY_STATE: | ||||
| 		return yaml_emitter_emit_flow_mapping_key(emitter, event, false) | ||||
| 		return yaml_emitter_emit_flow_mapping_key(emitter, event, false, false) | ||||
| 
 | ||||
| 	case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: | ||||
| 		return yaml_emitter_emit_flow_mapping_value(emitter, event, true) | ||||
| @ -298,6 +343,8 @@ func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t | ||||
| 	emitter.column = 0 | ||||
| 	emitter.whitespace = true | ||||
| 	emitter.indention = true | ||||
| 	emitter.space_above = true | ||||
| 	emitter.foot_indent = -1 | ||||
| 
 | ||||
| 	if emitter.encoding != yaml_UTF8_ENCODING { | ||||
| 		if !yaml_emitter_write_bom(emitter) { | ||||
| @ -392,13 +439,22 @@ func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event | ||||
| 			if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) { | ||||
| 				return false | ||||
| 			} | ||||
| 			if emitter.canonical { | ||||
| 			if emitter.canonical || true { | ||||
| 				if !yaml_emitter_write_indent(emitter) { | ||||
| 					return false | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if len(emitter.head_comment) > 0 { | ||||
| 			if !yaml_emitter_process_head_comment(emitter) { | ||||
| 				return false | ||||
| 			} | ||||
| 			if !put_break(emitter) { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE | ||||
| 		return true | ||||
| 	} | ||||
| @ -425,7 +481,20 @@ func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event | ||||
| // Expect the root node. | ||||
| func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { | ||||
| 	emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) | ||||
| 	return yaml_emitter_emit_node(emitter, event, true, false, false, false) | ||||
| 
 | ||||
| 	if !yaml_emitter_process_head_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !yaml_emitter_emit_node(emitter, event, true, false, false, false) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !yaml_emitter_process_line_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !yaml_emitter_process_foot_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Expect DOCUMENT-END. | ||||
| @ -433,6 +502,12 @@ func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t | ||||
| 	if event.typ != yaml_DOCUMENT_END_EVENT { | ||||
| 		return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END") | ||||
| 	} | ||||
| 	// [Go] Force document foot separation. | ||||
| 	emitter.foot_indent = 0 | ||||
| 	if !yaml_emitter_process_foot_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	emitter.foot_indent = -1 | ||||
| 	if !yaml_emitter_write_indent(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| @ -454,7 +529,7 @@ func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t | ||||
| } | ||||
| 
 | ||||
| // Expect a flow item node. | ||||
| func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { | ||||
| func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool { | ||||
| 	if first { | ||||
| 		if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { | ||||
| 			return false | ||||
| @ -466,13 +541,15 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e | ||||
| 	} | ||||
| 
 | ||||
| 	if event.typ == yaml_SEQUENCE_END_EVENT { | ||||
| 		emitter.flow_level-- | ||||
| 		emitter.indent = emitter.indents[len(emitter.indents)-1] | ||||
| 		emitter.indents = emitter.indents[:len(emitter.indents)-1] | ||||
| 		if emitter.canonical && !first { | ||||
| 		if emitter.canonical && !first && !trail { | ||||
| 			if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| 		emitter.flow_level-- | ||||
| 		emitter.indent = emitter.indents[len(emitter.indents)-1] | ||||
| 		emitter.indents = emitter.indents[:len(emitter.indents)-1] | ||||
| 		if emitter.column == 0 || emitter.canonical && !first { | ||||
| 			if !yaml_emitter_write_indent(emitter) { | ||||
| 				return false | ||||
| 			} | ||||
| @ -480,29 +557,62 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e | ||||
| 		if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { | ||||
| 			return false | ||||
| 		} | ||||
| 		if !yaml_emitter_process_line_comment(emitter) { | ||||
| 			return false | ||||
| 		} | ||||
| 		if !yaml_emitter_process_foot_comment(emitter) { | ||||
| 			return false | ||||
| 		} | ||||
| 		emitter.state = emitter.states[len(emitter.states)-1] | ||||
| 		emitter.states = emitter.states[:len(emitter.states)-1] | ||||
| 
 | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	if !first { | ||||
| 	if !first && !trail { | ||||
| 		if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if !yaml_emitter_process_head_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if emitter.column == 0 { | ||||
| 		if !yaml_emitter_write_indent(emitter) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if emitter.canonical || emitter.column > emitter.best_width { | ||||
| 		if !yaml_emitter_write_indent(emitter) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) | ||||
| 	return yaml_emitter_emit_node(emitter, event, false, true, false, false) | ||||
| 	if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { | ||||
| 		emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE) | ||||
| 	} else { | ||||
| 		emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) | ||||
| 	} | ||||
| 	if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { | ||||
| 		if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	if !yaml_emitter_process_line_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !yaml_emitter_process_foot_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Expect a flow key node. | ||||
| func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { | ||||
| func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool { | ||||
| 	if first { | ||||
| 		if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { | ||||
| 			return false | ||||
| @ -514,13 +624,18 @@ func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_eve | ||||
| 	} | ||||
| 
 | ||||
| 	if event.typ == yaml_MAPPING_END_EVENT { | ||||
| 		if (emitter.canonical || len(emitter.head_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0) && !first && !trail { | ||||
| 			if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| 		if !yaml_emitter_process_head_comment(emitter) { | ||||
| 			return false | ||||
| 		} | ||||
| 		emitter.flow_level-- | ||||
| 		emitter.indent = emitter.indents[len(emitter.indents)-1] | ||||
| 		emitter.indents = emitter.indents[:len(emitter.indents)-1] | ||||
| 		if emitter.canonical && !first { | ||||
| 			if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { | ||||
| 				return false | ||||
| 			} | ||||
| 			if !yaml_emitter_write_indent(emitter) { | ||||
| 				return false | ||||
| 			} | ||||
| @ -528,16 +643,33 @@ func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_eve | ||||
| 		if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { | ||||
| 			return false | ||||
| 		} | ||||
| 		if !yaml_emitter_process_line_comment(emitter) { | ||||
| 			return false | ||||
| 		} | ||||
| 		if !yaml_emitter_process_foot_comment(emitter) { | ||||
| 			return false | ||||
| 		} | ||||
| 		emitter.state = emitter.states[len(emitter.states)-1] | ||||
| 		emitter.states = emitter.states[:len(emitter.states)-1] | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	if !first { | ||||
| 	if !first && !trail { | ||||
| 		if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if !yaml_emitter_process_head_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	if emitter.column == 0 { | ||||
| 		if !yaml_emitter_write_indent(emitter) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if emitter.canonical || emitter.column > emitter.best_width { | ||||
| 		if !yaml_emitter_write_indent(emitter) { | ||||
| 			return false | ||||
| @ -571,14 +703,32 @@ func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_e | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) | ||||
| 	return yaml_emitter_emit_node(emitter, event, false, false, true, false) | ||||
| 	if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { | ||||
| 		emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE) | ||||
| 	} else { | ||||
| 		emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) | ||||
| 	} | ||||
| 	if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { | ||||
| 		if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	if !yaml_emitter_process_line_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !yaml_emitter_process_foot_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Expect a block item node. | ||||
| func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { | ||||
| 	if first { | ||||
| 		if !yaml_emitter_increase_indent(emitter, false, emitter.mapping_context && !emitter.indention) { | ||||
| 		if !yaml_emitter_increase_indent(emitter, false, false) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| @ -589,6 +739,9 @@ func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_ | ||||
| 		emitter.states = emitter.states[:len(emitter.states)-1] | ||||
| 		return true | ||||
| 	} | ||||
| 	if !yaml_emitter_process_head_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !yaml_emitter_write_indent(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| @ -596,7 +749,16 @@ func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_ | ||||
| 		return false | ||||
| 	} | ||||
| 	emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE) | ||||
| 	return yaml_emitter_emit_node(emitter, event, false, true, false, false) | ||||
| 	if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !yaml_emitter_process_line_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !yaml_emitter_process_foot_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Expect a block key node. | ||||
| @ -606,6 +768,9 @@ func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_ev | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	if !yaml_emitter_process_head_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if event.typ == yaml_MAPPING_END_EVENT { | ||||
| 		emitter.indent = emitter.indents[len(emitter.indents)-1] | ||||
| 		emitter.indents = emitter.indents[:len(emitter.indents)-1] | ||||
| @ -616,6 +781,13 @@ func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_ev | ||||
| 	if !yaml_emitter_write_indent(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if len(emitter.line_comment) > 0 { | ||||
| 		// [Go] A line comment was provided for the key. That's unusual as the | ||||
| 		//      scanner associates line comments with the value. Either way, | ||||
| 		//      save the line comment and render it appropriately later. | ||||
| 		emitter.key_line_comment = emitter.line_comment | ||||
| 		emitter.line_comment = nil | ||||
| 	} | ||||
| 	if yaml_emitter_check_simple_key(emitter) { | ||||
| 		emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE) | ||||
| 		return yaml_emitter_emit_node(emitter, event, false, false, true, true) | ||||
| @ -641,8 +813,42 @@ func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_ | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	if len(emitter.key_line_comment) > 0 { | ||||
| 		// [Go] Line comments are generally associated with the value, but when there's | ||||
| 		//      no value on the same line as a mapping key they end up attached to the | ||||
| 		//      key itself. | ||||
| 		if event.typ == yaml_SCALAR_EVENT { | ||||
| 			if len(emitter.line_comment) == 0 { | ||||
| 				// A scalar is coming and it has no line comments by itself yet, | ||||
| 				// so just let it handle the line comment as usual. If it has a | ||||
| 				// line comment, we can't have both so the one from the key is lost. | ||||
| 				emitter.line_comment = emitter.key_line_comment | ||||
| 				emitter.key_line_comment = nil | ||||
| 			} | ||||
| 		} else if event.sequence_style() != yaml_FLOW_SEQUENCE_STYLE && (event.typ == yaml_MAPPING_START_EVENT || event.typ == yaml_SEQUENCE_START_EVENT) { | ||||
| 			// An indented block follows, so write the comment right now. | ||||
| 			emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment | ||||
| 			if !yaml_emitter_process_line_comment(emitter) { | ||||
| 				return false | ||||
| 			} | ||||
| 			emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment | ||||
| 		} | ||||
| 	} | ||||
| 	emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) | ||||
| 	return yaml_emitter_emit_node(emitter, event, false, false, true, false) | ||||
| 	if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !yaml_emitter_process_line_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !yaml_emitter_process_foot_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func yaml_emitter_silent_nil_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { | ||||
| 	return event.typ == yaml_SCALAR_EVENT && event.implicit && !emitter.canonical && len(emitter.scalar_data.value) == 0 | ||||
| } | ||||
| 
 | ||||
| // Expect a node. | ||||
| @ -908,6 +1114,71 @@ func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool { | ||||
| 	panic("unknown scalar style") | ||||
| } | ||||
| 
 | ||||
| // Write a head comment. | ||||
| func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool { | ||||
| 	if len(emitter.tail_comment) > 0 { | ||||
| 		if !yaml_emitter_write_indent(emitter) { | ||||
| 			return false | ||||
| 		} | ||||
| 		if !yaml_emitter_write_comment(emitter, emitter.tail_comment) { | ||||
| 			return false | ||||
| 		} | ||||
| 		emitter.tail_comment = emitter.tail_comment[:0] | ||||
| 		emitter.foot_indent = emitter.indent | ||||
| 		if emitter.foot_indent < 0 { | ||||
| 			emitter.foot_indent = 0 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if len(emitter.head_comment) == 0 { | ||||
| 		return true | ||||
| 	} | ||||
| 	if !yaml_emitter_write_indent(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !yaml_emitter_write_comment(emitter, emitter.head_comment) { | ||||
| 		return false | ||||
| 	} | ||||
| 	emitter.head_comment = emitter.head_comment[:0] | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Write an line comment. | ||||
| func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool { | ||||
| 	if len(emitter.line_comment) == 0 { | ||||
| 		return true | ||||
| 	} | ||||
| 	if !emitter.whitespace { | ||||
| 		if !put(emitter, ' ') { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	if !yaml_emitter_write_comment(emitter, emitter.line_comment) { | ||||
| 		return false | ||||
| 	} | ||||
| 	emitter.line_comment = emitter.line_comment[:0] | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Write a foot comment. | ||||
| func yaml_emitter_process_foot_comment(emitter *yaml_emitter_t) bool { | ||||
| 	if len(emitter.foot_comment) == 0 { | ||||
| 		return true | ||||
| 	} | ||||
| 	if !yaml_emitter_write_indent(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !yaml_emitter_write_comment(emitter, emitter.foot_comment) { | ||||
| 		return false | ||||
| 	} | ||||
| 	emitter.foot_comment = emitter.foot_comment[:0] | ||||
| 	emitter.foot_indent = emitter.indent | ||||
| 	if emitter.foot_indent < 0 { | ||||
| 		emitter.foot_indent = 0 | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Check if a %YAML directive is valid. | ||||
| func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool { | ||||
| 	if version_directive.major != 1 || version_directive.minor != 1 { | ||||
| @ -987,6 +1258,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { | ||||
| 		flow_indicators    = false | ||||
| 		line_breaks        = false | ||||
| 		special_characters = false | ||||
| 		tab_characters     = false | ||||
| 
 | ||||
| 		leading_space  = false | ||||
| 		leading_break  = false | ||||
| @ -1055,7 +1327,9 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { | ||||
| 		if value[i] == '\t' { | ||||
| 			tab_characters = true | ||||
| 		} else if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { | ||||
| 			special_characters = true | ||||
| 		} | ||||
| 		if is_space(value, i) { | ||||
| @ -1110,10 +1384,12 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { | ||||
| 		emitter.scalar_data.block_plain_allowed = false | ||||
| 		emitter.scalar_data.single_quoted_allowed = false | ||||
| 	} | ||||
| 	if space_break || special_characters { | ||||
| 	if space_break || tab_characters || special_characters { | ||||
| 		emitter.scalar_data.flow_plain_allowed = false | ||||
| 		emitter.scalar_data.block_plain_allowed = false | ||||
| 		emitter.scalar_data.single_quoted_allowed = false | ||||
| 	} | ||||
| 	if space_break || special_characters { | ||||
| 		emitter.scalar_data.block_allowed = false | ||||
| 	} | ||||
| 	if line_breaks { | ||||
| @ -1137,6 +1413,19 @@ func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bo | ||||
| 	emitter.tag_data.suffix = nil | ||||
| 	emitter.scalar_data.value = nil | ||||
| 
 | ||||
| 	if len(event.head_comment) > 0 { | ||||
| 		emitter.head_comment = event.head_comment | ||||
| 	} | ||||
| 	if len(event.line_comment) > 0 { | ||||
| 		emitter.line_comment = event.line_comment | ||||
| 	} | ||||
| 	if len(event.foot_comment) > 0 { | ||||
| 		emitter.foot_comment = event.foot_comment | ||||
| 	} | ||||
| 	if len(event.tail_comment) > 0 { | ||||
| 		emitter.tail_comment = event.tail_comment | ||||
| 	} | ||||
| 
 | ||||
| 	switch event.typ { | ||||
| 	case yaml_ALIAS_EVENT: | ||||
| 		if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) { | ||||
| @ -1208,13 +1497,20 @@ func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	if emitter.foot_indent == indent { | ||||
| 		if !put_break(emitter) { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	for emitter.column < indent { | ||||
| 		if !put(emitter, ' ') { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	emitter.whitespace = true | ||||
| 	emitter.indention = true | ||||
| 	//emitter.indention = true | ||||
| 	emitter.space_above = false | ||||
| 	emitter.foot_indent = -1 | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| @ -1311,7 +1607,7 @@ func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_ | ||||
| } | ||||
| 
 | ||||
| func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { | ||||
| 	if !emitter.whitespace { | ||||
| 	if len(value) > 0 && !emitter.whitespace { | ||||
| 		if !put(emitter, ' ') { | ||||
| 			return false | ||||
| 		} | ||||
| @ -1341,7 +1637,7 @@ func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allo | ||||
| 			if !write_break(emitter, value, &i) { | ||||
| 				return false | ||||
| 			} | ||||
| 			emitter.indention = true | ||||
| 			//emitter.indention = true | ||||
| 			breaks = true | ||||
| 		} else { | ||||
| 			if breaks { | ||||
| @ -1358,7 +1654,9 @@ func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allo | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	emitter.whitespace = false | ||||
| 	if len(value) > 0 { | ||||
| 		emitter.whitespace = false | ||||
| 	} | ||||
| 	emitter.indention = false | ||||
| 	if emitter.root_context { | ||||
| 		emitter.open_ended = true | ||||
| @ -1397,7 +1695,7 @@ func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []by | ||||
| 			if !write_break(emitter, value, &i) { | ||||
| 				return false | ||||
| 			} | ||||
| 			emitter.indention = true | ||||
| 			//emitter.indention = true | ||||
| 			breaks = true | ||||
| 		} else { | ||||
| 			if breaks { | ||||
| @ -1596,10 +1894,10 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo | ||||
| 	if !yaml_emitter_write_block_scalar_hints(emitter, value) { | ||||
| 		return false | ||||
| 	} | ||||
| 	if !put_break(emitter) { | ||||
| 	if !yaml_emitter_process_line_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	emitter.indention = true | ||||
| 	//emitter.indention = true | ||||
| 	emitter.whitespace = true | ||||
| 	breaks := true | ||||
| 	for i := 0; i < len(value); { | ||||
| @ -1607,7 +1905,7 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo | ||||
| 			if !write_break(emitter, value, &i) { | ||||
| 				return false | ||||
| 			} | ||||
| 			emitter.indention = true | ||||
| 			//emitter.indention = true | ||||
| 			breaks = true | ||||
| 		} else { | ||||
| 			if breaks { | ||||
| @ -1633,11 +1931,11 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo | ||||
| 	if !yaml_emitter_write_block_scalar_hints(emitter, value) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	if !put_break(emitter) { | ||||
| 	if !yaml_emitter_process_line_comment(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 	emitter.indention = true | ||||
| 
 | ||||
| 	//emitter.indention = true | ||||
| 	emitter.whitespace = true | ||||
| 
 | ||||
| 	breaks := true | ||||
| @ -1658,7 +1956,7 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo | ||||
| 			if !write_break(emitter, value, &i) { | ||||
| 				return false | ||||
| 			} | ||||
| 			emitter.indention = true | ||||
| 			//emitter.indention = true | ||||
| 			breaks = true | ||||
| 		} else { | ||||
| 			if breaks { | ||||
| @ -1683,3 +1981,40 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func yaml_emitter_write_comment(emitter *yaml_emitter_t, comment []byte) bool { | ||||
| 	breaks := false | ||||
| 	pound := false | ||||
| 	for i := 0; i < len(comment); { | ||||
| 		if is_break(comment, i) { | ||||
| 			if !write_break(emitter, comment, &i) { | ||||
| 				return false | ||||
| 			} | ||||
| 			//emitter.indention = true | ||||
| 			breaks = true | ||||
| 			pound = false | ||||
| 		} else { | ||||
| 			if breaks && !yaml_emitter_write_indent(emitter) { | ||||
| 				return false | ||||
| 			} | ||||
| 			if !pound { | ||||
| 				if comment[i] != '#' && (!put(emitter, '#') || !put(emitter, ' ')) { | ||||
| 					return false | ||||
| 				} | ||||
| 				pound = true | ||||
| 			} | ||||
| 			if !write(emitter, comment, &i) { | ||||
| 				return false | ||||
| 			} | ||||
| 			emitter.indention = false | ||||
| 			breaks = false | ||||
| 		} | ||||
| 	} | ||||
| 	if !breaks && !put_break(emitter) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	emitter.whitespace = true | ||||
| 	//emitter.indention = true | ||||
| 	return true | ||||
| } | ||||
							
								
								
									
										577
									
								
								vendor/gopkg.in/yaml.v3/encode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										577
									
								
								vendor/gopkg.in/yaml.v3/encode.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,577 @@ | ||||
| // | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package yaml | ||||
|  | ||||
| import ( | ||||
| 	"encoding" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"reflect" | ||||
| 	"regexp" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
|  | ||||
| type encoder struct { | ||||
| 	emitter  yaml_emitter_t | ||||
| 	event    yaml_event_t | ||||
| 	out      []byte | ||||
| 	flow     bool | ||||
| 	indent   int | ||||
| 	doneInit bool | ||||
| } | ||||
|  | ||||
| func newEncoder() *encoder { | ||||
| 	e := &encoder{} | ||||
| 	yaml_emitter_initialize(&e.emitter) | ||||
| 	yaml_emitter_set_output_string(&e.emitter, &e.out) | ||||
| 	yaml_emitter_set_unicode(&e.emitter, true) | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| func newEncoderWithWriter(w io.Writer) *encoder { | ||||
| 	e := &encoder{} | ||||
| 	yaml_emitter_initialize(&e.emitter) | ||||
| 	yaml_emitter_set_output_writer(&e.emitter, w) | ||||
| 	yaml_emitter_set_unicode(&e.emitter, true) | ||||
| 	return e | ||||
| } | ||||
|  | ||||
| func (e *encoder) init() { | ||||
| 	if e.doneInit { | ||||
| 		return | ||||
| 	} | ||||
| 	if e.indent == 0 { | ||||
| 		e.indent = 4 | ||||
| 	} | ||||
| 	e.emitter.best_indent = e.indent | ||||
| 	yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) | ||||
| 	e.emit() | ||||
| 	e.doneInit = true | ||||
| } | ||||
|  | ||||
| func (e *encoder) finish() { | ||||
| 	e.emitter.open_ended = false | ||||
| 	yaml_stream_end_event_initialize(&e.event) | ||||
| 	e.emit() | ||||
| } | ||||
|  | ||||
| func (e *encoder) destroy() { | ||||
| 	yaml_emitter_delete(&e.emitter) | ||||
| } | ||||
|  | ||||
| func (e *encoder) emit() { | ||||
| 	// This will internally delete the e.event value. | ||||
| 	e.must(yaml_emitter_emit(&e.emitter, &e.event)) | ||||
| } | ||||
|  | ||||
| func (e *encoder) must(ok bool) { | ||||
| 	if !ok { | ||||
| 		msg := e.emitter.problem | ||||
| 		if msg == "" { | ||||
| 			msg = "unknown problem generating YAML content" | ||||
| 		} | ||||
| 		failf("%s", msg) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (e *encoder) marshalDoc(tag string, in reflect.Value) { | ||||
| 	e.init() | ||||
| 	var node *Node | ||||
| 	if in.IsValid() { | ||||
| 		node, _ = in.Interface().(*Node) | ||||
| 	} | ||||
| 	if node != nil && node.Kind == DocumentNode { | ||||
| 		e.nodev(in) | ||||
| 	} else { | ||||
| 		yaml_document_start_event_initialize(&e.event, nil, nil, true) | ||||
| 		e.emit() | ||||
| 		e.marshal(tag, in) | ||||
| 		yaml_document_end_event_initialize(&e.event, true) | ||||
| 		e.emit() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (e *encoder) marshal(tag string, in reflect.Value) { | ||||
| 	tag = shortTag(tag) | ||||
| 	if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { | ||||
| 		e.nilv() | ||||
| 		return | ||||
| 	} | ||||
| 	iface := in.Interface() | ||||
| 	switch value := iface.(type) { | ||||
| 	case *Node: | ||||
| 		e.nodev(in) | ||||
| 		return | ||||
| 	case Node: | ||||
| 		if !in.CanAddr() { | ||||
| 			var n = reflect.New(in.Type()).Elem() | ||||
| 			n.Set(in) | ||||
| 			in = n | ||||
| 		} | ||||
| 		e.nodev(in.Addr()) | ||||
| 		return | ||||
| 	case time.Time: | ||||
| 		e.timev(tag, in) | ||||
| 		return | ||||
| 	case *time.Time: | ||||
| 		e.timev(tag, in.Elem()) | ||||
| 		return | ||||
| 	case time.Duration: | ||||
| 		e.stringv(tag, reflect.ValueOf(value.String())) | ||||
| 		return | ||||
| 	case Marshaler: | ||||
| 		v, err := value.MarshalYAML() | ||||
| 		if err != nil { | ||||
| 			fail(err) | ||||
| 		} | ||||
| 		if v == nil { | ||||
| 			e.nilv() | ||||
| 			return | ||||
| 		} | ||||
| 		e.marshal(tag, reflect.ValueOf(v)) | ||||
| 		return | ||||
| 	case encoding.TextMarshaler: | ||||
| 		text, err := value.MarshalText() | ||||
| 		if err != nil { | ||||
| 			fail(err) | ||||
| 		} | ||||
| 		in = reflect.ValueOf(string(text)) | ||||
| 	case nil: | ||||
| 		e.nilv() | ||||
| 		return | ||||
| 	} | ||||
| 	switch in.Kind() { | ||||
| 	case reflect.Interface: | ||||
| 		e.marshal(tag, in.Elem()) | ||||
| 	case reflect.Map: | ||||
| 		e.mapv(tag, in) | ||||
| 	case reflect.Ptr: | ||||
| 		e.marshal(tag, in.Elem()) | ||||
| 	case reflect.Struct: | ||||
| 		e.structv(tag, in) | ||||
| 	case reflect.Slice, reflect.Array: | ||||
| 		e.slicev(tag, in) | ||||
| 	case reflect.String: | ||||
| 		e.stringv(tag, in) | ||||
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||
| 		e.intv(tag, in) | ||||
| 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||||
| 		e.uintv(tag, in) | ||||
| 	case reflect.Float32, reflect.Float64: | ||||
| 		e.floatv(tag, in) | ||||
| 	case reflect.Bool: | ||||
| 		e.boolv(tag, in) | ||||
| 	default: | ||||
| 		panic("cannot marshal type: " + in.Type().String()) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (e *encoder) mapv(tag string, in reflect.Value) { | ||||
| 	e.mappingv(tag, func() { | ||||
| 		keys := keyList(in.MapKeys()) | ||||
| 		sort.Sort(keys) | ||||
| 		for _, k := range keys { | ||||
| 			e.marshal("", k) | ||||
| 			e.marshal("", in.MapIndex(k)) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) { | ||||
| 	for _, num := range index { | ||||
| 		for { | ||||
| 			if v.Kind() == reflect.Ptr { | ||||
| 				if v.IsNil() { | ||||
| 					return reflect.Value{} | ||||
| 				} | ||||
| 				v = v.Elem() | ||||
| 				continue | ||||
| 			} | ||||
| 			break | ||||
| 		} | ||||
| 		v = v.Field(num) | ||||
| 	} | ||||
| 	return v | ||||
| } | ||||
|  | ||||
| func (e *encoder) structv(tag string, in reflect.Value) { | ||||
| 	sinfo, err := getStructInfo(in.Type()) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	e.mappingv(tag, func() { | ||||
| 		for _, info := range sinfo.FieldsList { | ||||
| 			var value reflect.Value | ||||
| 			if info.Inline == nil { | ||||
| 				value = in.Field(info.Num) | ||||
| 			} else { | ||||
| 				value = e.fieldByIndex(in, info.Inline) | ||||
| 				if !value.IsValid() { | ||||
| 					continue | ||||
| 				} | ||||
| 			} | ||||
| 			if info.OmitEmpty && isZero(value) { | ||||
| 				continue | ||||
| 			} | ||||
| 			e.marshal("", reflect.ValueOf(info.Key)) | ||||
| 			e.flow = info.Flow | ||||
| 			e.marshal("", value) | ||||
| 		} | ||||
| 		if sinfo.InlineMap >= 0 { | ||||
| 			m := in.Field(sinfo.InlineMap) | ||||
| 			if m.Len() > 0 { | ||||
| 				e.flow = false | ||||
| 				keys := keyList(m.MapKeys()) | ||||
| 				sort.Sort(keys) | ||||
| 				for _, k := range keys { | ||||
| 					if _, found := sinfo.FieldsMap[k.String()]; found { | ||||
| 						panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String())) | ||||
| 					} | ||||
| 					e.marshal("", k) | ||||
| 					e.flow = false | ||||
| 					e.marshal("", m.MapIndex(k)) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func (e *encoder) mappingv(tag string, f func()) { | ||||
| 	implicit := tag == "" | ||||
| 	style := yaml_BLOCK_MAPPING_STYLE | ||||
| 	if e.flow { | ||||
| 		e.flow = false | ||||
| 		style = yaml_FLOW_MAPPING_STYLE | ||||
| 	} | ||||
| 	yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) | ||||
| 	e.emit() | ||||
| 	f() | ||||
| 	yaml_mapping_end_event_initialize(&e.event) | ||||
| 	e.emit() | ||||
| } | ||||
|  | ||||
| func (e *encoder) slicev(tag string, in reflect.Value) { | ||||
| 	implicit := tag == "" | ||||
| 	style := yaml_BLOCK_SEQUENCE_STYLE | ||||
| 	if e.flow { | ||||
| 		e.flow = false | ||||
| 		style = yaml_FLOW_SEQUENCE_STYLE | ||||
| 	} | ||||
| 	e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) | ||||
| 	e.emit() | ||||
| 	n := in.Len() | ||||
| 	for i := 0; i < n; i++ { | ||||
| 		e.marshal("", in.Index(i)) | ||||
| 	} | ||||
| 	e.must(yaml_sequence_end_event_initialize(&e.event)) | ||||
| 	e.emit() | ||||
| } | ||||
|  | ||||
| // isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. | ||||
| // | ||||
| // The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported | ||||
| // in YAML 1.2 and by this package, but these should be marshalled quoted for | ||||
| // the time being for compatibility with other parsers. | ||||
| func isBase60Float(s string) (result bool) { | ||||
| 	// Fast path. | ||||
| 	if s == "" { | ||||
| 		return false | ||||
| 	} | ||||
| 	c := s[0] | ||||
| 	if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { | ||||
| 		return false | ||||
| 	} | ||||
| 	// Do the full match. | ||||
| 	return base60float.MatchString(s) | ||||
| } | ||||
|  | ||||
| // From http://yaml.org/type/float.html, except the regular expression there | ||||
| // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. | ||||
| var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) | ||||
|  | ||||
| // isOldBool returns whether s is bool notation as defined in YAML 1.1. | ||||
| // | ||||
| // We continue to force strings that YAML 1.1 would interpret as booleans to be | ||||
| // rendered as quotes strings so that the marshalled output valid for YAML 1.1 | ||||
| // parsing. | ||||
| func isOldBool(s string) (result bool) { | ||||
| 	switch s { | ||||
| 	case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON", | ||||
| 		"n", "N", "no", "No", "NO", "off", "Off", "OFF": | ||||
| 		return true | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (e *encoder) stringv(tag string, in reflect.Value) { | ||||
| 	var style yaml_scalar_style_t | ||||
| 	s := in.String() | ||||
| 	canUsePlain := true | ||||
| 	switch { | ||||
| 	case !utf8.ValidString(s): | ||||
| 		if tag == binaryTag { | ||||
| 			failf("explicitly tagged !!binary data must be base64-encoded") | ||||
| 		} | ||||
| 		if tag != "" { | ||||
| 			failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) | ||||
| 		} | ||||
| 		// It can't be encoded directly as YAML so use a binary tag | ||||
| 		// and encode it as base64. | ||||
| 		tag = binaryTag | ||||
| 		s = encodeBase64(s) | ||||
| 	case tag == "": | ||||
| 		// Check to see if it would resolve to a specific | ||||
| 		// tag when encoded unquoted. If it doesn't, | ||||
| 		// there's no need to quote it. | ||||
| 		rtag, _ := resolve("", s) | ||||
| 		canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s)) | ||||
| 	} | ||||
| 	// Note: it's possible for user code to emit invalid YAML | ||||
| 	// if they explicitly specify a tag and a string containing | ||||
| 	// text that's incompatible with that tag. | ||||
| 	switch { | ||||
| 	case strings.Contains(s, "\n"): | ||||
| 		if e.flow { | ||||
| 			style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | ||||
| 		} else { | ||||
| 			style = yaml_LITERAL_SCALAR_STYLE | ||||
| 		} | ||||
| 	case canUsePlain: | ||||
| 		style = yaml_PLAIN_SCALAR_STYLE | ||||
| 	default: | ||||
| 		style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | ||||
| 	} | ||||
| 	e.emitScalar(s, "", tag, style, nil, nil, nil, nil) | ||||
| } | ||||
|  | ||||
| func (e *encoder) boolv(tag string, in reflect.Value) { | ||||
| 	var s string | ||||
| 	if in.Bool() { | ||||
| 		s = "true" | ||||
| 	} else { | ||||
| 		s = "false" | ||||
| 	} | ||||
| 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | ||||
| } | ||||
|  | ||||
| func (e *encoder) intv(tag string, in reflect.Value) { | ||||
| 	s := strconv.FormatInt(in.Int(), 10) | ||||
| 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | ||||
| } | ||||
|  | ||||
| func (e *encoder) uintv(tag string, in reflect.Value) { | ||||
| 	s := strconv.FormatUint(in.Uint(), 10) | ||||
| 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | ||||
| } | ||||
|  | ||||
| func (e *encoder) timev(tag string, in reflect.Value) { | ||||
| 	t := in.Interface().(time.Time) | ||||
| 	s := t.Format(time.RFC3339Nano) | ||||
| 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | ||||
| } | ||||
|  | ||||
| func (e *encoder) floatv(tag string, in reflect.Value) { | ||||
| 	// Issue #352: When formatting, use the precision of the underlying value | ||||
| 	precision := 64 | ||||
| 	if in.Kind() == reflect.Float32 { | ||||
| 		precision = 32 | ||||
| 	} | ||||
|  | ||||
| 	s := strconv.FormatFloat(in.Float(), 'g', -1, precision) | ||||
| 	switch s { | ||||
| 	case "+Inf": | ||||
| 		s = ".inf" | ||||
| 	case "-Inf": | ||||
| 		s = "-.inf" | ||||
| 	case "NaN": | ||||
| 		s = ".nan" | ||||
| 	} | ||||
| 	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | ||||
| } | ||||
|  | ||||
| func (e *encoder) nilv() { | ||||
| 	e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) | ||||
| } | ||||
|  | ||||
| func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) { | ||||
| 	// TODO Kill this function. Replace all initialize calls by their underlining Go literals. | ||||
| 	implicit := tag == "" | ||||
| 	if !implicit { | ||||
| 		tag = longTag(tag) | ||||
| 	} | ||||
| 	e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) | ||||
| 	e.event.head_comment = head | ||||
| 	e.event.line_comment = line | ||||
| 	e.event.foot_comment = foot | ||||
| 	e.event.tail_comment = tail | ||||
| 	e.emit() | ||||
| } | ||||
|  | ||||
| func (e *encoder) nodev(in reflect.Value) { | ||||
| 	e.node(in.Interface().(*Node), "") | ||||
| } | ||||
|  | ||||
| func (e *encoder) node(node *Node, tail string) { | ||||
| 	// Zero nodes behave as nil. | ||||
| 	if node.Kind == 0 && node.IsZero() { | ||||
| 		e.nilv() | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// If the tag was not explicitly requested, and dropping it won't change the | ||||
| 	// implicit tag of the value, don't include it in the presentation. | ||||
| 	var tag = node.Tag | ||||
| 	var stag = shortTag(tag) | ||||
| 	var forceQuoting bool | ||||
| 	if tag != "" && node.Style&TaggedStyle == 0 { | ||||
| 		if node.Kind == ScalarNode { | ||||
| 			if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { | ||||
| 				tag = "" | ||||
| 			} else { | ||||
| 				rtag, _ := resolve("", node.Value) | ||||
| 				if rtag == stag { | ||||
| 					tag = "" | ||||
| 				} else if stag == strTag { | ||||
| 					tag = "" | ||||
| 					forceQuoting = true | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			var rtag string | ||||
| 			switch node.Kind { | ||||
| 			case MappingNode: | ||||
| 				rtag = mapTag | ||||
| 			case SequenceNode: | ||||
| 				rtag = seqTag | ||||
| 			} | ||||
| 			if rtag == stag { | ||||
| 				tag = "" | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	switch node.Kind { | ||||
| 	case DocumentNode: | ||||
| 		yaml_document_start_event_initialize(&e.event, nil, nil, true) | ||||
| 		e.event.head_comment = []byte(node.HeadComment) | ||||
| 		e.emit() | ||||
| 		for _, node := range node.Content { | ||||
| 			e.node(node, "") | ||||
| 		} | ||||
| 		yaml_document_end_event_initialize(&e.event, true) | ||||
| 		e.event.foot_comment = []byte(node.FootComment) | ||||
| 		e.emit() | ||||
|  | ||||
| 	case SequenceNode: | ||||
| 		style := yaml_BLOCK_SEQUENCE_STYLE | ||||
| 		if node.Style&FlowStyle != 0 { | ||||
| 			style = yaml_FLOW_SEQUENCE_STYLE | ||||
| 		} | ||||
| 		e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)) | ||||
| 		e.event.head_comment = []byte(node.HeadComment) | ||||
| 		e.emit() | ||||
| 		for _, node := range node.Content { | ||||
| 			e.node(node, "") | ||||
| 		} | ||||
| 		e.must(yaml_sequence_end_event_initialize(&e.event)) | ||||
| 		e.event.line_comment = []byte(node.LineComment) | ||||
| 		e.event.foot_comment = []byte(node.FootComment) | ||||
| 		e.emit() | ||||
|  | ||||
| 	case MappingNode: | ||||
| 		style := yaml_BLOCK_MAPPING_STYLE | ||||
| 		if node.Style&FlowStyle != 0 { | ||||
| 			style = yaml_FLOW_MAPPING_STYLE | ||||
| 		} | ||||
| 		yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style) | ||||
| 		e.event.tail_comment = []byte(tail) | ||||
| 		e.event.head_comment = []byte(node.HeadComment) | ||||
| 		e.emit() | ||||
|  | ||||
| 		// The tail logic below moves the foot comment of prior keys to the following key, | ||||
| 		// since the value for each key may be a nested structure and the foot needs to be | ||||
| 		// processed only the entirety of the value is streamed. The last tail is processed | ||||
| 		// with the mapping end event. | ||||
| 		var tail string | ||||
| 		for i := 0; i+1 < len(node.Content); i += 2 { | ||||
| 			k := node.Content[i] | ||||
| 			foot := k.FootComment | ||||
| 			if foot != "" { | ||||
| 				kopy := *k | ||||
| 				kopy.FootComment = "" | ||||
| 				k = &kopy | ||||
| 			} | ||||
| 			e.node(k, tail) | ||||
| 			tail = foot | ||||
|  | ||||
| 			v := node.Content[i+1] | ||||
| 			e.node(v, "") | ||||
| 		} | ||||
|  | ||||
| 		yaml_mapping_end_event_initialize(&e.event) | ||||
| 		e.event.tail_comment = []byte(tail) | ||||
| 		e.event.line_comment = []byte(node.LineComment) | ||||
| 		e.event.foot_comment = []byte(node.FootComment) | ||||
| 		e.emit() | ||||
|  | ||||
| 	case AliasNode: | ||||
| 		yaml_alias_event_initialize(&e.event, []byte(node.Value)) | ||||
| 		e.event.head_comment = []byte(node.HeadComment) | ||||
| 		e.event.line_comment = []byte(node.LineComment) | ||||
| 		e.event.foot_comment = []byte(node.FootComment) | ||||
| 		e.emit() | ||||
|  | ||||
| 	case ScalarNode: | ||||
| 		value := node.Value | ||||
| 		if !utf8.ValidString(value) { | ||||
| 			if stag == binaryTag { | ||||
| 				failf("explicitly tagged !!binary data must be base64-encoded") | ||||
| 			} | ||||
| 			if stag != "" { | ||||
| 				failf("cannot marshal invalid UTF-8 data as %s", stag) | ||||
| 			} | ||||
| 			// It can't be encoded directly as YAML so use a binary tag | ||||
| 			// and encode it as base64. | ||||
| 			tag = binaryTag | ||||
| 			value = encodeBase64(value) | ||||
| 		} | ||||
|  | ||||
| 		style := yaml_PLAIN_SCALAR_STYLE | ||||
| 		switch { | ||||
| 		case node.Style&DoubleQuotedStyle != 0: | ||||
| 			style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | ||||
| 		case node.Style&SingleQuotedStyle != 0: | ||||
| 			style = yaml_SINGLE_QUOTED_SCALAR_STYLE | ||||
| 		case node.Style&LiteralStyle != 0: | ||||
| 			style = yaml_LITERAL_SCALAR_STYLE | ||||
| 		case node.Style&FoldedStyle != 0: | ||||
| 			style = yaml_FOLDED_SCALAR_STYLE | ||||
| 		case strings.Contains(value, "\n"): | ||||
| 			style = yaml_LITERAL_SCALAR_STYLE | ||||
| 		case forceQuoting: | ||||
| 			style = yaml_DOUBLE_QUOTED_SCALAR_STYLE | ||||
| 		} | ||||
|  | ||||
| 		e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail)) | ||||
| 	default: | ||||
| 		failf("cannot encode node with unknown kind %d", node.Kind) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										165
									
								
								vendor/gopkg.in/yaml.v2/parserc.go → vendor/gopkg.in/yaml.v3/parserc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										165
									
								
								vendor/gopkg.in/yaml.v2/parserc.go → vendor/gopkg.in/yaml.v3/parserc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +1,25 @@ | ||||
| // | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // Copyright (c) 2006-2010 Kirill Simonov | ||||
| // | ||||
| // 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. | ||||
| 
 | ||||
| package yaml | ||||
| 
 | ||||
| import ( | ||||
| @ -45,11 +67,46 @@ import ( | ||||
| // Peek the next token in the token queue. | ||||
| func peek_token(parser *yaml_parser_t) *yaml_token_t { | ||||
| 	if parser.token_available || yaml_parser_fetch_more_tokens(parser) { | ||||
| 		return &parser.tokens[parser.tokens_head] | ||||
| 		token := &parser.tokens[parser.tokens_head] | ||||
| 		yaml_parser_unfold_comments(parser, token) | ||||
| 		return token | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // yaml_parser_unfold_comments walks through the comments queue and joins all | ||||
| // comments behind the position of the provided token into the respective | ||||
| // top-level comment slices in the parser. | ||||
| func yaml_parser_unfold_comments(parser *yaml_parser_t, token *yaml_token_t) { | ||||
| 	for parser.comments_head < len(parser.comments) && token.start_mark.index >= parser.comments[parser.comments_head].token_mark.index { | ||||
| 		comment := &parser.comments[parser.comments_head] | ||||
| 		if len(comment.head) > 0 { | ||||
| 			if token.typ == yaml_BLOCK_END_TOKEN { | ||||
| 				// No heads on ends, so keep comment.head for a follow up token. | ||||
| 				break | ||||
| 			} | ||||
| 			if len(parser.head_comment) > 0 { | ||||
| 				parser.head_comment = append(parser.head_comment, '\n') | ||||
| 			} | ||||
| 			parser.head_comment = append(parser.head_comment, comment.head...) | ||||
| 		} | ||||
| 		if len(comment.foot) > 0 { | ||||
| 			if len(parser.foot_comment) > 0 { | ||||
| 				parser.foot_comment = append(parser.foot_comment, '\n') | ||||
| 			} | ||||
| 			parser.foot_comment = append(parser.foot_comment, comment.foot...) | ||||
| 		} | ||||
| 		if len(comment.line) > 0 { | ||||
| 			if len(parser.line_comment) > 0 { | ||||
| 				parser.line_comment = append(parser.line_comment, '\n') | ||||
| 			} | ||||
| 			parser.line_comment = append(parser.line_comment, comment.line...) | ||||
| 		} | ||||
| 		*comment = yaml_comment_t{} | ||||
| 		parser.comments_head++ | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Remove the next token from the queue (must be called after peek_token). | ||||
| func skip_token(parser *yaml_parser_t) { | ||||
| 	parser.token_available = false | ||||
| @ -224,10 +281,32 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t | ||||
| 		parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) | ||||
| 		parser.state = yaml_PARSE_BLOCK_NODE_STATE | ||||
| 
 | ||||
| 		var head_comment []byte | ||||
| 		if len(parser.head_comment) > 0 { | ||||
| 			// [Go] Scan the header comment backwards, and if an empty line is found, break | ||||
| 			//      the header so the part before the last empty line goes into the | ||||
| 			//      document header, while the bottom of it goes into a follow up event. | ||||
| 			for i := len(parser.head_comment) - 1; i > 0; i-- { | ||||
| 				if parser.head_comment[i] == '\n' { | ||||
| 					if i == len(parser.head_comment)-1 { | ||||
| 						head_comment = parser.head_comment[:i] | ||||
| 						parser.head_comment = parser.head_comment[i+1:] | ||||
| 						break | ||||
| 					} else if parser.head_comment[i-1] == '\n' { | ||||
| 						head_comment = parser.head_comment[:i-1] | ||||
| 						parser.head_comment = parser.head_comment[i+1:] | ||||
| 						break | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		*event = yaml_event_t{ | ||||
| 			typ:        yaml_DOCUMENT_START_EVENT, | ||||
| 			start_mark: token.start_mark, | ||||
| 			end_mark:   token.end_mark, | ||||
| 
 | ||||
| 			head_comment: head_comment, | ||||
| 		} | ||||
| 
 | ||||
| 	} else if token.typ != yaml_STREAM_END_TOKEN { | ||||
| @ -284,6 +363,7 @@ func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event | ||||
| 	if token == nil { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	if token.typ == yaml_VERSION_DIRECTIVE_TOKEN || | ||||
| 		token.typ == yaml_TAG_DIRECTIVE_TOKEN || | ||||
| 		token.typ == yaml_DOCUMENT_START_TOKEN || | ||||
| @ -327,9 +407,25 @@ func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) | ||||
| 		end_mark:   end_mark, | ||||
| 		implicit:   implicit, | ||||
| 	} | ||||
| 	yaml_parser_set_event_comments(parser, event) | ||||
| 	if len(event.head_comment) > 0 && len(event.foot_comment) == 0 { | ||||
| 		event.foot_comment = event.head_comment | ||||
| 		event.head_comment = nil | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) { | ||||
| 	event.head_comment = parser.head_comment | ||||
| 	event.line_comment = parser.line_comment | ||||
| 	event.foot_comment = parser.foot_comment | ||||
| 	parser.head_comment = nil | ||||
| 	parser.line_comment = nil | ||||
| 	parser.foot_comment = nil | ||||
| 	parser.tail_comment = nil | ||||
| 	parser.stem_comment = nil | ||||
| } | ||||
| 
 | ||||
| // Parse the productions: | ||||
| // block_node_or_indentless_sequence    ::= | ||||
| //                          ALIAS | ||||
| @ -373,6 +469,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i | ||||
| 			end_mark:   token.end_mark, | ||||
| 			anchor:     token.value, | ||||
| 		} | ||||
| 		yaml_parser_set_event_comments(parser, event) | ||||
| 		skip_token(parser) | ||||
| 		return true | ||||
| 	} | ||||
| @ -486,6 +583,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i | ||||
| 			quoted_implicit: quoted_implicit, | ||||
| 			style:           yaml_style_t(token.style), | ||||
| 		} | ||||
| 		yaml_parser_set_event_comments(parser, event) | ||||
| 		skip_token(parser) | ||||
| 		return true | ||||
| 	} | ||||
| @ -502,6 +600,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i | ||||
| 			implicit:   implicit, | ||||
| 			style:      yaml_style_t(yaml_FLOW_SEQUENCE_STYLE), | ||||
| 		} | ||||
| 		yaml_parser_set_event_comments(parser, event) | ||||
| 		return true | ||||
| 	} | ||||
| 	if token.typ == yaml_FLOW_MAPPING_START_TOKEN { | ||||
| @ -516,6 +615,7 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i | ||||
| 			implicit:   implicit, | ||||
| 			style:      yaml_style_t(yaml_FLOW_MAPPING_STYLE), | ||||
| 		} | ||||
| 		yaml_parser_set_event_comments(parser, event) | ||||
| 		return true | ||||
| 	} | ||||
| 	if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN { | ||||
| @ -530,6 +630,10 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i | ||||
| 			implicit:   implicit, | ||||
| 			style:      yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), | ||||
| 		} | ||||
| 		if parser.stem_comment != nil { | ||||
| 			event.head_comment = parser.stem_comment | ||||
| 			parser.stem_comment = nil | ||||
| 		} | ||||
| 		return true | ||||
| 	} | ||||
| 	if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN { | ||||
| @ -544,6 +648,10 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i | ||||
| 			implicit:   implicit, | ||||
| 			style:      yaml_style_t(yaml_BLOCK_MAPPING_STYLE), | ||||
| 		} | ||||
| 		if parser.stem_comment != nil { | ||||
| 			event.head_comment = parser.stem_comment | ||||
| 			parser.stem_comment = nil | ||||
| 		} | ||||
| 		return true | ||||
| 	} | ||||
| 	if len(anchor) > 0 || len(tag) > 0 { | ||||
| @ -579,6 +687,9 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i | ||||
| func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { | ||||
| 	if first { | ||||
| 		token := peek_token(parser) | ||||
| 		if token == nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		parser.marks = append(parser.marks, token.start_mark) | ||||
| 		skip_token(parser) | ||||
| 	} | ||||
| @ -590,7 +701,9 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e | ||||
| 
 | ||||
| 	if token.typ == yaml_BLOCK_ENTRY_TOKEN { | ||||
| 		mark := token.end_mark | ||||
| 		prior_head_len := len(parser.head_comment) | ||||
| 		skip_token(parser) | ||||
| 		yaml_parser_split_stem_comment(parser, prior_head_len) | ||||
| 		token = peek_token(parser) | ||||
| 		if token == nil { | ||||
| 			return false | ||||
| @ -636,7 +749,9 @@ func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *y | ||||
| 
 | ||||
| 	if token.typ == yaml_BLOCK_ENTRY_TOKEN { | ||||
| 		mark := token.end_mark | ||||
| 		prior_head_len := len(parser.head_comment) | ||||
| 		skip_token(parser) | ||||
| 		yaml_parser_split_stem_comment(parser, prior_head_len) | ||||
| 		token = peek_token(parser) | ||||
| 		if token == nil { | ||||
| 			return false | ||||
| @ -662,6 +777,32 @@ func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *y | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| // Split stem comment from head comment. | ||||
| // | ||||
| // When a sequence or map is found under a sequence entry, the former head comment | ||||
| // is assigned to the underlying sequence or map as a whole, not the individual | ||||
| // sequence or map entry as would be expected otherwise. To handle this case the | ||||
| // previous head comment is moved aside as the stem comment. | ||||
| func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { | ||||
| 	if stem_len == 0 { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	token := peek_token(parser) | ||||
| 	if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	parser.stem_comment = parser.head_comment[:stem_len] | ||||
| 	if len(parser.head_comment) == stem_len { | ||||
| 		parser.head_comment = nil | ||||
| 	} else { | ||||
| 		// Copy suffix to prevent very strange bugs if someone ever appends | ||||
| 		// further bytes to the prefix in the stem_comment slice above. | ||||
| 		parser.head_comment = append([]byte(nil), parser.head_comment[stem_len+1:]...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Parse the productions: | ||||
| // block_mapping        ::= BLOCK-MAPPING_START | ||||
| //                          ******************* | ||||
| @ -675,6 +816,9 @@ func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *y | ||||
| func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { | ||||
| 	if first { | ||||
| 		token := peek_token(parser) | ||||
| 		if token == nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		parser.marks = append(parser.marks, token.start_mark) | ||||
| 		skip_token(parser) | ||||
| 	} | ||||
| @ -684,6 +828,19 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// [Go] A tail comment was left from the prior mapping value processed. Emit an event | ||||
| 	//      as it needs to be processed with that value and not the following key. | ||||
| 	if len(parser.tail_comment) > 0 { | ||||
| 		*event = yaml_event_t{ | ||||
| 			typ:          yaml_TAIL_COMMENT_EVENT, | ||||
| 			start_mark:   token.start_mark, | ||||
| 			end_mark:     token.end_mark, | ||||
| 			foot_comment: parser.tail_comment, | ||||
| 		} | ||||
| 		parser.tail_comment = nil | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	if token.typ == yaml_KEY_TOKEN { | ||||
| 		mark := token.end_mark | ||||
| 		skip_token(parser) | ||||
| @ -709,6 +866,7 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even | ||||
| 			start_mark: token.start_mark, | ||||
| 			end_mark:   token.end_mark, | ||||
| 		} | ||||
| 		yaml_parser_set_event_comments(parser, event) | ||||
| 		skip_token(parser) | ||||
| 		return true | ||||
| 	} | ||||
| @ -770,6 +928,9 @@ func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_ev | ||||
| func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { | ||||
| 	if first { | ||||
| 		token := peek_token(parser) | ||||
| 		if token == nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		parser.marks = append(parser.marks, token.start_mark) | ||||
| 		skip_token(parser) | ||||
| 	} | ||||
| @ -820,6 +981,7 @@ func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_ev | ||||
| 		start_mark: token.start_mark, | ||||
| 		end_mark:   token.end_mark, | ||||
| 	} | ||||
| 	yaml_parser_set_event_comments(parser, event) | ||||
| 
 | ||||
| 	skip_token(parser) | ||||
| 	return true | ||||
| @ -959,6 +1121,7 @@ func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event | ||||
| 		start_mark: token.start_mark, | ||||
| 		end_mark:   token.end_mark, | ||||
| 	} | ||||
| 	yaml_parser_set_event_comments(parser, event) | ||||
| 	skip_token(parser) | ||||
| 	return true | ||||
| } | ||||
							
								
								
									
										24
									
								
								vendor/gopkg.in/yaml.v2/readerc.go → vendor/gopkg.in/yaml.v3/readerc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										24
									
								
								vendor/gopkg.in/yaml.v2/readerc.go → vendor/gopkg.in/yaml.v3/readerc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +1,25 @@ | ||||
| //  | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // Copyright (c) 2006-2010 Kirill Simonov | ||||
| //  | ||||
| // 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. | ||||
| 
 | ||||
| package yaml | ||||
| 
 | ||||
| import ( | ||||
| @ -95,7 +117,7 @@ func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { | ||||
| 
 | ||||
| 	// [Go] This function was changed to guarantee the requested length size at EOF. | ||||
| 	// The fact we need to do this is pretty awful, but the description above implies | ||||
| 	// for that to be the case, and there are tests  | ||||
| 	// for that to be the case, and there are tests | ||||
| 
 | ||||
| 	// If the EOF flag is set and the raw buffer is empty, do nothing. | ||||
| 	if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { | ||||
							
								
								
									
										138
									
								
								vendor/gopkg.in/yaml.v2/resolve.go → vendor/gopkg.in/yaml.v3/resolve.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										138
									
								
								vendor/gopkg.in/yaml.v2/resolve.go → vendor/gopkg.in/yaml.v3/resolve.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +1,18 @@ | ||||
| // | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
| 
 | ||||
| package yaml | ||||
| 
 | ||||
| import ( | ||||
| @ -34,18 +49,14 @@ func init() { | ||||
| 		tag string | ||||
| 		l   []string | ||||
| 	}{ | ||||
| 		{true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}}, | ||||
| 		{true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}}, | ||||
| 		{true, yaml_BOOL_TAG, []string{"on", "On", "ON"}}, | ||||
| 		{false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}}, | ||||
| 		{false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}}, | ||||
| 		{false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}}, | ||||
| 		{nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}}, | ||||
| 		{math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}}, | ||||
| 		{math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}}, | ||||
| 		{math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}}, | ||||
| 		{math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}}, | ||||
| 		{"<<", yaml_MERGE_TAG, []string{"<<"}}, | ||||
| 		{true, boolTag, []string{"true", "True", "TRUE"}}, | ||||
| 		{false, boolTag, []string{"false", "False", "FALSE"}}, | ||||
| 		{nil, nullTag, []string{"", "~", "null", "Null", "NULL"}}, | ||||
| 		{math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}}, | ||||
| 		{math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}}, | ||||
| 		{math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}}, | ||||
| 		{math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}}, | ||||
| 		{"<<", mergeTag, []string{"<<"}}, | ||||
| 	} | ||||
| 
 | ||||
| 	m := resolveMap | ||||
| @ -56,11 +67,37 @@ func init() { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	nullTag      = "!!null" | ||||
| 	boolTag      = "!!bool" | ||||
| 	strTag       = "!!str" | ||||
| 	intTag       = "!!int" | ||||
| 	floatTag     = "!!float" | ||||
| 	timestampTag = "!!timestamp" | ||||
| 	seqTag       = "!!seq" | ||||
| 	mapTag       = "!!map" | ||||
| 	binaryTag    = "!!binary" | ||||
| 	mergeTag     = "!!merge" | ||||
| ) | ||||
| 
 | ||||
| var longTags = make(map[string]string) | ||||
| var shortTags = make(map[string]string) | ||||
| 
 | ||||
| func init() { | ||||
| 	for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} { | ||||
| 		ltag := longTag(stag) | ||||
| 		longTags[stag] = ltag | ||||
| 		shortTags[ltag] = stag | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| const longTagPrefix = "tag:yaml.org,2002:" | ||||
| 
 | ||||
| func shortTag(tag string) string { | ||||
| 	// TODO This can easily be made faster and produce less garbage. | ||||
| 	if strings.HasPrefix(tag, longTagPrefix) { | ||||
| 		if stag, ok := shortTags[tag]; ok { | ||||
| 			return stag | ||||
| 		} | ||||
| 		return "!!" + tag[len(longTagPrefix):] | ||||
| 	} | ||||
| 	return tag | ||||
| @ -68,6 +105,9 @@ func shortTag(tag string) string { | ||||
| 
 | ||||
| func longTag(tag string) string { | ||||
| 	if strings.HasPrefix(tag, "!!") { | ||||
| 		if ltag, ok := longTags[tag]; ok { | ||||
| 			return ltag | ||||
| 		} | ||||
| 		return longTagPrefix + tag[2:] | ||||
| 	} | ||||
| 	return tag | ||||
| @ -75,7 +115,7 @@ func longTag(tag string) string { | ||||
| 
 | ||||
| func resolvableTag(tag string) bool { | ||||
| 	switch tag { | ||||
| 	case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG: | ||||
| 	case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag: | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| @ -84,23 +124,24 @@ func resolvableTag(tag string) bool { | ||||
| var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) | ||||
| 
 | ||||
| func resolve(tag string, in string) (rtag string, out interface{}) { | ||||
| 	tag = shortTag(tag) | ||||
| 	if !resolvableTag(tag) { | ||||
| 		return tag, in | ||||
| 	} | ||||
| 
 | ||||
| 	defer func() { | ||||
| 		switch tag { | ||||
| 		case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG: | ||||
| 		case "", rtag, strTag, binaryTag: | ||||
| 			return | ||||
| 		case yaml_FLOAT_TAG: | ||||
| 			if rtag == yaml_INT_TAG { | ||||
| 		case floatTag: | ||||
| 			if rtag == intTag { | ||||
| 				switch v := out.(type) { | ||||
| 				case int64: | ||||
| 					rtag = yaml_FLOAT_TAG | ||||
| 					rtag = floatTag | ||||
| 					out = float64(v) | ||||
| 					return | ||||
| 				case int: | ||||
| 					rtag = yaml_FLOAT_TAG | ||||
| 					rtag = floatTag | ||||
| 					out = float64(v) | ||||
| 					return | ||||
| 				} | ||||
| @ -115,7 +156,7 @@ func resolve(tag string, in string) (rtag string, out interface{}) { | ||||
| 	if in != "" { | ||||
| 		hint = resolveTable[in[0]] | ||||
| 	} | ||||
| 	if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG { | ||||
| 	if hint != 0 && tag != strTag && tag != binaryTag { | ||||
| 		// Handle things we can lookup in a map. | ||||
| 		if item, ok := resolveMap[in]; ok { | ||||
| 			return item.tag, item.value | ||||
| @ -133,17 +174,17 @@ func resolve(tag string, in string) (rtag string, out interface{}) { | ||||
| 			// Not in the map, so maybe a normal float. | ||||
| 			floatv, err := strconv.ParseFloat(in, 64) | ||||
| 			if err == nil { | ||||
| 				return yaml_FLOAT_TAG, floatv | ||||
| 				return floatTag, floatv | ||||
| 			} | ||||
| 
 | ||||
| 		case 'D', 'S': | ||||
| 			// Int, float, or timestamp. | ||||
| 			// Only try values as a timestamp if the value is unquoted or there's an explicit | ||||
| 			// !!timestamp tag. | ||||
| 			if tag == "" || tag == yaml_TIMESTAMP_TAG { | ||||
| 			if tag == "" || tag == timestampTag { | ||||
| 				t, ok := parseTimestamp(in) | ||||
| 				if ok { | ||||
| 					return yaml_TIMESTAMP_TAG, t | ||||
| 					return timestampTag, t | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| @ -151,49 +192,76 @@ func resolve(tag string, in string) (rtag string, out interface{}) { | ||||
| 			intv, err := strconv.ParseInt(plain, 0, 64) | ||||
| 			if err == nil { | ||||
| 				if intv == int64(int(intv)) { | ||||
| 					return yaml_INT_TAG, int(intv) | ||||
| 					return intTag, int(intv) | ||||
| 				} else { | ||||
| 					return yaml_INT_TAG, intv | ||||
| 					return intTag, intv | ||||
| 				} | ||||
| 			} | ||||
| 			uintv, err := strconv.ParseUint(plain, 0, 64) | ||||
| 			if err == nil { | ||||
| 				return yaml_INT_TAG, uintv | ||||
| 				return intTag, uintv | ||||
| 			} | ||||
| 			if yamlStyleFloat.MatchString(plain) { | ||||
| 				floatv, err := strconv.ParseFloat(plain, 64) | ||||
| 				if err == nil { | ||||
| 					return yaml_FLOAT_TAG, floatv | ||||
| 					return floatTag, floatv | ||||
| 				} | ||||
| 			} | ||||
| 			if strings.HasPrefix(plain, "0b") { | ||||
| 				intv, err := strconv.ParseInt(plain[2:], 2, 64) | ||||
| 				if err == nil { | ||||
| 					if intv == int64(int(intv)) { | ||||
| 						return yaml_INT_TAG, int(intv) | ||||
| 						return intTag, int(intv) | ||||
| 					} else { | ||||
| 						return yaml_INT_TAG, intv | ||||
| 						return intTag, intv | ||||
| 					} | ||||
| 				} | ||||
| 				uintv, err := strconv.ParseUint(plain[2:], 2, 64) | ||||
| 				if err == nil { | ||||
| 					return yaml_INT_TAG, uintv | ||||
| 					return intTag, uintv | ||||
| 				} | ||||
| 			} else if strings.HasPrefix(plain, "-0b") { | ||||
| 				intv, err := strconv.ParseInt("-" + plain[3:], 2, 64) | ||||
| 				intv, err := strconv.ParseInt("-"+plain[3:], 2, 64) | ||||
| 				if err == nil { | ||||
| 					if true || intv == int64(int(intv)) { | ||||
| 						return yaml_INT_TAG, int(intv) | ||||
| 						return intTag, int(intv) | ||||
| 					} else { | ||||
| 						return yaml_INT_TAG, intv | ||||
| 						return intTag, intv | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			// Octals as introduced in version 1.2 of the spec. | ||||
| 			// Octals from the 1.1 spec, spelled as 0777, are still | ||||
| 			// decoded by default in v3 as well for compatibility. | ||||
| 			// May be dropped in v4 depending on how usage evolves. | ||||
| 			if strings.HasPrefix(plain, "0o") { | ||||
| 				intv, err := strconv.ParseInt(plain[2:], 8, 64) | ||||
| 				if err == nil { | ||||
| 					if intv == int64(int(intv)) { | ||||
| 						return intTag, int(intv) | ||||
| 					} else { | ||||
| 						return intTag, intv | ||||
| 					} | ||||
| 				} | ||||
| 				uintv, err := strconv.ParseUint(plain[2:], 8, 64) | ||||
| 				if err == nil { | ||||
| 					return intTag, uintv | ||||
| 				} | ||||
| 			} else if strings.HasPrefix(plain, "-0o") { | ||||
| 				intv, err := strconv.ParseInt("-"+plain[3:], 8, 64) | ||||
| 				if err == nil { | ||||
| 					if true || intv == int64(int(intv)) { | ||||
| 						return intTag, int(intv) | ||||
| 					} else { | ||||
| 						return intTag, intv | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		default: | ||||
| 			panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")") | ||||
| 			panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")") | ||||
| 		} | ||||
| 	} | ||||
| 	return yaml_STR_TAG, in | ||||
| 	return strTag, in | ||||
| } | ||||
| 
 | ||||
| // encodeBase64 encodes s as base64 that is broken up into multiple lines | ||||
							
								
								
									
										363
									
								
								vendor/gopkg.in/yaml.v2/scannerc.go → vendor/gopkg.in/yaml.v3/scannerc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										363
									
								
								vendor/gopkg.in/yaml.v2/scannerc.go → vendor/gopkg.in/yaml.v3/scannerc.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +1,25 @@ | ||||
| // | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // Copyright (c) 2006-2010 Kirill Simonov | ||||
| // | ||||
| // 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. | ||||
| 
 | ||||
| package yaml | ||||
| 
 | ||||
| import ( | ||||
| @ -489,6 +511,9 @@ func cache(parser *yaml_parser_t, length int) bool { | ||||
| 
 | ||||
| // Advance the buffer pointer. | ||||
| func skip(parser *yaml_parser_t) { | ||||
| 	if !is_blank(parser.buffer, parser.buffer_pos) { | ||||
| 		parser.newlines = 0 | ||||
| 	} | ||||
| 	parser.mark.index++ | ||||
| 	parser.mark.column++ | ||||
| 	parser.unread-- | ||||
| @ -502,17 +527,22 @@ func skip_line(parser *yaml_parser_t) { | ||||
| 		parser.mark.line++ | ||||
| 		parser.unread -= 2 | ||||
| 		parser.buffer_pos += 2 | ||||
| 		parser.newlines++ | ||||
| 	} else if is_break(parser.buffer, parser.buffer_pos) { | ||||
| 		parser.mark.index++ | ||||
| 		parser.mark.column = 0 | ||||
| 		parser.mark.line++ | ||||
| 		parser.unread-- | ||||
| 		parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) | ||||
| 		parser.newlines++ | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Copy a character to a string buffer and advance pointers. | ||||
| func read(parser *yaml_parser_t, s []byte) []byte { | ||||
| 	if !is_blank(parser.buffer, parser.buffer_pos) { | ||||
| 		parser.newlines = 0 | ||||
| 	} | ||||
| 	w := width(parser.buffer[parser.buffer_pos]) | ||||
| 	if w == 0 { | ||||
| 		panic("invalid character sequence") | ||||
| @ -564,6 +594,7 @@ func read_line(parser *yaml_parser_t, s []byte) []byte { | ||||
| 	parser.mark.column = 0 | ||||
| 	parser.mark.line++ | ||||
| 	parser.unread-- | ||||
| 	parser.newlines++ | ||||
| 	return s | ||||
| } | ||||
| 
 | ||||
| @ -626,9 +657,13 @@ func trace(args ...interface{}) func() { | ||||
| func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { | ||||
| 	// While we need more tokens to fetch, do it. | ||||
| 	for { | ||||
| 		if parser.tokens_head != len(parser.tokens) { | ||||
| 			// If queue is non-empty, check if any potential simple key may | ||||
| 			// occupy the head position. | ||||
| 		// [Go] The comment parsing logic requires a lookahead of two tokens | ||||
| 		// so that foot comments may be parsed in time of associating them | ||||
| 		// with the tokens that are parsed before them, and also for line | ||||
| 		// comments to be transformed into head comments in some edge cases. | ||||
| 		if parser.tokens_head < len(parser.tokens)-2 { | ||||
| 			// If a potential simple key is at the head position, we need to fetch | ||||
| 			// the next token to disambiguate it. | ||||
| 			head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed] | ||||
| 			if !ok { | ||||
| 				break | ||||
| @ -649,7 +684,7 @@ func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { | ||||
| } | ||||
| 
 | ||||
| // The dispatcher for token fetchers. | ||||
| func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { | ||||
| func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) { | ||||
| 	// Ensure that the buffer is initialized. | ||||
| 	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { | ||||
| 		return false | ||||
| @ -660,13 +695,19 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { | ||||
| 		return yaml_parser_fetch_stream_start(parser) | ||||
| 	} | ||||
| 
 | ||||
| 	scan_mark := parser.mark | ||||
| 
 | ||||
| 	// Eat whitespaces and comments until we reach the next token. | ||||
| 	if !yaml_parser_scan_to_next_token(parser) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| 	// [Go] While unrolling indents, transform the head comments of prior | ||||
| 	// indentation levels observed after scan_start into foot comments at | ||||
| 	// the respective indexes. | ||||
| 
 | ||||
| 	// Check the indentation level against the current column. | ||||
| 	if !yaml_parser_unroll_indent(parser, parser.mark.column) { | ||||
| 	if !yaml_parser_unroll_indent(parser, parser.mark.column, scan_mark) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| @ -699,6 +740,26 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { | ||||
| 		return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN) | ||||
| 	} | ||||
| 
 | ||||
| 	comment_mark := parser.mark | ||||
| 	if len(parser.tokens) > 0 && (parser.flow_level == 0 && buf[pos] == ':' || parser.flow_level > 0 && buf[pos] == ',') { | ||||
| 		// Associate any following comments with the prior token. | ||||
| 		comment_mark = parser.tokens[len(parser.tokens)-1].start_mark | ||||
| 	} | ||||
| 	defer func() { | ||||
| 		if !ok { | ||||
| 			return | ||||
| 		} | ||||
| 		if len(parser.tokens) > 0 && parser.tokens[len(parser.tokens)-1].typ == yaml_BLOCK_ENTRY_TOKEN { | ||||
| 			// Sequence indicators alone have no line comments. It becomes | ||||
| 			// a head comment for whatever follows. | ||||
| 			return | ||||
| 		} | ||||
| 		if !yaml_parser_scan_line_comment(parser, comment_mark) { | ||||
| 			ok = false | ||||
| 			return | ||||
| 		} | ||||
| 	}() | ||||
| 
 | ||||
| 	// Is it the flow sequence start indicator? | ||||
| 	if buf[pos] == '[' { | ||||
| 		return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN) | ||||
| @ -792,7 +853,7 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool { | ||||
| 	// if it is followed by a non-space character. | ||||
| 	// | ||||
| 	// The last rule is more restrictive than the specification requires. | ||||
| 	// [Go] Make this logic more reasonable. | ||||
| 	// [Go] TODO Make this logic more reasonable. | ||||
| 	//switch parser.buffer[parser.buffer_pos] { | ||||
| 	//case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`': | ||||
| 	//} | ||||
| @ -965,19 +1026,49 @@ func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml | ||||
| // Pop indentation levels from the indents stack until the current level | ||||
| // becomes less or equal to the column.  For each indentation level, append | ||||
| // the BLOCK-END token. | ||||
| func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool { | ||||
| func yaml_parser_unroll_indent(parser *yaml_parser_t, column int, scan_mark yaml_mark_t) bool { | ||||
| 	// In the flow context, do nothing. | ||||
| 	if parser.flow_level > 0 { | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	block_mark := scan_mark | ||||
| 	block_mark.index-- | ||||
| 
 | ||||
| 	// Loop through the indentation levels in the stack. | ||||
| 	for parser.indent > column { | ||||
| 
 | ||||
| 		// [Go] Reposition the end token before potential following | ||||
| 		//      foot comments of parent blocks. For that, search | ||||
| 		//      backwards for recent comments that were at the same | ||||
| 		//      indent as the block that is ending now. | ||||
| 		stop_index := block_mark.index | ||||
| 		for i := len(parser.comments) - 1; i >= 0; i-- { | ||||
| 			comment := &parser.comments[i] | ||||
| 
 | ||||
| 			if comment.end_mark.index < stop_index { | ||||
| 				// Don't go back beyond the start of the comment/whitespace scan, unless column < 0. | ||||
| 				// If requested indent column is < 0, then the document is over and everything else | ||||
| 				// is a foot anyway. | ||||
| 				break | ||||
| 			} | ||||
| 			if comment.start_mark.column == parser.indent+1 { | ||||
| 				// This is a good match. But maybe there's a former comment | ||||
| 				// at that same indent level, so keep searching. | ||||
| 				block_mark = comment.start_mark | ||||
| 			} | ||||
| 
 | ||||
| 			// While the end of the former comment matches with | ||||
| 			// the start of the following one, we know there's | ||||
| 			// nothing in between and scanning is still safe. | ||||
| 			stop_index = comment.scan_mark.index | ||||
| 		} | ||||
| 
 | ||||
| 		// Create a token and append it to the queue. | ||||
| 		token := yaml_token_t{ | ||||
| 			typ:        yaml_BLOCK_END_TOKEN, | ||||
| 			start_mark: parser.mark, | ||||
| 			end_mark:   parser.mark, | ||||
| 			start_mark: block_mark, | ||||
| 			end_mark:   block_mark, | ||||
| 		} | ||||
| 		yaml_insert_token(parser, -1, &token) | ||||
| 
 | ||||
| @ -1026,7 +1117,7 @@ func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { | ||||
| 	} | ||||
| 
 | ||||
| 	// Reset the indentation level. | ||||
| 	if !yaml_parser_unroll_indent(parser, -1) { | ||||
| 	if !yaml_parser_unroll_indent(parser, -1, parser.mark) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| @ -1050,7 +1141,7 @@ func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { | ||||
| // Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. | ||||
| func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { | ||||
| 	// Reset the indentation level. | ||||
| 	if !yaml_parser_unroll_indent(parser, -1) { | ||||
| 	if !yaml_parser_unroll_indent(parser, -1, parser.mark) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| @ -1074,7 +1165,7 @@ func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { | ||||
| // Produce the DOCUMENT-START or DOCUMENT-END token. | ||||
| func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool { | ||||
| 	// Reset the indentation level. | ||||
| 	if !yaml_parser_unroll_indent(parser, -1) { | ||||
| 	if !yaml_parser_unroll_indent(parser, -1, parser.mark) { | ||||
| 		return false | ||||
| 	} | ||||
| 
 | ||||
| @ -1107,6 +1198,7 @@ func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_ | ||||
| 
 | ||||
| // Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. | ||||
| func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool { | ||||
| 
 | ||||
| 	// The indicators '[' and '{' may start a simple key. | ||||
| 	if !yaml_parser_save_simple_key(parser) { | ||||
| 		return false | ||||
| @ -1442,6 +1534,8 @@ func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool { | ||||
| // Eat whitespaces and comments until the next token is found. | ||||
| func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { | ||||
| 
 | ||||
| 	scan_mark := parser.mark | ||||
| 
 | ||||
| 	// Until the next token is not found. | ||||
| 	for { | ||||
| 		// Allow the BOM mark to start a line. | ||||
| @ -1468,13 +1562,33 @@ func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// Check if we just had a line comment under a sequence entry that | ||||
| 		// looks more like a header to the following content. Similar to this: | ||||
| 		// | ||||
| 		// - # The comment | ||||
| 		//   - Some data | ||||
| 		// | ||||
| 		// If so, transform the line comment to a head comment and reposition. | ||||
| 		if len(parser.comments) > 0 && len(parser.tokens) > 1 { | ||||
| 			tokenA := parser.tokens[len(parser.tokens)-2] | ||||
| 			tokenB := parser.tokens[len(parser.tokens)-1] | ||||
| 			comment := &parser.comments[len(parser.comments)-1] | ||||
| 			if tokenA.typ == yaml_BLOCK_SEQUENCE_START_TOKEN && tokenB.typ == yaml_BLOCK_ENTRY_TOKEN && len(comment.line) > 0 && !is_break(parser.buffer, parser.buffer_pos) { | ||||
| 				// If it was in the prior line, reposition so it becomes a | ||||
| 				// header of the follow up token. Otherwise, keep it in place | ||||
| 				// so it becomes a header of the former. | ||||
| 				comment.head = comment.line | ||||
| 				comment.line = nil | ||||
| 				if comment.start_mark.line == parser.mark.line-1 { | ||||
| 					comment.token_mark = parser.mark | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		// Eat a comment until a line break. | ||||
| 		if parser.buffer[parser.buffer_pos] == '#' { | ||||
| 			for !is_breakz(parser.buffer, parser.buffer_pos) { | ||||
| 				skip(parser) | ||||
| 				if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { | ||||
| 					return false | ||||
| 				} | ||||
| 			if !yaml_parser_scan_comments(parser, scan_mark) { | ||||
| 				return false | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| @ -1572,6 +1686,10 @@ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool | ||||
| 	} | ||||
| 
 | ||||
| 	if parser.buffer[parser.buffer_pos] == '#' { | ||||
| 		// [Go] Discard this inline comment for the time being. | ||||
| 		//if !yaml_parser_scan_line_comment(parser, start_mark) { | ||||
| 		//	return false | ||||
| 		//} | ||||
| 		for !is_breakz(parser.buffer, parser.buffer_pos) { | ||||
| 			skip(parser) | ||||
| 			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { | ||||
| @ -1987,7 +2105,7 @@ func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte | ||||
| 	//      '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', | ||||
| 	//      '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', | ||||
| 	//      '%'. | ||||
| 	// [Go] Convert this into more reasonable logic. | ||||
| 	// [Go] TODO Convert this into more reasonable logic. | ||||
| 	for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' || | ||||
| 		parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' || | ||||
| 		parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' || | ||||
| @ -2142,6 +2260,9 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l | ||||
| 		} | ||||
| 	} | ||||
| 	if parser.buffer[parser.buffer_pos] == '#' { | ||||
| 		if !yaml_parser_scan_line_comment(parser, start_mark) { | ||||
| 			return false | ||||
| 		} | ||||
| 		for !is_breakz(parser.buffer, parser.buffer_pos) { | ||||
| 			skip(parser) | ||||
| 			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { | ||||
| @ -2709,3 +2830,209 @@ func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) b | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t) bool { | ||||
| 	if parser.newlines > 0 { | ||||
| 		return true | ||||
| 	} | ||||
| 
 | ||||
| 	var start_mark yaml_mark_t | ||||
| 	var text []byte | ||||
| 
 | ||||
| 	for peek := 0; peek < 512; peek++ { | ||||
| 		if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) { | ||||
| 			break | ||||
| 		} | ||||
| 		if is_blank(parser.buffer, parser.buffer_pos+peek) { | ||||
| 			continue | ||||
| 		} | ||||
| 		if parser.buffer[parser.buffer_pos+peek] == '#' { | ||||
| 			seen := parser.mark.index+peek | ||||
| 			for { | ||||
| 				if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { | ||||
| 					return false | ||||
| 				} | ||||
| 				if is_breakz(parser.buffer, parser.buffer_pos) { | ||||
| 					if parser.mark.index >= seen { | ||||
| 						break | ||||
| 					} | ||||
| 					if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { | ||||
| 						return false | ||||
| 					} | ||||
| 					skip_line(parser) | ||||
| 				} else if parser.mark.index >= seen { | ||||
| 					if len(text) == 0 { | ||||
| 						start_mark = parser.mark | ||||
| 					} | ||||
| 					text = read(parser, text) | ||||
| 				} else { | ||||
| 					skip(parser) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		break | ||||
| 	} | ||||
| 	if len(text) > 0 { | ||||
| 		parser.comments = append(parser.comments, yaml_comment_t{ | ||||
| 			token_mark: token_mark, | ||||
| 			start_mark: start_mark, | ||||
| 			line: text, | ||||
| 		}) | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
| 
 | ||||
| func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) bool { | ||||
| 	token := parser.tokens[len(parser.tokens)-1] | ||||
| 
 | ||||
| 	if token.typ == yaml_FLOW_ENTRY_TOKEN && len(parser.tokens) > 1 { | ||||
| 		token = parser.tokens[len(parser.tokens)-2] | ||||
| 	} | ||||
| 
 | ||||
| 	var token_mark = token.start_mark | ||||
| 	var start_mark yaml_mark_t | ||||
| 	var next_indent = parser.indent | ||||
| 	if next_indent < 0 { | ||||
| 		next_indent = 0 | ||||
| 	} | ||||
| 
 | ||||
| 	var recent_empty = false | ||||
| 	var first_empty = parser.newlines <= 1 | ||||
| 
 | ||||
| 	var line = parser.mark.line | ||||
| 	var column = parser.mark.column | ||||
| 
 | ||||
| 	var text []byte | ||||
| 
 | ||||
| 	// The foot line is the place where a comment must start to | ||||
| 	// still be considered as a foot of the prior content. | ||||
| 	// If there's some content in the currently parsed line, then | ||||
| 	// the foot is the line below it. | ||||
| 	var foot_line = -1 | ||||
| 	if scan_mark.line > 0 { | ||||
| 		foot_line = parser.mark.line-parser.newlines+1 | ||||
| 		if parser.newlines == 0 && parser.mark.column > 1 { | ||||
| 			foot_line++ | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	var peek = 0 | ||||
| 	for ; peek < 512; peek++ { | ||||
| 		if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) { | ||||
| 			break | ||||
| 		} | ||||
| 		column++ | ||||
| 		if is_blank(parser.buffer, parser.buffer_pos+peek) { | ||||
| 			continue | ||||
| 		} | ||||
| 		c := parser.buffer[parser.buffer_pos+peek] | ||||
| 		var close_flow = parser.flow_level > 0 && (c == ']' || c == '}') | ||||
| 		if close_flow || is_breakz(parser.buffer, parser.buffer_pos+peek) { | ||||
| 			// Got line break or terminator. | ||||
| 			if close_flow || !recent_empty { | ||||
| 				if close_flow || first_empty && (start_mark.line == foot_line && token.typ != yaml_VALUE_TOKEN || start_mark.column-1 < next_indent) { | ||||
| 					// This is the first empty line and there were no empty lines before, | ||||
| 					// so this initial part of the comment is a foot of the prior token | ||||
| 					// instead of being a head for the following one. Split it up. | ||||
| 					// Alternatively, this might also be the last comment inside a flow | ||||
| 					// scope, so it must be a footer. | ||||
| 					if len(text) > 0 { | ||||
| 						if start_mark.column-1 < next_indent { | ||||
| 							// If dedented it's unrelated to the prior token. | ||||
| 							token_mark = start_mark | ||||
| 						} | ||||
| 						parser.comments = append(parser.comments, yaml_comment_t{ | ||||
| 							scan_mark:  scan_mark, | ||||
| 							token_mark: token_mark, | ||||
| 							start_mark: start_mark, | ||||
| 							end_mark:   yaml_mark_t{parser.mark.index + peek, line, column}, | ||||
| 							foot:       text, | ||||
| 						}) | ||||
| 						scan_mark = yaml_mark_t{parser.mark.index + peek, line, column} | ||||
| 						token_mark = scan_mark | ||||
| 						text = nil | ||||
| 					} | ||||
| 				} else { | ||||
| 					if len(text) > 0 && parser.buffer[parser.buffer_pos+peek] != 0 { | ||||
| 						text = append(text, '\n') | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			if !is_break(parser.buffer, parser.buffer_pos+peek) { | ||||
| 				break | ||||
| 			} | ||||
| 			first_empty = false | ||||
| 			recent_empty = true | ||||
| 			column = 0 | ||||
| 			line++ | ||||
| 			continue | ||||
| 		} | ||||
| 
 | ||||
| 		if len(text) > 0 && (close_flow || column-1 < next_indent && column != start_mark.column) { | ||||
| 			// The comment at the different indentation is a foot of the | ||||
| 			// preceding data rather than a head of the upcoming one. | ||||
| 			parser.comments = append(parser.comments, yaml_comment_t{ | ||||
| 				scan_mark:  scan_mark, | ||||
| 				token_mark: token_mark, | ||||
| 				start_mark: start_mark, | ||||
| 				end_mark:   yaml_mark_t{parser.mark.index + peek, line, column}, | ||||
| 				foot:       text, | ||||
| 			}) | ||||
| 			scan_mark = yaml_mark_t{parser.mark.index + peek, line, column} | ||||
| 			token_mark = scan_mark | ||||
| 			text = nil | ||||
| 		} | ||||
| 
 | ||||
| 		if parser.buffer[parser.buffer_pos+peek] != '#' { | ||||
| 			break | ||||
| 		} | ||||
| 
 | ||||
| 		if len(text) == 0 { | ||||
| 			start_mark = yaml_mark_t{parser.mark.index + peek, line, column} | ||||
| 		} else { | ||||
| 			text = append(text, '\n') | ||||
| 		} | ||||
| 
 | ||||
| 		recent_empty = false | ||||
| 
 | ||||
| 		// Consume until after the consumed comment line. | ||||
| 		seen := parser.mark.index+peek | ||||
| 		for { | ||||
| 			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { | ||||
| 				return false | ||||
| 			} | ||||
| 			if is_breakz(parser.buffer, parser.buffer_pos) { | ||||
| 				if parser.mark.index >= seen { | ||||
| 					break | ||||
| 				} | ||||
| 				if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { | ||||
| 					return false | ||||
| 				} | ||||
| 				skip_line(parser) | ||||
| 			} else if parser.mark.index >= seen { | ||||
| 				text = read(parser, text) | ||||
| 			} else { | ||||
| 				skip(parser) | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		peek = 0 | ||||
| 		column = 0 | ||||
| 		line = parser.mark.line | ||||
| 		next_indent = parser.indent | ||||
| 		if next_indent < 0 { | ||||
| 			next_indent = 0 | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if len(text) > 0 { | ||||
| 		parser.comments = append(parser.comments, yaml_comment_t{ | ||||
| 			scan_mark:  scan_mark, | ||||
| 			token_mark: start_mark, | ||||
| 			start_mark: start_mark, | ||||
| 			end_mark:   yaml_mark_t{parser.mark.index + peek - 1, line, column}, | ||||
| 			head:       text, | ||||
| 		}) | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
							
								
								
									
										25
									
								
								vendor/gopkg.in/yaml.v2/sorter.go → vendor/gopkg.in/yaml.v3/sorter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										25
									
								
								vendor/gopkg.in/yaml.v2/sorter.go → vendor/gopkg.in/yaml.v3/sorter.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +1,18 @@ | ||||
| // | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
| 
 | ||||
| package yaml | ||||
| 
 | ||||
| import ( | ||||
| @ -37,8 +52,10 @@ func (l keyList) Less(i, j int) bool { | ||||
| 		return ak < bk | ||||
| 	} | ||||
| 	ar, br := []rune(a.String()), []rune(b.String()) | ||||
| 	digits := false | ||||
| 	for i := 0; i < len(ar) && i < len(br); i++ { | ||||
| 		if ar[i] == br[i] { | ||||
| 			digits = unicode.IsDigit(ar[i]) | ||||
| 			continue | ||||
| 		} | ||||
| 		al := unicode.IsLetter(ar[i]) | ||||
| @ -47,12 +64,16 @@ func (l keyList) Less(i, j int) bool { | ||||
| 			return ar[i] < br[i] | ||||
| 		} | ||||
| 		if al || bl { | ||||
| 			return bl | ||||
| 			if digits { | ||||
| 				return al | ||||
| 			} else { | ||||
| 				return bl | ||||
| 			} | ||||
| 		} | ||||
| 		var ai, bi int | ||||
| 		var an, bn int64 | ||||
| 		if ar[i] == '0' || br[i] == '0' { | ||||
| 			for j := i-1; j >= 0 && unicode.IsDigit(ar[j]); j-- { | ||||
| 			for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { | ||||
| 				if ar[j] != '0' { | ||||
| 					an = 1 | ||||
| 					bn = 1 | ||||
							
								
								
									
										48
									
								
								vendor/gopkg.in/yaml.v3/writerc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								vendor/gopkg.in/yaml.v3/writerc.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,48 @@ | ||||
| //  | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // Copyright (c) 2006-2010 Kirill Simonov | ||||
| //  | ||||
| // 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. | ||||
|  | ||||
| package yaml | ||||
|  | ||||
| // Set the writer error and return false. | ||||
| func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { | ||||
| 	emitter.error = yaml_WRITER_ERROR | ||||
| 	emitter.problem = problem | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| // Flush the output buffer. | ||||
| func yaml_emitter_flush(emitter *yaml_emitter_t) bool { | ||||
| 	if emitter.write_handler == nil { | ||||
| 		panic("write handler not set") | ||||
| 	} | ||||
|  | ||||
| 	// Check if the buffer is empty. | ||||
| 	if emitter.buffer_pos == 0 { | ||||
| 		return true | ||||
| 	} | ||||
|  | ||||
| 	if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { | ||||
| 		return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) | ||||
| 	} | ||||
| 	emitter.buffer_pos = 0 | ||||
| 	return true | ||||
| } | ||||
							
								
								
									
										350
									
								
								vendor/gopkg.in/yaml.v2/yaml.go → vendor/gopkg.in/yaml.v3/yaml.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										350
									
								
								vendor/gopkg.in/yaml.v2/yaml.go → vendor/gopkg.in/yaml.v3/yaml.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +1,18 @@ | ||||
| // | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
| 
 | ||||
| // Package yaml implements YAML support for the Go language. | ||||
| // | ||||
| // Source code and other details for the project are available at GitHub: | ||||
| @ -13,23 +28,16 @@ import ( | ||||
| 	"reflect" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"unicode/utf8" | ||||
| ) | ||||
| 
 | ||||
| // MapSlice encodes and decodes as a YAML map. | ||||
| // The order of keys is preserved when encoding and decoding. | ||||
| type MapSlice []MapItem | ||||
| 
 | ||||
| // MapItem is an item in a MapSlice. | ||||
| type MapItem struct { | ||||
| 	Key, Value interface{} | ||||
| // The Unmarshaler interface may be implemented by types to customize their | ||||
| // behavior when being unmarshaled from a YAML document. | ||||
| type Unmarshaler interface { | ||||
| 	UnmarshalYAML(value *Node) error | ||||
| } | ||||
| 
 | ||||
| // The Unmarshaler interface may be implemented by types to customize their | ||||
| // behavior when being unmarshaled from a YAML document. The UnmarshalYAML | ||||
| // method receives a function that may be called to unmarshal the original | ||||
| // YAML value into a field or variable. It is safe to call the unmarshal | ||||
| // function parameter more than once if necessary. | ||||
| type Unmarshaler interface { | ||||
| type obsoleteUnmarshaler interface { | ||||
| 	UnmarshalYAML(unmarshal func(interface{}) error) error | ||||
| } | ||||
| 
 | ||||
| @ -81,18 +89,10 @@ func Unmarshal(in []byte, out interface{}) (err error) { | ||||
| 	return unmarshal(in, out, false) | ||||
| } | ||||
| 
 | ||||
| // UnmarshalStrict is like Unmarshal except that any fields that are found | ||||
| // in the data that do not have corresponding struct members, or mapping | ||||
| // keys that are duplicates, will result in | ||||
| // an error. | ||||
| func UnmarshalStrict(in []byte, out interface{}) (err error) { | ||||
| 	return unmarshal(in, out, true) | ||||
| } | ||||
| 
 | ||||
| // A Decoder reads and decodes YAML values from an input stream. | ||||
| type Decoder struct { | ||||
| 	strict bool | ||||
| 	parser *parser | ||||
| 	parser      *parser | ||||
| 	knownFields bool | ||||
| } | ||||
| 
 | ||||
| // NewDecoder returns a new decoder that reads from r. | ||||
| @ -105,10 +105,10 @@ func NewDecoder(r io.Reader) *Decoder { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // SetStrict sets whether strict decoding behaviour is enabled when | ||||
| // decoding items in the data (see UnmarshalStrict). By default, decoding is not strict. | ||||
| func (dec *Decoder) SetStrict(strict bool) { | ||||
| 	dec.strict = strict | ||||
| // KnownFields ensures that the keys in decoded mappings to | ||||
| // exist as fields in the struct being decoded into. | ||||
| func (dec *Decoder) KnownFields(enable bool) { | ||||
| 	dec.knownFields = enable | ||||
| } | ||||
| 
 | ||||
| // Decode reads the next YAML-encoded value from its input | ||||
| @ -117,7 +117,8 @@ func (dec *Decoder) SetStrict(strict bool) { | ||||
| // See the documentation for Unmarshal for details about the | ||||
| // conversion of YAML into a Go value. | ||||
| func (dec *Decoder) Decode(v interface{}) (err error) { | ||||
| 	d := newDecoder(dec.strict) | ||||
| 	d := newDecoder() | ||||
| 	d.knownFields = dec.knownFields | ||||
| 	defer handleErr(&err) | ||||
| 	node := dec.parser.parse() | ||||
| 	if node == nil { | ||||
| @ -134,9 +135,27 @@ func (dec *Decoder) Decode(v interface{}) (err error) { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Decode decodes the node and stores its data into the value pointed to by v. | ||||
| // | ||||
| // See the documentation for Unmarshal for details about the | ||||
| // conversion of YAML into a Go value. | ||||
| func (n *Node) Decode(v interface{}) (err error) { | ||||
| 	d := newDecoder() | ||||
| 	defer handleErr(&err) | ||||
| 	out := reflect.ValueOf(v) | ||||
| 	if out.Kind() == reflect.Ptr && !out.IsNil() { | ||||
| 		out = out.Elem() | ||||
| 	} | ||||
| 	d.unmarshal(n, out) | ||||
| 	if len(d.terrors) > 0 { | ||||
| 		return &TypeError{d.terrors} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func unmarshal(in []byte, out interface{}, strict bool) (err error) { | ||||
| 	defer handleErr(&err) | ||||
| 	d := newDecoder(strict) | ||||
| 	d := newDecoder() | ||||
| 	p := newParser(in) | ||||
| 	defer p.destroy() | ||||
| 	node := p.parse() | ||||
| @ -233,6 +252,32 @@ func (e *Encoder) Encode(v interface{}) (err error) { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Encode encodes value v and stores its representation in n. | ||||
| // | ||||
| // See the documentation for Marshal for details about the | ||||
| // conversion of Go values into YAML. | ||||
| func (n *Node) Encode(v interface{}) (err error) { | ||||
| 	defer handleErr(&err) | ||||
| 	e := newEncoder() | ||||
| 	defer e.destroy() | ||||
| 	e.marshalDoc("", reflect.ValueOf(v)) | ||||
| 	e.finish() | ||||
| 	p := newParser(e.out) | ||||
| 	p.textless = true | ||||
| 	defer p.destroy() | ||||
| 	doc := p.parse() | ||||
| 	*n = *doc.Content[0] | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // SetIndent changes the used indentation used when encoding. | ||||
| func (e *Encoder) SetIndent(spaces int) { | ||||
| 	if spaces < 0 { | ||||
| 		panic("yaml: cannot indent to a negative number of spaces") | ||||
| 	} | ||||
| 	e.encoder.indent = spaces | ||||
| } | ||||
| 
 | ||||
| // Close closes the encoder by writing any remaining data. | ||||
| // It does not write a stream terminating string "...". | ||||
| func (e *Encoder) Close() (err error) { | ||||
| @ -275,6 +320,168 @@ func (e *TypeError) Error() string { | ||||
| 	return fmt.Sprintf("yaml: unmarshal errors:\n  %s", strings.Join(e.Errors, "\n  ")) | ||||
| } | ||||
| 
 | ||||
| type Kind uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	DocumentNode Kind = 1 << iota | ||||
| 	SequenceNode | ||||
| 	MappingNode | ||||
| 	ScalarNode | ||||
| 	AliasNode | ||||
| ) | ||||
| 
 | ||||
| type Style uint32 | ||||
| 
 | ||||
| const ( | ||||
| 	TaggedStyle Style = 1 << iota | ||||
| 	DoubleQuotedStyle | ||||
| 	SingleQuotedStyle | ||||
| 	LiteralStyle | ||||
| 	FoldedStyle | ||||
| 	FlowStyle | ||||
| ) | ||||
| 
 | ||||
| // Node represents an element in the YAML document hierarchy. While documents | ||||
| // are typically encoded and decoded into higher level types, such as structs | ||||
| // and maps, Node is an intermediate representation that allows detailed | ||||
| // control over the content being decoded or encoded. | ||||
| // | ||||
| // It's worth noting that although Node offers access into details such as | ||||
| // line numbers, colums, and comments, the content when re-encoded will not | ||||
| // have its original textual representation preserved. An effort is made to | ||||
| // render the data plesantly, and to preserve comments near the data they | ||||
| // describe, though. | ||||
| // | ||||
| // Values that make use of the Node type interact with the yaml package in the | ||||
| // same way any other type would do, by encoding and decoding yaml data | ||||
| // directly or indirectly into them. | ||||
| // | ||||
| // For example: | ||||
| // | ||||
| //     var person struct { | ||||
| //             Name    string | ||||
| //             Address yaml.Node | ||||
| //     } | ||||
| //     err := yaml.Unmarshal(data, &person) | ||||
| //  | ||||
| // Or by itself: | ||||
| // | ||||
| //     var person Node | ||||
| //     err := yaml.Unmarshal(data, &person) | ||||
| // | ||||
| type Node struct { | ||||
| 	// Kind defines whether the node is a document, a mapping, a sequence, | ||||
| 	// a scalar value, or an alias to another node. The specific data type of | ||||
| 	// scalar nodes may be obtained via the ShortTag and LongTag methods. | ||||
| 	Kind  Kind | ||||
| 
 | ||||
| 	// Style allows customizing the apperance of the node in the tree. | ||||
| 	Style Style | ||||
| 
 | ||||
| 	// Tag holds the YAML tag defining the data type for the value. | ||||
| 	// When decoding, this field will always be set to the resolved tag, | ||||
| 	// even when it wasn't explicitly provided in the YAML content. | ||||
| 	// When encoding, if this field is unset the value type will be | ||||
| 	// implied from the node properties, and if it is set, it will only | ||||
| 	// be serialized into the representation if TaggedStyle is used or | ||||
| 	// the implicit tag diverges from the provided one. | ||||
| 	Tag string | ||||
| 
 | ||||
| 	// Value holds the unescaped and unquoted represenation of the value. | ||||
| 	Value string | ||||
| 
 | ||||
| 	// Anchor holds the anchor name for this node, which allows aliases to point to it. | ||||
| 	Anchor string | ||||
| 
 | ||||
| 	// Alias holds the node that this alias points to. Only valid when Kind is AliasNode. | ||||
| 	Alias *Node | ||||
| 
 | ||||
| 	// Content holds contained nodes for documents, mappings, and sequences. | ||||
| 	Content []*Node | ||||
| 
 | ||||
| 	// HeadComment holds any comments in the lines preceding the node and | ||||
| 	// not separated by an empty line. | ||||
| 	HeadComment string | ||||
| 
 | ||||
| 	// LineComment holds any comments at the end of the line where the node is in. | ||||
| 	LineComment string | ||||
| 
 | ||||
| 	// FootComment holds any comments following the node and before empty lines. | ||||
| 	FootComment string | ||||
| 
 | ||||
| 	// Line and Column hold the node position in the decoded YAML text. | ||||
| 	// These fields are not respected when encoding the node. | ||||
| 	Line   int | ||||
| 	Column int | ||||
| } | ||||
| 
 | ||||
| // IsZero returns whether the node has all of its fields unset. | ||||
| func (n *Node) IsZero() bool { | ||||
| 	return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil && | ||||
| 		n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // LongTag returns the long form of the tag that indicates the data type for | ||||
| // the node. If the Tag field isn't explicitly defined, one will be computed | ||||
| // based on the node properties. | ||||
| func (n *Node) LongTag() string { | ||||
| 	return longTag(n.ShortTag()) | ||||
| } | ||||
| 
 | ||||
| // ShortTag returns the short form of the YAML tag that indicates data type for | ||||
| // the node. If the Tag field isn't explicitly defined, one will be computed | ||||
| // based on the node properties. | ||||
| func (n *Node) ShortTag() string { | ||||
| 	if n.indicatedString() { | ||||
| 		return strTag | ||||
| 	} | ||||
| 	if n.Tag == "" || n.Tag == "!" { | ||||
| 		switch n.Kind { | ||||
| 		case MappingNode: | ||||
| 			return mapTag | ||||
| 		case SequenceNode: | ||||
| 			return seqTag | ||||
| 		case AliasNode: | ||||
| 			if n.Alias != nil { | ||||
| 				return n.Alias.ShortTag() | ||||
| 			} | ||||
| 		case ScalarNode: | ||||
| 			tag, _ := resolve("", n.Value) | ||||
| 			return tag | ||||
| 		case 0: | ||||
| 			// Special case to make the zero value convenient. | ||||
| 			if n.IsZero() { | ||||
| 				return nullTag | ||||
| 			} | ||||
| 		} | ||||
| 		return "" | ||||
| 	} | ||||
| 	return shortTag(n.Tag) | ||||
| } | ||||
| 
 | ||||
| func (n *Node) indicatedString() bool { | ||||
| 	return n.Kind == ScalarNode && | ||||
| 		(shortTag(n.Tag) == strTag || | ||||
| 			(n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0) | ||||
| } | ||||
| 
 | ||||
| // SetString is a convenience function that sets the node to a string value | ||||
| // and defines its style in a pleasant way depending on its content. | ||||
| func (n *Node) SetString(s string) { | ||||
| 	n.Kind = ScalarNode | ||||
| 	if utf8.ValidString(s) { | ||||
| 		n.Value = s | ||||
| 		n.Tag = strTag | ||||
| 	} else { | ||||
| 		n.Value = encodeBase64(s) | ||||
| 		n.Tag = binaryTag | ||||
| 	} | ||||
| 	if strings.Contains(n.Value, "\n") { | ||||
| 		n.Style = LiteralStyle | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // -------------------------------------------------------------------------- | ||||
| // Maintain a mapping of keys to structure field indexes | ||||
| 
 | ||||
| @ -289,6 +496,10 @@ type structInfo struct { | ||||
| 	// InlineMap is the number of the field in the struct that | ||||
| 	// contains an ,inline map, or -1 if there's none. | ||||
| 	InlineMap int | ||||
| 
 | ||||
| 	// InlineUnmarshalers holds indexes to inlined fields that | ||||
| 	// contain unmarshaler values. | ||||
| 	InlineUnmarshalers [][]int | ||||
| } | ||||
| 
 | ||||
| type fieldInfo struct { | ||||
| @ -306,6 +517,12 @@ type fieldInfo struct { | ||||
| 
 | ||||
| var structMap = make(map[reflect.Type]*structInfo) | ||||
| var fieldMapMutex sync.RWMutex | ||||
| var unmarshalerType reflect.Type | ||||
| 
 | ||||
| func init() { | ||||
| 	var v Unmarshaler | ||||
| 	unmarshalerType = reflect.ValueOf(&v).Elem().Type() | ||||
| } | ||||
| 
 | ||||
| func getStructInfo(st reflect.Type) (*structInfo, error) { | ||||
| 	fieldMapMutex.RLock() | ||||
| @ -319,6 +536,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) { | ||||
| 	fieldsMap := make(map[string]fieldInfo) | ||||
| 	fieldsList := make([]fieldInfo, 0, n) | ||||
| 	inlineMap := -1 | ||||
| 	inlineUnmarshalers := [][]int(nil) | ||||
| 	for i := 0; i != n; i++ { | ||||
| 		field := st.Field(i) | ||||
| 		if field.PkgPath != "" && !field.Anonymous { | ||||
| @ -347,7 +565,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) { | ||||
| 				case "inline": | ||||
| 					inline = true | ||||
| 				default: | ||||
| 					return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st)) | ||||
| 					return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st)) | ||||
| 				} | ||||
| 			} | ||||
| 			tag = fields[0] | ||||
| @ -357,34 +575,47 @@ func getStructInfo(st reflect.Type) (*structInfo, error) { | ||||
| 			switch field.Type.Kind() { | ||||
| 			case reflect.Map: | ||||
| 				if inlineMap >= 0 { | ||||
| 					return nil, errors.New("Multiple ,inline maps in struct " + st.String()) | ||||
| 					return nil, errors.New("multiple ,inline maps in struct " + st.String()) | ||||
| 				} | ||||
| 				if field.Type.Key() != reflect.TypeOf("") { | ||||
| 					return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String()) | ||||
| 					return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String()) | ||||
| 				} | ||||
| 				inlineMap = info.Num | ||||
| 			case reflect.Struct: | ||||
| 				sinfo, err := getStructInfo(field.Type) | ||||
| 				if err != nil { | ||||
| 					return nil, err | ||||
| 			case reflect.Struct, reflect.Ptr: | ||||
| 				ftype := field.Type | ||||
| 				for ftype.Kind() == reflect.Ptr { | ||||
| 					ftype = ftype.Elem() | ||||
| 				} | ||||
| 				for _, finfo := range sinfo.FieldsList { | ||||
| 					if _, found := fieldsMap[finfo.Key]; found { | ||||
| 						msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String() | ||||
| 						return nil, errors.New(msg) | ||||
| 				if ftype.Kind() != reflect.Struct { | ||||
| 					return nil, errors.New("option ,inline may only be used on a struct or map field") | ||||
| 				} | ||||
| 				if reflect.PtrTo(ftype).Implements(unmarshalerType) { | ||||
| 					inlineUnmarshalers = append(inlineUnmarshalers, []int{i}) | ||||
| 				} else { | ||||
| 					sinfo, err := getStructInfo(ftype) | ||||
| 					if err != nil { | ||||
| 						return nil, err | ||||
| 					} | ||||
| 					if finfo.Inline == nil { | ||||
| 						finfo.Inline = []int{i, finfo.Num} | ||||
| 					} else { | ||||
| 						finfo.Inline = append([]int{i}, finfo.Inline...) | ||||
| 					for _, index := range sinfo.InlineUnmarshalers { | ||||
| 						inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...)) | ||||
| 					} | ||||
| 					for _, finfo := range sinfo.FieldsList { | ||||
| 						if _, found := fieldsMap[finfo.Key]; found { | ||||
| 							msg := "duplicated key '" + finfo.Key + "' in struct " + st.String() | ||||
| 							return nil, errors.New(msg) | ||||
| 						} | ||||
| 						if finfo.Inline == nil { | ||||
| 							finfo.Inline = []int{i, finfo.Num} | ||||
| 						} else { | ||||
| 							finfo.Inline = append([]int{i}, finfo.Inline...) | ||||
| 						} | ||||
| 						finfo.Id = len(fieldsList) | ||||
| 						fieldsMap[finfo.Key] = finfo | ||||
| 						fieldsList = append(fieldsList, finfo) | ||||
| 					} | ||||
| 					finfo.Id = len(fieldsList) | ||||
| 					fieldsMap[finfo.Key] = finfo | ||||
| 					fieldsList = append(fieldsList, finfo) | ||||
| 				} | ||||
| 			default: | ||||
| 				//return nil, errors.New("Option ,inline needs a struct value or map field") | ||||
| 				return nil, errors.New("Option ,inline needs a struct value field") | ||||
| 				return nil, errors.New("option ,inline may only be used on a struct or map field") | ||||
| 			} | ||||
| 			continue | ||||
| 		} | ||||
| @ -396,7 +627,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) { | ||||
| 		} | ||||
| 
 | ||||
| 		if _, found = fieldsMap[info.Key]; found { | ||||
| 			msg := "Duplicated key '" + info.Key + "' in struct " + st.String() | ||||
| 			msg := "duplicated key '" + info.Key + "' in struct " + st.String() | ||||
| 			return nil, errors.New(msg) | ||||
| 		} | ||||
| 
 | ||||
| @ -406,9 +637,10 @@ func getStructInfo(st reflect.Type) (*structInfo, error) { | ||||
| 	} | ||||
| 
 | ||||
| 	sinfo = &structInfo{ | ||||
| 		FieldsMap:  fieldsMap, | ||||
| 		FieldsList: fieldsList, | ||||
| 		InlineMap:  inlineMap, | ||||
| 		FieldsMap:          fieldsMap, | ||||
| 		FieldsList:         fieldsList, | ||||
| 		InlineMap:          inlineMap, | ||||
| 		InlineUnmarshalers: inlineUnmarshalers, | ||||
| 	} | ||||
| 
 | ||||
| 	fieldMapMutex.Lock() | ||||
| @ -464,15 +696,3 @@ func isZero(v reflect.Value) bool { | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
| 
 | ||||
| // FutureLineWrap globally disables line wrapping when encoding long strings. | ||||
| // This is a temporary and thus deprecated method introduced to faciliate | ||||
| // migration towards v3, which offers more control of line lengths on | ||||
| // individual encodings, and has a default matching the behavior introduced | ||||
| // by this function. | ||||
| // | ||||
| // The default formatting of v2 was erroneously changed in v2.3.0 and reverted | ||||
| // in v2.4.0, at which point this function was introduced to help migration. | ||||
| func FutureLineWrap() { | ||||
| 	disableLineWrapping = true | ||||
| } | ||||
							
								
								
									
										80
									
								
								vendor/gopkg.in/yaml.v2/yamlh.go → vendor/gopkg.in/yaml.v3/yamlh.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										80
									
								
								vendor/gopkg.in/yaml.v2/yamlh.go → vendor/gopkg.in/yaml.v3/yamlh.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +1,25 @@ | ||||
| // | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // Copyright (c) 2006-2010 Kirill Simonov | ||||
| // | ||||
| // 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. | ||||
| 
 | ||||
| package yaml | ||||
| 
 | ||||
| import ( | ||||
| @ -73,13 +95,13 @@ type yaml_scalar_style_t yaml_style_t | ||||
| // Scalar styles. | ||||
| const ( | ||||
| 	// Let the emitter choose the style. | ||||
| 	yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota | ||||
| 	yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0 | ||||
| 
 | ||||
| 	yaml_PLAIN_SCALAR_STYLE         // The plain scalar style. | ||||
| 	yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. | ||||
| 	yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. | ||||
| 	yaml_LITERAL_SCALAR_STYLE       // The literal scalar style. | ||||
| 	yaml_FOLDED_SCALAR_STYLE        // The folded scalar style. | ||||
| 	yaml_PLAIN_SCALAR_STYLE         yaml_scalar_style_t = 1 << iota // The plain scalar style. | ||||
| 	yaml_SINGLE_QUOTED_SCALAR_STYLE                                 // The single-quoted scalar style. | ||||
| 	yaml_DOUBLE_QUOTED_SCALAR_STYLE                                 // The double-quoted scalar style. | ||||
| 	yaml_LITERAL_SCALAR_STYLE                                       // The literal scalar style. | ||||
| 	yaml_FOLDED_SCALAR_STYLE                                        // The folded scalar style. | ||||
| ) | ||||
| 
 | ||||
| type yaml_sequence_style_t yaml_style_t | ||||
| @ -238,6 +260,7 @@ const ( | ||||
| 	yaml_SEQUENCE_END_EVENT   // A SEQUENCE-END event. | ||||
| 	yaml_MAPPING_START_EVENT  // A MAPPING-START event. | ||||
| 	yaml_MAPPING_END_EVENT    // A MAPPING-END event. | ||||
| 	yaml_TAIL_COMMENT_EVENT | ||||
| ) | ||||
| 
 | ||||
| var eventStrings = []string{ | ||||
| @ -252,6 +275,7 @@ var eventStrings = []string{ | ||||
| 	yaml_SEQUENCE_END_EVENT:   "sequence end", | ||||
| 	yaml_MAPPING_START_EVENT:  "mapping start", | ||||
| 	yaml_MAPPING_END_EVENT:    "mapping end", | ||||
| 	yaml_TAIL_COMMENT_EVENT:   "tail comment", | ||||
| } | ||||
| 
 | ||||
| func (e yaml_event_type_t) String() string { | ||||
| @ -279,6 +303,12 @@ type yaml_event_t struct { | ||||
| 	// The list of tag directives (for yaml_DOCUMENT_START_EVENT). | ||||
| 	tag_directives []yaml_tag_directive_t | ||||
| 
 | ||||
| 	// The comments | ||||
| 	head_comment []byte | ||||
| 	line_comment []byte | ||||
| 	foot_comment []byte | ||||
| 	tail_comment []byte | ||||
| 
 | ||||
| 	// The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). | ||||
| 	anchor []byte | ||||
| 
 | ||||
| @ -554,6 +584,8 @@ type yaml_parser_t struct { | ||||
| 
 | ||||
| 	unread int // The number of unread characters in the buffer. | ||||
| 
 | ||||
| 	newlines int // The number of line breaks since last non-break/non-blank character | ||||
| 
 | ||||
| 	raw_buffer     []byte // The raw buffer. | ||||
| 	raw_buffer_pos int    // The current position of the buffer. | ||||
| 
 | ||||
| @ -562,6 +594,17 @@ type yaml_parser_t struct { | ||||
| 	offset int         // The offset of the current position (in bytes). | ||||
| 	mark   yaml_mark_t // The mark of the current position. | ||||
| 
 | ||||
| 	// Comments | ||||
| 
 | ||||
| 	head_comment []byte // The current head comments | ||||
| 	line_comment []byte // The current line comments | ||||
| 	foot_comment []byte // The current foot comments | ||||
| 	tail_comment []byte // Foot comment that happens at the end of a block. | ||||
| 	stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc) | ||||
| 
 | ||||
| 	comments      []yaml_comment_t // The folded comments for all parsed tokens | ||||
| 	comments_head int | ||||
| 
 | ||||
| 	// Scanner stuff | ||||
| 
 | ||||
| 	stream_start_produced bool // Have we started to scan the input stream? | ||||
| @ -595,6 +638,18 @@ type yaml_parser_t struct { | ||||
| 	document *yaml_document_t // The currently parsed document. | ||||
| } | ||||
| 
 | ||||
| type yaml_comment_t struct { | ||||
| 
 | ||||
| 	scan_mark  yaml_mark_t // Position where scanning for comments started | ||||
| 	token_mark yaml_mark_t // Position after which tokens will be associated with this comment | ||||
| 	start_mark yaml_mark_t // Position of '#' comment mark | ||||
| 	end_mark   yaml_mark_t // Position where comment terminated | ||||
| 
 | ||||
| 	head []byte | ||||
| 	line []byte | ||||
| 	foot []byte | ||||
| } | ||||
| 
 | ||||
| // Emitter Definitions | ||||
| 
 | ||||
| // The prototype of a write handler. | ||||
| @ -625,8 +680,10 @@ const ( | ||||
| 	yaml_EMIT_DOCUMENT_CONTENT_STATE           // Expect the content of a document. | ||||
| 	yaml_EMIT_DOCUMENT_END_STATE               // Expect DOCUMENT-END. | ||||
| 	yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE   // Expect the first item of a flow sequence. | ||||
| 	yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE   // Expect the next item of a flow sequence, with the comma already written out | ||||
| 	yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE         // Expect an item of a flow sequence. | ||||
| 	yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE     // Expect the first key of a flow mapping. | ||||
| 	yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE     // Expect the next key of a flow mapping, with the comma already written out | ||||
| 	yaml_EMIT_FLOW_MAPPING_KEY_STATE           // Expect a key of a flow mapping. | ||||
| 	yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE  // Expect a value for a simple key of a flow mapping. | ||||
| 	yaml_EMIT_FLOW_MAPPING_VALUE_STATE         // Expect a value of a flow mapping. | ||||
| @ -698,6 +755,9 @@ type yaml_emitter_t struct { | ||||
| 	indention  bool // If the last character was an indentation character (' ', '-', '?', ':')? | ||||
| 	open_ended bool // If an explicit document end is required? | ||||
| 
 | ||||
| 	space_above bool // Is there's an empty line above? | ||||
| 	foot_indent int  // The indent used to write the foot comment above, or -1 if none. | ||||
| 
 | ||||
| 	// Anchor analysis. | ||||
| 	anchor_data struct { | ||||
| 		anchor []byte // The anchor value. | ||||
| @ -721,6 +781,14 @@ type yaml_emitter_t struct { | ||||
| 		style                 yaml_scalar_style_t // The output style. | ||||
| 	} | ||||
| 
 | ||||
| 	// Comments | ||||
| 	head_comment []byte | ||||
| 	line_comment []byte | ||||
| 	foot_comment []byte | ||||
| 	tail_comment []byte | ||||
| 
 | ||||
| 	key_line_comment []byte | ||||
| 
 | ||||
| 	// Dumper stuff | ||||
| 
 | ||||
| 	opened bool // If the stream was already opened? | ||||
							
								
								
									
										37
									
								
								vendor/gopkg.in/yaml.v2/yamlprivateh.go → vendor/gopkg.in/yaml.v3/yamlprivateh.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										37
									
								
								vendor/gopkg.in/yaml.v2/yamlprivateh.go → vendor/gopkg.in/yaml.v3/yamlprivateh.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @ -1,3 +1,25 @@ | ||||
| //  | ||||
| // Copyright (c) 2011-2019 Canonical Ltd | ||||
| // Copyright (c) 2006-2010 Kirill Simonov | ||||
| //  | ||||
| // 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. | ||||
| 
 | ||||
| package yaml | ||||
| 
 | ||||
| const ( | ||||
| @ -114,8 +136,9 @@ func is_crlf(b []byte, i int) bool { | ||||
| // Check if the character is a line break or NUL. | ||||
| func is_breakz(b []byte, i int) bool { | ||||
| 	//return is_break(b, i) || is_z(b, i) | ||||
| 	return (        // is_break: | ||||
| 	b[i] == '\r' || // CR (#xD) | ||||
| 	return ( | ||||
| 		// is_break: | ||||
| 		b[i] == '\r' || // CR (#xD) | ||||
| 		b[i] == '\n' || // LF (#xA) | ||||
| 		b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) | ||||
| 		b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) | ||||
| @ -127,8 +150,9 @@ func is_breakz(b []byte, i int) bool { | ||||
| // Check if the character is a line break, space, or NUL. | ||||
| func is_spacez(b []byte, i int) bool { | ||||
| 	//return is_space(b, i) || is_breakz(b, i) | ||||
| 	return ( // is_space: | ||||
| 	b[i] == ' ' || | ||||
| 	return ( | ||||
| 		// is_space: | ||||
| 		b[i] == ' ' || | ||||
| 		// is_breakz: | ||||
| 		b[i] == '\r' || // CR (#xD) | ||||
| 		b[i] == '\n' || // LF (#xA) | ||||
| @ -141,8 +165,9 @@ func is_spacez(b []byte, i int) bool { | ||||
| // Check if the character is a line break, space, tab, or NUL. | ||||
| func is_blankz(b []byte, i int) bool { | ||||
| 	//return is_blank(b, i) || is_breakz(b, i) | ||||
| 	return ( // is_blank: | ||||
| 	b[i] == ' ' || b[i] == '\t' || | ||||
| 	return ( | ||||
| 		// is_blank: | ||||
| 		b[i] == ' ' || b[i] == '\t' || | ||||
| 		// is_breakz: | ||||
| 		b[i] == '\r' || // CR (#xD) | ||||
| 		b[i] == '\n' || // LF (#xA) | ||||
							
								
								
									
										9
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							| @ -1,3 +1,6 @@ | ||||
| # github.com/TwiN/deepmerge v0.1.0 | ||||
| ## explicit; go 1.19 | ||||
| github.com/TwiN/deepmerge | ||||
| # github.com/TwiN/g8 v1.4.0 | ||||
| ## explicit; go 1.19 | ||||
| github.com/TwiN/g8 | ||||
| @ -210,9 +213,9 @@ gopkg.in/mail.v2 | ||||
| gopkg.in/square/go-jose.v2 | ||||
| gopkg.in/square/go-jose.v2/cipher | ||||
| gopkg.in/square/go-jose.v2/json | ||||
| # gopkg.in/yaml.v2 v2.4.0 | ||||
| ## explicit; go 1.15 | ||||
| gopkg.in/yaml.v2 | ||||
| # gopkg.in/yaml.v3 v3.0.1 | ||||
| ## explicit | ||||
| gopkg.in/yaml.v3 | ||||
| # lukechampine.com/uint128 v1.2.0 | ||||
| ## explicit; go 1.12 | ||||
| lukechampine.com/uint128 | ||||
|  | ||||
		Reference in New Issue
	
	Block a user