|
import { |
|
assertEquals, |
|
assertThrows, |
|
} from "https://deno.land/[email protected]/testing/asserts.ts"; |
|
import { parse } from "https://deno.land/[email protected]/flags/mod.ts"; |
|
import * as fs from "https://deno.land/[email protected]/node/fs.ts"; |
|
import * as path from "https://deno.land/[email protected]/node/path.ts"; |
|
|
|
type DateString = string; // YYYY-MM-DD |
|
|
|
function range(n: number): Array<number> { |
|
if (n < 0) throw new Error("n < 0"); |
|
return new Array(n).fill(0).map((_, i) => i); |
|
} |
|
|
|
Deno.test("range", async () => { |
|
assertThrows(() => range(-1)); |
|
assertEquals(range(0), []); |
|
assertEquals(range(5), [0, 1, 2, 3, 4]); |
|
}); |
|
|
|
function toDateString(d: Date): DateString { |
|
return d.toISOString().substring(0, "YYYY-MM-DD".length); |
|
} |
|
|
|
Deno.test("toDateString", async () => { |
|
assertEquals(toDateString(new Date("2020-05-20")), "2020-05-20"); |
|
}); |
|
|
|
function dateStringArray(today: DateString, count: number): Array<DateString> { |
|
const oneYearInMs = 365 * 24 * 60 * 60 * 1000; |
|
const d = new Date(today).getTime(); |
|
return range(count + 1).map((i) => |
|
toDateString(new Date(d - i * oneYearInMs)) |
|
); |
|
} |
|
|
|
Deno.test("dateStringArray", async () => { |
|
assertEquals(dateStringArray("2020-05-20", 0), ["2020-05-20"]); |
|
// 2020 is leap year |
|
assertEquals(dateStringArray("2020-05-20", 1), ["2020-05-20", "2019-05-21"]); |
|
assertEquals( |
|
dateStringArray("2020-05-20", 2), |
|
["2020-05-20", "2019-05-21", "2018-05-21"], |
|
); |
|
assertEquals( |
|
dateStringArray("2019-05-20", 2), |
|
["2019-05-20", "2018-05-20", "2017-05-20"], |
|
); |
|
}); |
|
|
|
function parseArgs( |
|
def: { count: number; date: DateString; dir: string }, |
|
args: string[], |
|
): { count: number; date: DateString; dir: string } { |
|
const flags = parse(args, { string: ["count", "date", "dir"] }); |
|
const countString: string = flags.count ?? String(def.count); |
|
const count = Number.parseInt(countString, 10); |
|
if (Number.isNaN(count)) throw new Error("count is invalid"); |
|
const date: string = flags.date ?? def.date; |
|
if (new Date(date).toString() === "Invalid Date") { |
|
throw new Error("date is invalid"); |
|
} |
|
const dir: string = flags.dir ?? def.dir; |
|
if (dir.length === 0) { |
|
throw new Error("dir is empty"); |
|
} |
|
return { count, date, dir }; |
|
} |
|
|
|
Deno.test("parseArgs", () => { |
|
const d = { |
|
"count": 5, |
|
"date": "2020-05-20", |
|
"dir": "/Users/bouzuya/bbn-data", |
|
}; |
|
assertEquals(parseArgs(d, []), d); |
|
assertThrows(() => parseArgs(d, ["--count"])); |
|
assertThrows(() => parseArgs(d, ["--count", "abc"])); |
|
assertEquals(parseArgs(d, ["--count", "1"]), { ...d, count: 1 }); |
|
assertThrows(() => parseArgs(d, ["--date"])); |
|
assertThrows(() => parseArgs(d, ["--date", "abc"])); |
|
assertThrows(() => parseArgs(d, ["--date", "2019-25-29"])); |
|
assertEquals( |
|
parseArgs(d, ["--date", "2020-05-21"]), |
|
{ ...d, date: "2020-05-21" }, |
|
); |
|
assertThrows(() => parseArgs(d, ["--dir"])); |
|
assertEquals(parseArgs(d, ["--dir", "dir1"]), { ...d, dir: "dir1" }); |
|
}); |
|
|
|
export function main(): void { |
|
const home = Deno.env.get("HOME"); |
|
if (typeof home === "undefined") throw new Error("HOME is not defined"); |
|
const { count, date, dir } = parseArgs( |
|
{ |
|
count: 5, |
|
date: toDateString(new Date()), |
|
dir: path.join(home, "bbn-data"), |
|
}, |
|
Deno.args, |
|
); |
|
for (const d of dateStringArray(date, count)) { |
|
const [y, m, _] = d.split("-"); |
|
const p = path.join(dir, y, m, d + ".json"); |
|
const readTitle = () => { |
|
try { |
|
const data = fs.readFileSync(p, { encoding: "utf8" }) as string; |
|
return JSON.parse(data).title; |
|
} catch (_) { |
|
return null; |
|
} |
|
}; |
|
const title = readTitle(); |
|
const url = "https://blog.bouzuya.net/" + d.split("-").join("/") + "/"; |
|
console.log(`${d} ${title}\n${url}\n`); |
|
} |
|
} |