From b3f35b1415204a332053efd040e7a5381e36f2ac Mon Sep 17 00:00:00 2001 From: Zygmunt Krynicki <zygmunt.krynicki@huawei.com> Date: Sat, 18 Dec 2021 21:57:06 +0000 Subject: [PATCH] Depend on gitlab.com/zygoon/go-ini, drop local copy The ini code has moved to a mini-library, now shared with NetOTA. Drop the local copy and add a module dependency. Signed-off-by: Zygmunt Krynicki <zygmunt.krynicki@huawei.com> --- .tours/overview-of-go-packages.tour | 6 - cspell.json | 3 +- encoding/ini/decoder.go | 373 ----------------- encoding/ini/decoder_test.go | 529 ------------------------- encoding/ini/encoder.go | 117 ------ encoding/ini/encoder_test.go | 399 ------------------- encoding/ini/export_test.go | 9 - encoding/ini/info.go | 127 ------ encoding/ini/info_test.go | 160 -------- encoding/ini/ini.go | 84 ---- encoding/ini/ini_test.go | 33 -- encoding/ini/math_legacy_32bit_test.go | 16 - encoding/ini/math_legacy_64bit_test.go | 16 - encoding/ini/math_test.go | 15 - encoding/ini/testenum_test.go | 43 -- go.mod | 1 + go.sum | 2 + ota/ota.go | 3 +- ota/ota_test.go | 2 +- ota/state.go | 3 +- 20 files changed, 10 insertions(+), 1931 deletions(-) delete mode 100644 encoding/ini/decoder.go delete mode 100644 encoding/ini/decoder_test.go delete mode 100644 encoding/ini/encoder.go delete mode 100644 encoding/ini/encoder_test.go delete mode 100644 encoding/ini/export_test.go delete mode 100644 encoding/ini/info.go delete mode 100644 encoding/ini/info_test.go delete mode 100644 encoding/ini/ini.go delete mode 100644 encoding/ini/ini_test.go delete mode 100644 encoding/ini/math_legacy_32bit_test.go delete mode 100644 encoding/ini/math_legacy_64bit_test.go delete mode 100644 encoding/ini/math_test.go delete mode 100644 encoding/ini/testenum_test.go diff --git a/.tours/overview-of-go-packages.tour b/.tours/overview-of-go-packages.tour index ebb2547..c085ca1 100644 --- a/.tours/overview-of-go-packages.tour +++ b/.tours/overview-of-go-packages.tour @@ -62,12 +62,6 @@ "line": 8, "title": "The dbusutil/objmgr package" }, - { - "file": "encoding/ini/ini.go", - "description": "The `encoding/ini` package provides an INI-file encoder/decoder interface, similar to `encoding/json`.\n\nINI files are interesting for several reasons, first of all they are used by both RAUC and Systemd, our two principal dependencies. Second, unlike JSON, they are friendly to be edited by humans, supporting comments and really forgiving (I'm looking at you JSON commas) syntax. Lastly unlike YAML they are inherently simple. There is no way to express cyclic data structures, there are far far fewer syntax elements and the overall surprise factor is very low.\n\nOur package uses structure tags for effortless marshaling and unmarshaling of structure types. It also re-uses the `encoding.TextMarshaler` and `TextUnmarshaler` interface, to extend the basic set of supported field types to arbitrary custom types, as long as they can marshal to and from a text string.", - "line": 7, - "title": "The encoding/ini package" - }, { "file": "ota/ota.go", "description": "The `ota` package contains code that us unique to the essence of SystemOTA.\n\nHere we define `Config` - representing the configuration of the service. Here we also define `SystemState` which describes all of the state used by the `sysotad` - for the moment that is literally *one bit* of state we need to keep!\n\nThere are some assorted types here like `BootLoaderType`, `ConfigError`, `LoadStateError` and `SaveStateError`. There are some utility functions related to both configuration and state.\n\nThere are two sub packages relate to how SystemOTA interacts with RAUC. We will look at them now.", diff --git a/cspell.json b/cspell.json index bd72906..29162cc 100644 --- a/cspell.json +++ b/cspell.json @@ -215,6 +215,7 @@ "zmk", // library for make "zstd", // compression format "zyga", // nickname for Zygmunt - "Zygmunt" // first name + "Zygmunt", // first name + "zygoon" // another nickname for Zygmunt ] } diff --git a/encoding/ini/decoder.go b/encoding/ini/decoder.go deleted file mode 100644 index d32d56c..0000000 --- a/encoding/ini/decoder.go +++ /dev/null @@ -1,373 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. - -package ini - -import ( - "bufio" - "bytes" - "encoding" - "errors" - "fmt" - "io" - "reflect" - "strconv" - "strings" -) - -var ( - // ErrEmptyFieldName records assignments with empty field name. - ErrEmptyFieldName = errors.New("empty field name") - // ErrUnterminatedContinuationAtEOF records unterminated multi-line entry. - ErrUnterminatedContinuationAtEOF = errors.New("unterminated continuation at end of input") -) - -// DecoderError records problem encountered during parsing. -type DecoderError struct { - LineNo int - Err error -} - -// Unwrap returns the error wrapped inside the decoder error. -func (decErr *DecoderError) Unwrap() error { - return decErr.Err -} - -// Error implements the error interface. -func (decErr *DecoderError) Error() string { - return fmt.Sprintf("cannot decode line %d: %s", decErr.LineNo, decErr.Err) -} - -// UnexpectedInputError records input not expected by the parser. -type UnexpectedInputError []byte - -// Error implements the error interface. -func (e UnexpectedInputError) Error() string { - return fmt.Sprintf("unexpected input: %q", string(e)) -} - -// UnknownSectionError records an unknown section. -// -// Unknown sections are ignored unless Decoder.DisallowUnknownSections is -// called on a new decoder instance. -type UnknownSectionError string - -// SectionName returns the name of the section with '[' and ']' stripped out. -func (e UnknownSectionError) SectionName() string { - return string(e) -} - -// Error implements the error interface. -func (e UnknownSectionError) Error() string { - return fmt.Sprintf("unknown section: %s", e.SectionName()) -} - -// UnknownFieldError records an unknown field. -// -// Unknown fields are ignored unless Decoder.DisallowUnknownFields is -// called on a new decoder instance. -type UnknownFieldError string - -// FieldName returns the name of the field. -func (e UnknownFieldError) FieldName() string { - return string(e) -} - -// Error implements the error interface. -func (e UnknownFieldError) Error() string { - return fmt.Sprintf("unknown field: %s", e.FieldName()) -} - -// FieldDecodeError records failed attempt to decode a structure field. -type FieldDecodeError struct { - StructName string - FieldName string - Err error -} - -// Error implements error. -func (e *FieldDecodeError) Error() string { - return fmt.Sprintf("cannot decode %s.%s: %v", e.StructName, e.FieldName, e.Err) -} - -// Unwrap returns the error wrapped by field decode error. -func (e *FieldDecodeError) Unwrap() error { - return e.Err -} - -// Decoder reads INI values from an input stream. -type Decoder struct { - r io.Reader - disallowUnknownFields bool - disallowUnknownSections bool - caseSensitive bool - commentRune rune -} - -// NewDecoder returns a new decoder that reads from r. -func NewDecoder(r io.Reader) *Decoder { - return &Decoder{r: r} -} - -// DisallowUnknownSections makes the decoder reject unknown section names. -func (dec *Decoder) DisallowUnknownSections() { - dec.disallowUnknownSections = true -} - -// DisallowUnknownFields makes the decoder reject unknown field names. -func (dec *Decoder) DisallowUnknownFields() { - dec.disallowUnknownFields = true -} - -// UseUnixComments makes the decoder recognize comments introduced by '#'. -func (dec *Decoder) UseUnixComments() { - dec.commentRune = '#' -} - -// UseWindowsComments makes the decoder recognize comments introduced by ';'. -func (dec *Decoder) UseWindowsComments() { - dec.commentRune = ';' -} - -// CaseSensitive makes the decoder case-sensitive for both sections and field names. -func (dec *Decoder) CaseSensitive() { - dec.caseSensitive = true -} - -// Decode decodes a structure from the INI representation. -// -// The argument must be a pointer to a structure. -// -// Comments introduced with '#' or ';' are ignored. If a preference for certain -// style has been set, then only such comments are recognized. -// -// See the documentation of Unmarshal more details. -func (dec *Decoder) Decode(v interface{}) error { - if reflect.TypeOf(v).Kind() != reflect.Ptr || reflect.TypeOf(v).Elem().Kind() != reflect.Struct { - return fmt.Errorf("cannot decode values of type %T", v) - } - - structValue := reflect.Indirect(reflect.ValueOf(v)) - structType := structValue.Type() - - iniFieldSlice, iniFieldIndexMap, err := iniMapOfType(structType, dec.caseSensitive) - if err != nil { - return err - } - - scanner := bufio.NewScanner(dec.r) - - var sectionRealName, sectionLookupName string - - var accumulationBuf bytes.Buffer - - var accumulating bool - - var lineno int - - for lineno = 1; scanner.Scan(); lineno++ { - rawLine := scanner.Bytes() - - // Trim whitespace. In general whitespace is irrelevant, including - // around the '=' rune in key=value pairs below. - rawLine = bytes.TrimSpace(rawLine) - - // Skip lines that are entirely empty. - if len(rawLine) == 0 { - continue - } - - // Skip comments. Those take priority over line continuations, allowing comments - // in the middle of input. If a preference was set, recognize only specific type. - // If no preference was set recognize both '#' and ';' comments, assuming they - // are the first rune of a line. - commentFound := false - - switch dec.commentRune { - case '#': - commentFound = bytes.HasPrefix(rawLine, []byte("#")) - case ';': - commentFound = bytes.HasPrefix(rawLine, []byte(";")) - default: - commentFound = bytes.HasPrefix(rawLine, []byte("#")) || bytes.HasPrefix(rawLine, []byte(";")) - } - - if commentFound { - continue - } - - // Handle continuations from previous line. Long lines can be split into - // multiple sections, each but the last one terminated with a '\' rune. - // If the continuation buffer is not empty, then the first line without - // the continuation rune terminates the sequence. - if bytes.HasSuffix(rawLine, []byte("\\")) { - accumulationBuf.Write(rawLine[:len(rawLine)-1]) - accumulationBuf.WriteRune(' ') - - accumulating = true - - continue - } else if accumulating { - accumulationBuf.Write(rawLine) - rawLine = accumulationBuf.Bytes() - accumulationBuf.Reset() - accumulating = false - } - - // Handle [sections]. - if bytes.HasPrefix(rawLine, []byte(`[`)) && bytes.HasSuffix(rawLine, []byte(`]`)) { - sectionRealName = string(rawLine[1 : len(rawLine)-1]) - sectionLookupName = sectionRealName - - if !dec.caseSensitive { - sectionLookupName = strings.ToLower(sectionLookupName) - } - - if _, ok := iniFieldIndexMap[sectionLookupName]; !ok && dec.disallowUnknownSections { - return &DecoderError{LineNo: lineno, Err: UnknownSectionError(sectionRealName)} - } - - continue - } - - // Handle key=value pairs. - if idx := bytes.IndexRune(rawLine, '='); idx != -1 { - fieldRealName := string(bytes.TrimSpace(rawLine[:idx])) - fieldLookupName := fieldRealName - - if len(fieldRealName) == 0 { - return &DecoderError{LineNo: lineno, Err: ErrEmptyFieldName} - } - - fieldRawValue := bytes.TrimSpace(rawLine[idx+1:]) - - if !dec.caseSensitive { - fieldLookupName = strings.ToLower(fieldLookupName) - } - - if iniFieldIndex, ok := iniFieldIndexMap[sectionLookupName][fieldLookupName]; ok { - if err := dec.decodeField(fieldRawValue, structType, &structValue, &iniFieldSlice[iniFieldIndex]); err != nil { - return err - } - } else if dec.disallowUnknownFields { - return &DecoderError{LineNo: lineno, Err: UnknownFieldError(fieldRealName)} - } - - continue - } - - // Report everything else as syntax error. - return &DecoderError{LineNo: lineno, Err: UnexpectedInputError(rawLine)} - } - - if accumulating { - return &DecoderError{LineNo: lineno, Err: ErrUnterminatedContinuationAtEOF} - } - - if err := scanner.Err(); err != nil { - return err - } - - return nil -} - -// StringUnquoteError records problem with removing quoting marks from a string. -type StringUnquoteError struct { - Err error -} - -// Error implements the error interface. -func (e *StringUnquoteError) Error() string { - return fmt.Sprintf("cannot unquote string: %v", e.Err) -} - -// Unwrap returns the error wrapped inside the string unquote error. -func (e *StringUnquoteError) Unwrap() error { - return e.Err -} - -func (dec *Decoder) decodeField(valueData []byte, structType reflect.Type, structValue *reflect.Value, iniFieldInfo *iniFieldInfo) (err error) { - structField := structType.FieldByIndex(iniFieldInfo.FieldIndex) - indirectFieldValue := reflect.Indirect(structValue.FieldByIndex(iniFieldInfo.FieldIndex)) - - if reflect.PtrTo(structField.Type).Implements(textUnmarshalerType) { - err = structValue.FieldByIndex(iniFieldInfo.FieldIndex).Addr().Interface().(encoding.TextUnmarshaler).UnmarshalText(valueData) - } else { - valueDataText := string(valueData) - switch kind := structField.Type.Kind(); kind { - case reflect.Bool: - var boolValue bool - boolValue, err = strconv.ParseBool(valueDataText) - if err == nil { - indirectFieldValue.SetBool(boolValue) - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - var intValue int64 - bitSize := 0 - switch kind { - case reflect.Int8: - bitSize = 8 - case reflect.Int16: - bitSize = 16 - case reflect.Int32: - bitSize = 32 - case reflect.Int64: - bitSize = 64 - } - intValue, err = strconv.ParseInt(valueDataText, 10, bitSize) - if err == nil { - indirectFieldValue.SetInt(int64(intValue)) - } - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - var uintValue uint64 - bitSize := 0 - switch kind { - case reflect.Uint8: - bitSize = 8 - case reflect.Uint16: - bitSize = 16 - case reflect.Uint32: - bitSize = 32 - case reflect.Uint64: - bitSize = 64 - } - uintValue, err = strconv.ParseUint(valueDataText, 10, bitSize) - if err == nil { - indirectFieldValue.SetUint(uint64(uintValue)) - } - case reflect.Float32, reflect.Float64: - var floatValue float64 - bitSize := 32 - if kind == reflect.Float64 { - bitSize = 64 - } - floatValue, err = strconv.ParseFloat(valueDataText, bitSize) - if err == nil { - indirectFieldValue.SetFloat(floatValue) - } - case reflect.String: - strValue := valueDataText - if iniFieldInfo.Quote { - var unquotedStrValue string - unquotedStrValue, err = strconv.Unquote(strValue) - if err != nil { - err = &StringUnquoteError{Err: err} - } else { - strValue = unquotedStrValue - } - } - if err == nil { - structValue.FieldByIndex(iniFieldInfo.FieldIndex).SetString(strValue) - } - default: - // This can never happen if iniSliceOfType is in sync. - panic(fmt.Sprintf("cannot decode value of unsupported kind %v", kind)) - } - } - - if err != nil { - return &FieldDecodeError{StructName: structType.Name(), FieldName: structField.Name, Err: err} - } - - return nil -} diff --git a/encoding/ini/decoder_test.go b/encoding/ini/decoder_test.go deleted file mode 100644 index 34c09c4..0000000 --- a/encoding/ini/decoder_test.go +++ /dev/null @@ -1,529 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. - -package ini_test - -import ( - "bytes" - "errors" - "math" - "reflect" - "strconv" - - "booting.oniroproject.org/distro/components/sysota/encoding/ini" - . "gopkg.in/check.v1" -) - -type decoderSuite struct{} - -var _ = Suite(&decoderSuite{}) - -func (s *decoderSuite) TestDecodeNonStructType(c *C) { - var value int - - dec := ini.NewDecoder(bytes.NewBufferString("")) - err := dec.Decode(&value) - c.Assert(err, ErrorMatches, "cannot decode values of type .*") -} - -func (s *decoderSuite) TestDecodeNonPointerType(c *C) { - dec := ini.NewDecoder(bytes.NewBufferString("")) - err := dec.Decode(struct{}{}) - c.Assert(err, ErrorMatches, "cannot decode values of type .*") -} - -func (s *decoderSuite) TestUseUnixComments(c *C) { - dec := ini.NewDecoder(bytes.NewBufferString("# This is an UNIX comment")) - dec.UseUnixComments() - err := dec.Decode(&struct{}{}) - c.Assert(err, IsNil) - - dec = ini.NewDecoder(bytes.NewBufferString("; This is a Windows comment")) - dec.UseUnixComments() - err = dec.Decode(&struct{}{}) - c.Assert(err, ErrorMatches, `cannot decode line 1: unexpected input: "; This is a Windows comment"`) -} - -func (s *decoderSuite) TestUseWindowsComments(c *C) { - dec := ini.NewDecoder(bytes.NewBufferString("; This is a Windows comment")) - dec.UseWindowsComments() - err := dec.Decode(&struct{}{}) - c.Assert(err, IsNil) - - dec = ini.NewDecoder(bytes.NewBufferString("# This is an UNIX comment")) - dec.UseWindowsComments() - err = dec.Decode(&struct{}{}) - c.Assert(err, ErrorMatches, `cannot decode line 1: unexpected input: "# This is an UNIX comment"`) -} - -func (s *decoderSuite) TestDecodeCommentTypes(c *C) { - for _, input := range []string{ - "# First comment\n# Second comment\n", - "; First comment\n; Second comment\n", - "# First comment\n; Second comment\n", - "; First comment\n# Second comment\n", - } { - dec := ini.NewDecoder(bytes.NewBufferString(input)) - err := dec.Decode(&struct{}{}) - c.Assert(err, IsNil, Commentf("input: %q", input)) - } -} - -func (s *decoderSuite) TestDecodeContinuations(c *C) { - type testType struct { - Field string `ini:"F"` - } - - for _, input := range []string{ - "F=v a l u e", - "F=v a l\\\nu e", - "F=v a l\\\n u e", - "F=v a l\\\n u e ", - "F=\\\nv a l u e", - "F=\\\nv\\\na\\\nl\\\nu\\\ne", - "F=\\\n# comment\nv\\\na\\\nl\\\n;comment\nu\\\ne", - } { - comment := Commentf("input: %q", input) - dec := ini.NewDecoder(bytes.NewBufferString(input)) - - var value testType - - err := dec.Decode(&value) - c.Assert(err, IsNil, comment) - c.Check(value.Field, Equals, "v a l u e", comment) - } -} - -func (s *decoderSuite) TestDecodeContinuationsDangling(c *C) { - dec := ini.NewDecoder(bytes.NewBufferString("\\")) - err := dec.Decode(&struct{}{}) - c.Assert(err, ErrorMatches, `cannot decode line 2: unterminated continuation at end of input`) - c.Assert(errors.Unwrap(err), Equals, ini.ErrUnterminatedContinuationAtEOF) -} - -func (s *decoderSuite) TestDecodeEmptyLines(c *C) { - for _, input := range []string{ - "", - "\n", - "\n \n", - "\r\n", - "\t", - "\v", - " ", - "\r\n\t\v ", - } { - comment := Commentf("input: %q", input) - dec := ini.NewDecoder(bytes.NewBufferString(input)) - err := dec.Decode(&struct{}{}) - c.Assert(err, IsNil, comment) - } -} - -func (s *decoderSuite) TestDecodeFieldWithoutValue(c *C) { - dec := ini.NewDecoder(bytes.NewBufferString("FieldWithoutValue")) - err := dec.Decode(&struct{}{}) - c.Assert(err, ErrorMatches, `cannot decode line 1: unexpected input: "FieldWithoutValue"`) -} - -func (s *decoderSuite) TestDecodeValueWithoutField(c *C) { - dec := ini.NewDecoder(bytes.NewBufferString("=ValueWithoutField")) - err := dec.Decode(&struct{}{}) - c.Assert(err, ErrorMatches, `cannot decode line 1: empty field name`) - c.Assert(errors.Unwrap(err), Equals, ini.ErrEmptyFieldName) -} - -func (s *decoderSuite) TestDisallowUnknownSections(c *C) { - dec := ini.NewDecoder(bytes.NewBufferString("[Section]\n")) - dec.DisallowUnknownSections() - err := dec.Decode(&struct{}{}) - c.Assert(err, ErrorMatches, `cannot decode line 1: unknown section: Section`) - c.Assert(errors.Unwrap(err), Equals, ini.UnknownSectionError("Section")) -} - -func (s *decoderSuite) TestDisallowUnknownFields(c *C) { - dec := ini.NewDecoder(bytes.NewBufferString("Field=Value\n")) - dec.DisallowUnknownFields() - err := dec.Decode(&struct{}{}) - c.Assert(err, ErrorMatches, `cannot decode line 1: unknown field: Field`) - c.Assert(errors.Unwrap(err), Equals, ini.UnknownFieldError("Field")) -} - -func (s *decoderSuite) TestSectionTracking(c *C) { - dec := ini.NewDecoder(bytes.NewBufferString("" + - "[Integers]\n" + - "IntField1=1\n" + - "[Surprise]\n" + - "IntField3=3\n")) - - type testType struct { - IntField1 int `ini:"IntField1,[Integers]"` - IntField2 int `ini:"IntField2,[Surprise],omitempty"` - IntField3 int `ini:"IntField3"` - } - - var value testType - - err := dec.Decode(&value) - c.Assert(err, IsNil) - c.Check(value.IntField1, Equals, 1) - c.Check(value.IntField2, Equals, 0) - c.Check(value.IntField3, Equals, 3) -} - -func (s *decoderSuite) TestCaseInsensitivity(c *C) { - dec := ini.NewDecoder(bytes.NewBufferString("" + - "[SECTION]\n" + - "FIELD=ok\n")) - dec.DisallowUnknownFields() - dec.DisallowUnknownSections() - - type testType struct { - Field string `ini:"Field,[Section]"` - } - - var value testType - - err := dec.Decode(&value) - c.Assert(err, IsNil) - c.Check(value, Equals, testType{Field: "ok"}) -} - -func (s *decoderSuite) TestCaseSensitivity(c *C) { - dec := ini.NewDecoder(bytes.NewBufferString("" + - "[SECTION]\n" + - "FIELD=ok\n")) - dec.CaseSensitive() - dec.DisallowUnknownFields() - dec.DisallowUnknownSections() - - type testType struct { - Field string `ini:"Field,[Section]"` - } - - var value testType - - err := dec.Decode(&value) - c.Assert(err, ErrorMatches, `cannot decode line 1: unknown section: SECTION`) - - dec = ini.NewDecoder(bytes.NewBufferString("" + - "[Section]\n" + - "FIELD=ok\n")) - dec.CaseSensitive() - dec.DisallowUnknownFields() - dec.DisallowUnknownSections() - err = dec.Decode(&value) - c.Assert(err, ErrorMatches, `cannot decode line 2: unknown field: FIELD`) - - dec = ini.NewDecoder(bytes.NewBufferString("" + - "[Section]\n" + - "Field=ok\n")) - dec.CaseSensitive() - dec.DisallowUnknownFields() - dec.DisallowUnknownSections() - err = dec.Decode(&value) - c.Assert(err, IsNil) - c.Check(value, Equals, testType{Field: "ok"}) -} - -func (s *decoderSuite) TestDecodeBoolean(c *C) { - type testType struct { - Field bool `ini:"F"` - } - - var value testType - - dec := ini.NewDecoder(bytes.NewBufferString("F=true")) - c.Assert(dec.Decode(&value), IsNil) - c.Check(value.Field, Equals, true) - - dec = ini.NewDecoder(bytes.NewBufferString("F=false")) - c.Assert(dec.Decode(&value), IsNil) - c.Check(value.Field, Equals, false) - - dec = ini.NewDecoder(bytes.NewBufferString("F=potato")) - err := dec.Decode(&value) - c.Assert(err, ErrorMatches, `cannot decode testType.Field: strconv.ParseBool: parsing "potato": invalid syntax`) - c.Assert(errors.Unwrap(err), DeepEquals, &strconv.NumError{ - Func: "ParseBool", - Num: "potato", - Err: strconv.ErrSyntax, - }) -} - -func (s *decoderSuite) TestDecodeInt(c *C) { - type testCase struct { - value interface{} - min int64 - max int64 - } - - for _, tc := range []testCase{ - { - value: &struct { - Field int `ini:"F"` - }{}, - min: MinInt, - max: MaxInt, - }, - { - value: &struct { - Field int8 `ini:"F"` - }{}, - min: math.MinInt8, - max: math.MaxInt8, - }, - { - value: &struct { - Field int16 `ini:"F"` - }{}, - min: math.MinInt16, - max: math.MaxInt16, - }, - { - value: &struct { - Field int32 `ini:"F"` - }{}, - min: math.MinInt32, - max: math.MaxInt32, - }, - { - value: &struct { - Field int64 `ini:"F"` - }{}, - min: math.MinInt64, - max: math.MaxInt64, - }, - } { - comment := Commentf("test case: %#v", tc) - - dec := ini.NewDecoder(bytes.NewBufferString("F=" + strconv.FormatInt(tc.min, 10))) - c.Assert(dec.Decode(tc.value), IsNil, comment) - c.Check(reflect.ValueOf(tc.value).Elem().FieldByName("Field").Int(), Equals, tc.min, comment) - - dec = ini.NewDecoder(bytes.NewBufferString("F=" + strconv.FormatInt(tc.max, 10))) - c.Assert(dec.Decode(tc.value), IsNil, comment) - c.Check(reflect.ValueOf(tc.value).Elem().FieldByName("Field").Int(), Equals, tc.max, comment) - - // We cannot compute math.MinInt64-1 without underflowing. - if tc.min > math.MinInt64 { - dec = ini.NewDecoder(bytes.NewBufferString("F=" + strconv.FormatInt(tc.min-1, 10))) - err := dec.Decode(tc.value) - c.Assert(err, ErrorMatches, `cannot decode .Field: strconv.ParseInt: parsing "-[0-9]+": value out of range`, comment) - c.Assert(errors.Unwrap(err), DeepEquals, &strconv.NumError{ - Func: "ParseInt", - Num: strconv.FormatInt(tc.min-1, 10), - Err: strconv.ErrRange, - }, comment) - } - - // We cannot compute math.MaxInt64+1 without overflowing. - if tc.max < math.MaxInt64 { - dec = ini.NewDecoder(bytes.NewBufferString("F=" + strconv.FormatInt(tc.max+1, 10))) - err := dec.Decode(tc.value) - c.Assert(err, ErrorMatches, `cannot decode .Field: strconv.ParseInt: parsing "[0-9]+": value out of range`, comment) - c.Assert(errors.Unwrap(err), DeepEquals, &strconv.NumError{ - Func: "ParseInt", - Num: strconv.FormatInt(tc.max+1, 10), - Err: strconv.ErrRange, - }, comment) - } - - dec = ini.NewDecoder(bytes.NewBufferString("F=potato")) - err := dec.Decode(tc.value) - c.Assert(err, ErrorMatches, `cannot decode .Field: strconv.ParseInt: parsing "potato": invalid syntax`, comment) - c.Assert(errors.Unwrap(err), DeepEquals, &strconv.NumError{ - Func: "ParseInt", - Num: "potato", - Err: strconv.ErrSyntax, - }, comment) - } -} - -func (s *decoderSuite) TestDecodeUint(c *C) { - type testCase struct { - value interface{} - max uint64 - } - - for _, tc := range []testCase{ - { - value: &struct { - Field uint `ini:"F"` - }{}, - max: MaxUint, - }, - { - value: &struct { - Field uint8 `ini:"F"` - }{}, - max: math.MaxUint8, - }, - { - value: &struct { - Field uint16 `ini:"F"` - }{}, - max: math.MaxUint16, - }, - { - value: &struct { - Field uint32 `ini:"F"` - }{}, - max: math.MaxUint32, - }, - { - value: &struct { - Field uint64 `ini:"F"` - }{}, - max: math.MaxUint64, - }, - } { - comment := Commentf("test case: %#v", tc) - - dec := ini.NewDecoder(bytes.NewBufferString("F=0")) - c.Assert(dec.Decode(tc.value), IsNil, comment) - c.Check(reflect.ValueOf(tc.value).Elem().FieldByName("Field").Uint(), Equals, uint64(0), comment) - - dec = ini.NewDecoder(bytes.NewBufferString("F=" + strconv.FormatUint(tc.max, 10))) - c.Assert(dec.Decode(tc.value), IsNil, comment) - c.Check(reflect.ValueOf(tc.value).Elem().FieldByName("Field").Uint(), Equals, tc.max, comment) - - // We cannot compute math.MaxUint64+1 without overflowing. - if tc.max < math.MaxUint64 { - dec = ini.NewDecoder(bytes.NewBufferString("F=" + strconv.FormatUint(tc.max+1, 10))) - err := dec.Decode(tc.value) - c.Assert(err, ErrorMatches, `cannot decode .Field: strconv.ParseUint: parsing "[0-9]+": value out of range`, comment) - c.Assert(errors.Unwrap(err), DeepEquals, &strconv.NumError{ - Func: "ParseUint", - Num: strconv.FormatUint(tc.max+1, 10), - Err: strconv.ErrRange, - }, comment) - } - - dec = ini.NewDecoder(bytes.NewBufferString("F=potato")) - err := dec.Decode(tc.value) - c.Assert(err, ErrorMatches, `cannot decode .Field: strconv.ParseUint: parsing "potato": invalid syntax`, comment) - c.Assert(errors.Unwrap(err), DeepEquals, &strconv.NumError{ - Func: "ParseUint", - Num: "potato", - Err: strconv.ErrSyntax, - }, comment) - } -} - -func (s *decoderSuite) TestDecodeFloat(c *C) { - type testCase struct { - value interface{} - min float64 - max float64 - bitSize int - } - - for _, tc := range []testCase{ - { - value: &struct { - Field float32 `ini:"F"` - }{}, - min: math.SmallestNonzeroFloat32, - max: math.MaxFloat32, - bitSize: 32, - }, - { - value: &struct { - Field float64 `ini:"F"` - }{}, - min: math.SmallestNonzeroFloat64, - max: math.MaxFloat64, - bitSize: 64, - }, - } { - comment := Commentf("test case: %#v", tc) - - dec := ini.NewDecoder(bytes.NewBufferString("F=" + strconv.FormatFloat(tc.min, 'x', -1, tc.bitSize))) - c.Assert(dec.Decode(tc.value), IsNil, comment) - c.Check(reflect.ValueOf(tc.value).Elem().FieldByName("Field").Float(), Equals, tc.min, comment) - - dec = ini.NewDecoder(bytes.NewBufferString("F=" + strconv.FormatFloat(tc.max, 'x', -1, tc.bitSize))) - c.Assert(dec.Decode(tc.value), IsNil, comment) - c.Check(reflect.ValueOf(tc.value).Elem().FieldByName("Field").Float(), Equals, tc.max, comment) - - dec = ini.NewDecoder(bytes.NewBufferString("F=potato")) - err := dec.Decode(tc.value) - c.Assert(err, ErrorMatches, `cannot decode .Field: strconv.ParseFloat: parsing "potato": invalid syntax`, comment) - c.Assert(errors.Unwrap(err), DeepEquals, &strconv.NumError{ - Func: "ParseFloat", - Num: "potato", - Err: strconv.ErrSyntax, - }, comment) - } -} - -func (s *decoderSuite) TestDecodeString(c *C) { - type testCase struct { - value interface{} - textIn string - textOut string - errPattern string - } - - for _, tc := range []testCase{ - { - value: &struct { - Field string `ini:"F"` - }{}, - textIn: "hello world", - textOut: "hello world", - }, - { - value: &struct { - Field string `ini:"F,quote"` - }{}, - textIn: `"hello world"`, - textOut: "hello world", - }, - { - value: &struct { - Field string `ini:"F,quote"` - }{}, - textIn: `"hello world`, - errPattern: `cannot decode .Field: cannot unquote string: invalid syntax`, - }, - } { - comment := Commentf("test case: %#v", tc) - - dec := ini.NewDecoder(bytes.NewBufferString("F=" + tc.textIn)) - if tc.errPattern == "" { - c.Assert(dec.Decode(tc.value), IsNil, comment) - c.Check(reflect.ValueOf(tc.value).Elem().FieldByName("Field").String(), Equals, tc.textOut, comment) - } else { - err := dec.Decode(tc.value) - c.Assert(err, ErrorMatches, tc.errPattern, comment) - c.Check(errors.Is(err, strconv.ErrSyntax), Equals, true) - } - } - - dec := ini.NewDecoder(bytes.NewBufferString(`F="broken`)) - err := dec.Decode(&struct { - Field string `ini:"F,quote"` - }{}) - c.Assert(err, NotNil) - c.Assert(errors.Unwrap(err), ErrorMatches, `cannot unquote string: invalid syntax`) -} - -func (s *decoderSuite) TestDecodeTextUnmarshaler(c *C) { - type testType struct { - Field testEnum `ini:"F"` - } - - var value testType - - dec := ini.NewDecoder(bytes.NewBufferString("F=foo")) - c.Assert(dec.Decode(&value), IsNil) - c.Check(value.Field, Equals, testFoo) - - dec = ini.NewDecoder(bytes.NewBufferString("F=bar")) - c.Assert(dec.Decode(&value), IsNil) - c.Check(value.Field, Equals, testBar) - - dec = ini.NewDecoder(bytes.NewBufferString("F=froz")) - c.Assert(dec.Decode(&value), ErrorMatches, `cannot decode testType.Field: cannot unmarshal testEnum: unknown value: "froz"`) -} diff --git a/encoding/ini/encoder.go b/encoding/ini/encoder.go deleted file mode 100644 index 43b52d1..0000000 --- a/encoding/ini/encoder.go +++ /dev/null @@ -1,117 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. - -package ini - -import ( - "encoding" - "errors" - "fmt" - "io" - "reflect" - "strconv" - "strings" -) - -// Encoder writes INI value to an output stream. -type Encoder struct { - w io.Writer -} - -// NewEncoder returns a new encoder that writes to w. -func NewEncoder(w io.Writer) *Encoder { - return &Encoder{w: w} -} - -var ( - // ErrEncodeNil reports attempt to encode a nil value - ErrEncodeNil = errors.New("cannot encode nil values") -) - -// Encode encodes a structure to the INI representation. -func (enc *Encoder) Encode(v interface{}) error { - if v == nil { - return ErrEncodeNil - } - - structValue := reflect.ValueOf(v) - if structValue.Kind() == reflect.Ptr { - structValue = reflect.Indirect(structValue) - } - - if structValue.Type().Kind() != reflect.Struct { - return fmt.Errorf("cannot encode values of type: %T", v) - } - - structType := structValue.Type() - - iniFieldSlice, err := iniSliceOfType(structType) - if err != nil { - return err - } - - lastSection := "" - - for _, iniFieldInfo := range iniFieldSlice { - structField := structType.FieldByIndex(iniFieldInfo.FieldIndex) - - fieldValue := structValue.FieldByIndex(iniFieldInfo.FieldIndex) - if fieldValue.IsZero() && iniFieldInfo.OmitEmpty { - continue - } - - var valueDataText string - - if textMarshaler, ok := fieldValue.Interface().(encoding.TextMarshaler); ok { - var valueData []byte - - valueData, err = textMarshaler.MarshalText() - if err == nil { - valueDataText = string(valueData) - } - } else { - switch kind := structField.Type.Kind(); kind { - case reflect.Bool: - valueDataText = strconv.FormatBool(fieldValue.Bool()) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - valueDataText = strconv.FormatInt(fieldValue.Int(), 10) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - valueDataText = strconv.FormatUint(fieldValue.Uint(), 10) - case reflect.Float32, reflect.Float64: - bitSize := 32 - if kind == reflect.Float64 { - bitSize = 64 - } - valueDataText = strconv.FormatFloat(fieldValue.Float(), 'f', -1, bitSize) - case reflect.String: - valueDataText = fieldValue.String() - if iniFieldInfo.Quote { - valueDataText = strconv.Quote(valueDataText) - } else { - prefix := strings.Repeat(" ", len(iniFieldInfo.Name)+1) - valueDataText = strings.ReplaceAll(valueDataText, "\n", "\\\n"+prefix) - } - default: - // This can never happen if iniSliceOfType is in sync. - panic(fmt.Sprintf("cannot encode value of unsupported kind %v", kind)) - } - } - - if err == nil { - if iniFieldInfo.Section != lastSection { - _, err = fmt.Fprintf(enc.w, "[%s]\n", iniFieldInfo.Section) - lastSection = iniFieldInfo.Section - } - - if err == nil { - _, err = fmt.Fprintf(enc.w, "%s=%s\n", iniFieldInfo.Name, valueDataText) - } - } - - if err != nil { - return fmt.Errorf("cannot encode %s.%s: %w", structType.Name(), structField.Name, err) - } - } - - return nil -} diff --git a/encoding/ini/encoder_test.go b/encoding/ini/encoder_test.go deleted file mode 100644 index 05eeb85..0000000 --- a/encoding/ini/encoder_test.go +++ /dev/null @@ -1,399 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. - -package ini_test - -import ( - "bytes" - "errors" - "math" - "reflect" - "strconv" - - "booting.oniroproject.org/distro/components/sysota/encoding/ini" - . "gopkg.in/check.v1" -) - -type encoderSuite struct { - buf bytes.Buffer -} - -var _ = Suite(&encoderSuite{}) - -func (s *encoderSuite) SetUpTest(c *C) { - s.buf.Reset() -} - -func (s *encoderSuite) TestEncodeNilValue(c *C) { - enc := ini.NewEncoder(&s.buf) - err := enc.Encode(nil) - c.Assert(err, ErrorMatches, `cannot encode nil values`) - c.Assert(errors.Is(err, ini.ErrEncodeNil), Equals, true) -} - -func (s *encoderSuite) TestEncodeNonStructurePointer(c *C) { - var i int - - enc := ini.NewEncoder(&s.buf) - c.Assert(enc.Encode(&i), ErrorMatches, `cannot encode values of type: \*int`) -} - -func (s *encoderSuite) TestEncodeSkipsUntaggedFields(c *C) { - type testType struct { - Field int - } - - enc := ini.NewEncoder(&s.buf) - c.Assert(enc.Encode(&testType{Field: 42}), IsNil) - c.Check(s.buf.String(), Equals, "") -} - -func (s *encoderSuite) TestEncodeSkipsUnexportedFields(c *C) { - type testType struct { - field int `ini:"f"` - } - - enc := ini.NewEncoder(&s.buf) - c.Assert(enc.Encode(&testType{field: 42}), IsNil) - c.Check(s.buf.String(), Equals, "") -} - -func (s *encoderSuite) TestEncodeVariousTypes(c *C) { - type testType struct { - IntField int `ini:"IntField"` - BoolField bool `ini:"BoolField"` - StringField string `ini:"StringField"` - } - - enc := ini.NewEncoder(&s.buf) - value := &testType{ - IntField: 42, - BoolField: true, - StringField: "hello world", - } - c.Assert(enc.Encode(value), IsNil) - c.Check(s.buf.String(), Equals, ""+ - "IntField=42\n"+ - "BoolField=true\n"+ - "StringField=hello world\n") -} - -func (s *encoderSuite) TestEncodeRespectsOmitEmptyTag(c *C) { - type testType struct { - IntField int `ini:"IntField,omitempty"` - BoolField bool `ini:"BoolField,omitempty"` - StringField string `ini:"StringField,omitempty"` - } - - enc := ini.NewEncoder(&s.buf) - c.Assert(enc.Encode(&testType{}), IsNil) - c.Check(s.buf.String(), Equals, "") -} - -func (s *encoderSuite) TestEncodeRespectsIniSections(c *C) { - type testType struct { - IntField int `ini:"IntField,[Integers]"` - BoolField bool `ini:"BoolField,[Booleans]"` - StringField string `ini:"StringField,[Strings]"` - } - - enc := ini.NewEncoder(&s.buf) - value := &testType{ - IntField: 42, - BoolField: true, - StringField: "hello world", - } - c.Assert(enc.Encode(value), IsNil) - c.Check(s.buf.String(), Equals, ""+ - "[Integers]\n"+ - "IntField=42\n"+ - "[Booleans]\n"+ - "BoolField=true\n"+ - "[Strings]\n"+ - "StringField=hello world\n") -} - -func (s *encoderSuite) TestEncodeKeepsTrackOfSections(c *C) { - type testType struct { - IntField1 int `ini:"IntField1,[Integers]"` - IntField2 int `ini:"IntField2"` - IntField3 int `ini:"IntField3,[Integers]"` - BoolField bool `ini:"BoolField,[Booleans]"` - } - - enc := ini.NewEncoder(&s.buf) - value := &testType{ - IntField1: 1, - IntField2: 2, - IntField3: 3, - BoolField: true, - } - c.Assert(enc.Encode(value), IsNil) - c.Check(s.buf.String(), Equals, ""+ - "[Integers]\n"+ - "IntField1=1\n"+ - "IntField2=2\n"+ - "IntField3=3\n"+ - "[Booleans]\n"+ - "BoolField=true\n") -} - -func (s *encoderSuite) TestEncodeSectinsAndOmittedEmptyFields(c *C) { - type testType struct { - IntField1 int `ini:"IntField1,[Integers]"` - // Field is omitted when value is zero, but the section name sticks. - IntField2 int `ini:"IntField2,[Surprise],omitempty"` - IntField3 int `ini:"IntField3"` - } - - enc := ini.NewEncoder(&s.buf) - value := &testType{ - IntField1: 1, - IntField2: 0, - IntField3: 3, - } - c.Assert(enc.Encode(value), IsNil) - c.Check(s.buf.String(), Equals, ""+ - "[Integers]\n"+ - "IntField1=1\n"+ - "[Surprise]\n"+ - "IntField3=3\n") -} - -func (s *encoderSuite) TestEncodeBool(c *C) { - type testType struct { - Field bool `ini:"F"` - } - - enc := ini.NewEncoder(&s.buf) - value := &testType{Field: false} - c.Assert(enc.Encode(value), IsNil) - c.Check(s.buf.String(), Equals, "F=false\n") - s.buf.Reset() - - value = &testType{Field: true} - c.Assert(enc.Encode(value), IsNil) - c.Check(s.buf.String(), Equals, "F=true\n") - s.buf.Reset() -} - -func (s *encoderSuite) TestEncodeInt(c *C) { - type testCase struct { - value interface{} - min int64 - max int64 - } - - enc := ini.NewEncoder(&s.buf) - - for _, tc := range []testCase{ - { - value: &struct { - Field int `ini:"F"` - }{}, - min: MinInt, - max: MaxInt, - }, - { - value: &struct { - Field int8 `ini:"F"` - }{}, - min: math.MinInt8, - max: math.MaxInt8, - }, - { - value: &struct { - Field int16 `ini:"F"` - }{}, - min: math.MinInt16, - max: math.MaxInt16, - }, - { - value: &struct { - Field int32 `ini:"F"` - }{}, - min: math.MinInt32, - max: math.MaxInt32, - }, - { - value: &struct { - Field int64 `ini:"F"` - }{}, - min: math.MinInt64, - max: math.MaxInt64, - }, - } { - comment := Commentf("test case: %#v", tc) - - reflect.ValueOf(tc.value).Elem().FieldByName("Field").SetInt(tc.min) - c.Assert(enc.Encode(tc.value), IsNil) - c.Check(s.buf.String(), Equals, "F="+strconv.FormatInt(tc.min, 10)+"\n", comment) - s.buf.Reset() - - reflect.ValueOf(tc.value).Elem().FieldByName("Field").SetInt(tc.max) - c.Assert(enc.Encode(tc.value), IsNil) - c.Check(s.buf.String(), Equals, "F="+strconv.FormatInt(tc.max, 10)+"\n", comment) - s.buf.Reset() - } -} - -func (s *encoderSuite) TestEncodeUint(c *C) { - enc := ini.NewEncoder(&s.buf) - - type testCase struct { - value interface{} - max uint64 - } - - for _, tc := range []testCase{ - { - value: &struct { - Field uint `ini:"F"` - }{}, - max: MaxUint, - }, - { - value: &struct { - Field uint8 `ini:"F"` - }{}, - max: math.MaxUint8, - }, - { - value: &struct { - Field uint16 `ini:"F"` - }{}, - max: math.MaxUint16, - }, - { - value: &struct { - Field uint32 `ini:"F"` - }{}, - max: math.MaxUint32, - }, - { - value: &struct { - Field uint64 `ini:"F"` - }{}, - max: math.MaxUint64, - }, - } { - comment := Commentf("test case: %#v", tc) - - reflect.ValueOf(tc.value).Elem().FieldByName("Field").SetUint(tc.max) - c.Assert(enc.Encode(tc.value), IsNil) - c.Check(s.buf.String(), Equals, "F="+strconv.FormatUint(tc.max, 10)+"\n", comment) - s.buf.Reset() - - reflect.ValueOf(tc.value).Elem().FieldByName("Field").SetUint(0) - c.Assert(enc.Encode(tc.value), IsNil) - c.Check(s.buf.String(), Equals, "F=0\n", comment) - s.buf.Reset() - } -} - -func (s *encoderSuite) TestEncodeFloat(c *C) { - enc := ini.NewEncoder(&s.buf) - - type testCase struct { - value interface{} - min float64 - max float64 - bitSize int - } - - for _, tc := range []testCase{ - { - value: &struct { - Field float32 `ini:"F"` - }{}, - min: math.SmallestNonzeroFloat32, - max: math.MaxFloat32, - bitSize: 32, - }, - { - value: &struct { - Field float64 `ini:"F"` - }{}, - min: math.SmallestNonzeroFloat64, - max: math.MaxFloat64, - bitSize: 64, - }, - } { - comment := Commentf("test case: %#v", tc) - - reflect.ValueOf(tc.value).Elem().FieldByName("Field").SetFloat(tc.min) - c.Assert(enc.Encode(tc.value), IsNil) - c.Check(s.buf.String(), Equals, "F="+strconv.FormatFloat(tc.min, 'f', -1, tc.bitSize)+"\n", comment) - s.buf.Reset() - - reflect.ValueOf(tc.value).Elem().FieldByName("Field").SetFloat(tc.max) - c.Assert(enc.Encode(tc.value), IsNil) - c.Check(s.buf.String(), Equals, "F="+strconv.FormatFloat(tc.max, 'f', -1, tc.bitSize)+"\n", comment) - s.buf.Reset() - } -} - -func (s *encoderSuite) TestEncodeString(c *C) { - enc := ini.NewEncoder(&s.buf) - - type testCase struct { - value interface{} - textIn string - textOut string - } - - for _, tc := range []testCase{ - { - value: &struct { - Field string `ini:"F"` - }{}, - textIn: "hello", - textOut: "hello", - }, - { - value: &struct { - Field string `ini:"F"` - }{}, - textIn: "hello\nnewline\nfriendly", - // The two spaces on each following line are to align with F= on the - // first line, not pictured here. - textOut: "hello\\\n newline\\\n friendly", - }, - { - value: &struct { - Field string `ini:"F,quote"` - }{}, - textIn: "hello", - textOut: `"hello"`, - }, - { - value: &struct { - Field string `ini:"F,quote"` - }{}, - textIn: "hello\nnewline\nfriendly", - textOut: `"hello\nnewline\nfriendly"`, - }, - } { - comment := Commentf("test case: %#v", tc) - - reflect.ValueOf(tc.value).Elem().FieldByName("Field").SetString(tc.textIn) - c.Assert(enc.Encode(tc.value), IsNil, comment) - c.Check(s.buf.String(), Equals, "F="+tc.textOut+"\n", comment) - s.buf.Reset() - } -} - -func (s *encoderSuite) TestEncodeTextMarshaler(c *C) { - enc := ini.NewEncoder(&s.buf) - - type testType struct { - Field testEnum `ini:"F"` - } - - value := &testType{Field: testFoo} - c.Assert(enc.Encode(value), IsNil) - c.Check(s.buf.String(), Equals, "F=foo\n") - - value = &testType{Field: testEnum(42)} - c.Assert(enc.Encode(value), ErrorMatches, `cannot encode testType.Field: cannot marshal testEnum: 42`) -} diff --git a/encoding/ini/export_test.go b/encoding/ini/export_test.go deleted file mode 100644 index 0d45a31..0000000 --- a/encoding/ini/export_test.go +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. - -package ini - -var ( - IniSliceOfType = iniSliceOfType - IniMapOfType = iniMapOfType -) diff --git a/encoding/ini/info.go b/encoding/ini/info.go deleted file mode 100644 index e038d69..0000000 --- a/encoding/ini/info.go +++ /dev/null @@ -1,127 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. - -package ini - -import ( - "encoding" - "fmt" - "reflect" - "strings" -) - -type iniFieldInfo struct { - Section string - Name string - OmitEmpty bool - Quote bool - - FieldIndex []int -} - -var ( - textMarshalerType = reflect.TypeOf((*encoding.TextMarshaler)(nil)).Elem() - textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() -) - -func iniSliceOfType(structType reflect.Type) ([]iniFieldInfo, error) { - result := make([]iniFieldInfo, 0, structType.NumField()) - section := "" // keeps track of current section name - - for i := 0; i < structType.NumField(); i++ { - structField := structType.Field(i) - // TODO: switch to structField.IsExported available since go 1.17 - if structField.PkgPath != "" { - continue - } - - switch structField.Type.Kind() { - case reflect.Bool: - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - case reflect.Float32, reflect.Float64: - case reflect.String: - default: - // Check if the field can be an encoding.TextMarshaler and encoding.TextUnmarshaler. - if !structField.Type.Implements(textMarshalerType) { - return nil, fmt.Errorf("ini error: field %s non-basic type does not implement encoding.TextMarshaler", structField.Name) - } - // Since TextUnmarshaler requires practical implementations to be called by pointer, - // perform the check on the pointer to the type of the field. - if !reflect.PtrTo(structField.Type).Implements(textUnmarshalerType) { - return nil, fmt.Errorf("ini error: field %s non-basic type does not implement encoding.TextUnmarshaler", structField.Name) - } - } - - tagValue, ok := structField.Tag.Lookup("ini") - if !ok { - continue - } - - var name, newSection string - - var omitempty, quote bool - - iniTags := strings.Split(tagValue, ",") - for i, tag := range iniTags { - if i == 0 { - name = tag - continue - } - - switch tag { - case "omitempty": - omitempty = true - case "quote": - quote = true - default: - if strings.HasPrefix(tag, "[") && strings.HasSuffix(tag, "]") { - newSection = tag[1 : len(tag)-1] - } else { - return nil, fmt.Errorf("ini error: field %s contains unsupported struct-tag: %q", structField.Name, tag) - } - } - } - - if name == "" { - return nil, fmt.Errorf("ini error: field %s lacks struct-tag with field name", structField.Name) - } - - if newSection != "" && section != newSection { - section = newSection - } - - result = append(result, iniFieldInfo{ - Name: name, - Section: section, - FieldIndex: structField.Index, - OmitEmpty: omitempty, - Quote: quote, - }) - } - - return result, nil -} - -func iniMapOfType(structType reflect.Type, caseSensitive bool) (slice []iniFieldInfo, indexMap map[string]map[string]int, err error) { - slice, err = iniSliceOfType(structType) - indexMap = make(map[string]map[string]int, len(slice)) - - for idx, info := range slice { - section := info.Section - name := info.Name - - if !caseSensitive { - section = strings.ToLower(section) - name = strings.ToLower(name) - } - - if indexMap[section] == nil { - indexMap[section] = make(map[string]int) - } - - indexMap[section][name] = idx - } - - return slice, indexMap, err -} diff --git a/encoding/ini/info_test.go b/encoding/ini/info_test.go deleted file mode 100644 index 1e83eae..0000000 --- a/encoding/ini/info_test.go +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. - -package ini_test - -import ( - "reflect" - - "booting.oniroproject.org/distro/components/sysota/encoding/ini" - . "gopkg.in/check.v1" -) - -type infoSuite struct{} - -var _ = Suite(&infoSuite{}) - -func (s *infoSuite) TestIniSliceOfTypeUnsupportedStructTags(c *C) { - var value struct { - Field int `ini:"field,magic"` - } - - _, err := ini.IniSliceOfType(reflect.TypeOf(value)) - c.Assert(err, ErrorMatches, `ini error: field Field contains unsupported struct-tag: "magic"`) -} - -func (s *infoSuite) TestIniSliceOfTypeMissingFieldName(c *C) { - var value struct { - Field int `ini:""` - } - - _, err := ini.IniSliceOfType(reflect.TypeOf(value)) - c.Assert(err, ErrorMatches, `ini error: field Field lacks struct-tag with field name`) -} - -func (s *infoSuite) TestIniSliceOfTypeTagParsing(c *C) { - var value struct { - internal int `ini:"internal,[section]"` - Exported int `ini:"Exported"` - OmitEmpty int `ini:"OmitEmpty,omitempty"` - Quoted string `ini:"Quoted,quote"` - } - - slice, err := ini.IniSliceOfType(reflect.TypeOf(value)) - c.Assert(err, IsNil) - c.Assert(slice, HasLen, 3) - c.Check(slice[0].Name, Equals, "Exported") - c.Check(slice[0].Section, Equals, "") - c.Check(slice[0].Quote, Equals, false) - c.Check(slice[1].Name, Equals, "OmitEmpty") - c.Check(slice[1].Section, Equals, "") - c.Check(slice[1].Quote, Equals, false) - c.Check(slice[2].Name, Equals, "Quoted") - c.Check(slice[2].Section, Equals, "") - c.Check(slice[2].Quote, Equals, true) -} - -func (s *infoSuite) TestIniSliceOfTypeSectionTracking(c *C) { - var value struct { - internal int `ini:"internal,[ignored]"` - Exported int `ini:"Exported,[section]"` - OmitEmpty int `ini:"OmitEmpty,omitempty"` - Quoted string `ini:"Quoted,quote,[another]"` - } - - slice, err := ini.IniSliceOfType(reflect.TypeOf(value)) - c.Assert(err, IsNil) - c.Assert(slice, HasLen, 3) - c.Check(slice[0].Section, Equals, "section") - c.Check(slice[1].Section, Equals, "section") - c.Check(slice[2].Section, Equals, "another") -} - -func (s *infoSuite) TestIniMapOfTypeCaseSensitivity(c *C) { - var value struct { - Field int `ini:"FIELD,[SECTION]"` - } - - const caseSensitive = false - - slice, idxMap, err := ini.IniMapOfType(reflect.TypeOf(value), caseSensitive) - c.Assert(err, IsNil) - c.Assert(slice, HasLen, 1) - c.Check(slice[0].Section, Equals, "SECTION") - c.Check(slice[0].Name, Equals, "FIELD") - c.Assert(idxMap, HasLen, 1) - c.Assert(idxMap["section"], NotNil) - c.Assert(idxMap["section"], HasLen, 1) - c.Assert(idxMap["section"]["field"], Equals, 0) - - slice, idxMap, err = ini.IniMapOfType(reflect.TypeOf(value), !caseSensitive) - c.Assert(err, IsNil) - c.Assert(slice, HasLen, 1) - c.Check(slice[0].Section, Equals, "SECTION") - c.Check(slice[0].Name, Equals, "FIELD") - c.Assert(idxMap, HasLen, 1) - c.Assert(idxMap["SECTION"], NotNil) - c.Assert(idxMap["SECTION"], HasLen, 1) - c.Assert(idxMap["SECTION"]["FIELD"], Equals, 0) -} - -type neitherMarshalNorUnmarshal struct{} - -func (s *infoSuite) TestIniSliceOfTypeCustomTypeNeitherMarshalNorUnmarshal(c *C) { - var value struct { - NeitherMarshalNorUnmarshal neitherMarshalNorUnmarshal `ini:"NeitherMarshalNorUnmarshal"` - } - - _, err := ini.IniSliceOfType(reflect.TypeOf(value)) - // XXX: error message doesn't mention TextUnmarshaler even though it is also missing. - c.Assert(err, ErrorMatches, `ini error: field NeitherMarshalNorUnmarshal non-basic type does not implement encoding.TextMarshaler`) -} - -type onlyMarshal struct{} - -func (onlyMarshal) MarshalText() ([]byte, error) { - return []byte(""), nil -} - -func (s *infoSuite) TestIniSliceOfTypeCustomTypeOnlyMarshal(c *C) { - var value struct { - OnlyMarshal onlyMarshal `ini:"OnlyMarshal"` - } - - _, err := ini.IniSliceOfType(reflect.TypeOf(value)) - c.Assert(err, ErrorMatches, `ini error: field OnlyMarshal non-basic type does not implement encoding.TextUnmarshaler`) -} - -type onlyUnmarshal struct{} - -func (o *onlyUnmarshal) UnmarshalText(data []byte) error { - return nil -} - -func (s *infoSuite) TestIniSliceOfTypeCustomTypeOnlyUnmarshal(c *C) { - var value struct { - OnlyUnmarshal onlyUnmarshal `ini:"OnlyUnmarshal"` - } - - _, err := ini.IniSliceOfType(reflect.TypeOf(value)) - c.Assert(err, ErrorMatches, `ini error: field OnlyUnmarshal non-basic type does not implement encoding.TextMarshaler`) -} - -type bothMarshalAndUnmarshal struct{} - -func (bothMarshalAndUnmarshal) MarshalText() ([]byte, error) { - return []byte(""), nil -} - -func (o *bothMarshalAndUnmarshal) UnmarshalText(data []byte) error { - return nil -} - -func (s *infoSuite) TestIniSliceOfTypeCustomTypeBothMarshalAndUnmarshal(c *C) { - var value struct { - BothMarshalAndUnmarshal bothMarshalAndUnmarshal `ini:"BothMarshalAndUnmarshal"` - } - - _, err := ini.IniSliceOfType(reflect.TypeOf(value)) - c.Assert(err, IsNil) -} diff --git a/encoding/ini/ini.go b/encoding/ini/ini.go deleted file mode 100644 index a9e7621..0000000 --- a/encoding/ini/ini.go +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. - -// Package ini implements encoding and decoding of Ini-files. The mapping -// between Ini and Go values is described in the documentation for the Marshal -// and Unmarshal functions. -package ini - -import ( - "bytes" -) - -// Marshal marshals a value to the INI representation. -// -// Only structures and non-nil pointers to structures can be marshaled. For all -// the fields of a structure, the following rules apply: -// -// - Fields are visited in order in which they are declared. -// -// - Only exported fields are considered. -// -// - Only fields with the "ini" struct tag are considered. -// -// - The struct tag must define the name of the field, for example `ini:"foo"`. -// -// - The struct tag may define the section name of the field, for example: -// `ini:"foo,[section]"`. -// -// - The section name "sticks" to subsequent structure fields, until defined -// again, making it easier to express groups sharing the same section easily. -// -// - The struct tag may indicate that zero-values are skipped by using -// the "omitempty" flag, for example: `ini:"foo,omitempty"`. -// -// - Fields must be of one of the basic types listed below, or must implement -// the encoding.TextMarshaler interface. -// -// Basic types supported directly are: -// - bool -// - {,u}int{,8,16,32,64} -// - float{32,64} -// - string -// -// All of those types are converted to strings using functions of the strconv -// package. Strings can be optionally marshaled to a quoted representation, -// using strconv.Quote, when the "quote" struct tag is present. -func Marshal(v interface{}) ([]byte, error) { - var buf bytes.Buffer - err := NewEncoder(&buf).Encode(v) - - return buf.Bytes(), err -} - -// Unmarshal unmarshals a value from the INI representation. -// -// Marshaled content can be unmarshaled only to non-nil pointers to structures. -// -// Lines starting with ';' or '#' are treated as comments and skipped. Lines -// ending with '\' are joined together before parsing, with replacing ' ' with -// the space character. This happens after comment elision, allowing comments to -// be placed in the middle of multi-line entry. -// -// Lines starting with '[' and anding with ']' indicate sections. Section matching -// is case insensitive. Unknown sections are ignored. -// -// Lines containing '=' are indicate Field=Value pairs. Field name cannot be -// empty. Field matching is case insensitive. Unknown fields are ignored. Field -// value is decoded according to the type of the corresponding structure field. -// If the type of structure field implements the encoding.TextUnmarshaler -// interface, that method takes priority. Otherwise the value is decoded using -// the appropriate function from the strconv package, either ParseBool, -// ParseInt, ParseUint or ParseFloat. Numeric types use bit size matching that -// of the structure field type. Structure fields of string type are stored -// directly, except when the "quote" struct tag is present, when strconv.Unquote -// is used to obtain the final value. -// -// Fields are assigned in the order they are encountered in the document. When -// the same field is present multiple times, it is decoded normally, overwriting -// earlier values. -// -// For more control over the unmarshal process, use the Decoder directly. -func Unmarshal(data []byte, v interface{}) error { - return NewDecoder(bytes.NewBuffer(data)).Decode(v) -} diff --git a/encoding/ini/ini_test.go b/encoding/ini/ini_test.go deleted file mode 100644 index 158dd91..0000000 --- a/encoding/ini/ini_test.go +++ /dev/null @@ -1,33 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. - -package ini_test - -import ( - "testing" - - "booting.oniroproject.org/distro/components/sysota/encoding/ini" - . "gopkg.in/check.v1" -) - -func Test(t *testing.T) { TestingT(t) } - -type iniSuite struct{} - -var _ = Suite(&iniSuite{}) - -func (s *iniSuite) TestMarshalUnmarshal(c *C) { - type Person struct { - Name string `ini:"Name,[Person]"` - Age int `ini:"Age,omitempty"` - } - - data, err := ini.Marshal(&Person{Name: "Bob", Age: 30}) - c.Assert(err, IsNil) - c.Check(string(data), Equals, "[Person]\nName=Bob\nAge=30\n") - - var p Person - err = ini.Unmarshal(data, &p) - c.Assert(err, IsNil) - c.Check(p, Equals, Person{Name: "Bob", Age: 30}) -} diff --git a/encoding/ini/math_legacy_32bit_test.go b/encoding/ini/math_legacy_32bit_test.go deleted file mode 100644 index 0e45d45..0000000 --- a/encoding/ini/math_legacy_32bit_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. - -//go:build !go1.17 && (386 || arm) -// +build !go1.17 -// +build 386 arm - -package ini_test - -import "math" - -const ( - MaxInt = math.MaxInt32 - MinInt = math.MinInt32 - MaxUint = math.MaxUint32 -) diff --git a/encoding/ini/math_legacy_64bit_test.go b/encoding/ini/math_legacy_64bit_test.go deleted file mode 100644 index e8b2358..0000000 --- a/encoding/ini/math_legacy_64bit_test.go +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. - -//go:build !go1.17 && (amd64 || arm64) -// +build !go1.17 -// +build amd64 arm64 - -package ini_test - -import "math" - -const ( - MaxInt = math.MaxInt64 - MinInt = math.MinInt64 - MaxUint = math.MaxUint64 -) diff --git a/encoding/ini/math_test.go b/encoding/ini/math_test.go deleted file mode 100644 index a1bd973..0000000 --- a/encoding/ini/math_test.go +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. -// -//go:build go1.17 -// +build go1.17 - -package ini_test - -import "math" - -const ( - MaxInt = math.MaxInt - MinInt = math.MinInt - MaxUint = math.MaxUint -) diff --git a/encoding/ini/testenum_test.go b/encoding/ini/testenum_test.go deleted file mode 100644 index a64739d..0000000 --- a/encoding/ini/testenum_test.go +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// SPDX-FileCopyrightText: Huawei Inc. - -package ini_test - -import ( - "encoding" - "fmt" -) - -type testEnum int - -const ( - testFoo testEnum = iota + 1 - testBar -) - -func (v testEnum) MarshalText() ([]byte, error) { - switch v { - case testFoo: - return []byte("foo"), nil - case testBar: - return []byte("foo"), nil - default: - return nil, fmt.Errorf("cannot marshal testEnum: %d", int(v)) - } -} - -func (v *testEnum) UnmarshalText(data []byte) error { - switch s := string(data); s { - case "foo": - *v = testFoo - case "bar": - *v = testBar - default: - return fmt.Errorf("cannot unmarshal testEnum: unknown value: %q", s) - } - - return nil -} - -var _ encoding.TextUnmarshaler = (*testEnum)(nil) -var _ encoding.TextMarshaler = (*testEnum)(nil) diff --git a/go.mod b/go.mod index b801702..7f9713e 100644 --- a/go.mod +++ b/go.mod @@ -4,5 +4,6 @@ go 1.13 require ( github.com/godbus/dbus/v5 v5.0.4 + gitlab.com/zygoon/go-ini v1.0.2 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c ) diff --git a/go.sum b/go.sum index d8c966d..225a836 100644 --- a/go.sum +++ b/go.sum @@ -5,5 +5,7 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +gitlab.com/zygoon/go-ini v1.0.2 h1:OjG+qWgF6In/SotXeDuyipFKrWC4X5/1uoE0UlbPlQ8= +gitlab.com/zygoon/go-ini v1.0.2/go.mod h1:mtZx8c3n2RxFvoh6i0SUunY6RV31Fyr/8Osc86cqNxo= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/ota/ota.go b/ota/ota.go index 84cf522..83cdff0 100644 --- a/ota/ota.go +++ b/ota/ota.go @@ -11,8 +11,9 @@ import ( "path/filepath" "sync" + "gitlab.com/zygoon/go-ini" + "booting.oniroproject.org/distro/components/sysota/atomicfile" - "booting.oniroproject.org/distro/components/sysota/encoding/ini" "booting.oniroproject.org/distro/components/sysota/errutil" "booting.oniroproject.org/distro/components/sysota/picfg/pimodel" ) diff --git a/ota/ota_test.go b/ota/ota_test.go index dc44e9d..0223a54 100644 --- a/ota/ota_test.go +++ b/ota/ota_test.go @@ -10,9 +10,9 @@ import ( "path/filepath" "testing" + "gitlab.com/zygoon/go-ini" . "gopkg.in/check.v1" - "booting.oniroproject.org/distro/components/sysota/encoding/ini" "booting.oniroproject.org/distro/components/sysota/ota" ) diff --git a/ota/state.go b/ota/state.go index 19b25b0..9322478 100644 --- a/ota/state.go +++ b/ota/state.go @@ -10,9 +10,10 @@ import ( "path/filepath" "sync" + "gitlab.com/zygoon/go-ini" + "booting.oniroproject.org/distro/components/sysota/atomicfile" "booting.oniroproject.org/distro/components/sysota/boot" - "booting.oniroproject.org/distro/components/sysota/encoding/ini" "booting.oniroproject.org/distro/components/sysota/errutil" ) -- GitLab