kellnr_docs/
lib.rs

1pub mod api;
2mod doc_archive;
3pub mod doc_queue;
4pub mod doc_queue_response;
5pub mod docs_error;
6pub mod upload_response;
7
8use std::convert::TryFrom;
9use std::path::Path;
10
11use kellnr_common::version::Version;
12use kellnr_settings::Settings;
13
14pub fn get_latest_doc_url(crate_name: &str, settings: &Settings) -> Option<String> {
15    let version = get_latest_version_with_doc(crate_name, settings);
16    version.map(|v| compute_doc_url(crate_name, &v))
17}
18
19pub fn get_doc_url(crate_name: &str, crate_version: &Version, docs_path: &Path) -> Option<String> {
20    let docs_name = crate_name_to_docs_name(crate_name);
21
22    if doc_exists(crate_name, crate_version, docs_path) {
23        Some(format!(
24            "/docs/{crate_name}/{crate_version}/doc/{docs_name}/index.html"
25        ))
26    } else {
27        None
28    }
29}
30
31pub fn compute_doc_url(crate_name: &str, crate_version: &Version) -> String {
32    let docs_name = crate_name_to_docs_name(crate_name);
33    format!("/docs/{crate_name}/{crate_version}/doc/{docs_name}/index.html")
34}
35
36fn crate_name_to_docs_name(crate_name: &str) -> String {
37    // Cargo replaces the `-` with `_` in the crate name when
38    // docs are generated. As such, the docs folder name is not "foo-bar" but "foo_bar".
39    crate_name.replace('-', "_")
40}
41
42fn doc_exists(crate_name: &str, crate_version: &str, docs_path: &Path) -> bool {
43    let docs_name = crate_name_to_docs_name(crate_name);
44    docs_path
45        .join(crate_name)
46        .join(crate_version)
47        .join("doc")
48        .join(docs_name)
49        .join("index.html")
50        .exists()
51}
52
53fn get_latest_version_with_doc(crate_name: &str, settings: &Settings) -> Option<Version> {
54    let versions_path = settings.docs_path().join(crate_name);
55    let Ok(version_folders) = std::fs::read_dir(versions_path) else {
56        return None;
57    };
58
59    let mut versions: Vec<Version> = version_folders
60        .flatten()
61        .filter(|entry| entry.path().is_dir())
62        .flat_map(|dir| Version::try_from(&dir.file_name().to_string_lossy().to_string()))
63        .collect();
64
65    // Sort and reverse the order such that the biggest version
66    // for which docs exist will be returned.
67    versions.sort();
68    versions.reverse();
69    versions
70        .into_iter()
71        .find(|v| doc_exists(crate_name, &v.to_string(), &settings.docs_path()))
72}
73
74pub async fn delete(
75    crate_name: &str,
76    crate_version: &str,
77    settings: &Settings,
78) -> Result<(), std::io::Error> {
79    // Delete the docs folder for the crate version.
80    let docs_path = settings.docs_path().join(crate_name).join(crate_version);
81    if docs_path.exists() {
82        tokio::fs::remove_dir_all(docs_path).await?;
83    }
84
85    // If it was the last version, delete the empty crate docs folder.
86    if get_latest_version_with_doc(crate_name, settings).is_none() {
87        let crate_path = settings.docs_path().join(crate_name);
88        if crate_path.exists() {
89            tokio::fs::remove_dir_all(crate_path).await?;
90        }
91    }
92
93    Ok(())
94}