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 "io"
|
||||||
import "bufio"
|
import "bufio"
|
||||||
import "strings"
|
import "strings"
|
||||||
|
import "strconv"
|
||||||
|
import "unicode"
|
||||||
import "git.tebibyte.media/tomo/xdg/locale"
|
import "git.tebibyte.media/tomo/xdg/locale"
|
||||||
|
|
||||||
type keyValueError string
|
type keyValueError string
|
||||||
@ -42,6 +44,16 @@ const (
|
|||||||
ErrNoDefaultValue = keyValueError("no default value")
|
ErrNoDefaultValue = keyValueError("no default value")
|
||||||
// ErrEntryOutsideGroup indicates that an entry was found.
|
// ErrEntryOutsideGroup indicates that an entry was found.
|
||||||
ErrEntryOutsideGroup = keyValueError("entry outside group")
|
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.
|
// 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
|
// 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