aboutsummaryrefslogtreecommitdiff
path: root/.run/src/bin/deploy-api-docs.rs
blob: ea52886caec2ed6eec9ca70ae94995fd94e1d522 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use std::path::Path;

use tools::{println_cargo_style, run_cmd};

const DOCS_RS_MANIFEST: &str = "mingling/Cargo.toml";
const OUTPUT_DIR: &str = "docs/api-docs";

fn main() {
    let repo_root = find_git_repo().expect("Failed to find git repository root");

    let manifest_path = repo_root.join(DOCS_RS_MANIFEST);
    if !manifest_path.exists() {
        eprintln!("Error: Manifest not found at {}", manifest_path.display());
        std::process::exit(1);
    }

    // Read and parse [package.metadata.docs.rs]
    let manifest_content =
        std::fs::read_to_string(&manifest_path).expect("Failed to read Cargo.toml");
    let cargo_toml: toml::Value = manifest_content
        .parse()
        .expect("Failed to parse Cargo.toml");

    let doc_features = cargo_toml
        .get("package")
        .and_then(|p| p.get("metadata"))
        .and_then(|m| m.get("docs"))
        .and_then(|d| d.get("rs"))
        .and_then(|rs| rs.get("features"))
        .and_then(|f| f.as_array())
        .unwrap_or_else(|| {
            eprintln!("Error: [package.metadata.docs.rs] or its 'features' key not found in mingling/Cargo.toml");
            std::process::exit(1);
        });

    let features: Vec<&str> = doc_features.iter().filter_map(|v| v.as_str()).collect();

    if features.is_empty() {
        eprintln!("Error: No features defined in [package.metadata.docs.rs]");
        std::process::exit(1);
    }

    let features_arg = features.join(",");

    // Ensure output directory exists
    let output_path = repo_root.join(OUTPUT_DIR);
    std::fs::create_dir_all(&output_path).expect("Failed to create output directory");

    // Build cargo doc command
    let cmd = format!(
        "cargo doc --no-deps --features \"{}\" -p mingling --target-dir \"{}\" --color always",
        features_arg,
        output_path.join("target").to_string_lossy()
    );

    println_cargo_style!("Features: {}", features_arg);
    println_cargo_style!("Output: {}", output_path.display());

    // Run cargo doc, then copy generated docs to output directory
    println_cargo_style!("Building: docs (cargo doc --no-deps)");
    run_cmd!(&cmd).unwrap_or_else(|code| {
        eprintln!("Error: cargo doc failed with exit code {}", code);
        std::process::exit(code);
    });

    // Copy generated docs from target/doc to OUTPUT_DIR (top level)
    let doc_source = output_path.join("target").join("doc");
    let doc_dest = &output_path;

    if doc_source.exists() {
        println_cargo_style!("Copying: docs to output directory");
        // Remove old docs in destination (except target/)
        if let Ok(entries) = std::fs::read_dir(doc_dest) {
            for entry in entries.flatten() {
                let path = entry.path();
                if path.file_name().and_then(|n| n.to_str()) == Some("target") {
                    continue;
                }
                if path.is_dir() {
                    std::fs::remove_dir_all(&path).ok();
                } else {
                    std::fs::remove_file(&path).ok();
                }
            }
        }
        copy_dir_recursively(&doc_source, doc_dest).expect("Failed to copy documentation");
    }

    // Clean up the intermediate target directory to save space
    std::fs::remove_dir_all(output_path.join("target")).ok();

    println_cargo_style!("Done: API docs deployed to {}", output_path.display());
}

fn copy_dir_recursively(src: &Path, dst: &Path) -> std::io::Result<()> {
    if !dst.exists() {
        std::fs::create_dir_all(dst)?;
    }

    for entry in std::fs::read_dir(src)? {
        let entry = entry?;
        let file_type = entry.file_type()?;
        let src_path = entry.path();
        let file_name = src_path.file_name().expect("Failed to get file name");
        let dst_path = dst.join(file_name);

        if file_type.is_dir() {
            copy_dir_recursively(&src_path, &dst_path)?;
        } else {
            std::fs::copy(&src_path, &dst_path)?;
        }
    }

    Ok(())
}

fn find_git_repo() -> Option<std::path::PathBuf> {
    let mut current_dir = std::env::current_dir().ok()?;

    loop {
        let git_dir = current_dir.join(".git");
        if git_dir.exists() && git_dir.is_dir() {
            return Some(current_dir);
        }

        if !current_dir.pop() {
            break;
        }
    }

    None
}