/**
* GET /errors・GET /clean-logs・GET /clean-logs/export の `schedule` 絞り込みの検証と照合。
*
* ★`lib/api/master-id.ts` と同じ置き場・同じ書き方の純粋関数★(指示書BH §12-2)。
*/
import { fail, type QueryParseResult } from "@/lib/api/query-result";
import type { ScheduleFilter } from "@/lib/types";
/** 受け付ける値(★この順★ — details の文言に使う) */
export const SCHEDULE_FILTERS = ["scheduled", "unscheduled"] as const;
/**
* `schedule` クエリパラメータを検証する。
*
* | 入力 | 結果 |
* |---|---|
* | 省略・空文字・空白だけ | `null`(すべて) |
* | `scheduled` / `unscheduled` | その値 |
* | それ以外 | 422(★入力値をエコーしない★) |
*/
export function parseScheduleFilter(
searchParams: URLSearchParams
): QueryParseResult<ScheduleFilter | null> {
const raw = searchParams.get("schedule")?.trim() || null;
if (raw === null) {
return { ok: true, query: null };
}
if (!SCHEDULE_FILTERS.includes(raw as ScheduleFilter)) {
return fail(422, "validation_failed", "scheduleの値が不正です", {
schedule: [`${SCHEDULE_FILTERS.join(" / ")} のいずれかを指定してください`],
});
}
return { ok: true, query: raw as ScheduleFilter };
}
/**
* 予定あり/予定外の絞り込みに行が一致するか。
*
* ★引数 `isScheduled` は「予定あり=true」で固定★ — エラー一覧は `key.scheduled`、
* 清掃詳細ログは `!r.unscheduled` を渡す(指示書BH §12-5 罠9)。
*/
export function matchesScheduleFilter(
isScheduled: boolean,
filter: ScheduleFilter | null
): boolean {
if (filter === null) return true;
switch (filter) {
case "scheduled":
return isScheduled;
case "unscheduled":
return !isScheduled;
default: {
const _exhaustive: never = filter;
return _exhaustive;
}
}
}