aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2019-05-26 03:28:53 +0700
committerShulhan <ms@kilabit.info>2019-05-26 03:35:02 +0700
commit671de3811ee64e7224fe8056290f5d3da2a1556d (patch)
treed33c7bac71d125393e20af3aa2538dbe2bb3fa29
parent9e0f6248bce30a6517c8d8cf3a08e3e7fa748c2a (diff)
downloadpakakeh.go-671de3811ee64e7224fe8056290f5d3da2a1556d.tar.xz
ini: refactoring section methods
This is the fifth part of refactoring ini package. Some of the changes, * add() now will append the new variable if the same already exist but the value is different. * remove unused getFirstIndex() method * add method getVariable() that return the last variable in the section by key * set() method will not add or append new variable if key is not exist, instead it will return false. * unset() method will remove the last variable on section, even if key is duplicate.
-rw-r--r--lib/ini/ini_test.go11
-rw-r--r--lib/ini/section.go135
-rw-r--r--lib/ini/section_test.go414
3 files changed, 306 insertions, 254 deletions
diff --git a/lib/ini/ini_test.go b/lib/ini/ini_test.go
index 803f67c7..859b63cc 100644
--- a/lib/ini/ini_test.go
+++ b/lib/ini/ini_test.go
@@ -5,7 +5,6 @@
package ini
import (
- "os"
"testing"
"github.com/shuLhan/share/lib/debug"
@@ -20,16 +19,6 @@ const (
testdataVarWithoutSection = "testdata/var_without_section.ini"
)
-var (
- sec *section //nolint: gochecknoglobals
- lastSec *section //nolint: gochecknoglobals
-)
-
-func TestMain(m *testing.M) {
- sec = newSection("test", "")
- os.Exit(m.Run())
-}
-
func TestOpen(t *testing.T) {
cases := []struct {
desc string
diff --git a/lib/ini/section.go b/lib/ini/section.go
index db0589c8..898393c9 100644
--- a/lib/ini/section.go
+++ b/lib/ini/section.go
@@ -91,21 +91,46 @@ func (sec *section) String() string {
//
// add append variable with `key` and `value` to current section.
//
-// If section already contains the same key, the value will not be replaced.
-// Use set() or ReplaceAll() to set existing value without duplication.
// If key is empty, no variable will be appended.
// If value is empty, it will be set to true.
+// If key and value already exist, no variable will be appended.
+// Use set() or replaceAll() to set existing value without duplication.
//
-func (sec *section) add(key, value string) {
+// It will return true if new variable is appended, otherwise it will return
+// false.
+//
+func (sec *section) add(key, value string) bool {
if len(key) == 0 {
- return
+ return false
+ }
+ if len(value) == 0 {
+ value = varValueTrue
+ }
+
+ keyLower := strings.ToLower(key)
+
+ for x := 0; x < len(sec.vars); x++ {
+ if !isLineModeVar(sec.vars[x].mode) {
+ continue
+ }
+ if sec.vars[x].keyLower != keyLower {
+ continue
+ }
+ if sec.vars[x].value == value {
+ return false
+ }
}
+
v := &variable{
- mode: lineModeValue,
- key: key,
- value: value,
+ mode: lineModeValue,
+ key: key,
+ keyLower: keyLower,
+ value: value,
}
- sec.addVariable(v)
+
+ sec.vars = append(sec.vars, v)
+
+ return true
}
//
@@ -153,31 +178,6 @@ func (sec *section) addVariable(v *variable) {
}
//
-// getFirstIndex will return the first index of variable `key`. If current
-// section have duplicate `key` it will return true.
-// If no variable with key found it will return -1 and false.
-//
-func (sec *section) getFirstIndex(key string) (idx int, dup bool) {
- idx = -1
- n := 0
- for x := 0; x < len(sec.vars); x++ {
- if sec.vars[x].keyLower != key {
- continue
- }
- if idx < 0 {
- idx = x
- }
- n++
- if n > 1 {
- dup = true
- return
- }
- }
-
- return
-}
-
-//
// get will return the last variable value based on key.
// If no key found it will return default value and false.
//
@@ -204,6 +204,25 @@ func (sec *section) get(key, def string) (val string, ok bool) {
}
//
+// getVariable return the last variable that have the same key.
+// The key MUST have been converted to lowercase.
+//
+func (sec *section) getVariable(key string) (idx int, v *variable) {
+ idx = len(sec.vars) - 1
+ for ; idx >= 0; idx-- {
+ if !isLineModeVar(sec.vars[idx].mode) {
+ continue
+ }
+ if sec.vars[idx].keyLower == key {
+ v = sec.vars[idx]
+ return
+ }
+ }
+
+ return 0, nil
+}
+
+//
// gets all variable values that have the same key under section from top to
// bottom.
// If no key found it will return default values and false.
@@ -241,68 +260,46 @@ func (sec *section) replaceAll(key, value string) {
//
// set will replace variable with matching key with value.
-// If key is empty, no variable will be changed or added, and it will
-// return false.
-// If section contains two or more variable with the same `key`, it will
-// return false.
-// If no variable key matched, the new variable will be added to list.
+// The key MUST be not empty and has been converted to lowercase.
// If value is empty, it will be set to true.
//
func (sec *section) set(key, value string) bool {
- if len(key) == 0 {
+ if len(sec.vars) == 0 || len(key) == 0 {
return false
}
- keyLower := strings.ToLower(key)
+ key = strings.ToLower(key)
- idx, dup := sec.getFirstIndex(keyLower)
- if dup {
+ _, v := sec.getVariable(key)
+ if v == nil {
return false
}
-
- if idx < 0 {
- sec.addVariable(&variable{
- mode: lineModeValue,
- key: key,
- value: value,
- })
- return true
- }
-
if len(value) == 0 {
- sec.vars[idx].value = varValueTrue
- } else {
- sec.vars[idx].value = value
+ value = varValueTrue
}
+ v.value = value
+
return true
}
//
-// unset remove the variable with name `key` on current section.
-//
-// If key is empty, no variable will be removed, and it will return true.
-//
-// If current section contains two or more variables with the same key,
-// no variables will be removed and it will return false.
+// unset remove the last variable with name `key` on current section.
//
-// On success, where no variable removed or one variable is removed, it will
-// return true.
+// On success, where a variable removed or one variable is removed, it will
+// return true, otherwise it will be removed.
//
func (sec *section) unset(key string) bool {
if len(key) == 0 {
- return true
+ return false
}
key = strings.ToLower(key)
- idx, dup := sec.getFirstIndex(key)
- if dup {
+ idx, v := sec.getVariable(key)
+ if v == nil {
return false
}
- if idx < 0 {
- return true
- }
copy(sec.vars[idx:], sec.vars[idx+1:])
sec.vars[len(sec.vars)-1] = nil
diff --git a/lib/ini/section_test.go b/lib/ini/section_test.go
index d0bf3d37..3e7df78e 100644
--- a/lib/ini/section_test.go
+++ b/lib/ini/section_test.go
@@ -51,6 +51,23 @@ func TestNewSection(t *testing.T) {
}
func TestSectionSet(t *testing.T) {
+ sec := &section{
+ mode: lineModeSection,
+ name: "section",
+ nameLower: "section",
+ vars: []*variable{{
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v1",
+ }, {
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v2",
+ }},
+ }
+
cases := []struct {
desc string
k string
@@ -58,15 +75,8 @@ func TestSectionSet(t *testing.T) {
expOK bool
expSec *section
}{{
- desc: "With empty key",
- expSec: &section{
- mode: sec.mode,
- name: sec.name,
- nameLower: sec.nameLower,
- },
- }, {
- desc: "With empty value (Key-1) (will be added)",
- k: "Key-1",
+ desc: "With empty value",
+ k: "k",
expOK: true,
expSec: &section{
mode: sec.mode,
@@ -74,14 +84,19 @@ func TestSectionSet(t *testing.T) {
nameLower: sec.nameLower,
vars: []*variable{{
mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
+ key: "k",
+ keyLower: "k",
+ value: "v1",
+ }, {
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
value: "true",
}},
},
}, {
- desc: "With new value (Key-1)",
- k: "Key-1",
+ desc: "With value",
+ k: "k",
v: "false",
expOK: true,
expSec: &section{
@@ -90,50 +105,14 @@ func TestSectionSet(t *testing.T) {
nameLower: sec.nameLower,
vars: []*variable{{
mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
- value: "false",
- }},
- },
- }, {
- desc: "With key not found (Key-2) (added)",
- k: "Key-2",
- v: "2",
- expOK: true,
- expSec: &section{
- mode: sec.mode,
- name: sec.name,
- nameLower: sec.nameLower,
- vars: []*variable{{
- mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
- value: "false",
+ key: "k",
+ keyLower: "k",
+ value: "v1",
}, {
mode: lineModeValue,
- key: "Key-2",
- keyLower: "key-2",
- value: "2",
- }},
- },
- }, {
- desc: "With empty value on Key-2 (true)",
- k: "Key-2",
- expOK: true,
- expSec: &section{
- mode: sec.mode,
- name: sec.name,
- nameLower: sec.nameLower,
- vars: []*variable{{
- mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
+ key: "k",
+ keyLower: "k",
value: "false",
- }, {
- mode: lineModeValue,
- key: "Key-2",
- keyLower: "key-2",
- value: "true",
}},
},
}}
@@ -145,72 +124,97 @@ func TestSectionSet(t *testing.T) {
test.Assert(t, "ok", c.expOK, ok, true)
test.Assert(t, "section", c.expSec, sec, true)
-
- lastSec = c.expSec
}
}
func TestSectionAdd(t *testing.T) {
+ sec := &section{
+ mode: lineModeSection,
+ name: "section",
+ nameLower: "section",
+ vars: []*variable{{
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v1",
+ }, {
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v2",
+ }},
+ }
+
cases := []struct {
desc string
k string
v string
expSec *section
}{{
- desc: "Empty key (no change)",
- expSec: lastSec,
+ desc: "With empty key",
+ expSec: &section{
+ mode: sec.mode,
+ name: sec.name,
+ nameLower: sec.nameLower,
+ vars: []*variable{{
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v1",
+ }, {
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v2",
+ }},
+ },
}, {
- desc: "Duplicate key-1 (no value)",
- k: "Key-1",
+ desc: "With no value",
+ k: "k",
expSec: &section{
mode: sec.mode,
name: sec.name,
nameLower: sec.nameLower,
vars: []*variable{{
mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
- value: "false",
+ key: "k",
+ keyLower: "k",
+ value: "v1",
}, {
mode: lineModeValue,
- key: "Key-2",
- keyLower: "key-2",
- value: "true",
+ key: "k",
+ keyLower: "k",
+ value: "v2",
}, {
mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
+ key: "k",
+ keyLower: "k",
value: "true",
}},
},
}, {
- desc: "Duplicate key-1 (1)",
- k: "Key-1",
- v: "1",
+ desc: "Duplicate key and value",
+ k: "k",
+ v: "v1",
expSec: &section{
mode: sec.mode,
name: sec.name,
nameLower: sec.nameLower,
vars: []*variable{{
mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
- value: "false",
+ key: "k",
+ keyLower: "k",
+ value: "v1",
}, {
mode: lineModeValue,
- key: "Key-2",
- keyLower: "key-2",
- value: "true",
+ key: "k",
+ keyLower: "k",
+ value: "v2",
}, {
mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
+ key: "k",
+ keyLower: "k",
value: "true",
- }, {
- mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
- value: "1",
}},
},
}}
@@ -221,84 +225,54 @@ func TestSectionAdd(t *testing.T) {
sec.add(c.k, c.v)
test.Assert(t, "section", c.expSec, sec, true)
-
- lastSec = c.expSec
}
}
-func TestSectionSet2(t *testing.T) {
- cases := []struct {
- desc string
- k string
- v string
- expOK bool
- expSec *section
- }{{
- desc: "Set duplicate Key-1",
- k: "Key-1",
- v: "new value",
- expSec: lastSec,
- }, {
- desc: "Set duplicate key-1",
- k: "key-1",
- v: "new value",
- expSec: lastSec,
- }}
-
- for _, c := range cases {
- t.Log(c.desc)
-
- ok := sec.set(c.k, c.v)
-
- test.Assert(t, "ok", c.expOK, ok, true)
- test.Assert(t, "section", c.expSec, sec, true)
-
- lastSec = c.expSec
+func TestSectionUnset(t *testing.T) {
+ sec := &section{
+ mode: lineModeSection,
+ name: "section",
+ nameLower: "section",
+ vars: []*variable{{
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v1",
+ }, {
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v2",
+ }},
}
-}
-func TestSectionUnset(t *testing.T) {
cases := []struct {
desc string
k string
expOK bool
expSec *section
}{{
- desc: "With empty key",
- expOK: true,
- expSec: lastSec,
- }, {
- desc: "With duplicate key-1",
- k: "key-1",
- expSec: lastSec,
- }, {
- desc: "With valid key-2",
- k: "key-2",
- expOK: true,
+ desc: "With empty key",
+ expOK: false,
expSec: &section{
mode: sec.mode,
name: sec.name,
nameLower: sec.nameLower,
vars: []*variable{{
mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
- value: "false",
- }, {
- mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
- value: "true",
+ key: "k",
+ keyLower: "k",
+ value: "v1",
}, {
mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
- value: "1",
+ key: "k",
+ keyLower: "k",
+ value: "v2",
}},
},
}, {
- desc: "With valid key-2 (again)",
- k: "key-2",
+ desc: "With duplicate key",
+ k: "k",
expOK: true,
expSec: &section{
mode: sec.mode,
@@ -306,21 +280,35 @@ func TestSectionUnset(t *testing.T) {
nameLower: sec.nameLower,
vars: []*variable{{
mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
- value: "false",
- }, {
- mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
- value: "true",
- }, {
+ key: "k",
+ keyLower: "k",
+ value: "v1",
+ }},
+ },
+ }, {
+ desc: "With invalid key",
+ k: "key-2",
+ expSec: &section{
+ mode: sec.mode,
+ name: sec.name,
+ nameLower: sec.nameLower,
+ vars: []*variable{{
mode: lineModeValue,
- key: "Key-1",
- keyLower: "key-1",
- value: "1",
+ key: "k",
+ keyLower: "k",
+ value: "v1",
}},
},
+ }, {
+ desc: "With valid key (again)",
+ k: "k",
+ expOK: true,
+ expSec: &section{
+ mode: sec.mode,
+ name: sec.name,
+ nameLower: sec.nameLower,
+ vars: []*variable{},
+ },
}}
for _, c := range cases {
@@ -330,34 +318,79 @@ func TestSectionUnset(t *testing.T) {
test.Assert(t, "ok", c.expOK, ok, true)
test.Assert(t, "section", c.expSec, sec, true)
-
- lastSec = c.expSec
}
}
func TestSectionUnsetAll(t *testing.T) {
+ sec := &section{
+ mode: lineModeSection,
+ name: "section",
+ nameLower: "section",
+ vars: []*variable{{
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v1",
+ }, {
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v2",
+ }},
+ }
+
cases := []struct {
desc string
k string
expSec *section
}{{
- desc: "With empty key",
- expSec: lastSec,
+ desc: "With empty key",
+ expSec: &section{
+ mode: sec.mode,
+ name: sec.name,
+ nameLower: sec.nameLower,
+ vars: []*variable{{
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v1",
+ }, {
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v2",
+ }},
+ },
}, {
- desc: "With invalid key-3",
- k: "key-3",
- expSec: lastSec,
+ desc: "With unmatch key",
+ k: "unmatch",
+ expSec: &section{
+ mode: sec.mode,
+ name: sec.name,
+ nameLower: sec.nameLower,
+ vars: []*variable{{
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v1",
+ }, {
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v2",
+ }},
+ },
}, {
- desc: "With valid key-1",
- k: "KEY-1",
+ desc: "With valid k",
+ k: "K",
expSec: &section{
mode: sec.mode,
name: sec.name,
nameLower: sec.nameLower,
},
}, {
- desc: "With valid key-1 (again)",
- k: "KEY-1",
+ desc: "With valid key (again)",
+ k: "K",
expSec: &section{
mode: sec.mode,
name: sec.name,
@@ -371,13 +404,15 @@ func TestSectionUnsetAll(t *testing.T) {
sec.unsetAll(c.k)
test.Assert(t, "section", c.expSec, sec, true)
-
- lastSec = c.expSec
}
}
func TestSectionReplaceAll(t *testing.T) {
- sec.addVariable(nil)
+ sec := &section{
+ mode: lineModeSection,
+ name: "section",
+ nameLower: "section",
+ }
sec.add("key-3", "3")
sec.add("key-3", "33")
@@ -418,7 +453,7 @@ func TestSectionReplaceAll(t *testing.T) {
}},
},
}, {
- desc: "With invalid key-4 (will be added)",
+ desc: "With invalid key",
k: "KEY-4",
v: "4",
expSec: &section{
@@ -453,7 +488,7 @@ func TestSectionReplaceAll(t *testing.T) {
}},
},
}, {
- desc: "With valid key-3",
+ desc: "With valid key",
k: "KEY-3",
v: "replaced",
expSec: &section{
@@ -484,6 +519,23 @@ func TestSectionReplaceAll(t *testing.T) {
}
func TestSectionGet(t *testing.T) {
+ sec := &section{
+ mode: lineModeSection,
+ name: "section",
+ nameLower: "section",
+ vars: []*variable{{
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v1",
+ }, {
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v2",
+ }},
+ }
+
cases := []struct {
desc string
k string
@@ -491,19 +543,16 @@ func TestSectionGet(t *testing.T) {
expOK bool
expVal string
}{{
- desc: "On empty vars",
- k: "key-1",
- }, {
- desc: "On empty vars with default",
+ desc: "With invalid key and default",
k: "key-1",
def: "default value",
expVal: "default value",
}, {
desc: "Valid key",
- k: "key-3",
+ k: "k",
def: "default value",
expOK: true,
- expVal: "replaced",
+ expVal: "v2",
}}
for _, c := range cases {
@@ -517,6 +566,23 @@ func TestSectionGet(t *testing.T) {
}
func TestSectionGets(t *testing.T) {
+ sec := &section{
+ mode: lineModeSection,
+ name: "section",
+ nameLower: "section",
+ vars: []*variable{{
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v1",
+ }, {
+ mode: lineModeValue,
+ key: "k",
+ keyLower: "k",
+ value: "v2",
+ }},
+ }
+
sec.add("dup", "value 1")
sec.add("dup", "value 2")