Compare commits

...
Sign in to create a new pull request.

4 commits

8 changed files with 280 additions and 3 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/bin

7
build.sh Executable file
View file

@ -0,0 +1,7 @@
#!/bin/sh
set -e
cd "$(dirname "$(realpath "$0")")"
mkdir -p bin
deno compile -A --unstable-kv -o bin/kvsh ./src/kvsh.ts

81
deno.lock generated
View file

@ -1,6 +1,87 @@
{ {
"version": "3", "version": "3",
"redirects": {
"https://deno.land/std/asserts/assert_equals.ts": "https://deno.land/std@0.224.0/asserts/assert_equals.ts",
"https://deno.land/std/io/write_all.ts?s=writeAll": "https://deno.land/std@0.224.0/io/write_all.ts"
},
"remote": { "remote": {
"https://deno.land/std@0.221.0/assert/assert.ts": "bec068b2fccdd434c138a555b19a2c2393b71dfaada02b7d568a01541e67cdc5",
"https://deno.land/std@0.221.0/assert/assertion_error.ts": "9f689a101ee586c4ce92f52fa7ddd362e86434ffdf1f848e45987dc7689976b8",
"https://deno.land/std@0.221.0/console/_data.json": "cf2cc9d039a192b3adbfe64627167c7e6212704c888c25c769fc8f1709e1e1b8",
"https://deno.land/std@0.221.0/console/_run_length.ts": "7da8642a0f4f41ac27c0adb1364e18886be856c1d08c5cce6c6b5c00543c8722",
"https://deno.land/std@0.221.0/console/unicode_width.ts": "d92f085c0ab9c7ab171e4e7862dfd9d3a36ffd369939be5d3e1140ec58bc820f",
"https://deno.land/std@0.221.0/fmt/colors.ts": "d239d84620b921ea520125d778947881f62c50e78deef2657073840b8af9559a",
"https://deno.land/std@0.221.0/text/closest_string.ts": "8a91ee8b6d69ff96addcb7c251dad53b476ac8be9c756a0ef786abe9e13a93a5",
"https://deno.land/std@0.221.0/text/levenshtein_distance.ts": "24be5cc88326bbba83ca7c1ea89259af0050cffda2817ff3a6d240ad6495eae2",
"https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
"https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74",
"https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
"https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47",
"https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
"https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6",
"https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2",
"https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e",
"https://deno.land/std@0.224.0/io/write_all.ts": "24aac2312bb21096ae3ae0b102b22c26164d3249dff96dbac130958aa736f038",
"https://deno.land/std@0.224.0/streams/text_line_stream.ts": "21f33d3922e019ec1a1676474beb543929cb564ec99b69cd2654e029e0f45bd5",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/_argument_types.ts": "ab269dacea2030f865a07c2a1e953ec437a64419a05bad1f1ddaab3f99752ead",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/_errors.ts": "d78e1b4d69d84b8b476b5f3c0b028e3906d48f21b8f1ca1d36d5abe9ccfe48bc",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/_spread.ts": "0cc6eb70a6df97b5d7d26008822d39f3e8a1232ee0a27f395aa19e68de738245",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/_type_utils.ts": "820004a59bc858e355b11f80e5b3ff1be2c87e66f31f53f253610170795602f0",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/_utils.ts": "fa0e88cc4215b18554a7308e8e2ae3a12be0fb91c54d1473c54c530dbd4adfcb",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/command.ts": "83cbece11c1459d5bc5add32c3cad0bf49e92c4ddd3ef00f22f80efdae30994e",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/completions/_bash_completions_generator.ts": "0c6cb1df4d378d22f001155781d97a9c3519fd10c48187a198fef2cc63b0f84a",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/completions/_fish_completions_generator.ts": "8ba4455f7f76a756e05c3db4ce35332b2951af65a2891f2750b530e06880f495",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/completions/_zsh_completions_generator.ts": "9df79fbac17a32b9645d01628c41a2bfd295d7976b87b0ae235f50a9c8975fbc",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/completions/bash.ts": "53fe78994eb2359110dc4fa79235bdd86800a38c1d6b1c4fe673c81756f3a0e2",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/completions/complete.ts": "58df61caa5e6220ff2768636a69337923ad9d4b8c1932aeb27165081c4d07d8b",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/completions/completions_command.ts": "506f97f1c6b0b1c3e9956e5069070028b818942310600d4157f64c9b644d3c49",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/completions/fish.ts": "6f0b44b4067740b2931c9ec8863b6619b1d3410fea0c5a3988525a4c53059197",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/completions/mod.ts": "8dda715ca25f3f66d5ec232b76d7c9a96dd4c64b5029feff91738cc0c9586fb1",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/completions/zsh.ts": "f1263c3946975e090d4aadc8681db811d86b52a8ae680f246e03248025885c21",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/deprecated.ts": "bbe6670f1d645b773d04b725b8b8e7814c862c9f1afba460c4d599ffe9d4983c",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/deps.ts": "a58ea2fa4e2ed9b39bb8dd8c35dd0498c74f05392517ff230a9a4d04c4c766b7",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/help/_help_generator.ts": "98619da83ff25523280a6fdcad89af3f13a6fafefc81b71f8230f3344b5ff2c5",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/help/help_command.ts": "fbbf0c0827dd21d3cec7bcc68c00c20b55f53e2b621032891b9d23ac4191231c",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/help/mod.ts": "8369b292761dcc9ddaf41f2d34bfb06fb6800b69efe80da4fc9752c3b890275b",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/mod.ts": "4b708df1b97152522bee0e3828f06abbbc1d2250168910e5cf454950d7b7404b",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/type.ts": "f588f5d9635b79100044e62aced4b00e510e75b83801f9b089c40c2d98674de2",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/types.ts": "bc9ff7459b9cc1079eeb95ff101690a51b4b4afa4af5623340076ee361d08dbb",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/types/action_list.ts": "33c98d449617c7a563a535c9ceb3741bde9f6363353fd492f90a74570c611c27",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/types/boolean.ts": "3879ec16092b4b5b1a0acb8675f8c9250c0b8a972e1e4c7adfba8335bd2263ed",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/types/child_command.ts": "f1fca390c7fbfa7a713ca15ef55c2c7656bcbb394d50e8ef54085bdf6dc22559",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/types/command.ts": "325d0382e383b725fd8d0ef34ebaeae082c5b76a1f6f2e843fee5dbb1a4fe3ac",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/types/enum.ts": "8a7cd2898e03089234083bb78c8b1d9b7172254c53c32d4710321638165a48ec",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/types/file.ts": "8618f16ac9015c8589cbd946b3de1988cc4899b90ea251f3325c93c46745140e",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/types/integer.ts": "29864725fd48738579d18123d7ee78fed37515e6dc62146c7544c98a82f1778d",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/types/number.ts": "aeba96e6f470309317a16b308c82e0e4138a830ec79c9877e4622c682012bc1f",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/types/string.ts": "e4dadb08a11795474871c7967beab954593813bb53d9f69ea5f9b734e43dc0e0",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/upgrade/_check_version.ts": "6cfa7dc26bc0dc46381500e8d4b130fb224f4c5456152dada15bd3793edca89b",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/upgrade/mod.ts": "4eff69c489467be17dea27fb95a795396111ee385d170ac0cbcc82f0ea38156c",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/upgrade/provider.ts": "c23253334097dc4b8a147ccdeb3aa44f5a95aa953a6386cb5396f830d95d77a5",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/upgrade/provider/deno_land.ts": "24f8d82e38c51e09be989f30f8ad21f9dd41ac1bb1973b443a13883e8ba06d6d",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/upgrade/provider/github.ts": "99e1b133dd446c6aa79f69e69c46eb8bc1c968dd331c2a7d4064514a317c7b59",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/upgrade/provider/nest_land.ts": "0e07936cea04fa41ac9297f32d87f39152ea873970c54cb5b4934b12fee1885e",
"https://deno.land/x/cliffy@v1.0.0-rc.4/command/upgrade/upgrade_command.ts": "27191f4b1ce93581b6d5ee2fff6003fe4fca437f476ecb98b6eae92f2b4d0716",
"https://deno.land/x/cliffy@v1.0.0-rc.4/flags/_errors.ts": "f1fbb6bfa009e7950508c9d491cfb4a5551027d9f453389606adb3f2327d048f",
"https://deno.land/x/cliffy@v1.0.0-rc.4/flags/_utils.ts": "25e519ce1f35acc8b43c75d1ca1c4ab591e7dab08327b7b408705b591e27d8bd",
"https://deno.land/x/cliffy@v1.0.0-rc.4/flags/_validate_flags.ts": "e60b9038c0136ab7e6bd1baf0e993a07bf23f18afbfb6e12c59adf665a622957",
"https://deno.land/x/cliffy@v1.0.0-rc.4/flags/deprecated.ts": "a72a35de3cc7314e5ebea605ca23d08385b218ef171c32a3f135fb4318b08126",
"https://deno.land/x/cliffy@v1.0.0-rc.4/flags/deps.ts": "bed26afff36eeb25509440edec9d5d141b3411e08cc7a90e38a370969b5166bb",
"https://deno.land/x/cliffy@v1.0.0-rc.4/flags/flags.ts": "3e62c4a9756b5705aada29e7e94847001356b3a83cd18ad56f4207387a71cf51",
"https://deno.land/x/cliffy@v1.0.0-rc.4/flags/types.ts": "9e2f75edff2217d972fc711a21676a59dfd88378da2f1ace440ea84c07db1dcc",
"https://deno.land/x/cliffy@v1.0.0-rc.4/flags/types/boolean.ts": "4c026dd66ec9c5436860dc6d0241427bdb8d8e07337ad71b33c08193428a2236",
"https://deno.land/x/cliffy@v1.0.0-rc.4/flags/types/integer.ts": "b60d4d590f309ddddf066782d43e4dc3799f0e7d08e5ede7dc62a5ee94b9a6d9",
"https://deno.land/x/cliffy@v1.0.0-rc.4/flags/types/number.ts": "610936e2d29de7c8c304b65489a75ebae17b005c6122c24e791fbed12444d51e",
"https://deno.land/x/cliffy@v1.0.0-rc.4/flags/types/string.ts": "e89b6a5ce322f65a894edecdc48b44956ec246a1d881f03e97bbda90dd8638c5",
"https://deno.land/x/cliffy@v1.0.0-rc.4/table/_layout.ts": "73a9bcb8a87b3a6817c4c9d2a31a21b874a7dd690ade1c64c9a1f066d628d626",
"https://deno.land/x/cliffy@v1.0.0-rc.4/table/_utils.ts": "13390db3f11977b7a4fc1202fa8386be14696b475a7f46a65178354f9a6640b7",
"https://deno.land/x/cliffy@v1.0.0-rc.4/table/border.ts": "5c6e9ef5078c6930169aacb668b274bdbb498461c724a7693ac9270fe9d3f5d5",
"https://deno.land/x/cliffy@v1.0.0-rc.4/table/cell.ts": "65e3ee699c3cebeb4d4d44e8f156e37a8532a0f317359d73178a95724d3f9267",
"https://deno.land/x/cliffy@v1.0.0-rc.4/table/column.ts": "cf14009f2cb14bad156f879946186c1893acdc6a2fee6845db152edddb6a2714",
"https://deno.land/x/cliffy@v1.0.0-rc.4/table/consume_words.ts": "369d065dbf7f15c664ea8523e0ef750fb952aea6d88e146c375e64aec9503052",
"https://deno.land/x/cliffy@v1.0.0-rc.4/table/deps.ts": "cbb896e8d7a6b5e3c2b9dda7d16638c202d9b46eb738c2dae1fa9480d8091486",
"https://deno.land/x/cliffy@v1.0.0-rc.4/table/row.ts": "79eb1468aafdd951e5963898cdafe0752d4ab4c519d5f847f3d8ecb8fe857d4f",
"https://deno.land/x/cliffy@v1.0.0-rc.4/table/table.ts": "298671e72e61f1ab18b42ae36643181993f79e29b39dc411fdc6ffd53aa04684",
"https://deno.land/x/zod@v3.22.4/ZodError.ts": "4de18ff525e75a0315f2c12066b77b5c2ae18c7c15ef7df7e165d63536fdf2ea", "https://deno.land/x/zod@v3.22.4/ZodError.ts": "4de18ff525e75a0315f2c12066b77b5c2ae18c7c15ef7df7e165d63536fdf2ea",
"https://deno.land/x/zod@v3.22.4/errors.ts": "5285922d2be9700cc0c70c95e4858952b07ae193aa0224be3cbd5cd5567eabef", "https://deno.land/x/zod@v3.22.4/errors.ts": "5285922d2be9700cc0c70c95e4858952b07ae193aa0224be3cbd5cd5567eabef",
"https://deno.land/x/zod@v3.22.4/external.ts": "a6cfbd61e9e097d5f42f8a7ed6f92f93f51ff927d29c9fbaec04f03cbce130fe", "https://deno.land/x/zod@v3.22.4/external.ts": "a6cfbd61e9e097d5f42f8a7ed6f92f93f51ff927d29c9fbaec04f03cbce130fe",

188
src/kvsh.ts Executable file
View file

@ -0,0 +1,188 @@
#!/usr/bin/env -S deno run -A --unstable-kv
import { TextLineStream } from "https://deno.land/std@0.224.0/streams/text_line_stream.ts";
import { writeAll } from "https://deno.land/std@0.224.0/io/write_all.ts";
import { Command } from "https://deno.land/x/cliffy@v1.0.0-rc.4/command/mod.ts";
async function main() {
const parsed = await new Command().name("kvsh").arguments("<path:string>").parse();
const [path] = parsed.args;
const kv = await Deno.openKv(path);
const context = new Context(kv);
const line_stream = Deno.stdin.readable
.pipeThrough(new TextDecoderStream())
.pipeThrough(new TextLineStream());
context.init();
for await (const line of line_stream) context.run(line);
}
class Context {
kv;
pwd;
public constructor(kv: Deno.Kv) {
this.kv = kv;
this.pwd = [] as string[];
}
public async init() {
console.log(`
kvsh shell
use 'help' for more info
`);
await this.print_prefix();
}
public async run(line: string) {
const result = await this.try_run(line);
if (result instanceof Failure) console.log("Failure :", result.message);
await this.print_prefix();
}
private async print_prefix() {
const prefix = ` /${this.pwd.join("/")} > `;
await writeAll(Deno.stdout, new TextEncoder().encode(prefix));
}
private async try_run(line: string) {
const parsed = parse_line(line);
if (parsed instanceof Failure) return parsed;
if (parsed.cmd === "help") console.log("Available commands:\nhelp cd ls cat find clear");
if (parsed.cmd === "clear") console.clear();
if (parsed.cmd === "ls") return await this.run_ls(parsed.args);
if (parsed.cmd === "cd") return this.run_cd(parsed.arg);
if (parsed.cmd === "cat") return await this.run_cat(parsed.args);
if (parsed.cmd === "find") return await this.run_find(parsed.args);
}
private async run_ls(args: string[]) {
if (args.length === 0) args.push(".");
for (const arg of args) {
if (args.length > 1) console.log(`${arg}:`);
await ls(this.kv, this.resolve(arg));
}
}
private run_cd(arg: string) {
if (arg === undefined) return new Failure("Usage: cd <path>");
const resolved = this.resolve(arg);
this.pwd = resolved;
}
private async run_cat(args: string[]) {
if (args.length < 1) return new Failure("Usage: cat <entry> [...entries]");
for (const arg of args) {
const resolved = this.resolve(arg);
const entry = await this.kv.get(resolved);
console.log(JSON.stringify(entry.value));
}
}
private async run_find(args: string[]) {
if (args.length < 1) args.push(".");
for (const arg of args) {
const prefix = this.resolve(arg);
for await (const entry of this.kv.list({ prefix })) {
const parts = entry.key.map((part) => part.toString().replaceAll('"', '\\"').replaceAll(" ", "\\ "));
console.log(`/${parts.join("/")}`);
}
}
}
private resolve(path: string) {
let base = this.pwd;
if (path.startsWith("/")) base = [], path = path.slice(1);
const acc = [] as string[];
for (const part of [...base, ...path.split("/")]) {
if (part === ".") continue;
if (part === "..") acc.pop();
else acc.push(part);
}
return acc;
}
}
async function ls(kv: Deno.Kv, prefix: string[]) {
const result_set = new Set<string>();
for await (const entry of kv.list({ prefix })) {
const name = entry.key[prefix.length].toString()
.replaceAll('"', '\\"')
.replaceAll(" ", "\\ ");
const is_file = entry.key.length === prefix.length + 1;
result_set.add(`${is_file ? "f" : "d"} ${name}`);
}
const results = Array.from(result_set.values());
const column_width = Math.max(...results.map((w) => w.length)) + 2;
const column_count = 80 / column_width;
const per_columns = (result_set.size / column_count) + 1;
const columns = [] as string[][];
for (const index of range(0, column_count)) {
columns.push(results.slice(index * per_columns, (index + 1) * per_columns));
}
for (const index of range(0, per_columns)) {
const items = columns.map((c) => c[index]).filter((item) => item != undefined);
const line = items.map((item) => item.padEnd(column_width)).join("");
if (line === "") continue;
console.log(line);
}
}
function parse_line(line: string) {
const parts = split_words(line);
if (parts === null) return new Failure("Unclosed capture.");
const [cmd, ...args] = parts, [arg] = args;
if (cmd === "help") return { cmd } as const;
if (cmd === "clear") return { cmd } as const;
if (cmd === "cd") return { cmd, arg } as const;
if (cmd === "ls") return { cmd, args } as const;
if (cmd === "cat") return { cmd, args } as const;
if (cmd === "find") return { cmd, args } as const;
else return new Failure(`Unknown command '${cmd}'`);
}
function split_words(text: string) {
const words = [] as string[];
let capturing = false;
let escaping = false;
let current = "";
const rotate_current = () => (words.push(current), current = "");
for (const character of text) {
if (escaping) {
current += character;
escaping = false;
} else if (character === "\\") escaping = true;
else if (character === '"') capturing = !capturing;
else if (character === " ") {
if (capturing) current += character;
else if (current.length > 0) rotate_current();
} else current += character;
}
if (capturing) return null;
if (current.length > 0) rotate_current();
return words;
}
Deno.test("test_split_words", async () => {
const { assertEquals } = await import("https://deno.land/std@0.224.0/assert/assert_equals.ts");
assertEquals(split_words('arbre "mort \\\\ "'), ["arbre", "mort \\ "]);
});
class Failure {
public readonly message;
public constructor(message: string) {
this.message = message;
}
}
function* range(from: number, to: number) {
while (from < to) yield from++;
}
if (import.meta.main) await main();

View file

@ -1,3 +1,3 @@
export { Collection, Entry } from "./entry.ts"; export { Collection, Entry } from "./lib/entry.ts";
export { Schema, Store } from "./store.ts"; export { Schema, Store } from "./lib/store.ts";
export type { EntryFor, Field, Property, QueriedFor, QueriedOf, Relation, Structure, Table } from "./typing.ts"; export type { EntryFor, Field, Property, QueriedFor, QueriedOf, Relation, Structure, Table } from "./lib/typing.ts";