Skip to content
File

Blob: src/worker/caldav/ical/search.ts

typescript56 lines
1import { propertyValues } from "@/worker/caldav/ical/core";
2import { parseCalendarDocument } from "@/worker/caldav/ical/parse";
3import { matchesTimeRange } from "@/worker/caldav/ical/recurrence";
4import type { CalendarSearchFilter, ParsedCalendarDocument, RecurrenceBounds } from "@/worker/caldav/ical/types";
5 
6function caseInsensitiveIncludes(value: string | null, text: string): boolean {
7 return value?.toLocaleLowerCase().includes(text.toLocaleLowerCase()) ?? false;
8}
9 
10function matchesFilter(
11 document: ParsedCalendarDocument,
12 filter: CalendarSearchFilter,
13 bounds: RecurrenceBounds,
14): boolean {
15 switch (filter.kind) {
16 case "component":
17 return document.componentType === filter.component;
18 case "uid":
19 return caseInsensitiveIncludes(document.uid, filter.text);
20 case "summary":
21 return document.components.some((component) => caseInsensitiveIncludes(component.summary, filter.text));
22 case "property-text":
23 return document.components.some((component) =>
24 propertyValues(component, filter.name).some((value) => caseInsensitiveIncludes(value, filter.text)),
25 );
26 case "property-defined":
27 return (
28 document.components.some((component) => propertyValues(component, filter.name).length > 0) === filter.defined
29 );
30 case "time-range":
31 return matchesTimeRange(document, filter.range, bounds);
32 }
33}
34 
35export function calendarObjectMatchesFilters(
36 body: string,
37 filters: CalendarSearchFilter[],
38 bounds: RecurrenceBounds,
39): boolean {
40 let document: ParsedCalendarDocument;
41 try {
42 document = parseCalendarDocument(body);
43 } catch {
44 return false;
45 }
46 return filters.every((filter) => {
47 if (filter.kind !== "time-range" || !filter.range.floatingTimeZone) return matchesFilter(document, filter, bounds);
48 try {
49 const zonedDocument = parseCalendarDocument(body, { floatingTimeZone: filter.range.floatingTimeZone });
50 return matchesFilter(zonedDocument, filter, bounds);
51 } catch {
52 return false;
53 }
54 });
55}