Added value parsing for key/value files
This commit is contained in:
parent
aba688f2d2
commit
0e0fbeaa88
@ -14,6 +14,8 @@ package keyValue
|
||||
import "io"
|
||||
import "bufio"
|
||||
import "strings"
|
||||
import "strconv"
|
||||
import "unicode"
|
||||
import "git.tebibyte.media/tomo/xdg/locale"
|
||||
|
||||
type keyValueError string
|
||||
@ -42,6 +44,16 @@ const (
|
||||
ErrNoDefaultValue = keyValueError("no default value")
|
||||
// ErrEntryOutsideGroup indicates that an entry was found.
|
||||
ErrEntryOutsideGroup = keyValueError("entry outside group")
|
||||
// ErrUnsupportedEscape indicates that an unsupported escape sequence
|
||||
// was found.
|
||||
ErrUnsupportedEscape = keyValueError("unsupported escape")
|
||||
|
||||
// ErrStringNotASCII indicates that a string or iconstring value was not
|
||||
// found to contain valid ASCII text.
|
||||
ErrStringNotASCII = keyValueError("string not ascii")
|
||||
// ErrBooleanNotTrueOrFalse indicates that a boolean value was not found
|
||||
// to equal "true" or "false".
|
||||
ErrBooleanNotTrueOrFalse = keyValueError("boolean not true or false")
|
||||
)
|
||||
|
||||
// Parse parses a key/value file from a Reader.
|
||||
@ -216,3 +228,133 @@ func (entry Entry) Localize (locale locale.Locale) string {
|
||||
}
|
||||
|
||||
// TODO have functions to parse/validate all data types
|
||||
|
||||
// ParseString parses a value of type string.
|
||||
// Values of type string may contain all ASCII characters except for control
|
||||
// characters.
|
||||
// The escape sequences \s, \n, \t, \r, and \\ are supported, meaning ASCII
|
||||
// space, newline, tab, carriage return, and backslash, respectively.
|
||||
func ParseString (value string) (string, error) {
|
||||
value, err := escapeString(value)
|
||||
if err != nil { return "", err }
|
||||
if !isAsciiText(value) { return "", ErrStringNotASCII }
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// ParseLocaleString parses a value of type localestring.
|
||||
// Values of type localestring are user displayable, and are encoded in UTF-8.
|
||||
// The escape sequences \s, \n, \t, \r, and \\ are supported, meaning ASCII
|
||||
// space, newline, tab, carriage return, and backslash, respectively.
|
||||
func ParseLocaleString (value string) (string, error) {
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// ParseIconString parses a value of type iconstring.
|
||||
// Values of type iconstring are the names of icons; these may be absolute
|
||||
// paths, or symbolic names for icons located using the algorithm described in
|
||||
// the Icon Theme Specification. Such values are not user-displayable, and are
|
||||
// encoded in UTF-8.
|
||||
// The escape sequences \s, \n, \t, \r, and \\ are supported, meaning ASCII
|
||||
// space, newline, tab, carriage return, and backslash, respectively.
|
||||
func ParseIconString (value string) (string, error) {
|
||||
value, err := escapeString(value)
|
||||
if err != nil { return "", err }
|
||||
if !isAsciiText(value) { return "", ErrStringNotASCII }
|
||||
return value, nil
|
||||
}
|
||||
|
||||
// ParseBoolean parses a value of type boolean.
|
||||
// Values of type boolean must either be the string true or false.
|
||||
func ParseBoolean (value string) (bool, error) {
|
||||
if value == "true" { return true, nil }
|
||||
if value == "false" { return false, nil }
|
||||
return false, ErrBooleanNotTrueOrFalse
|
||||
}
|
||||
|
||||
// ParseNumeric parses a value of type numeric.
|
||||
// Values of type numeric must be a valid floating point number as recognized by
|
||||
// the %f specifier for scanf in the C locale.
|
||||
func ParseNumeric (value string) (float64, error) {
|
||||
// TODO ensure this is compliant
|
||||
return strconv.ParseFloat(value, 64)
|
||||
}
|
||||
|
||||
// ParseMultiple parses multiple of a value type. Any value parsing function can
|
||||
// be specified. The multiple values should be separated by a semicolon and the
|
||||
// input string may be optionally terminated by a semicolon. Trailing empty
|
||||
// strings must always be terminated with a semicolon. Semicolons in these
|
||||
// values need to be escaped using \;.
|
||||
func ParseMultiple[T any] (parser func (string) (T, error), value string) ([]T, error) {
|
||||
values := []T { }
|
||||
builder := strings.Builder { }
|
||||
|
||||
newValue := func () error {
|
||||
value, err := parser(builder.String())
|
||||
if err != nil { return err }
|
||||
values = append(values, value)
|
||||
builder.Reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
backslash := false
|
||||
for _, char := range value {
|
||||
switch char {
|
||||
case '\\':
|
||||
backslash = true
|
||||
case ';':
|
||||
if backslash {
|
||||
builder.WriteRune(';')
|
||||
backslash = false
|
||||
} else {
|
||||
err := newValue()
|
||||
if err != nil { return nil, err }
|
||||
}
|
||||
default:
|
||||
if backslash {
|
||||
builder.WriteRune('\\')
|
||||
backslash = false
|
||||
}
|
||||
builder.WriteRune(char)
|
||||
}
|
||||
}
|
||||
if backslash {
|
||||
builder.WriteRune('\\')
|
||||
}
|
||||
if builder.Len() > 0 {
|
||||
err := newValue()
|
||||
if err != nil { return nil, err }
|
||||
}
|
||||
return values, nil
|
||||
}
|
||||
|
||||
func isAsciiText (value string) bool {
|
||||
for _, char := range value {
|
||||
// must be an ascii character that isn't a control character.
|
||||
if char <= 0x1F || char > unicode.MaxASCII {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func escapeString (value string) (string, error) {
|
||||
builder := strings.Builder { }
|
||||
backslash := false
|
||||
for _, char := range value {
|
||||
if char == '\\' { backslash = true; continue }
|
||||
|
||||
if backslash {
|
||||
switch char {
|
||||
case 's': builder.WriteRune(' ')
|
||||
case 'n': builder.WriteRune('\n')
|
||||
case 't': builder.WriteRune('\t')
|
||||
case 'r': builder.WriteRune('\r')
|
||||
case '\\': builder.WriteRune('\\')
|
||||
default: return "", ErrUnsupportedEscape
|
||||
}
|
||||
} else {
|
||||
builder.WriteRune(char)
|
||||
}
|
||||
}
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user