Module:Format TemplateData/global

local Export = { suite   = "TemplateDataGlobal", serial  = "2020-08-01", item    = 51435481, subpages = "TemplateData", suffix  = "tab" } --[=[ Retrieve TemplateData from Commons:Data (or other global source) require Inspired by User:Yurik. ]=] local Failsafe = Export

local failsafe = function ( atleast ) -- Retrieve versioning and check for compliance -- Precondition: --    atleast  -- string, with required version --                        or "wikidata" or "~" or "@" or false -- Postcondition: --    Returns  string  -- with queried version/item, also if problem --             false   -- if appropriate -- 2020-08-01   local last  = ( atleast == "~" ) local link = ( atleast == "@" ) local since = atleast local r   if last  or  link  or  since == "wikidata" then local item = Failsafe.item since = false if type( item ) == "number" and  item > 0 then local suited = string.format( "Q%d", item ) local entity = mw.wikibase.getEntity( suited ) if type( entity ) == "table" then local seek = Failsafe.serialProperty or "P348" local vsn = entity:formatPropertyValues( seek ) if type( vsn ) == "table" and type( vsn.value ) == "string" and vsn.value ~= "" then if last and  vsn.value == Failsafe.serial then r = false elseif link then if mw.title.getCurrentTitle.prefixedText == mw.wikibase.getSitelink( suited ) then r = false else r = suited end else r = vsn.value end end end end end if type( r ) == "nil" then if not since or  since <= Failsafe.serial then r = Failsafe.serial else r = false end end return r end -- failsafe

local function fair( already, adapt, append ) -- Merge local definitions into global base -- Parameter: --    already  -- global item --    adapt    -- local override item --    append   -- append to sequence table -- Returns merged item local r   if already and adapt then if type( already ) == "table" and type( adapt ) == "table" then r = already if append then for i = 1, #adapt do                   table.insert( r, adapt[ i ] ) end   -- for i            else for k, v in pairs( adapt ) do                   r[ k ] = v                end    -- for k, v            end else r = adapt end else r = already or adapt end return r end -- fair

local function feed( apply ) -- Retrieve override from JSON code -- Parameter: --    apply  --  string, with JSON -- Returns string, with error message, or table local lucky, r = pcall( mw.text.jsonDecode, apply ) if not lucky then r = "fatal JSON error in LOCAL override" end return r end -- feed

local function find( access ) -- Fetch data from page -- Parameter: --   access  -- string, with core page name -- Returns --   1. string, with prefixed page name --   2. table with JSON data, or error message local storage = access local lucky, r   if Export.suffix  and  not storage:find( ".", 2, true ) then local k = -1 - #Export.suffix if storage:sub( k ) ~= "." .. Export.suffix then storage = string.format( "%s.%s", storage, Export.suffix ) end end if Export.subpages and  not storage:find( "/", 1, true ) then storage = string.format( "%s/%s", Export.subpages, storage ) end lucky, r = pcall( mw.ext.data.get, storage, "_" ) storage = "Data:" .. storage if mw.site.siteName ~= "Wikimedia Commons" then storage = "commons:" .. storage end if type( r ) ~= "table" and  type( r ) ~= "string" then r = "INVALID" end return storage, r end -- find

local function flat( apply ) -- Convert tabular data into TemplateData -- Parameter: --    apply -- table, with tabular data -- Returns string, with error message, or table, with TemplateData local r, scream local function failed( at, alert ) if scream then scream = string.format( "%s * #%d: %s",                                         scream, at, alert ) else scream = add end end -- failed if type( apply.schema ) == "table" and type( apply.schema.fields ) == "table" and type( apply.data ) == "table" then local order = { } local entry, got, params, parOrder, s, sign, td, v       for k, v in pairs( apply.schema.fields ) do            if type( v ) == "table" then table.insert( order, v.name ) end end   -- for k, v        for i = 1, #apply.data do            entry = apply.data[ i ] if type( entry ) == "table" then got = { } sign = false for j = 1, #entry do                   s = order[ j ] v = entry[ j ] if type( v ) == "string" then v = mw.text.trim( v ) if v == "" then v = false end end if v then if s == "name" then sign = v                       elseif s == "aliases" then if type( v ) == "string" then got.aliases = mw.text.split( v,                                                            "%s*|%s*" ) else failed( i, "aliases not a string" ) end else got[ s ] = v                       end end end   -- for j                if sign == "|" then if td then failed( i, "root repeated" ) else td = { description = got.description } if type( got.type ) == "string" then td.format = got.type:gsub( "N", "\n" ) end end elseif sign then if params then if params[ sign ] then failed( i, "name repeated: " .. sign ) end else params  = { } parOrder = { } end params[ sign ] = got table.insert( parOrder, sign ) else failed( i, "missing name" ) end else failed( i, "invalid component" ) end end   -- for i        r = td or { } r.params    = params r.paramOrder = parOrder else r = "bad tabular structure" end return scream or r or "EMPTY" end -- flat

