Skip to main content

aprender_contracts_cli/commands/
book.rs

1use 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}