import { CalendarConfig, InclusionMode } from "./types"; const VIEW_ALIASES: Record = { day: "day", d: "day", week: "week", w: "week", month: "month", m: "month", }; const INCLUSION_MODES: InclusionMode[] = ["gtd-only", "all", "none"]; /** * Normalise a parsed gtd-calendar YAML object into a CalendarConfig, * applying SPEC.md defaults. Unknown keys and invalid values produce * warnings rather than failures. */ export default function parseCalendarConfig(raw: any): CalendarConfig { const warnings: string[] = []; const input = raw && typeof raw === "object" ? raw : {}; const knownKeys = [ "view", "title", "scope", "notes", "todos", "sort", "sort-type", ]; for (const key of Object.keys(input)) { if (!knownKeys.includes(key)) warnings.push(`Unknown option "${key}"`); } // view let view: CalendarConfig["view"] = "day"; if (input.view !== undefined) { const candidate = String(input.view).toLowerCase(); if (VIEW_ALIASES[candidate]) { view = VIEW_ALIASES[candidate]; } else { warnings.push(`Invalid view "${input.view}" (using "day")`); } } // title const title = input.title !== undefined && input.title !== null ? String(input.title) : null; // scope let scopeDepth = 0; if (input.scope !== undefined) { const value = input.scope; if (value === "this-folder") { scopeDepth = 0; } else if (value === "children") { scopeDepth = Infinity; } else if (Number.isInteger(Number(value)) && Number(value) >= 0) { scopeDepth = Number(value); } else { warnings.push(`Invalid scope "${value}" (using "this-folder")`); } } // notes / todos const notes = parseInclusion(input.notes, "notes", warnings); const todos = parseInclusion(input.todos, "todos", warnings); // sort let sort: CalendarConfig["sort"] = "asc"; if (input.sort !== undefined) { const candidate = String(input.sort).toLowerCase(); if (candidate === "asc" || candidate === "desc") { sort = candidate; } else { warnings.push(`Invalid sort "${input.sort}" (using "asc")`); } } // sort-type let sortType: CalendarConfig["sortType"] = "title"; if (input["sort-type"] !== undefined) { const candidate = String(input["sort-type"]).toLowerCase(); if (candidate === "title" || candidate === "modified_date") { sortType = candidate as CalendarConfig["sortType"]; } else { warnings.push( `Invalid sort-type "${input["sort-type"]}" (using "title")` ); } } return { view, title, scopeDepth, notes, todos, sort, sortType, warnings }; } function parseInclusion( value: any, optionName: string, warnings: string[] ): InclusionMode { if (value === undefined) return "gtd-only"; const candidate = String(value).toLowerCase() as InclusionMode; if (INCLUSION_MODES.includes(candidate)) return candidate; warnings.push(`Invalid ${optionName} "${value}" (using "gtd-only")`); return "gtd-only"; }