Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,16 @@ sh.repr = { newshell = { ... } }
```
This would overwrite the entire `repr` table, erasing any already there.

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

## For nix users
Expand Down
38 changes: 34 additions & 4 deletions lua/sh.lua
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,21 @@ local function tbl_get(t, default, ...)
return t or default
end

---@param t table
---@param value any
local function setNested(t, value, ...)
local keys = {...}
local cur = t
for i = 1, #keys - 1 do
local k = keys[i]
if type(cur[k]) ~= "table" then
cur[k] = {}
end
cur = cur[k]
end
cur[keys[#keys]] = value
end

local warned_run_cmd_shim = false

---@param opts Shelua.Opts
Expand Down Expand Up @@ -419,6 +434,7 @@ local cmd_mt = {
end,
}

local has_warned_about_x_ = false
local MT = {
---@type Shelua.Opts
__metatable = {
Expand All @@ -441,13 +457,27 @@ local MT = {
-- change settings by assigning them to table
__newindex = function(self, key, value)
if type(key) == "string" and key:sub(1, 3) == "_x_" then
local fkey = key:sub(4)
if not has_warned_about_x_ then
io.stderr:write("shelua: Using `sh._x_name = { nested = { settings = { to_be = \"merged\" } } }` to deep extend settings has been deprecated.\n")
io.stderr:write("Use `sh[{\"name\"}] = { nested = { settings = { to_be = \"merged\" } } }` instead.\n")
end
key = key:sub(4)
local opts = getmetatable(self)
if type(rawget(opts, key)) ~= "table" then
opts[key] = value
else
recUpdate(opts[key], value)
end
elseif type(key) == "table" and key[1] and #key == 1 then
key = key[1]
local opts = getmetatable(self)
if type(rawget(opts, fkey)) ~= "table" then
opts[fkey] = value
if type(rawget(opts, key)) ~= "table" then
opts[key] = value
else
recUpdate(opts[fkey], value)
recUpdate(opts[key], value)
end
elseif type(key) == "table" and #key > 1 then
setNested(getmetatable(self), value, (unpack or table.unpack)(key))
else
getmetatable(self)[key] = value
end
Expand Down
36 changes: 36 additions & 0 deletions tests/test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,42 @@ test('Check command with table args', function()
local r = sh.stat('/bin', { format = '%a %n' })
ok(tostring(r) == '755 /bin', 'stat --format "%a %n" /bin')
end)
test('Check settings deep merge via sh[{"key"}]', function()
local s = sh()
s[{"repr"}] = { posix = { foo = "bar" } }
local opts = getmetatable(s)
ok(opts.repr.posix.foo == "bar", "merge adds new nested key")
ok(opts.repr.posix.escape ~= nil, "merge preserves existing keys")
end)

test('Check settings nested set via multi-key table', function()
local s = sh()
s[{"repr", "posix", "foo"}] = "bar"
local opts = getmetatable(s)
ok(opts.repr.posix.foo == "bar", "nested set via multi-key table")
end)

test('Check table key merge preserves keys set via nested set', function()
local s = sh()
s[{"repr", "posix", "foo"}] = "bar"
s[{"repr"}] = { posix = { bar = "baz" } }
local opts = getmetatable(s)
ok(opts.repr.posix.foo == "bar", "merge preserves keys set via nested set")
ok(opts.repr.posix.bar == "baz", "merge adds new keys")
end)

test('Check sh[{"key"}] overwrites non-table settings', function()
local s = sh()
s[{"escape_args"}] = true
ok(getmetatable(s).escape_args == true, "sh[{key}] with non-table value overwrites")
end)

test('Check normal setting still works', function()
local s = sh()
s.escape_args = true
ok(getmetatable(s).escape_args == true, "direct setting works")
end)

test('Check concat command results', function()
local r = sh.echo 'Hello World' :sed("s/Hello/Goodbye/g") .. " " .. sh.echo 'Hello Lua' :sed "s/Hello/Goodbye/g"
ok(tostring(r) == 'Goodbye World Goodbye Lua', 'concat commands with string')
Expand Down