Compare commits

...

3 commits

Author SHA1 Message Date
soup 6e7d7999da
[porthole] init 2025-04-12 00:02:45 -04:00
soup 0668e96496
. 2025-04-12 00:02:40 -04:00
soup 142129d6eb
. 2025-02-14 21:28:56 -05:00
34 changed files with 1556 additions and 94 deletions

View file

@ -2,11 +2,11 @@
"nodes": {
"crane": {
"locked": {
"lastModified": 1734541973,
"narHash": "sha256-1wIgLmhvtfxbJVnhFHUYhPqL3gpLn5JhiS4maaD9RRk=",
"lastModified": 1744386647,
"narHash": "sha256-DXwQEJllxpYeVOiSlBhQuGjfvkoGHTtILLYO2FvcyzQ=",
"owner": "ipetkov",
"repo": "crane",
"rev": "fdd502f921936105869eba53db6593fc2a424c16",
"rev": "d02c1cdd7ec539699aa44e6ff912e15535969803",
"type": "github"
},
"original": {
@ -41,11 +41,11 @@
"rust-analyzer-src": "rust-analyzer-src"
},
"locked": {
"lastModified": 1734676450,
"narHash": "sha256-iwcxhTVe4h5TqW0HsNiOQP27eMBmbBshF+q2UjEy5aU=",
"lastModified": 1744231114,
"narHash": "sha256-60gLl2rJFt6SRwqWimsTAeHgfsIE1iV0zChdJFOvx8w=",
"owner": "nix-community",
"repo": "fenix",
"rev": "46e19fa0eb3260b2c3ee5b2cf89e73343c1296ab",
"rev": "0ccfe532b1433da8e5a23cd513ff6847e0f6a8c2",
"type": "github"
},
"original": {
@ -92,10 +92,10 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1733550349,
"narHash": "sha256-NcGumB4Lr6KSDq+nIqXtNA8QwAQKDSZT7N9OTGWbTrs=",
"path": "/nix/store/sqmn1ky3k66661h32djyjvsr8l99330z-source",
"rev": "e2605d0744c2417b09f8bf850dfca42fcf537d34",
"lastModified": 1741048562,
"narHash": "sha256-W4YZ3fvWZiFYYyd900kh8P8wU6DHSiwaH0j4+fai1Sk=",
"path": "/nix/store/cdjqlnn7kx4hfmxkry9yjfdvqp2pradh-source",
"rev": "6af28b834daca767a7ef99f8a7defa957d0ade6f",
"type": "path"
},
"original": {
@ -115,11 +115,11 @@
"rust-analyzer-src": {
"flake": false,
"locked": {
"lastModified": 1734622712,
"narHash": "sha256-2Oc2LbFypF1EG3zTVIVcuT5XFJ7R3oAwu2tS8B0qQ0I=",
"lastModified": 1742296961,
"narHash": "sha256-gCpvEQOrugHWLimD1wTFOJHagnSEP6VYBDspq96Idu0=",
"owner": "rust-lang",
"repo": "rust-analyzer",
"rev": "fe027d79d22f2a7645da4143f5cc0f5f56239b97",
"rev": "15d87419f1a123d8f888d608129c3ce3ff8f13d4",
"type": "github"
},
"original": {

273
llmc/Cargo.lock generated Normal file
View file

@ -0,0 +1,273 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [
"memchr",
]
[[package]]
name = "atelier"
version = "0.0.0"
[[package]]
name = "bstr"
version = "1.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
dependencies = [
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[package]]
name = "globset"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "ignore"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
dependencies = [
"crossbeam-deque",
"globset",
"log",
"memchr",
"regex-automata",
"same-file",
"walkdir",
"winapi-util",
]
[[package]]
name = "llmc"
version = "0.0.0"
dependencies = [
"atelier",
"ignore",
]
[[package]]
name = "log"
version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "proc-macro2"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "serde"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.217"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "2.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034"
[[package]]
name = "walkdir"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "winapi-util"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

8
llmc/Cargo.toml Normal file
View file

@ -0,0 +1,8 @@
[package]
name = "llmc"
version = "0.0.0"
edition = "2024"
[dependencies]
atelier = { path = "../rs" }
ignore = "0.4.23"

103
llmc/src/main.rs Normal file
View file

@ -0,0 +1,103 @@
use atelier::preludes::fs::*;
use atelier::preludes::io::*;
static USAGE: &str = r#"
llmc - Collect code in a directory into a single text output, for feeding into an LLM
USAGE:
llmc [path] [options]...
OPTIONS:
-i, --input-exts <ext,...> The file extensions to include in the output
"#;
fn find_git_root(start: impl AsRef<Path>) -> IoResult<Option<PathBuf>> {
let mut current = Some(start.as_ref().to_path_buf().canonicalize()?);
while let Some(c) = current {
if c.join(".git").exists() {
return Ok(Some(c.to_path_buf().canonicalize()?));
} else {
current = c.parent().map(|o| o.to_path_buf());
}
}
Ok(None)
}
fn walk_dir(path: impl AsRef<Path>, included_exts: &[&str]) -> IoResult<()> {
let mut types = ignore::types::TypesBuilder::new();
for ext in included_exts.iter() {
types.add(&ext[1..], &format!("*{ext}")).unwrap();
}
types.select("all");
let types = types.build().unwrap();
let mut builder = if let Some(root) = find_git_root(path.as_ref())? {
ignore::WalkBuilder::new(root)
} else {
ignore::WalkBuilder::new(path)
};
builder.types(types);
let walk = builder.build();
for result in walk {
let entry = result.unwrap();
let ft = entry.file_type().unwrap();
if ft.is_dir() {
continue;
}
let path = entry.path().as_os_str().to_string_lossy();
println!("##### Contents of {} #####", path);
println!("{}", std::fs::read_to_string(path.as_ref())?);
}
Ok(())
}
fn main() -> std::io::Result<()> {
let mut included_exts = vec![];
let args: Vec<_> = std::env::args().skip(1).collect();
let mut args = args.iter().map(|s| s.as_str());
let mut params = vec![];
let mut fail = false;
while !fail {
let arg = match args.next() {
None => break,
Some(v) => v,
};
if arg.starts_with('-') {
match arg {
"-h" | "--help" => {
println!("{USAGE}");
return Ok(());
},
"-i" | "--include-exts" => {
fail = args
.next()
.map(|a| included_exts.extend(a.split(',')))
.is_none()
},
_ => (),
}
} else {
params.push(arg);
}
}
if fail {
eprintln!("{USAGE}");
return Ok(());
}
let dir = params.get(0).copied().unwrap_or(".");
walk_dir(dir, &included_exts)?;
Ok(())
}

402
porthole/Cargo.lock generated Normal file
View file

@ -0,0 +1,402 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "arrayref"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "base64"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "bitflags"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
[[package]]
name = "blake2b_simd"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06e903a20b159e944f91ec8499fe1e55651480c541ea0a584f5d967c49ad9d99"
dependencies = [
"arrayref",
"arrayvec",
"constant_time_eq",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "constant_time_eq"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "getrandom"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi",
]
[[package]]
name = "hashbrown"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
[[package]]
name = "indexmap"
version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]]
name = "libc"
version = "0.2.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "porthole"
version = "0.0.0"
dependencies = [
"rand",
"rpassword",
"rust-argon2",
"serde",
"toml",
"xdg",
]
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "proc-macro2"
version = "1.0.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
[[package]]
name = "rand"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94"
dependencies = [
"rand_chacha",
"rand_core",
"zerocopy",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
dependencies = [
"getrandom",
]
[[package]]
name = "rpassword"
version = "7.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80472be3c897911d0137b2d2b9055faf6eeac5b14e324073d83bc17b191d7e3f"
dependencies = [
"libc",
"rtoolbox",
"windows-sys",
]
[[package]]
name = "rtoolbox"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c247d24e63230cdb56463ae328478bd5eac8b8faa8c69461a77e8e323afac90e"
dependencies = [
"libc",
"windows-sys",
]
[[package]]
name = "rust-argon2"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d9848531d60c9cbbcf9d166c885316c24bc0e2a9d3eba0956bb6cbbd79bc6e8"
dependencies = [
"base64",
"blake2b_simd",
"constant_time_eq",
]
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_spanned"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1"
dependencies = [
"serde",
]
[[package]]
name = "syn"
version = "2.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "toml"
version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "wasi"
version = "0.14.2+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
dependencies = [
"wit-bindgen-rt",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "winnow"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen-rt"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
dependencies = [
"bitflags",
]
[[package]]
name = "xdg"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546"
[[package]]
name = "zerocopy"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

12
porthole/Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "porthole"
version = "0.0.0"
edition = "2024"
[dependencies]
rpassword = "7.3.1"
xdg = "2.5.2"
rust-argon2 = "2.1"
rand = "0.9.0"
serde = { version = "1.0.219", features = ["derive"] }
toml = "0.8.20"

18
porthole/src/main.rs Normal file
View file

@ -0,0 +1,18 @@
pub mod prelude;
pub mod server;
pub mod utils;
fn main() {
let args: Vec<_> = std::env::args().skip(1).collect();
let args: Vec<_> = args.iter().map(String::as_str).collect();
match args.as_slice() {
&["configure", "server"] => server::configure(),
&["configure", "client"] => (),
&["start", "server"] => (),
&["start", "client", local_port] => (),
_ => {
println!("porthole <command>");
},
};
}

7
porthole/src/prelude.rs Normal file
View file

@ -0,0 +1,7 @@
pub use crate::{server, utils};
pub use core::fmt::Debug;
pub use core::str::FromStr;
pub use rand::prelude::*;
pub use serde::{Deserialize, Serialize};
pub use std::io::{BufRead, Read, Result as IoResult, Write};
pub use std::path::PathBuf;

27
porthole/src/server.rs Normal file
View file

@ -0,0 +1,27 @@
use crate::prelude::*;
#[derive(Serialize)]
pub struct Config {
password_hash: String,
password_salt: String,
port: u16,
}
pub fn configure() {
let port = utils::prompt("Port: ").unwrap();
let password = utils::read_matching_passwords().unwrap();
let (password_hash, password_salt) = utils::hash_password(&password);
let config = Config {
password_hash,
password_salt,
port,
};
let out_path = utils::get_config_file_path("server.toml").unwrap();
let data = toml::to_string_pretty(&config).unwrap();
std::fs::write(&out_path, data).unwrap();
println!("\nWrote config file to {}", out_path.display());
}

71
porthole/src/utils.rs Normal file
View file

@ -0,0 +1,71 @@
use crate::prelude::*;
pub fn get_config_file_path(name: &str) -> IoResult<PathBuf> {
let bd = xdg::BaseDirectories::new()?;
bd.place_config_file(format!("porthole/{name}"))
}
pub fn read_password(prompt: &str) -> IoResult<String> {
let mut stdin = std::io::stdin().lock();
let mut password = rpassword::prompt_password(prompt)?;
Ok(password)
}
pub fn read_matching_passwords() -> IoResult<String> {
loop {
let password = read_password("Password: ").unwrap();
let confirm_password = read_password("Confirm password: ").unwrap();
if password == confirm_password {
return Ok(password);
} else {
println!("Passwords did not match")
}
}
}
pub fn prompt<T: std::str::FromStr>(prompt: &str) -> IoResult<T>
where
<T as FromStr>::Err: Debug,
{
let mut stdin = std::io::stdin().lock();
loop {
print!("{}", prompt);
std::io::stdout().flush();
let mut s = String::new();
stdin.read_line(&mut s)?;
let parsed = match s.trim().parse() {
Ok(v) => v,
Err(e) => {
println!("{e:?}");
continue;
},
};
return Ok(parsed);
}
}
pub fn random_string(len: usize) -> String {
use rand::Rng;
rand::rng()
.sample_iter(&rand::distr::Alphanumeric)
.take(len)
.map(char::from)
.collect()
}
pub fn hash_password(password: &str) -> (String /* Hash */, String /* Salt */) {
let config = argon2::Config::default();
let salt = random_string(16);
let hash =
argon2::hash_encoded(password.as_bytes(), salt.as_bytes(), &config)
.unwrap();
(hash, salt)
}

60
pritty/colors/eink.toml Normal file
View file

@ -0,0 +1,60 @@
name = "eink"
[palette]
gray-100 = "#d6d6d6"
gray-700 = "#474747"
gray-800 = "#333333"
[dark.backgrounds]
primary = "@gray-800"
selection = "@gray-100"
status-bar-active = "@gray-100"
status-bar-inactive = "@gray-700"
cursor = "@gray-100"
[dark.text]
primary = "@gray-100"
selection = "@gray-800"
status-bar-active = "@gray-800"
[dark.syntax]
operator = "@gray-100"
import = "@gray-100"
function = "@gray-100"
constant = "@gray-100"
keyword = "@gray-100"
string = "@gray-100"
identifier = "@gray-100"
number = "@gray-100"
comment = "@gray-100"
macro = "@gray-100"
other = "@gray-100"
[light.backgrounds]
primary = "@gray-100"
selection = "@gray-800"
status-bar-active = "@gray-800"
status-bar-inactive = "@gray-700"
cursor = "@gray-800"
[light.text]
primary = "@gray-800"
selection = "@gray-100"
status-bar-active = "@gray-100"
[light.syntax]
operator = "@gray-800"
import = "@gray-800"
function = "@gray-800"
constant = "@gray-800"
keyword = "@gray-800"
string = "@gray-800"
identifier = "@gray-800"
number = "@gray-800"
comment = "@gray-800"
macro = "@gray-800"
other = "@gray-800"

View file

@ -223,12 +223,14 @@ async function generateEmacs(
background: get(tables['backgrounds']?.['primary']),
});
f('mode-line-active', {
foreground: get(tables['text']?.['primary']),
foreground: get(tables['text']?.['status-bar-active'] ?? tables['text']?.['primary']),
background: get(tables['backgrounds']?.['status-bar-active']),
box: true,
});
f('mode-line-inactive', {
foreground: get(tables['text']?.['primary']),
foreground: get(
tables['text']?.['status-bar-inactive'] ?? tables['text']?.['primary'],
),
background: get(tables['backgrounds']?.['status-bar-inactive']),
box: true,
});

View file

@ -3,8 +3,18 @@ name = "atelier"
version = "0.0.0"
edition = "2024"
[workspace]
resolver = "2"
members = [".", "newt"]
[dependencies]
rusqlite = { version = "*" }
async-channel = { version = "*" }
urlpattern = { version = "*" }
url = { version = "*" }
rusqlite = { version = "*", optional = true }
async-channel = { version = "*", optional = true }
urlpattern = { version = "*", optional = true }
url = { version = "*", optional = true }
newt = { path = "./newt", optional = true }
[features]
full = ["web"]
web = ["dep:rusqlite", "dep:async-channel", "dep:urlpattern", "dep:url", "dep:newt"]

7
rs/newt/Cargo.lock generated Normal file
View file

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "newt"
version = "0.0.0"

18
rs/newt/Cargo.toml Normal file
View file

@ -0,0 +1,18 @@
[package]
name = "newt"
[workspace]
resolver = "2"
# members = ["derive", "derive/core"]
[workspace.dependencies]
proc-macro-error = "1.0"
proc-macro2 = "1"
syn = { version = "2", features = ["full"] }
quote = "1"
[dependencies]
# newt-derive = { path = "derive", optional = true }
[features]
# derive = ["dep:newt-derive"]

View file

@ -0,0 +1,22 @@
use crate::prelude_internal::*;
impl Value for () {
}
impl Value for &str {
fn render(&self, fmt: &mut dyn Write) -> FmtResult {
write!(fmt, "{self}")
}
}
impl<const N: usize> Value for [(&str, &dyn Value); N] {
fn lookup(&self, name: &str) -> Option<&dyn Value> {
for (k, v) in self.iter() {
if *k == name {
return Some(*v);
}
}
None
}
}

90
rs/newt/src/lib.rs Normal file
View file

@ -0,0 +1,90 @@
mod builtin_impls;
pub mod parse;
pub mod prelude;
mod prelude_internal;
use crate::prelude_internal::*;
pub trait Value {
fn render(&self, w: &mut dyn Write) -> FmtResult {
Ok(())
}
fn lookup(&self, name: &str) -> Option<&dyn Value> {
None
}
}
pub struct Template {
commands: Vec<Command>,
}
impl Template {
pub fn render(&self, env: &dyn Value, target: &mut dyn Write) {
for command in self.commands.iter() {
match command {
Command::Print(s) => {
let _ = write!(target, "{s}");
},
Command::LookupPrint(s) => {
if let Some(value) = env.lookup(s) {
value.render(target);
} else {
let _ = write!(target, "<<<'{s}' not found>>>");
}
},
}
}
}
}
pub enum Command {
Print(String),
LookupPrint(String),
}
pub fn parse(input: &str) -> Template {
let mut state = parse::ParseState::new(input);
let mut commands = vec![];
state.parse_root(&mut commands);
Template { commands }
}
pub fn render(input: &str, env: &dyn Value) -> String {
let template = parse(input);
let mut output = String::new();
template.render(env, &mut output);
output
}
#[macro_export]
macro_rules! env {
($($name:literal : $value:expr),* $(,)?) => {
&[$(($name, &$value as &dyn crate::Value),)*]
};
}
#[cfg(test)]
mod test {
macro_rules! go {
(($input:expr, $env:expr), $output:expr) => {
assert_eq!(crate::render($input, $env), $output)
};
}
#[test]
fn simple() {
go!(("Hello, World!", &()), "Hello, World!");
}
#[test]
fn lookup() {
go!(
("Hello, {name}!", env! { "name": "Steve" }),
"Hello, Steve!"
);
}
}

78
rs/newt/src/parse.rs Normal file
View file

@ -0,0 +1,78 @@
use crate::prelude_internal::*;
pub struct ParseState<'a> {
input: &'a str,
at: usize,
}
impl<'a> ParseState<'a> {
pub fn new(input: &'a str) -> Self {
Self { input, at: 0 }
}
pub fn is_empty(&self) -> bool {
self.head().is_empty()
}
pub fn head(&self) -> &str {
&self.input[self.at..]
}
pub fn goto(&mut self, at: usize) {
self.at = at;
}
pub fn skip(&mut self, amt: usize) {
self.at += amt;
}
pub fn back(&mut self, amt: usize) {
self.at -= amt;
}
pub fn parse_root(&mut self, out: &mut Vec<Command>) {
while !self.is_empty() {
let head = self.head();
if let Some(bo) = head.find('{') {
if bo != 0 {
out.push(Command::Print(head[0..bo].to_string()));
}
self.goto(bo);
self.parse_directive(out);
} else {
out.push(Command::Print(head.to_string()));
return;
}
}
}
pub fn parse_directive(&mut self, out: &mut Vec<Command>) {
assert!(self.head().starts_with('{'));
let mut ok = Err(self.at);
'inner: {
self.skip(1);
let head = self.head();
if let Some(bo) = head.find('}') {
let inner = &head[..bo];
let bo = bo + 1;
let mut inner = inner.split(' ').collect::<Vec<_>>();
match inner.as_slice() {
&[name] => {
out.push(Command::LookupPrint(name.to_string()));
ok = Ok(bo);
},
_ => (),
}
} else {
}
};
match ok {
Err(checkpoint) => self.goto(checkpoint),
Ok(skip) => self.skip(skip),
}
}
}

1
rs/newt/src/prelude.rs Normal file
View file

@ -0,0 +1 @@
pub use crate::{Command, Template, Value};

View file

@ -0,0 +1,3 @@
pub use crate::prelude::*;
pub use std::fmt::{Display, Formatter, Result as FmtResult, Write};

1
rs/src/flags.rs Normal file
View file

@ -0,0 +1 @@

View file

@ -1,18 +1,21 @@
pub mod containers;
pub mod flags;
pub mod prelude;
#[cfg(feature = "web")]
pub mod router;
#[cfg(feature = "web")]
pub mod rusqlite_thread_pool;
pub fn add(left: u64, right: u64) -> u64 {
left + right
}
pub mod preludes {
pub mod fmt {
pub use core::fmt::{Display, Formatter, Result as FmtResult};
}
#[cfg(test)]
mod tests {
use super::*;
pub mod io {
pub use std::io::Result as IoResult;
}
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
pub mod fs {
pub use std::path::{Path, PathBuf};
}
}

1
rs/src/prelude.rs Normal file
View file

@ -0,0 +1 @@
pub use core::fmt::{Display, Formatter, Result as FmtResult};

View file

@ -37,7 +37,10 @@ impl<T> Router<T> {
}
}
pub fn register(&mut self, method: String, pathname: String, value: T) {
pub fn register(&mut self, method: &str, pathname: &str, value: T) {
let pathname = pathname.to_string();
let method = method.to_string();
let upwi = UrlPatternWithInput {
pathname: pathname.clone(),
pattern: UrlPattern::parse(
@ -90,3 +93,9 @@ impl<T> Router<T> {
Err(404)
}
}
pub mod methods {
pub const GET: &'static str = "GET";
pub const POST: &'static str = "POST";
pub const PUT: &'static str = "PUT";
}

View file

@ -3,3 +3,4 @@ hard_tabs = true
match_block_trailing_comma = true
max_width = 80
empty_item_single_line = false
format_strings = true

5
shelves/Cargo.lock generated
View file

@ -43,6 +43,7 @@ name = "atelier"
version = "0.0.0"
dependencies = [
"async-channel",
"newt",
"rusqlite",
"url",
"urlpattern",
@ -586,6 +587,10 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "newt"
version = "0.0.0"
[[package]]
name = "object"
version = "0.36.7"

View file

@ -1,21 +1,15 @@
mod prelude;
use prelude::*;
mod routes;
mod templates;
use std::net::SocketAddr;
use std::pin::Pin;
use std::sync::Arc;
use axum::handler::HandlerWithoutStateExt;
use axum::response::IntoResponse;
use std::error::Error;
use tokio::net::TcpListener;
pub type AnyError = Box<dyn Error + Send + Sync + 'static>;
pub type Result<T> = core::result::Result<T, AnyError>;
pub type Dbs = atelier::rusqlite_thread_pool::PoolSender;
pub struct RequestCtx {
dbs: Dbs,
path_params: atelier::router::PathParams,
}
fn migrate(connection: &mut rusqlite::Connection) -> Result<()> {
Ok(())
}
@ -24,58 +18,7 @@ fn init(connection: &mut rusqlite::Connection) -> rusqlite::Result<()> {
Ok(())
}
type Request = axum::extract::Request;
type Response = axum::response::Response;
type Handler = Box<
dyn Fn(
Request,
RequestCtx,
) -> Pin<Box<dyn Future<Output = Response> + Send + 'static>>
+ Send
+ Sync
+ 'static,
>;
type Router = atelier::router::Router<Handler>;
fn make_handler<Fut, F>(f: F) -> Handler
where
Fut: Future<Output = Response> + Send + 'static,
F: FnMut(Request, RequestCtx) -> Fut + Clone + Send + Sync + 'static,
{
Box::new(move |req, ctx| {
let mut f = f.clone();
Box::pin(async move { f(req, ctx).await })
})
}
async fn get_hello(req: Request, ctx: RequestCtx) -> Response {
"hello".into_response()
}
async fn get_hello_name(req: Request, ctx: RequestCtx) -> Response {
format!("Hello, {}", ctx.path_params.get("name")).into_response()
}
fn make_router() -> Router {
let mut router = atelier::router::Router::new();
macro_rules! r {
($m:expr, $p:expr, $h:expr) => {
router.register($m.to_string(), $p.to_string(), make_handler($h))
};
}
r!("GET", "/hello", get_hello);
r!("GET", "/hello/:name", get_hello_name);
router
}
async fn serve(
dbs: Dbs,
router: Arc<Router>,
req: axum::extract::Request,
) -> axum::response::Response {
async fn serve(dbs: DbS, router: Arc<Router>, req: Request) -> Response {
let method = req.method().as_str();
let uri = format!("{}", req.uri());
@ -104,7 +47,7 @@ async fn go() -> Result<()> {
let addr = "127.0.0.1:8333".parse::<SocketAddr>()?;
let listener = TcpListener::bind(addr).await?;
let router = Arc::new(make_router());
let router = Arc::new(routes::make_router());
let s = move |req: Request| {
let tx = tx.clone();
let router = router.clone();

View file

@ -0,0 +1,48 @@
use std::error::Error;
use std::pin::Pin;
pub use atelier::rusqlite_thread_pool::PoolSender as DbS;
pub type Request = axum::extract::Request;
pub struct RequestCtx {
pub dbs: DbS,
pub path_params: atelier::router::PathParams,
}
pub type Response = axum::response::Response;
pub type Handler = Box<
dyn Fn(
Request,
RequestCtx,
) -> Pin<Box<dyn Future<Output = Response> + Send + 'static>>
+ Send
+ Sync
+ 'static,
>;
pub type Router = atelier::router::Router<Handler>;
pub type AnyError = Box<dyn Error + Send + Sync + 'static>;
pub type Result<T> = core::result::Result<T, AnyError>;
pub use axum::response::IntoResponse;
pub enum HandlerError {
InternalServerError(AnyError),
}
impl<E> From<E> for HandlerError
where
E: Error + Send + Sync + 'static,
{
fn from(v: E) -> Self {
Self::InternalServerError(Box::new(v))
}
}
impl IntoResponse for HandlerError {
fn into_response(self) -> Response {
"".into_response()
}
}
pub type HandlerResult<T: IntoResponse> = core::result::Result<T, HandlerError>;
pub use crate::templates::{template_fn, Template, TemplateFn};
pub use core::fmt::{Display, Formatter};
pub type FmtResult = core::fmt::Result;
pub use axum::response::Html;

View file

@ -0,0 +1,17 @@
use crate::prelude::*;
fn Hello(name: &str) -> impl Template {
template_fn(move |f| write!(f, "Hello, {name}"))
}
pub async fn view(
request: Request,
ctx: RequestCtx,
) -> HandlerResult<impl IntoResponse> {
let name = ctx.path_params.get("name");
let template = Hello(name).display();
let output = format!("{template}");
Ok(Html(output))
}

View file

@ -0,0 +1,81 @@
use crate::prelude::*;
enum ActivityType {
CreatedItem,
}
struct Activity {
activity_type: ActivityType,
item_xid: String,
created_timestamp: String,
}
pub async fn view(req: Request, ctx: RequestCtx) -> HandlerResult<String> {
let activities: Vec<_> = ctx
.dbs
.send(|conn| {
let mut stmt = conn.prepare(
r#"
select activity_json_blob from activity
order by json_extract(activity_json_blob, '$.created_timestamp')
limit 20
"#,
)?;
Ok(stmt.query_map([], |r| r.get::<_, String>(0))?.collect())
})
.await?;
todo!()
/*
db.prepare(`
`).values<[string]>().map(([blob]) => JSON.parse(blob));
const body = `
<h1>Recent Activity</h1>
${ActivityList({ activities: recentActivities })}
`;
return html(View({ title: 'Home', body }));
*/
}
/*
import { View } from '@/backend/templates/index.ts';
import { html } from '@atelier/responses.ts';
import { arrayIsEmpty } from '@atelier/array.ts';
type ActivityType = 'created_item';
type Activity = {
activityType: ActivityType;
itemXid: string;
createdTimestamp: string;
};
function Activity(props: { activity: Activity }) {}
function ActivityList(props: { activities: Activity[] }) {
if (arrayIsEmpty(props.activities)) {
return 'No activities yet!';
}
const activities = props.activities.map((activity) => Activity({ activity }));
return `
<ul>
</ul>
`;
}
export function viewHome(req: Request, { db }: RequestCtx) {
const recentActivities: Activity[] = db.prepare(`
select activity_json_blob from activity
order by json_extract(activity_json_blob, '$.createdTimestamp')
limit 20
`).values<[string]>().map(([blob]) => JSON.parse(blob));
const body = `
<h1>Recent Activity</h1>
${ActivityList({ activities: recentActivities })}
`;
return html(View({ title: 'Home', body }));
}
*/

View file

@ -0,0 +1,23 @@
use crate::prelude::*;
pub async fn view(req: Request, ctx: RequestCtx) -> HandlerResult<String> {
todo!()
}
pub async fn view_one(req: Request, ctx: RequestCtx) -> HandlerResult<String> {
todo!()
}
pub async fn view_create(
req: Request,
ctx: RequestCtx,
) -> HandlerResult<String> {
todo!()
}
pub async fn post_create(
req: Request,
ctx: RequestCtx,
) -> HandlerResult<String> {
todo!()
}

View file

@ -0,0 +1,67 @@
mod hello;
mod home;
mod items;
mod shelves;
use atelier::router::methods::*;
use std::pin::Pin;
use crate::prelude::*;
fn make_handler<Fut, F, R>(f: F) -> Handler
where
Fut: Future<Output = R> + Send + 'static,
F: FnMut(Request, RequestCtx) -> Fut + Clone + Send + Sync + 'static,
R: IntoResponse,
{
Box::new(move |req, ctx| {
let mut f = f.clone();
Box::pin(async move { f(req, ctx).await.into_response() })
})
}
pub fn route(dbs: DbS, req: Request) -> Response {
let method = Some(req.method().as_str());
let uri = format!("{}", req.uri());
let path_parts: Vec<_> = uri.split('/').collect();
match path_parts.as_slice() {
&[] => match method.take() {
GET => return home::view(),
}
};
match method {
None => (http::status::StatusCode::from_u16(405), "Method not allowed").into_response(),
Some(_) => (http::status::StatusCode::from_u16(404), "Not found").into_response(),
}
}
pub fn make_router() -> Router {
let mut router = atelier::router::Router::new();
macro_rules! r {
($m:expr, $p:expr, $h:expr) => {
router.register($m, $p, make_handler($h))
};
}
r!(GET, "/", home::view);
r!(GET, "/shelves", shelves::view);
r!(GET, "/shelves/:shelf_xid", shelves::view_one);
r!(GET, "/items", items::view);
r!(GET, "/items/create", items::view_create);
r!(GET, "/items/:item_xid", items::view_one);
r!(POST, "/items/create", items::post_create);
r!(GET, "/hello/:name", hello::view);
r!(GET, "/static/*", serve_static);
router
}
pub async fn serve_static(req: Request, ctx: RequestCtx) -> Response {
let url = req.uri().path();
"".into_response()
}

View file

@ -0,0 +1,16 @@
use crate::prelude::*;
pub async fn view(req: Request, ctx: RequestCtx) -> HandlerResult<String> {
todo!()
}
pub async fn view_one(req: Request, ctx: RequestCtx) -> HandlerResult<String> {
todo!()
}
pub async fn view_create(
req: Request,
ctx: RequestCtx,
) -> HandlerResult<String> {
todo!()
}

View file

@ -0,0 +1,35 @@
use crate::prelude::*;
pub trait Template {
fn render(&self, fmt: &mut Formatter) -> FmtResult;
fn display(self) -> impl Display
where
Self: Sized,
{
struct D<T>(T);
impl<T: Template> Display for D<T> {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
self.0.render(fmt)
}
}
D(self)
}
}
pub struct TemplateFn<F>(F);
pub fn template_fn<F>(f: F) -> TemplateFn<F>
where
F: Fn(&mut Formatter) -> FmtResult,
{
TemplateFn(f)
}
impl<F> Template for TemplateFn<F>
where
F: Fn(&mut Formatter) -> FmtResult,
{
fn render(&self, fmt: &mut Formatter) -> FmtResult {
self.0(fmt)
}
}