···
10
10
mod cli;
11
11
12
12
fn main() -> Result<()> {
13
13
-
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap_or_else(|_| ".".to_string()));
14
14
-
let mut cmd = cli::LexFetchArgs::command();
13
13
+
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set"));
14
14
+
15
15
+
// Generate docs for lex-fetch
16
16
+
generate_docs_for_binary(
17
17
+
&out_dir,
18
18
+
cli::LexFetchArgs::command(),
19
19
+
"lex-fetch",
20
20
+
)?;
21
21
+
22
22
+
// Generate docs for jacquard-codegen
23
23
+
generate_docs_for_binary(
24
24
+
&out_dir,
25
25
+
cli::CodegenArgs::command(),
26
26
+
"jacquard-codegen",
27
27
+
)?;
28
28
+
29
29
+
println!(
30
30
+
"cargo:warning=Generated man pages and completions to {:?}",
31
31
+
out_dir
32
32
+
);
33
33
+
34
34
+
Ok(())
35
35
+
}
15
36
37
37
+
fn generate_docs_for_binary(
38
38
+
out_dir: &PathBuf,
39
39
+
mut cmd: clap::Command,
40
40
+
bin_name: &str,
41
41
+
) -> Result<()> {
16
42
// Generate man page
17
43
let man_dir = out_dir.join("man");
18
44
fs::create_dir_all(&man_dir)?;
···
20
46
let man = Man::new(cmd.clone());
21
47
let mut man_buffer = Vec::new();
22
48
man.render(&mut man_buffer)?;
23
23
-
fs::write(man_dir.join("lex-fetch.1"), man_buffer)?;
49
49
+
fs::write(man_dir.join(format!("{}.1", bin_name)), man_buffer)?;
24
50
25
51
// Generate shell completions
26
52
let comp_dir = out_dir.join("completions");
27
53
fs::create_dir_all(&comp_dir)?;
28
54
29
29
-
generate_to(shells::Bash, &mut cmd, "lex-fetch", &comp_dir)?;
30
30
-
generate_to(shells::Fish, &mut cmd, "lex-fetch", &comp_dir)?;
31
31
-
generate_to(shells::Zsh, &mut cmd, "lex-fetch", &comp_dir)?;
32
32
-
33
33
-
println!(
34
34
-
"cargo:warning=Generated man page and completions to {:?}",
35
35
-
out_dir
36
36
-
);
55
55
+
generate_to(shells::Bash, &mut cmd, bin_name, &comp_dir)?;
56
56
+
generate_to(shells::Fish, &mut cmd, bin_name, &comp_dir)?;
57
57
+
generate_to(shells::Zsh, &mut cmd, bin_name, &comp_dir)?;
37
58
38
59
Ok(())
39
60
}
···
1
1
+
// Example lex-fetch configuration file
2
2
+
// This demonstrates the available source types and configuration options
3
3
+
4
4
+
// Output configuration (required)
5
5
+
output {
6
6
+
// Where to save fetched lexicon JSON files
7
7
+
lexicons "lexicons"
8
8
+
9
9
+
// Where to generate Rust code
10
10
+
codegen "src"
11
11
+
12
12
+
// Path to Cargo.toml for feature generation (optional)
13
13
+
// cargo-toml "Cargo.toml"
14
14
+
15
15
+
// NOTE: root-module option is currently disabled due to issues when set to non-"crate" values
16
16
+
// It will always use "crate" as the root module name
17
17
+
}
18
18
+
19
19
+
// Fetch ATProto and Bluesky lexicons from official repo
20
20
+
// Higher priority (101) means these override conflicts from other sources
21
21
+
source "atproto" type="git" priority=101 {
22
22
+
repo "https://github.com/bluesky-social/atproto"
23
23
+
pattern "lexicons/**/*.json"
24
24
+
}
25
25
+
26
26
+
// Fetch lexicons from a Git repository
27
27
+
source "my-lexicons" type="git" priority=100 {
28
28
+
repo "https://github.com/example/my-lexicons"
29
29
+
30
30
+
// Optional: specific branch/tag/commit
31
31
+
// ref "main"
32
32
+
33
33
+
// Glob pattern for finding lexicon files (defaults to **/*.json)
34
34
+
pattern "**/*.json"
35
35
+
}
36
36
+
37
37
+
// Use local directory for custom/in-development lexicons
38
38
+
source "local-dev" type="local" priority=200 {
39
39
+
path "./my-lexicons"
40
40
+
41
41
+
// Optional pattern (omit to use **/*.json)
42
42
+
pattern "*.json"
43
43
+
}
44
44
+
45
45
+
// Fetch from an AT Protocol endpoint (user's repo)
46
46
+
source "custom-user" type="atproto" {
47
47
+
endpoint "did:plc:example123456789"
48
48
+
49
49
+
// Optional: specific slice within their repo
50
50
+
// slice "app.example.slice"
51
51
+
}
52
52
+
53
53
+
// Fetch from HTTP endpoint returning lexicon array
54
54
+
// source "http-lexicons" type="http" {
55
55
+
// url "https://api.example.com/lexicons"
56
56
+
// }
57
57
+
58
58
+
// Fetch from network.slices
59
59
+
// source "slices-example" type="slices" {
60
60
+
// slice "at://did:plc:example/network.slices.slice/record123"
61
61
+
// }
62
62
+
63
63
+
// Load a single JSON file containing multiple lexicons
64
64
+
// source "single-file" type="jsonfile" {
65
65
+
// path "./lexicons-bundle.json"
66
66
+
// }
67
67
+
68
68
+
// Priority notes:
69
69
+
// - Higher numbers override lower numbers when lexicons conflict
70
70
+
// - Useful for preferring official schemas over community forks
71
71
+
// - Default priority if not specified: 50
···
1
1
use clap::Parser;
2
2
+
use jacquard_lexicon::cli::CodegenArgs;
2
3
use jacquard_lexicon::codegen::CodeGenerator;
3
4
use jacquard_lexicon::corpus::LexiconCorpus;
4
4
-
use std::path::PathBuf;
5
5
-
6
6
-
#[derive(Parser, Debug)]
7
7
-
#[command(author, version, about = "Generate Rust code from Lexicon schemas")]
8
8
-
struct Args {
9
9
-
/// Directory containing Lexicon JSON files
10
10
-
#[arg(short = 'i', long)]
11
11
-
input: PathBuf,
12
12
-
13
13
-
/// Output directory for generated Rust code
14
14
-
#[arg(short = 'o', long)]
15
15
-
output: PathBuf,
16
16
-
17
17
-
/// Root module name (default: "crate")
18
18
-
#[arg(short = 'r', long, default_value = "crate")]
19
19
-
root_module: String,
20
20
-
}
21
5
22
6
fn main() -> miette::Result<()> {
23
23
-
let args = Args::parse();
7
7
+
let args = CodegenArgs::parse();
24
8
25
9
println!("Loading lexicons from {:?}...", args.input);
26
10
let corpus = LexiconCorpus::load_from_dir(&args.input)?;
···
28
12
println!("Loaded {} lexicon documents", corpus.iter().count());
29
13
30
14
println!("Generating code...");
31
31
-
let codegen = CodeGenerator::new(&corpus, args.root_module);
15
15
+
let codegen = CodeGenerator::new(&corpus, "crate".to_string());
32
16
codegen.write_to_disk(&args.output)?;
33
17
34
18
println!("Generated code to {:?}", args.output);
···
54
54
}
55
55
56
56
let corpus = LexiconCorpus::load_from_dir(&config.output.lexicons_dir)?;
57
57
-
let root_module = config
58
58
-
.output
59
59
-
.root_module
60
60
-
.unwrap_or_else(|| "crate".to_string());
61
61
-
let codegen = CodeGenerator::new(&corpus, root_module);
57
57
+
let codegen = CodeGenerator::new(&corpus, "crate".to_string());
62
58
std::fs::create_dir_all(&config.output.codegen_dir).into_diagnostic()?;
63
59
codegen.write_to_disk(&config.output.codegen_dir)?;
64
60
···
16
16
#[arg(short = 'v', long)]
17
17
pub verbose: bool,
18
18
}
19
19
+
20
20
+
#[derive(Parser, Debug)]
21
21
+
#[command(author, version, about = "Generate Rust code from Lexicon schemas")]
22
22
+
pub struct CodegenArgs {
23
23
+
/// Directory containing Lexicon JSON files
24
24
+
#[arg(short = 'i', long)]
25
25
+
pub input: PathBuf,
26
26
+
27
27
+
/// Output directory for generated Rust code
28
28
+
#[arg(short = 'o', long)]
29
29
+
pub output: PathBuf,
30
30
+
31
31
+
// TODO: root_module causes issues when set to anything other than "crate", needs rework
32
32
+
// /// Root module name (default: "crate")
33
33
+
// #[arg(short = 'r', long, default_value = "crate")]
34
34
+
// pub root_module: String,
35
35
+
}
···
15
15
pub struct OutputConfig {
16
16
pub lexicons_dir: PathBuf,
17
17
pub codegen_dir: PathBuf,
18
18
-
pub root_module: Option<String>,
18
18
+
// TODO: root_module causes issues when set to anything other than "crate", needs rework
19
19
+
// pub root_module: Option<String>,
19
20
pub cargo_toml_path: Option<PathBuf>,
20
21
}
21
22
···
58
59
59
60
let mut lexicons_dir: Option<PathBuf> = None;
60
61
let mut codegen_dir: Option<PathBuf> = None;
61
61
-
let mut root_module: Option<String> = None;
62
62
let mut cargo_toml_path: Option<PathBuf> = None;
63
63
64
64
for child in children.nodes() {
···
79
79
.ok_or_else(|| miette!("codegen expects a string value"))?;
80
80
codegen_dir = Some(PathBuf::from(val));
81
81
}
82
82
-
"root-module" => {
83
83
-
let val = child
84
84
-
.entries()
85
85
-
.get(0)
86
86
-
.and_then(|e| e.value().as_string())
87
87
-
.ok_or_else(|| miette!("root-module expects a string value"))?;
88
88
-
root_module = Some(val.to_string());
89
89
-
}
82
82
+
// TODO: root-module causes issues, disabled for now
83
83
+
// "root-module" => {
84
84
+
// let val = child
85
85
+
// .entries()
86
86
+
// .get(0)
87
87
+
// .and_then(|e| e.value().as_string())
88
88
+
// .ok_or_else(|| miette!("root-module expects a string value"))?;
89
89
+
// root_module = Some(val.to_string());
90
90
+
// }
90
91
"cargo-toml" => {
91
92
let val = child
92
93
.entries()
···
104
105
Ok(OutputConfig {
105
106
lexicons_dir: lexicons_dir.ok_or_else(|| miette!("Missing lexicons directory"))?,
106
107
codegen_dir: codegen_dir.ok_or_else(|| miette!("Missing codegen directory"))?,
107
107
-
root_module,
108
108
cargo_toml_path,
109
109
})
110
110
}
···
78
78
crane = {
79
79
args = {
80
80
buildInputs = commonBuildInputs;
81
81
+
nativeBuildInputs = [pkgs.installShellFiles];
81
82
doCheck = false; # Tests require lexicon corpus files not available in nix build
82
83
postInstall = ''
83
83
-
# Install man pages
84
84
-
if [ -d "$OUT_DIR/man" ]; then
85
85
-
install -Dm644 $OUT_DIR/man/*.1 -t $out/share/man/man1/
86
86
-
fi
84
84
+
# Install man pages and completions from build script output
85
85
+
for outdir in target/release/build/jacquard-lexicon-*/out; do
86
86
+
if [ -d "$outdir/man" ]; then
87
87
+
installManPage $outdir/man/*.1
88
88
+
fi
89
89
+
if [ -d "$outdir/completions" ]; then
90
90
+
# Install completions for both binaries
91
91
+
for completion in $outdir/completions/*; do
92
92
+
case "$(basename "$completion")" in
93
93
+
*.bash) installShellCompletion --bash "$completion" ;;
94
94
+
*.fish) installShellCompletion --fish "$completion" ;;
95
95
+
_*) installShellCompletion --zsh "$completion" ;;
96
96
+
esac
97
97
+
done
98
98
+
fi
99
99
+
done
87
100
88
88
-
# Install shell completions
89
89
-
if [ -d "$OUT_DIR/completions" ]; then
90
90
-
install -Dm644 $OUT_DIR/completions/lex-fetch.bash $out/share/bash-completion/completions/lex-fetch
91
91
-
install -Dm644 $OUT_DIR/completions/lex-fetch.fish $out/share/fish/vendor_completions.d/lex-fetch.fish
92
92
-
install -Dm644 $OUT_DIR/completions/_lex-fetch $out/share/zsh/site-functions/_lex-fetch
93
93
-
fi
101
101
+
# Install example lexicons.kdl config
102
102
+
install -Dm644 ${./../../crates/jacquard-lexicon/lexicons.kdl.example} $out/share/doc/jacquard-lexicon/lexicons.kdl.example
94
103
'';
95
104
};
96
105
};