Skip to main content

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, &settings.origin.path))
17}
18
19pub fn get_doc_url(
20    crate_name: &str,
21    crate_version: &Version,
22    docs_path: &Path,
23    path_prefix: &str,
24) -> Option<String> {
25    let docs_name = crate_name_to_docs_name(crate_name);
26    let path_prefix = path_prefix.trim();
27
28    if doc_exists(crate_name, crate_version, docs_path) {
29        Some(format!(
30            "{path_prefix}/docs/{crate_name}/{crate_version}/doc/{docs_name}/index.html"
31        ))
32    } else {
33        None
34    }
35}
36
37pub fn compute_doc_url(crate_name: &str, crate_version: &Version, path_prefix: &str) -> String {
38    let docs_name = crate_name_to_docs_name(crate_name);
39    let path_prefix = path_prefix.trim();
40    format!("{path_prefix}/docs/{crate_name}/{crate_version}/doc/{docs_name}/index.html")
41}
42
43fn crate_name_to_docs_name(crate_name: &str) -> String {
44    // Cargo replaces the `-` with `_` in the crate name when
45    // docs are generated. As such, the docs folder name is not "foo-bar" but "foo_bar".
46    crate_name.replace('-', "_")
47}
48
49fn doc_exists(crate_name: &str, crate_version: &str, docs_path: &Path) -> bool {
50    let docs_name = crate_name_to_docs_name(crate_name);
51    docs_path
52        .join(crate_name)
53        .join(crate_version)
54        .join("doc")
55        .join(docs_name)
56        .join("index.html")
57        .exists()
58}
59
60fn get_latest_version_with_doc(crate_name: &str, settings: &Settings) -> Option<Version> {
61    let versions_path = settings.docs_path().join(crate_name);
62    let Ok(version_folders) = std::fs::read_dir(versions_path) else {
63        return None;
64    };
65
66    let mut versions: Vec<Version> = version_folders
67        .flatten()
68        .filter(|entry| entry.path().is_dir())
69        .flat_map(|dir| Version::try_from(&dir.file_name().to_string_lossy().to_string()))
70        .collect();
71
72    // Sort and reverse the order such that the biggest version
73    // for which docs exist will be returned.
74    versions.sort();
75    versions.reverse();
76    versions
77        .into_iter()
78        .find(|v| doc_exists(crate_name, &v.to_string(), &settings.docs_path()))
79}
80
81pub async fn delete(
82    crate_name: &str,
83    crate_version: &str,
84    settings: &Settings,
85) -> Result<(), std::io::Error> {
86    // Delete the docs folder for the crate version.
87    let docs_path = settings.docs_path().join(crate_name).join(crate_version);
88    if docs_path.exists() {
89        tokio::fs::remove_dir_all(docs_path).await?;
90    }
91
92    // If it was the last version, delete the empty crate docs folder.
93    if get_latest_version_with_doc(crate_name, settings).is_none() {
94        let crate_path = settings.docs_path().join(crate_name);
95        if crate_path.exists() {
96            tokio::fs::remove_dir_all(crate_path).await?;
97        }
98    }
99
100    Ok(())
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    #[test]
108    fn compute_doc_url_without_path_prefix() {
109        let version = Version::try_from("1.0.0").unwrap();
110        let url = compute_doc_url("my-crate", &version, "");
111        assert_eq!(url, "/docs/my-crate/1.0.0/doc/my_crate/index.html");
112    }
113
114    #[test]
115    fn compute_doc_url_with_path_prefix() {
116        let version = Version::try_from("1.0.0").unwrap();
117        let url = compute_doc_url("my-crate", &version, "/kellnr");
118        assert_eq!(url, "/kellnr/docs/my-crate/1.0.0/doc/my_crate/index.html");
119    }
120
121    #[test]
122    fn compute_doc_url_trims_whitespace_from_path_prefix() {
123        let version = Version::try_from("1.0.0").unwrap();
124        let url = compute_doc_url("my-crate", &version, "  /kellnr  ");
125        assert_eq!(url, "/kellnr/docs/my-crate/1.0.0/doc/my_crate/index.html");
126    }
127
128    #[test]
129    fn compute_doc_url_replaces_hyphen_with_underscore_in_docs_name() {
130        let version = Version::try_from("2.0.0-beta1").unwrap();
131        let url = compute_doc_url("foo-bar-baz", &version, "");
132        assert_eq!(
133            url,
134            "/docs/foo-bar-baz/2.0.0-beta1/doc/foo_bar_baz/index.html"
135        );
136    }
137}