diff --git a/Cargo.lock b/Cargo.lock --- a/Cargo.lock +++ b/Cargo.lock @@ -5511,6 +5511,24 @@ checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" [[package]] +name = "lexicons" +version = "0.0.1" +dependencies = [ + "anyhow", + "bytes", + "chrono", + "cid", + "jacquard-common", + "jacquard-derive", + "jacquard-lexicon", + "miette", + "serde", + "serde_json", + "thiserror 2.0.18", + "walkdir", +] + +[[package]] name = "libc" version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ print_stderr = "warn" [workspace.dependencies] +lexicons = { path = "crates/lexicons" } trusted-proxies = { path = "crates/trusted-proxies" } bobbin-types = { path = "bobbin/crates/types" } diff --git a/crates/lexicons/Cargo.toml b/crates/lexicons/Cargo.toml new file mode 100644 --- /dev/null +++ b/crates/lexicons/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "lexicons" +version.workspace = true +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[dependencies] +bytes = { workspace = true } +chrono = { workspace = true } +cid = { workspace = true } +jacquard-common = { workspace = true } +jacquard-derive = { workspace = true } +jacquard-lexicon = { workspace = true } +miette = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +thiserror = { workspace = true } + +[build-dependencies] +anyhow = { workspace = true } +jacquard-lexicon = { workspace = true, features = ["codegen"] } +walkdir = { workspace = true } + +[features] +default = ["sh_tangled"] +com_atproto = [] +com_bad_example = [] +sh_tangled = ["com_atproto"] +streaming = ["jacquard-common/websocket"] diff --git a/crates/lexicons/build.rs b/crates/lexicons/build.rs new file mode 100644 --- /dev/null +++ b/crates/lexicons/build.rs @@ -0,0 +1,74 @@ +use std::path::{Path, PathBuf}; + +use anyhow::{Context, Result}; +use jacquard_lexicon::codegen::{CodeGenerator, CodegenMode}; +use jacquard_lexicon::corpus::LexiconCorpus; +use walkdir::WalkDir; + +const DEFAULT_LEXICONS_SUBDIR: &str = "lexicons"; +const STAGED_SUBDIR: &str = "lexicons-staged"; +const GENERATED_SUBDIR: &str = "src/_lex"; +const TEMP_SEGMENT: &str = "temp"; + +fn main() -> Result<()> { + let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?); + let workspace_root = manifest_dir + .parent() + .and_then(Path::parent) + .context("resolve workspace root from manifest dir")? + .to_path_buf(); + + let lexicons_dir = std::env::var("TANGLED_LEXICONS_DIR") + .map(PathBuf::from) + .unwrap_or_else(|_| workspace_root.join(DEFAULT_LEXICONS_SUBDIR)); + + println!("cargo:rerun-if-env-changed=TANGLED_LEXICONS_DIR"); + println!("cargo:rerun-if-changed={}", lexicons_dir.display()); + println!("cargo:rerun-if-changed=build.rs"); + + let out_dir = PathBuf::from(std::env::var("OUT_DIR")?); + + let staged = out_dir.join(STAGED_SUBDIR); + if staged.exists() { + std::fs::remove_dir_all(&staged).context("clean staged lexicons")?; + } + stage_lexicons(&lexicons_dir, &staged)?; + + let corpus = LexiconCorpus::load_from_dir(&staged) + .map_err(|e| anyhow::anyhow!("load lexicon corpus: {e:?}"))?; + + let generated = manifest_dir.join(GENERATED_SUBDIR); + if generated.exists() { + std::fs::remove_dir_all(&generated).context("clean generated dir")?; + } + std::fs::create_dir_all(&generated).context("create generated dir")?; + + let codegen = CodeGenerator::with_mode(&corpus, "crate", CodegenMode::Pretty); + codegen + .write_to_disk(&generated) + .map_err(|e| anyhow::anyhow!("write generated code: {e:?}"))?; + + Ok(()) +} + +fn stage_lexicons(src: &Path, dst: &Path) -> Result<()> { + WalkDir::new(src) + .into_iter() + .filter_map(std::result::Result::ok) + .filter(|e| e.file_type().is_file()) + .filter(|e| { + e.path() + .extension() + .is_some_and(|ext| ext.eq_ignore_ascii_case("json")) + }) + .filter(|e| !e.path().components().any(|c| c.as_os_str() == TEMP_SEGMENT)) + .try_for_each(|entry| -> Result<()> { + let rel = entry.path().strip_prefix(src).context("strip src prefix")?; + let target = dst.join(rel); + if let Some(parent) = target.parent() { + std::fs::create_dir_all(parent).context("create staged parent")?; + } + std::fs::copy(entry.path(), &target).context("copy lexicon file")?; + Ok(()) + }) +} diff --git a/crates/lexicons/src/lib.rs b/crates/lexicons/src/lib.rs new file mode 100644 --- /dev/null +++ b/crates/lexicons/src/lib.rs @@ -0,0 +1,18 @@ +extern crate alloc; + +#[path = "_lex/lib.rs"] +#[allow(non_snake_case, unused_imports)] +#[allow( + clippy::absurd_extreme_comparisons, + clippy::collapsible_if, + clippy::manual_strip, + clippy::needless_update, + clippy::new_ret_no_self, + clippy::new_without_default, + clippy::should_implement_trait, + clippy::type_complexity +)] +#[rustfmt::skip] +mod _lex; + +pub use _lex::*;