Skip to content

Commit e825a55

Browse files
authored
Merge pull request #98 from BirdeeHub/settings
feat(nested settings): better handling for setting nested settings
2 parents ddf4c04 + 36f887f commit e825a55

3 files changed

Lines changed: 78 additions & 6 deletions

File tree

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,16 @@ sh.repr = { newshell = { ... } }
223223
```
224224
This would overwrite the entire `repr` table, erasing any already there.
225225

226-
You may prefix the setting name with `_x_` to deep extend the tables together instead.
226+
There are 2 other methods designed for more easily setting nested values.
227227
```lua
228228
local sh = require('sh')
229-
sh._x_repr = { newshell = { ... } }
229+
sh.proper_pipes = true
230+
-- setting a top level value like this will perform a key-based deep merge
231+
sh[{"repr"}] = { posix = { transforms = { function(v) print(v) return v end } } }
232+
-- or provide a list of names to directly set a nested value! (does not merge)
233+
sh[{"repr", "posix", "transforms"}] = { function(v) print(v) return v end }
234+
-- The above examples add a print to the posix repr without overwriting the other representation functions
235+
-- (so you can see what the final command looks like!)
230236
```
231237

232238
## For nix users

lua/sh.lua

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,21 @@ local function tbl_get(t, default, ...)
136136
return t or default
137137
end
138138

139+
---@param t table
140+
---@param value any
141+
local function setNested(t, value, ...)
142+
local keys = {...}
143+
local cur = t
144+
for i = 1, #keys - 1 do
145+
local k = keys[i]
146+
if type(cur[k]) ~= "table" then
147+
cur[k] = {}
148+
end
149+
cur = cur[k]
150+
end
151+
cur[keys[#keys]] = value
152+
end
153+
139154
local warned_run_cmd_shim = false
140155

141156
---@param opts Shelua.Opts
@@ -419,6 +434,7 @@ local cmd_mt = {
419434
end,
420435
}
421436

437+
local has_warned_about_x_ = false
422438
local MT = {
423439
---@type Shelua.Opts
424440
__metatable = {
@@ -441,13 +457,27 @@ local MT = {
441457
-- change settings by assigning them to table
442458
__newindex = function(self, key, value)
443459
if type(key) == "string" and key:sub(1, 3) == "_x_" then
444-
local fkey = key:sub(4)
460+
if not has_warned_about_x_ then
461+
io.stderr:write("shelua: Using `sh._x_name = { nested = { settings = { to_be = \"merged\" } } }` to deep extend settings has been deprecated.\n")
462+
io.stderr:write("Use `sh[{\"name\"}] = { nested = { settings = { to_be = \"merged\" } } }` instead.\n")
463+
end
464+
key = key:sub(4)
465+
local opts = getmetatable(self)
466+
if type(rawget(opts, key)) ~= "table" then
467+
opts[key] = value
468+
else
469+
recUpdate(opts[key], value)
470+
end
471+
elseif type(key) == "table" and key[1] and #key == 1 then
472+
key = key[1]
445473
local opts = getmetatable(self)
446-
if type(rawget(opts, fkey)) ~= "table" then
447-
opts[fkey] = value
474+
if type(rawget(opts, key)) ~= "table" then
475+
opts[key] = value
448476
else
449-
recUpdate(opts[fkey], value)
477+
recUpdate(opts[key], value)
450478
end
479+
elseif type(key) == "table" and #key > 1 then
480+
setNested(getmetatable(self), value, (unpack or table.unpack)(key))
451481
else
452482
getmetatable(self)[key] = value
453483
end

tests/test.lua

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,42 @@ test('Check command with table args', function()
6565
local r = sh.stat('/bin', { format = '%a %n' })
6666
ok(tostring(r) == '755 /bin', 'stat --format "%a %n" /bin')
6767
end)
68+
test('Check settings deep merge via sh[{"key"}]', function()
69+
local s = sh()
70+
s[{"repr"}] = { posix = { foo = "bar" } }
71+
local opts = getmetatable(s)
72+
ok(opts.repr.posix.foo == "bar", "merge adds new nested key")
73+
ok(opts.repr.posix.escape ~= nil, "merge preserves existing keys")
74+
end)
75+
76+
test('Check settings nested set via multi-key table', function()
77+
local s = sh()
78+
s[{"repr", "posix", "foo"}] = "bar"
79+
local opts = getmetatable(s)
80+
ok(opts.repr.posix.foo == "bar", "nested set via multi-key table")
81+
end)
82+
83+
test('Check table key merge preserves keys set via nested set', function()
84+
local s = sh()
85+
s[{"repr", "posix", "foo"}] = "bar"
86+
s[{"repr"}] = { posix = { bar = "baz" } }
87+
local opts = getmetatable(s)
88+
ok(opts.repr.posix.foo == "bar", "merge preserves keys set via nested set")
89+
ok(opts.repr.posix.bar == "baz", "merge adds new keys")
90+
end)
91+
92+
test('Check sh[{"key"}] overwrites non-table settings', function()
93+
local s = sh()
94+
s[{"escape_args"}] = true
95+
ok(getmetatable(s).escape_args == true, "sh[{key}] with non-table value overwrites")
96+
end)
97+
98+
test('Check normal setting still works', function()
99+
local s = sh()
100+
s.escape_args = true
101+
ok(getmetatable(s).escape_args == true, "direct setting works")
102+
end)
103+
68104
test('Check concat command results', function()
69105
local r = sh.echo 'Hello World' :sed("s/Hello/Goodbye/g") .. " " .. sh.echo 'Hello Lua' :sed "s/Hello/Goodbye/g"
70106
ok(tostring(r) == 'Goodbye World Goodbye Lua', 'concat commands with string')

0 commit comments

Comments
 (0)