103 lines
2.4 KiB
TypeScript
103 lines
2.4 KiB
TypeScript
import * as yaml from "js-yaml";
|
|
import { err, ok, Result } from "neverthrow";
|
|
import { appError, AppError, AppErrorVariant } from "@/error";
|
|
import {
|
|
EMPTY_PROJECT_CONFIG,
|
|
normalizeTag,
|
|
type ProjectConfig,
|
|
} from "@/model/projectConfig";
|
|
import { isTaskStatus } from "@/model/taskStatus";
|
|
|
|
function parseShownHiddenColumns(
|
|
value: unknown,
|
|
): Result<string[], AppError> {
|
|
if (value === undefined) return ok([]);
|
|
if (!Array.isArray(value)) {
|
|
return err(
|
|
appError(AppErrorVariant.INVALID_PROJECT_CONFIG, {
|
|
detail: "shownHiddenColumns must be an array of strings",
|
|
}),
|
|
);
|
|
}
|
|
for (const item of value) {
|
|
if (typeof item !== "string" || !isTaskStatus(item)) {
|
|
return err(
|
|
appError(AppErrorVariant.INVALID_PROJECT_CONFIG, {
|
|
detail: `invalid status in shownHiddenColumns: ${item}`,
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
return ok(value as string[]);
|
|
}
|
|
|
|
export function parseProjectConfig(
|
|
content: string,
|
|
): Result<ProjectConfig, AppError> {
|
|
if (content.trim() === "") {
|
|
return ok({ ...EMPTY_PROJECT_CONFIG });
|
|
}
|
|
|
|
let data: unknown;
|
|
try {
|
|
data = yaml.load(content);
|
|
} catch (error) {
|
|
const detail = error instanceof Error ? error.message : "invalid YAML";
|
|
return err(appError(AppErrorVariant.INVALID_PROJECT_CONFIG, { detail }));
|
|
}
|
|
|
|
if (data === null || data === undefined) {
|
|
return ok({ ...EMPTY_PROJECT_CONFIG });
|
|
}
|
|
|
|
if (typeof data !== "object" || Array.isArray(data)) {
|
|
return err(
|
|
appError(AppErrorVariant.INVALID_PROJECT_CONFIG, {
|
|
detail: "root must be a mapping",
|
|
}),
|
|
);
|
|
}
|
|
|
|
const record = data as Record<string, unknown>;
|
|
|
|
let tags: string[];
|
|
if (record.tags === undefined) {
|
|
tags = [];
|
|
} else {
|
|
if (!Array.isArray(record.tags)) {
|
|
return err(
|
|
appError(AppErrorVariant.INVALID_PROJECT_CONFIG, {
|
|
detail: "tags must be an array of strings",
|
|
}),
|
|
);
|
|
}
|
|
|
|
tags = [];
|
|
const seen = new Set<string>();
|
|
for (const item of record.tags) {
|
|
if (typeof item !== "string") {
|
|
return err(
|
|
appError(AppErrorVariant.INVALID_PROJECT_CONFIG, {
|
|
detail: "tags must be an array of strings",
|
|
}),
|
|
);
|
|
}
|
|
const tag = normalizeTag(item);
|
|
if (!tag || seen.has(tag)) continue;
|
|
seen.add(tag);
|
|
tags.push(tag);
|
|
}
|
|
}
|
|
|
|
const shownHiddenColumnsResult = parseShownHiddenColumns(
|
|
record.shownHiddenColumns,
|
|
);
|
|
if (shownHiddenColumnsResult.isErr())
|
|
return err(shownHiddenColumnsResult.error);
|
|
|
|
return ok({
|
|
tags,
|
|
shownHiddenColumns: shownHiddenColumnsResult.value,
|
|
});
|
|
}
|