local function flush( assembly, avoid ) -- Remove element from sequence table -- Parameter: --    assembly  -- sequence table --    avoid     -- element for i = 1, #assembly do       if assembly[ i ] == avoid then table.remove( assembly, i ) break   -- for i        end end   -- for i end -- flush

local function fold( already, adapt ) -- Merge local parameter definitions into global base -- Parameter: --    already  -- table, with global data --    adapt    -- sequence table, with local params overrides -- Returns string, with error message, or table, with TemplateData local order = { } local params = { } local r = already local entry, override, s   r.params     = r.params or { } r.paramOrder = r.paramOrder or { } for i = 1, #adapt do       override = adapt[ i ] if type( override ) ~= "table" then r = string.format( "No object at LOCAL params[%d]", i ) break   -- for i        elseif type( override.global ) == "string" then s    = override.global entry = r.params[ s ] if type( entry ) == "table" then flush( r.paramOrder, s ) if type( override["local"] ) == "string" then s = override["local"] override["local"] = nil elseif override["local"] == false then entry = nil end if entry then override.global = nil for k, v in pairs( override ) do                       entry[ k ] = fair( entry[ k ], override[ k ],                                           ( k == "aliases" ) ) end   -- for k, v                    table.insert( order, s ) end params[ s ] = entry else r = string.format( "No GLOBAL params %s for LOCAL [%d]",                                  s, i ) break   -- for i            end elseif type( override["local"] ) == "string" then s = override["local"] override["local"] = nil params[ s ] = override table.insert( order, s ) else r = string.format( "No name for LOCAL params[%d]", i ) break   -- for i        end end   -- for i    if type( r ) == "table" then for i = 1, #r.paramOrder do           s = r.paramOrder[ i ] params[ s ] = r.params[ s ] table.insert( order, s ) end   -- for i        r.params     = params r.paramOrder = order end return r end -- fold

local function fork( already, adapt ) -- Merge local definitions into global base -- Parameter: --    already  -- table, with global data --    adapt    -- table, with local overrides -- Returns string, with error message, or table, with TemplateData local root = { "description", "format", "maps", "sets", "style" } local r = already for k, v in pairs( root ) do       if adapt[ v ] then r[ v ] = fair( r[ v ], adapt[ v ] ) end end   -- for k, v    if type( adapt.params ) == "table" then r = fold( r, adapt.params ) end return r end -- fork

local function furnish( apply, at, adapt ) -- Convert external data into TemplateData -- Parameter: --    apply  -- table, with external data --    at     -- string, with page name --    adapt  -- JSON string or table or not, with local overrides -- Returns string, with error message, or table, with TemplateData local r   if at:sub( -4 ) == ".tab" then r = flat( apply ) else r = "Unknown page format: " .. at   end if adapt and  type( r ) == "table" then local override = adapt if type( adapt ) == "string" then override = feed( adapt ) if type( override ) == "string" then r = override end end if type( override ) == "table" then r = fork( r, override ) end end return r end -- furnish

Export.failsafe = function ( frame ) -- Versioning interface local s = type( frame ) local since if s == "table" then since = frame.args[ 1 ] elseif s == "string" then since = frame end if since then since = mw.text.trim( since ) if since == "" then since = false end end return failsafe( since ) or  "" end -- Export.failsafe

Export.fetch = function ( access, adapt ) -- Fetch data from site -- Parameter: --    access  -- string, with page specification --    adapt   -- JSON string or table or not, with local overrides -- Returns --   1. string, with error message or prefixed page name --   2. table with TemplateData, or not local storage, t = find( access ) local s   if type( t ) == "table" then t = furnish( t, storage, adapt ) if type( t ) ~= "table" then s = t       end else s = t   end if type( t ) ~= "table" then storage = string.format( "%s", storage ) if s then storage = string.format( "%s * %s", storage, s ) end t = false end return storage, t end -- Export.fetch

return Export