aprender_contracts_cli/commands/
book.rs1use std::path::Path;
2
3use provable_contracts::book_gen::{generate_contract_page, update_summary};
4use provable_contracts::graph::dependency_graph;
5use provable_contracts::schema::{parse_contract, Contract};
6
7pub fn run(
8 contract_dir: &Path,
9 output_dir: &Path,
10 update_summary_flag: bool,
11 summary_path: Option<&Path>,
12) -> Result<(), Box<dyn std::error::Error>> {
13 let contracts = load_contracts(contract_dir)?;
14
15 let refs: Vec<(String, &Contract)> = contracts.iter().map(|(s, c)| (s.clone(), c)).collect();
16 let graph = dependency_graph(&refs);
17
18 std::fs::create_dir_all(output_dir)?;
19 let generated = write_contract_pages(&contracts, output_dir, &graph)?;
20
21 if update_summary_flag {
22 write_summary(&contracts, summary_path)?;
23 }
24
25 print_manifest(&generated, output_dir);
26 Ok(())
27}
28
29fn load_contracts(
30 contract_dir: &Path,
31) -> Result<Vec<(String, Contract)>, Box<dyn std::error::Error>> {
32 let mut contracts = Vec::new();
33 for entry in std::fs::read_dir(contract_dir)? {
34 let entry = entry?;
35 let path = entry.path();
36 if path.extension().and_then(|e| e.to_str()) != Some("yaml") {
37 continue;
38 }
39 let stem = path
40 .file_stem()
41 .and_then(|s| s.to_str())
42 .unwrap_or("unknown")
43 .to_string();
44 match parse_contract(&path) {
45 Ok(c) => contracts.push((stem, c)),
46 Err(e) => eprintln!("warning: skipping {}: {e}", path.display()),
47 }
48 }
49 contracts.sort_by(|a, b| a.0.cmp(&b.0));
50 Ok(contracts)
51}
52
53fn write_contract_pages(
54 contracts: &[(String, Contract)],
55 output_dir: &Path,
56 graph: &provable_contracts::graph::DependencyGraph,
57) -> Result<Vec<(String, usize)>, Box<dyn std::error::Error>> {
58 let mut generated = Vec::new();
59 for (stem, contract) in contracts {
60 let page = generate_contract_page(contract, stem, graph);
61 let out_path = output_dir.join(format!("{stem}.md"));
62 std::fs::write(&out_path, &page)?;
63 generated.push((stem.clone(), page.len()));
64 }
65 Ok(generated)
66}
67
68fn write_summary(
69 contracts: &[(String, Contract)],
70 summary_path: Option<&Path>,
71) -> Result<(), Box<dyn std::error::Error>> {
72 let summary = summary_path.unwrap_or_else(|| Path::new("book/src/SUMMARY.md"));
73 let existing = std::fs::read_to_string(summary)?;
74 let stems: Vec<&str> = contracts.iter().map(|(s, _)| s.as_str()).collect();
75 let updated = update_summary(&existing, &stems);
76 std::fs::write(summary, &updated)?;
77 println!("Updated {}", summary.display());
78 Ok(())
79}
80
81fn print_manifest(generated: &[(String, usize)], output_dir: &Path) {
82 println!("Generated {} contract pages:", generated.len());
83 for (stem, bytes) in generated {
84 println!(
85 " {output_dir}/{stem}.md ({bytes} bytes)",
86 output_dir = output_dir.display()
87 );
88 }
89}