Module:Item infobox

From Space Station 14 Wiki
Revision as of 00:51, 4 April 2025 by Aliser (talk | contribs) (GOD I LOVE FOOD)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Module documentation
View or edit this documentation (about module documentation)

Contains partial implementation for {{Item infobox}}.

Edible by params

Edible by parameters are implemented in this module, entrypoint generate_edible_by.

List of edible by params is defined by edible_by_display_tbl.

Tests

Module:Item infobox/testcases/edible by

6 tests failed.

Name Expected Actual
test_empty Module:Item_infobox/testcases/edible_by:10: Failed to assert that nil equals expected
test_everyone Lua error -- Module:Item_infobox:131: assign to undeclared variable 'find_edible_by_order'
test_everyone_except_other_options Lua error -- Module:Item_infobox:131: assign to undeclared variable 'find_edible_by_order'
test_everyone_except_reptilians Lua error -- Module:Item_infobox:131: assign to undeclared variable 'find_edible_by_order'
test_moth Lua error -- Module:Item_infobox:131: assign to undeclared variable 'find_edible_by_order'
test_reptilian Lua error -- Module:Item_infobox:131: assign to undeclared variable 'find_edible_by_order'

local p = {}
local getArgs = require('Module:Arguments').getArgs
local yesNo = require('Module:Yesno')

-- ==========================

local edible_by_rest_param_name_lc = "edible by rest"

-- A list of edible by parameters along with the texts to display for each.
-- Param names defined here must be lowercase.
--
-- "edible by rest" is a special case, hence the table format.
-- * If all options are selected, including "edible by rest" (ie food is edible by everyone),
-- then the first form will be used.
-- * If only some options are selected, including "edible by rest",
-- then the second form will be used, following by a list of unselected options.
local edible_by_display_tbl = {
    { param = "edible by reptilians",       display = "Reptilians" },
    { param = "edible by moths",            display = "Moth people" },
    { param = edible_by_rest_param_name_lc, display = { "Everyone", "Everyone, except:" } },
}

-- ==========================

local function starts_with(str, substr)
    return string.sub(str, 1, string.len(substr)) == substr
end

local function ends_with(str, substr)
    local substr_length = string.len(substr)
    return string.sub(str, string.len(str) - substr_length + 1, string.len(str)) == substr
end

local function starts_with_insensitive(str, substr)
    return starts_with(string.lower(str), string.lower(substr))
end

local function ends_with_insensitive(str, substr)
    return ends_with(string.lower(str), string.lower(substr))
end

local function table_filter(tbl, filterFn)
    local out = {}

    for k, v in pairs(tbl) do
        if filterFn(v, k, tbl) then out[k] = v end
    end

    return out
end

local function table_find(tbl, findFn)
    for k, v in pairs(tbl) do
        if findFn(v, k, tbl) then return v end
    end
end

local function table_contains(tbl, findFn)
    return table_find(tbl, findFn) ~= nil
end

local function table_find_key(tbl, findFn)
    for k, v in pairs(tbl) do
        if findFn(v, k, tbl) then return k end
    end
end

local function table_find_index(tbl, findFn)
    for i, v in ipairs(tbl) do
        if findFn(v, i, tbl) then return i end
    end
end

local function table_map(tbl, mapFn)
    local res = {}
    for k, v in pairs(tbl) do
        table.insert(res, mapFn(v, k, tbl))
    end
    return res
end

local function numeric_table_length(t)
    local count = 0
    for _ in ipairs(t) do count = count + 1 end
    return count
end

local function table_length(t)
    local count = 0
    for _ in pairs(t) do count = count + 1 end
    return count
end


-- ==========================

function p.generate_edible_by(frame)
    local args = getArgs(frame)

    local edible_by_args = table_filter(args, function(value, key)
        return starts_with_insensitive(key, "edible by")
    end)

    if table_length(edible_by_args) == 0 then
        return
    end

    local edible_by_rest_key = table_find_key(edible_by_args, function(value, key)
        return ends_with_insensitive(key, "rest")
    end)

    -- save the edible by rest value to use later
    local is_edible_by_rest = yesNo(edible_by_args[edible_by_rest_key])

    -- generate a temp table made up only of numerical params to make it sortable.
    -- get rid of unselected "edible by"s.
    local selected_params_lc = {}
    for key, value in pairs(edible_by_args) do
        -- iterate over each key except edible by rest
        if key ~= edible_by_rest_key then
            -- check if its selected
            if yesNo(value) then
                table.insert(selected_params_lc, string.lower(key))
            end
        end
    end

    -- searches for a param name in the params table,
    -- returns the index ("order") of that entry.
    -- throws if no param was found (ie not defined in the table).
    function find_edible_by_order(param_name_lc)
        local index = table_find_index(edible_by_display_tbl, function(value)
            return value.param == param_name_lc
        end)

        if index == nil then
            error("failed to find edible by order: param name '" ..
                param_name_lc .. "' if not defined in the edible by params table")
        end

        return index
    end

    -- sort the temp table based on predefine order
    table.sort(selected_params_lc, function(a, b)
        return find_edible_by_order(a) > find_edible_by_order(b)
    end)

    -- render out the params
    local edible_by_rest_entry = is_edible_by_rest
        and table_find(edible_by_display_tbl, function(value)
            return value.param == edible_by_rest_param_name_lc
        end)

    if is_edible_by_rest and edible_by_rest_entry == nil then
        error(
            "failed to generate edible by: edible by rest param is enabled, but it is not defined in the display table")
    end

    local selected_params_lc_len = numeric_table_length(selected_params_lc)
    -- count total selected "edible by" params, including the edible by rest
    local total_selected_params = selected_params_lc_len + (is_edible_by_rest and 1 or 0)
    local display_table_len = numeric_table_length(edible_by_display_tbl)

    local list_el = mw.html.create('ul')

    function append_li(text)
        list_el
            :tag("li")
            :wikitext(text) -- relates to the li el, not the list el
    end

    function append_li_and_subli(li_text, subli_els)
        local li_el = mw.html.create("li")
            :wikitext(li_text)

        local subli_ul = mw.html.create('ul')
        for _, node in ipairs(subli_els) do
            subli_ul
                :node(node)
        end

        li_el:
            node(subli_ul)

        list_el
            :node(li_el)
    end

    if total_selected_params == display_table_len then
        -- all params selected, edible by everyone
        -- edible by rest is assumed to be on implicitly, cz otherwise the count won't match.
        append_li(edible_by_rest_entry.display[1])
    else
        -- some params selected
        for _, key_lc in ipairs(selected_params_lc) do
            local entry = table_find(edible_by_display_tbl, function(value)
                return value.param == key_lc
            end)

            if entry == nil then
                error("failed to generate edible by param: param '" ..
                    key_lc .. "' (lowercase) is not defined in the edible by display table")
            end

            append_li(entry.display)
        end

        if is_edible_by_rest then
            -- edible by all, except those unselected

            local display_unselected_entries = table_filter(edible_by_display_tbl, function(edible_by_display_entry)
                local is_selected = table_contains(selected_params_lc, function(selected)
                    return selected == edible_by_display_entry.param
                end)

                local is_rest_param = edible_by_display_entry.param == edible_by_rest_param_name_lc
                if is_selected or is_rest_param then
                    return false
                end

                return true
            end)

            local display_unselected_as_li_els = table_map(display_unselected_entries, function(value)
                return mw.html.create("li")
                    :wikitext(value.display)
            end)

            append_li_and_subli(edible_by_rest_entry.display[2], display_unselected_as_li_els)
        end
    end

    return list_el
end

return p