Modulo:ChronoSpine/entry
Vai alla navigazione
Vai alla ricerca
La documentazione per questo modulo può essere creata in Modulo:ChronoSpine/entry/man
--[[
Module:ChronoSpine/entry
=========================
Purpose
-------
Defines the *normalised entry* -- the single, fixed-shape Lua table
that every ChronoSpine data-source adapter (manual, SMW, Wikibase, ...)
must produce. This is the "central contract" of the whole pipeline
(project briefing, section 4.3): whatever the entry's origin, once it
has passed through an adapter it looks exactly the same to everything
downstream (sorting, grouping, rendering).
What lives here
----------------
* M.new(fields) -- builds one entry, filling in defaults.
* M.compare(a, b) -- the sort comparator used by later pipeline
stages (Step 3) to order entries on the spine.
What does NOT live here
------------------------
This module never parses raw wikitext strings (that is
Module:ChronoSpine/date and Module:ChronoSpine/stardate), and it
never refers to any specific wiki, data source, or language. It is
pure data-shape logic, usable unchanged by any future adapter.
--]]
local M = {}
--[[
M.new(fields)
-------------
Builds one normalised entry table from a plain Lua table of fields.
Every field described in the contract (briefing sec. 4.3) is present
in the returned entry, even when the caller did not supply it: missing
values become `nil`, except for the two boolean flags `circa` and
`incerta`, which default to `false`. That way calling code can always
write `if entry.circa then ... end` without first checking whether the
field exists at all.
Parameters:
fields (table|nil) -- may contain any of the fields below. Anything
else in `fields` is ignored (this function is a strict filter, not
a raw copy), so a typo in a caller's field name fails silently by
being dropped rather than polluting the entry with an unknown key.
anno (number|nil) -- year, e.g. 2257. `nil` only when the
whole date is uncertain (`incerta`).
mese (number|nil) -- month, 1-12.
giorno (number|nil) -- day, 1-31.
circa (boolean|nil) -- true for an approximate date ("c. ...").
incerta (boolean|nil) -- true for a wholly unknown date ("?").
fine (table|nil) -- end of a range, e.g. "2256-2257"; same
shape as a plain date
(anno/mese/giorno/circa), no `incerta`.
stellare (number|nil) -- stardate; an in-year tie-breaker only,
never a substitute for anno/mese/giorno.
voce (string|nil) -- the entry's label (wikitext allowed).
fonte (string|nil) -- optional source/citation (wikitext).
nota (string|nil) -- optional short note under the label.
gruppo (string|nil) -- optional explicit group override.
categoria (string|nil) -- optional icon/colour category key.
stato (string|nil) -- optional status, e.g. "annunciato".
ordine (number|nil) -- input position; the final, stable
tie-breaker when everything else ties.
Returns:
(table) -- the normalised entry, with every documented field present
(as a real key, possibly holding `nil` -- note: Lua does
not actually store keys whose value is `nil`, so
"present" here means "safe to read", not "shows up in
`pairs()`").
--]]
function M.new(fields)
fields = fields or {}
return {
anno = fields.anno,
mese = fields.mese,
giorno = fields.giorno,
circa = fields.circa or false,
incerta = fields.incerta or false,
fine = fields.fine,
stellare = fields.stellare,
voce = fields.voce,
fonte = fields.fonte,
nota = fields.nota,
gruppo = fields.gruppo,
categoria = fields.categoria,
stato = fields.stato,
ordine = fields.ordine,
}
end
--[[
compare_dates_only(a, b)
-------------------------
Private helper. Compares two date-shaped tables (anything with
anno/mese/giorno fields -- this works equally for full entries and for
the plain date tables produced by Module:ChronoSpine/date) using only
those three fields, treating a missing month or day as "before" any
specified value.
This matches how a partial date is naturally read: "2022" means
"sometime in 2022", which comes before "2022-05" ("sometime in May
2022"), which comes before a fully-specified "2022-05-05".
Note: this logic is intentionally duplicated in
Module:ChronoSpine/date (as its own private helper, used there to
validate that a range's end is not earlier than its start). The two
copies are small and conceptually belong to different concerns --
"how do full entries sort" here, "is this range well-formed" there --
so we are not yet introducing a shared /util submodule for four lines
of code. This can be revisited if a third caller appears.
Returns:
-1 if `a` sorts before `b`
1 if `a` sorts after `b`
0 if they are equal at this level (the caller must break the tie
using some other field, e.g. stardate or input order)
--]]
local function compare_dates_only(a, b)
local a_anno, b_anno = a.anno or 0, b.anno or 0
if a_anno ~= b_anno then
return (a_anno < b_anno) and -1 or 1
end
local a_mese, b_mese = a.mese or 0, b.mese or 0
if a_mese ~= b_mese then
return (a_mese < b_mese) and -1 or 1
end
local a_giorno, b_giorno = a.giorno or 0, b.giorno or 0
if a_giorno ~= b_giorno then
return (a_giorno < b_giorno) and -1 or 1
end
return 0
end
--[[
M.compare(a, b)
----------------
The sort comparator for normalised entries. Implements the sort key
from the briefing (sec. 4.3):
year -> month -> day -> stardate -> input order
with all `incerta` ("?", wholly uncertain) entries placed together as
a final block, in their own input order, per section 6.2.
Meant to be passed straight to Lua's `table.sort`:
table.sort(entries, M.compare)
Parameters:
a, b (table) -- two normalised entries, as produced by M.new.
Returns:
(boolean) -- true if `a` must sort strictly before `b`.
--]]
function M.compare(a, b)
-- Rule 1: uncertain entries form their own trailing block.
-- Within that block there is no date to compare by, so they keep
-- only their relative input order.
if a.incerta ~= b.incerta then
-- Exactly one of the two is uncertain: whichever one is NOT
-- uncertain sorts first. `b.incerta` is true exactly when
-- `b` is the uncertain one, which is exactly when `a` (the
-- certain one) must come first -- so this doubles as the
-- return value we want.
return b.incerta
end
if a.incerta then
-- Both uncertain: same block, order preserved by `ordine`.
return (a.ordine or 0) < (b.ordine or 0)
end
-- Rule 2: year -> month -> day.
local date_cmp = compare_dates_only(a, b)
if date_cmp ~= 0 then
return date_cmp < 0
end
-- Rule 3: stardate, strictly as a tie-breaker. We only ever reach
-- this point when year/month/day are already equal, which is
-- exactly what the briefing means by "stardate is a tie-breaker
-- within a year, never a substitute for the date". If either
-- entry has no stardate at all, this level contributes nothing
-- and we fall through to input order -- we do not invent an
-- ordering for "has a stardate" vs. "does not".
if a.stellare ~= nil and b.stellare ~= nil and a.stellare ~= b.stellare then
return a.stellare < b.stellare
end
-- Rule 4: final, stable tie-break on input order.
return (a.ordine or 0) < (b.ordine or 0)
end
return M