import { propertyValues } from "@/worker/caldav/ical/core"; import { parseCalendarDocument } from "@/worker/caldav/ical/parse"; import { matchesTimeRange } from "@/worker/caldav/ical/recurrence"; import type { CalendarSearchFilter, ParsedCalendarDocument, RecurrenceBounds } from "@/worker/caldav/ical/types"; function caseInsensitiveIncludes(value: string | null, text: string): boolean { return value?.toLocaleLowerCase().includes(text.toLocaleLowerCase()) ?? false; } function matchesFilter( document: ParsedCalendarDocument, filter: CalendarSearchFilter, bounds: RecurrenceBounds, ): boolean { switch (filter.kind) { case "component": return document.componentType === filter.component; case "uid": return caseInsensitiveIncludes(document.uid, filter.text); case "summary": return document.components.some((component) => caseInsensitiveIncludes(component.summary, filter.text)); case "property-text": return document.components.some((component) => propertyValues(component, filter.name).some((value) => caseInsensitiveIncludes(value, filter.text)), ); case "property-defined": return ( document.components.some((component) => propertyValues(component, filter.name).length > 0) === filter.defined ); case "time-range": return matchesTimeRange(document, filter.range, bounds); } } export function calendarObjectMatchesFilters( body: string, filters: CalendarSearchFilter[], bounds: RecurrenceBounds, ): boolean { let document: ParsedCalendarDocument; try { document = parseCalendarDocument(body); } catch { return false; } return filters.every((filter) => { if (filter.kind !== "time-range" || !filter.range.floatingTimeZone) return matchesFilter(document, filter, bounds); try { const zonedDocument = parseCalendarDocument(body, { floatingTimeZone: filter.range.floatingTimeZone }); return matchesFilter(zonedDocument, filter, bounds); } catch { return false; } }); }