Vendor dependencies

This commit is contained in:
dwrz
2026-10-24 02:44:51 +00:00
parent ffb1e9a952
commit a577d3ff2d
1214 changed files with 315519 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
package ini
import "fmt"
// UnableToReadFile is an error indicating that a ini file could not be read
type UnableToReadFile struct {
Err error
}
// Error returns an error message and the underlying error message if present
func (e *UnableToReadFile) Error() string {
base := "unable to read file"
if e.Err == nil {
return base
}
return fmt.Sprintf("%s: %v", base, e.Err)
}
// Unwrap returns the underlying error
func (e *UnableToReadFile) Unwrap() error {
return e.Err
}
+56
View File
@@ -0,0 +1,56 @@
// Package ini implements parsing of the AWS shared config file.
//
// Example:
// sections, err := ini.OpenFile("/path/to/file")
// if err != nil {
// panic(err)
// }
//
// profile := "foo"
// section, ok := sections.GetSection(profile)
// if !ok {
// fmt.Printf("section %q could not be found", profile)
// }
package ini
import (
"fmt"
"io"
"os"
"strings"
)
// OpenFile parses shared config from the given file path.
func OpenFile(path string) (sections Sections, err error) {
f, oerr := os.Open(path)
if oerr != nil {
return Sections{}, &UnableToReadFile{Err: oerr}
}
defer func() {
closeErr := f.Close()
if err == nil {
err = closeErr
} else if closeErr != nil {
err = fmt.Errorf("close error: %v, original error: %w", closeErr, err)
}
}()
return Parse(f, path)
}
// Parse parses shared config from the given reader.
func Parse(r io.Reader, path string) (Sections, error) {
contents, err := io.ReadAll(r)
if err != nil {
return Sections{}, fmt.Errorf("read all: %v", err)
}
lines := strings.Split(string(contents), "\n")
tokens, err := tokenize(lines)
if err != nil {
return Sections{}, fmt.Errorf("tokenize: %v", err)
}
return parse(tokens, path), nil
}
+109
View File
@@ -0,0 +1,109 @@
package ini
import (
"fmt"
"strings"
)
func parse(tokens []lineToken, path string) Sections {
parser := &parser{
path: path,
sections: NewSections(),
}
parser.parse(tokens)
return parser.sections
}
type parser struct {
csection, ckey string // current state
path string // source file path
sections Sections // parse result
}
func (p *parser) parse(tokens []lineToken) {
for _, otok := range tokens {
switch tok := otok.(type) {
case *lineTokenProfile:
p.handleProfile(tok)
case *lineTokenProperty:
p.handleProperty(tok)
case *lineTokenSubProperty:
p.handleSubProperty(tok)
case *lineTokenContinuation:
p.handleContinuation(tok)
}
}
}
func (p *parser) handleProfile(tok *lineTokenProfile) {
name := tok.Name
if tok.Type != "" {
name = fmt.Sprintf("%s %s", tok.Type, tok.Name)
}
p.ckey = ""
p.csection = name
if _, ok := p.sections.container[name]; !ok {
p.sections.container[name] = NewSection(name)
}
}
func (p *parser) handleProperty(tok *lineTokenProperty) {
if p.csection == "" {
return // LEGACY: don't error on "global" properties
}
p.ckey = tok.Key
if _, ok := p.sections.container[p.csection].values[tok.Key]; ok {
section := p.sections.container[p.csection]
section.Logs = append(p.sections.container[p.csection].Logs,
fmt.Sprintf(
"For profile: %v, overriding %v value, with a %v value found in a duplicate profile defined later in the same file %v. \n",
p.csection, tok.Key, tok.Key, p.path,
),
)
p.sections.container[p.csection] = section
}
p.sections.container[p.csection].values[tok.Key] = Value{
str: tok.Value,
}
p.sections.container[p.csection].SourceFile[tok.Key] = p.path
}
func (p *parser) handleSubProperty(tok *lineTokenSubProperty) {
if p.csection == "" {
return // LEGACY: don't error on "global" properties
}
if p.ckey == "" || p.sections.container[p.csection].values[p.ckey].str != "" {
// This is an "orphaned" subproperty, either because it's at
// the beginning of a section or because the last property's
// value isn't empty. Either way we're lenient here and
// "promote" this to a normal property.
p.handleProperty(&lineTokenProperty{
Key: tok.Key,
Value: strings.TrimSpace(trimPropertyComment(tok.Value)),
})
return
}
if p.sections.container[p.csection].values[p.ckey].mp == nil {
p.sections.container[p.csection].values[p.ckey] = Value{
mp: map[string]string{},
}
}
p.sections.container[p.csection].values[p.ckey].mp[tok.Key] = tok.Value
}
func (p *parser) handleContinuation(tok *lineTokenContinuation) {
if p.ckey == "" {
return
}
value, _ := p.sections.container[p.csection].values[p.ckey]
if value.str != "" && value.mp == nil {
value.str = fmt.Sprintf("%s\n%s", value.str, tok.Value)
}
p.sections.container[p.csection].values[p.ckey] = value
}
+157
View File
@@ -0,0 +1,157 @@
package ini
import (
"sort"
)
// Sections is a map of Section structures that represent
// a configuration.
type Sections struct {
container map[string]Section
}
// NewSections returns empty ini Sections
func NewSections() Sections {
return Sections{
container: make(map[string]Section, 0),
}
}
// GetSection will return section p. If section p does not exist,
// false will be returned in the second parameter.
func (t Sections) GetSection(p string) (Section, bool) {
v, ok := t.container[p]
return v, ok
}
// HasSection denotes if Sections consist of a section with
// provided name.
func (t Sections) HasSection(p string) bool {
_, ok := t.container[p]
return ok
}
// SetSection sets a section value for provided section name.
func (t Sections) SetSection(p string, v Section) Sections {
t.container[p] = v
return t
}
// DeleteSection deletes a section entry/value for provided section name./
func (t Sections) DeleteSection(p string) {
delete(t.container, p)
}
// values represents a map of union values.
type values map[string]Value
// List will return a list of all sections that were successfully
// parsed.
func (t Sections) List() []string {
keys := make([]string, len(t.container))
i := 0
for k := range t.container {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}
// Section contains a name and values. This represent
// a sectioned entry in a configuration file.
type Section struct {
// Name is the Section profile name
Name string
// values are the values within parsed profile
values values
// Errors is the list of errors
Errors []error
// Logs is the list of logs
Logs []string
// SourceFile is the INI Source file from where this section
// was retrieved. They key is the property, value is the
// source file the property was retrieved from.
SourceFile map[string]string
}
// NewSection returns an initialize section for the name
func NewSection(name string) Section {
return Section{
Name: name,
values: values{},
SourceFile: map[string]string{},
}
}
// List will return a list of all
// services in values
func (t Section) List() []string {
keys := make([]string, len(t.values))
i := 0
for k := range t.values {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}
// UpdateSourceFile updates source file for a property to provided filepath.
func (t Section) UpdateSourceFile(property string, filepath string) {
t.SourceFile[property] = filepath
}
// UpdateValue updates value for a provided key with provided value
func (t Section) UpdateValue(k string, v Value) error {
t.values[k] = v
return nil
}
// Has will return whether or not an entry exists in a given section
func (t Section) Has(k string) bool {
_, ok := t.values[k]
return ok
}
// ValueType will returned what type the union is set to. If
// k was not found, the NoneType will be returned.
func (t Section) ValueType(k string) (ValueType, bool) {
v, ok := t.values[k]
return v.Type, ok
}
// Bool returns a bool value at k
func (t Section) Bool(k string) (bool, bool) {
return t.values[k].BoolValue()
}
// Int returns an integer value at k
func (t Section) Int(k string) (int64, bool) {
return t.values[k].IntValue()
}
// Map returns a map value at k
func (t Section) Map(k string) map[string]string {
return t.values[k].MapValue()
}
// Float64 returns a float value at k
func (t Section) Float64(k string) (float64, bool) {
return t.values[k].FloatValue()
}
// String returns the string value at k
func (t Section) String(k string) string {
_, ok := t.values[k]
if !ok {
return ""
}
return t.values[k].StringValue()
}
+89
View File
@@ -0,0 +1,89 @@
package ini
import (
"strings"
)
func trimProfileComment(s string) string {
r, _, _ := strings.Cut(s, "#")
r, _, _ = strings.Cut(r, ";")
return r
}
func trimPropertyComment(s string) string {
r, _, _ := strings.Cut(s, " #")
r, _, _ = strings.Cut(r, " ;")
r, _, _ = strings.Cut(r, "\t#")
r, _, _ = strings.Cut(r, "\t;")
return r
}
// assumes no surrounding comment
func splitProperty(s string) (string, string, bool) {
equalsi := strings.Index(s, "=")
coloni := strings.Index(s, ":") // LEGACY: also supported for property assignment
sep := "="
if equalsi == -1 || coloni != -1 && coloni < equalsi {
sep = ":"
}
k, v, ok := strings.Cut(s, sep)
if !ok {
return "", "", false
}
return strings.TrimSpace(k), strings.TrimSpace(v), true
}
// assumes no surrounding comment, whitespace, or profile brackets
func splitProfile(s string) (string, string) {
var first int
for i, r := range s {
if isLineSpace(r) {
if first == 0 {
first = i
}
} else {
if first != 0 {
return s[:first], s[i:]
}
}
}
if first == 0 {
return "", s // type component is effectively blank
}
return "", ""
}
func isLineSpace(r rune) bool {
return r == ' ' || r == '\t'
}
func unquote(s string) string {
if isSingleQuoted(s) || isDoubleQuoted(s) {
return s[1 : len(s)-1]
}
return s
}
// applies various legacy conversions to property values:
// - remote wrapping single/doublequotes
func legacyStrconv(s string) string {
s = unquote(s)
return s
}
func isSingleQuoted(s string) bool {
return hasAffixes(s, "'", "'")
}
func isDoubleQuoted(s string) bool {
return hasAffixes(s, `"`, `"`)
}
func isBracketed(s string) bool {
return hasAffixes(s, "[", "]")
}
func hasAffixes(s, left, right string) bool {
return strings.HasPrefix(s, left) && strings.HasSuffix(s, right)
}
+32
View File
@@ -0,0 +1,32 @@
package ini
type lineToken interface {
isLineToken()
}
type lineTokenProfile struct {
Type string
Name string
}
func (*lineTokenProfile) isLineToken() {}
type lineTokenProperty struct {
Key string
Value string
}
func (*lineTokenProperty) isLineToken() {}
type lineTokenContinuation struct {
Value string
}
func (*lineTokenContinuation) isLineToken() {}
type lineTokenSubProperty struct {
Key string
Value string
}
func (*lineTokenSubProperty) isLineToken() {}
+92
View File
@@ -0,0 +1,92 @@
package ini
import (
"strings"
)
func tokenize(lines []string) ([]lineToken, error) {
tokens := make([]lineToken, 0, len(lines))
for _, line := range lines {
if len(strings.TrimSpace(line)) == 0 || isLineComment(line) {
continue
}
if tok := asProfile(line); tok != nil {
tokens = append(tokens, tok)
} else if tok := asProperty(line); tok != nil {
tokens = append(tokens, tok)
} else if tok := asSubProperty(line); tok != nil {
tokens = append(tokens, tok)
} else if tok := asContinuation(line); tok != nil {
tokens = append(tokens, tok)
} // unrecognized tokens are effectively ignored
}
return tokens, nil
}
func isLineComment(line string) bool {
trimmed := strings.TrimLeft(line, " \t")
return strings.HasPrefix(trimmed, "#") || strings.HasPrefix(trimmed, ";")
}
func asProfile(line string) *lineTokenProfile { // " [ type name ] ; comment"
trimmed := strings.TrimSpace(trimProfileComment(line)) // "[ type name ]"
if !isBracketed(trimmed) {
return nil
}
trimmed = trimmed[1 : len(trimmed)-1] // " type name " (or just " name ")
trimmed = strings.TrimSpace(trimmed) // "type name" / "name"
typ, name := splitProfile(trimmed)
return &lineTokenProfile{
Type: typ,
Name: name,
}
}
func asProperty(line string) *lineTokenProperty {
if isLineSpace(rune(line[0])) {
return nil
}
trimmed := trimPropertyComment(line)
trimmed = strings.TrimRight(trimmed, " \t")
k, v, ok := splitProperty(trimmed)
if !ok {
return nil
}
return &lineTokenProperty{
Key: strings.ToLower(k), // LEGACY: normalize key case
Value: legacyStrconv(v), // LEGACY: see func docs
}
}
func asSubProperty(line string) *lineTokenSubProperty {
if !isLineSpace(rune(line[0])) {
return nil
}
// comments on sub-properties are included in the value
trimmed := strings.TrimLeft(line, " \t")
k, v, ok := splitProperty(trimmed)
if !ok {
return nil
}
return &lineTokenSubProperty{ // same LEGACY constraints as in normal property
Key: strings.ToLower(k),
Value: legacyStrconv(v),
}
}
func asContinuation(line string) *lineTokenContinuation {
if !isLineSpace(rune(line[0])) {
return nil
}
// includes comments like sub-properties
trimmed := strings.TrimLeft(line, " \t")
return &lineTokenContinuation{
Value: trimmed,
}
}
+93
View File
@@ -0,0 +1,93 @@
package ini
import (
"fmt"
"strconv"
"strings"
)
// ValueType is an enum that will signify what type
// the Value is
type ValueType int
func (v ValueType) String() string {
switch v {
case NoneType:
return "NONE"
case StringType:
return "STRING"
}
return ""
}
// ValueType enums
const (
NoneType = ValueType(iota)
StringType
QuotedStringType
)
// Value is a union container
type Value struct {
Type ValueType
str string
mp map[string]string
}
// NewStringValue returns a Value type generated using a string input.
func NewStringValue(str string) (Value, error) {
return Value{str: str}, nil
}
func (v Value) String() string {
switch v.Type {
case StringType:
return fmt.Sprintf("string: %s", string(v.str))
case QuotedStringType:
return fmt.Sprintf("quoted string: %s", string(v.str))
default:
return "union not set"
}
}
// MapValue returns a map value for sub properties
func (v Value) MapValue() map[string]string {
return v.mp
}
// IntValue returns an integer value
func (v Value) IntValue() (int64, bool) {
i, err := strconv.ParseInt(string(v.str), 0, 64)
if err != nil {
return 0, false
}
return i, true
}
// FloatValue returns a float value
func (v Value) FloatValue() (float64, bool) {
f, err := strconv.ParseFloat(string(v.str), 64)
if err != nil {
return 0, false
}
return f, true
}
// BoolValue returns a bool value
func (v Value) BoolValue() (bool, bool) {
// we don't use ParseBool as it recognizes more than what we've
// historically supported
if strings.EqualFold(v.str, "true") {
return true, true
} else if strings.EqualFold(v.str, "false") {
return false, true
}
return false, false
}
// StringValue returns the string value
func (v Value) StringValue() string {
return v.str
}