Skip to content

Commit

Permalink
fix: Only use quotes if necessary in toIni template function
Browse files Browse the repository at this point in the history
  • Loading branch information
twpayne committed Aug 31, 2022
1 parent 3e6e26d commit 03a91ca
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 17 deletions.
29 changes: 27 additions & 2 deletions pkg/cmd/templatefuncs.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,12 @@ type ioregData struct {
value map[string]any
}

var startOfLineRx = regexp.MustCompile(`(?m)^`)
var (
// needsQuoteRx matches any string that contains non-printable characters,
// double quotes, or a backslash.
needsQuoteRx = regexp.MustCompile(`[^\x21\x23-\x5b\x5d-\x7e]`)
startOfLineRx = regexp.MustCompile(`(?m)^`)
)

func (c *Config) commentTemplateFunc(prefix, s string) string {
return startOfLineRx.ReplaceAllString(s, prefix)
Expand Down Expand Up @@ -308,7 +313,11 @@ func writeIniMap(w io.Writer, data map[string]any, sectionPrefix string) error {
}
subsections = append(subsections, subsection)
case string:
fmt.Fprintf(w, "%s = %q\n", key, value)
if needsQuote(value) {
fmt.Fprintf(w, "%s = %q\n", key, value)
} else {
fmt.Fprintf(w, "%s = %s\n", key, value)
}
default:
return fmt.Errorf("%s%s: %T: unsupported type", sectionPrefix, key, value)
}
Expand All @@ -327,6 +336,22 @@ func writeIniMap(w io.Writer, data map[string]any, sectionPrefix string) error {
return nil
}

func needsQuote(s string) bool {
if s == "" {
return true
}
if needsQuoteRx.MatchString(s) {
return true
}
if _, err := strconv.ParseBool(s); err == nil {
return true
}
if _, err := strconv.ParseFloat(s, 64); err == nil {
return true
}
return false
}

func sortedKeys[K constraints.Ordered, V any](m map[K]V) []K {
keys := maps.Keys(m)
slices.Sort(keys)
Expand Down
76 changes: 63 additions & 13 deletions pkg/cmd/templatefuncs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,21 @@ func TestToIniTemplateFunc(t *testing.T) {
`bool = true`,
`float = 1.000000`,
`int = 1`,
`string = "string"`,
`string = string`,
),
},
{
data: map[string]any{
"bool": "true",
"float": "1.0",
"int": "1",
"string": "string string",
},
expected: chezmoitest.JoinLines(
`bool = "true"`,
`float = "1.0"`,
`int = "1"`,
`string = "string string"`,
),
},
{
Expand All @@ -74,10 +88,10 @@ func TestToIniTemplateFunc(t *testing.T) {
},
},
expected: chezmoitest.JoinLines(
`key = "value"`,
`key = value`,
``,
`[section]`,
`subKey = "subValue"`,
`subKey = subValue`,
),
},
{
Expand All @@ -93,7 +107,7 @@ func TestToIniTemplateFunc(t *testing.T) {
`[section]`,
``,
`[section.subsection]`,
`subSubKey = "subSubValue"`,
`subSubKey = subSubValue`,
),
},
{
Expand All @@ -107,13 +121,13 @@ func TestToIniTemplateFunc(t *testing.T) {
},
},
expected: chezmoitest.JoinLines(
`key = "value"`,
`key = value`,
``,
`[section]`,
`subKey = "subValue"`,
`subKey = subValue`,
``,
`[section.subsection]`,
`subSubKey = "subSubValue"`,
`subSubKey = subSubValue`,
),
},
{
Expand All @@ -140,22 +154,22 @@ func TestToIniTemplateFunc(t *testing.T) {
expected: chezmoitest.JoinLines(
``,
`[section1]`,
`subKey1 = "subValue1"`,
`subKey1 = subValue1`,
``,
`[section1.subsection1a]`,
`subSubKey1a = "subSubValue1a"`,
`subSubKey1a = subSubValue1a`,
``,
`[section1.subsection1b]`,
`subSubKey1b = "subSubValue1b"`,
`subSubKey1b = subSubValue1b`,
``,
`[section2]`,
`subKey2 = "subValue2"`,
`subKey2 = subValue2`,
``,
`[section2.subsection2a]`,
`subSubKey2a = "subSubValue2a"`,
`subSubKey2a = subSubValue2a`,
``,
`[section2.subsection2b]`,
`subSubKey2b = "subSubValue2b"`,
`subSubKey2b = subSubValue2b`,
),
},
} {
Expand All @@ -167,6 +181,42 @@ func TestToIniTemplateFunc(t *testing.T) {
}
}

func TestNeedsQuote(t *testing.T) {
for i, tc := range []struct {
s string
expected bool
}{
{
s: "",
expected: true,
},
{
s: "\\",
expected: true,
},
{
s: "\a",
expected: true,
},
{
s: "abc",
expected: false,
},
{
s: "true",
expected: true,
},
{
s: "1",
expected: true,
},
} {
t.Run(strconv.Itoa(i), func(t *testing.T) {
assert.Equal(t, tc.expected, needsQuote(tc.s))
})
}
}

func TestQuoteListTemplateFunc(t *testing.T) {
c, err := newConfig()
require.NoError(t, err)
Expand Down
4 changes: 2 additions & 2 deletions pkg/cmd/testdata/scripts/templatefuncs.txtar
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,10 @@ file2.txt
-- golden/include-relpath --
# contents of .local/share/chezmoi/.include
-- golden/toIni --
key = "value"
key = value

[section]
subkey = "subvalue"
subkey = subvalue
-- home/user/.include --
# contents of .include
-- home/user/.local/share/chezmoi/.include --
Expand Down

0 comments on commit 03a91ca

Please sign in to comment.