Now let's take a silly one
1use std::path::{Path, PathBuf};
2
3use anyhow::{Context, Result};
4use jacquard_lexicon::codegen::{CodeGenerator, CodegenMode};
5use jacquard_lexicon::corpus::LexiconCorpus;
6use walkdir::WalkDir;
7
8const DEFAULT_LEXICONS_SUBDIR: &str = "lexicons";
9const STAGED_SUBDIR: &str = "lexicons-staged";
10const GENERATED_SUBDIR: &str = "src/_lex";
11const TEMP_SEGMENT: &str = "temp";
12
13fn main() -> Result<()> {
14 let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);
15 let workspace_root = manifest_dir
16 .parent()
17 .and_then(Path::parent)
18 .context("resolve workspace root from manifest dir")?
19 .to_path_buf();
20
21 let lexicons_dir = std::env::var("KNOT_LEXICONS_DIR")
22 .map(PathBuf::from)
23 .unwrap_or_else(|_| workspace_root.join(DEFAULT_LEXICONS_SUBDIR));
24
25 println!("cargo:rerun-if-env-changed=KNOT_LEXICONS_DIR");
26 println!("cargo:rerun-if-changed={}", lexicons_dir.display());
27 println!("cargo:rerun-if-changed=build.rs");
28
29 let out_dir = PathBuf::from(std::env::var("OUT_DIR")?);
30 let staged = out_dir.join(STAGED_SUBDIR);
31 if staged.exists() {
32 std::fs::remove_dir_all(&staged).context("clean staged lexicons")?;
33 }
34 stage_lexicons(&lexicons_dir, &staged)?;
35
36 let corpus = LexiconCorpus::load_from_dir(&staged)
37 .map_err(|e| anyhow::anyhow!("load lexicon corpus: {e:?}"))?;
38
39 let generated = manifest_dir.join(GENERATED_SUBDIR);
40 if generated.exists() {
41 std::fs::remove_dir_all(&generated).context("clean generated dir")?;
42 }
43 std::fs::create_dir_all(&generated).context("create generated dir")?;
44
45 let codegen = CodeGenerator::with_mode(&corpus, "crate", CodegenMode::Pretty);
46 codegen
47 .write_to_disk(&generated)
48 .map_err(|e| anyhow::anyhow!("write generated code: {e:?}"))?;
49
50 Ok(())
51}
52
53fn stage_lexicons(src: &Path, dst: &Path) -> Result<()> {
54 WalkDir::new(src)
55 .into_iter()
56 .filter_map(std::result::Result::ok)
57 .filter(|entry| entry.file_type().is_file())
58 .filter(|entry| {
59 entry
60 .path()
61 .extension()
62 .is_some_and(|ext| ext.eq_ignore_ascii_case("json"))
63 })
64 .filter(|entry| {
65 !entry
66 .path()
67 .components()
68 .any(|component| component.as_os_str() == TEMP_SEGMENT)
69 })
70 .try_for_each(|entry| -> Result<()> {
71 let rel = entry.path().strip_prefix(src).context("strip src prefix")?;
72 let target = dst.join(rel);
73 if let Some(parent) = target.parent() {
74 std::fs::create_dir_all(parent).context("create staged parent")?;
75 }
76 std::fs::copy(entry.path(), &target).context("copy lexicon file")?;
77 Ok(())
78 })
79}