diff --git a/Cargo.lock b/Cargo.lock index 31fc9dd..9d29875 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,15 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "anstream" version = "0.6.18" @@ -100,6 +109,21 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.8.0" @@ -115,12 +139,31 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "bytes" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" +[[package]] +name = "cc" +version = "1.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -182,6 +225,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -229,6 +281,28 @@ version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + [[package]] name = "forest" version = "0.1.0" @@ -241,6 +315,9 @@ dependencies = [ "minijinja", "rusty-s3", "serde", + "serde_json", + "syntect", + "syntect-assets", "tokio", "tracing", "tracing-subscriber", @@ -292,6 +369,25 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +[[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 = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + [[package]] name = "heck" version = "0.5.0" @@ -446,6 +542,16 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -482,6 +588,12 @@ version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + [[package]] name = "litemap" version = "0.7.4" @@ -676,6 +788,28 @@ version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +[[package]] +name = "onig" +version = "6.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" +dependencies = [ + "bitflags 1.3.2", + "libc", + "once_cell", + "onig_sys", +] + +[[package]] +name = "onig_sys" +version = "69.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "overload" version = "0.1.1" @@ -717,6 +851,25 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "plist" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016" +dependencies = [ + "base64", + "indexmap", + "quick-xml 0.32.0", + "serde", + "time", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -732,6 +885,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2" +dependencies = [ + "memchr", +] + [[package]] name = "quick-xml" version = "0.37.2" @@ -757,9 +919,26 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ - "bitflags", + "bitflags 2.8.0", ] +[[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 = "rustc-demangle" version = "0.1.24" @@ -776,7 +955,7 @@ dependencies = [ "hmac", "md-5", "percent-encoding", - "quick-xml", + "quick-xml 0.37.2", "serde", "serde_json", "sha2", @@ -806,6 +985,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" + [[package]] name = "serde" version = "1.0.217" @@ -838,6 +1023,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha2" version = "0.10.8" @@ -858,6 +1056,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -923,6 +1127,46 @@ dependencies = [ "syn", ] +[[package]] +name = "syntect" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874dcfa363995604333cf947ae9f751ca3af4522c60886774c4963943b4746b1" +dependencies = [ + "bincode", + "bitflags 1.3.2", + "flate2", + "fnv", + "once_cell", + "onig", + "plist", + "regex-syntax", + "serde", + "serde_derive", + "serde_json", + "thiserror", + "walkdir", + "yaml-rust", +] + +[[package]] +name = "syntect-assets" +version = "0.23.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9555fce7f4031d83b10a9ca0e9c2fe8eba1542c06e2182b7a342e0f2ec2a38e" +dependencies = [ + "bincode", + "flate2", + "globset", + "log", + "once_cell", + "semver", + "serde", + "serde_yaml", + "syntect", + "thiserror", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -1099,6 +1343,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "url" version = "2.5.4" @@ -1302,7 +1552,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ - "bitflags", + "bitflags 2.8.0", ] [[package]] @@ -1317,6 +1567,15 @@ version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + [[package]] name = "yoke" version = "0.7.5" diff --git a/crates/forest/Cargo.toml b/crates/forest/Cargo.toml index 2e1f121..8e61916 100644 --- a/crates/forest/Cargo.toml +++ b/crates/forest/Cargo.toml @@ -19,3 +19,6 @@ kdl = "6.3.3" walkdir = "2.5.0" minijinja = "2.7.0" glob = "0.3.2" +serde_json = "1.0.138" +syntect = "5.2.0" +syntect-assets = "0.23.6" diff --git a/crates/forest/src/cli.rs b/crates/forest/src/cli.rs index 95f5be6..a7b9a2e 100644 --- a/crates/forest/src/cli.rs +++ b/crates/forest/src/cli.rs @@ -3,6 +3,13 @@ use std::{net::SocketAddr, path::PathBuf}; use clap::{Parser, Subcommand}; use kdl::KdlDocument; use rusty_s3::{Bucket, Credentials, S3Action}; +use syntect::{ + easy::HighlightLines, + highlighting::{Style, ThemeSet}, + parsing::SyntaxSet, + util::{as_24_bit_terminal_escaped, LinesWithEndings}, +}; +use syntect_assets::assets::HighlightingAssets; use tokio::io::AsyncWriteExt; use crate::{ @@ -31,6 +38,8 @@ enum Commands { Template {}, + Info {}, + Serve { #[arg(env = "FOREST_HOST", long, default_value = "127.0.0.1:3000")] host: SocketAddr, @@ -93,6 +102,23 @@ pub async fn execute() -> anyhow::Result<()> { tracing::trace!("found context: {:?}", context); } + Commands::Info {} => { + let output = serde_json::to_string_pretty(&context)?; + let assets = HighlightingAssets::from_binary(); + let theme = assets.get_theme("OneHalfDark"); + + let ss = SyntaxSet::load_defaults_nonewlines(); + + let syntax = ss.find_syntax_by_extension("json").unwrap(); + let mut h = HighlightLines::new(syntax, theme); + + for line in LinesWithEndings::from(&output) { + let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ss).unwrap(); + print!("{}", as_24_bit_terminal_escaped(&ranges[..], true)); + } + println!() + } + Commands::Template {} => { tracing::info!("templating"); diff --git a/crates/forest/src/model.rs b/crates/forest/src/model.rs index fca559b..3d1eb29 100644 --- a/crates/forest/src/model.rs +++ b/crates/forest/src/model.rs @@ -1,14 +1,15 @@ use std::{collections::BTreeMap, fmt::Debug, path::PathBuf}; use kdl::{KdlDocument, KdlEntry, KdlNode, KdlValue}; +use serde::Serialize; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] pub struct Context { pub project: Project, pub plan: Option, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] pub struct Plan { pub name: String, } @@ -38,7 +39,8 @@ impl TryFrom for Plan { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] +#[serde(untagged)] pub enum ProjectPlan { Local { path: PathBuf }, NoPlan, @@ -66,7 +68,8 @@ impl TryFrom<&KdlNode> for ProjectPlan { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] +#[serde(untagged)] pub enum GlobalVariable { Map(BTreeMap), String(String), @@ -125,7 +128,7 @@ impl TryFrom<&KdlValue> for GlobalVariable { } } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Serialize, Default)] pub struct Global { items: BTreeMap, } @@ -153,14 +156,15 @@ impl TryFrom<&KdlNode> for Global { } } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Serialize, Default)] pub enum TemplateType { #[default] Jinja2, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] pub struct Templates { + #[serde(rename = "type")] pub ty: TemplateType, pub path: String, pub output: PathBuf, @@ -219,13 +223,50 @@ impl TryFrom<&KdlNode> for Templates { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Serialize)] +pub struct Action {} + +#[derive(Debug, Clone, Serialize)] +pub struct Scripts { + pub path: PathBuf, + pub actions: BTreeMap, +} + +impl TryFrom<&KdlNode> for Scripts { + type Error = anyhow::Error; + + fn try_from(value: &KdlNode) -> Result { + let val = Self { + path: value + .get("path") + .and_then(|p| p.as_string()) + .map(PathBuf::from) + .unwrap_or(PathBuf::from("scripts/")), + actions: value + .children() + .and_then(|c| c.get("actions")) + .and_then(|a| a.children()) + .map(|d| { + d.nodes() + .iter() + .map(|n| (n.name().value().to_string(), Action {})) + .collect::>() + }) + .unwrap_or_default(), + }; + + Ok(val) + } +} + +#[derive(Debug, Clone, Serialize)] pub struct Project { pub name: String, pub description: Option, pub plan: Option, pub global: Global, pub templates: Option, + pub scripts: Option, } impl TryFrom for Project { @@ -274,6 +315,10 @@ impl TryFrom for Project { .get("templates") .map(|t| t.try_into()) .transpose()?, + scripts: project_children + .get("scripts") + .map(|m| m.try_into()) + .transpose()?, }) } } diff --git a/examples/project/forest.kdl b/examples/project/forest.kdl index ca69701..fc8a035 100644 --- a/examples/project/forest.kdl +++ b/examples/project/forest.kdl @@ -25,5 +25,12 @@ project { path "templates/*.jinja2" output "output/" } + + scripts type=shell { + path "scripts/" + actions { + build + } + } } diff --git a/todos/hyperlog/graph.json b/todos/hyperlog/graph.json index 3298afe..02243cf 100644 --- a/todos/hyperlog/graph.json +++ b/todos/hyperlog/graph.json @@ -21,7 +21,7 @@ "type": "item", "title": "should be able to template from a remote plan", "description": "", - "state": "not-done" + "state": "done" }, "should be able to use scripts from a remote plan": { "type": "item", diff --git a/todos/hyperlog/graph.lock b/todos/hyperlog/graph.lock deleted file mode 100644 index 016d04c..0000000 --- a/todos/hyperlog/graph.lock +++ /dev/null @@ -1 +0,0 @@ -hyperlog-lock \ No newline at end of file