flams_math_archives/
mathhub.rs

1use crate::{Archive, utils::path_ext::PathExt};
2use std::path::{Path, PathBuf};
3
4#[allow(clippy::doc_markdown)]
5/// The default MathHub directories on the user's file system; determined from environment
6/// variables, or the path stated in `~/.mathhub/mathhub.path`, or `~/MathHub`.
7///
8/// # Panics
9/// If it fails to do any of those
10#[must_use]
11pub fn default_mathhubs() -> Vec<PathBuf> {
12    if let Ok(f) = std::env::var("MATHHUB") {
13        return f.split(',').map(|s| PathBuf::from(s.trim())).collect();
14    }
15    if let Some(d) = simple_home_dir::home_dir() {
16        let p = d.join(".mathhub").join("mathhub.path");
17        if let Ok(f) = std::fs::read_to_string(p) {
18            return f
19                .split('\n')
20                .map(|s: &str| PathBuf::from(s.trim()))
21                .collect();
22        }
23        return vec![d.join("MathHub")];
24    }
25    panic!(
26        "No MathHub directory found and default ~/MathHub not accessible!\n\
27Please set the MATHHUB environment variable or create a file ~/.mathhub/mathhub.path containing \
28the path to the MathHub directory."
29    )
30}
31
32static MH: std::sync::OnceLock<&'static [&'static Path]> = std::sync::OnceLock::new();
33
34/// The mathhub directories used by this run. Static, initilized as [`default_mathhubs`]
35/// on first access. Can be set *before* any call using [`set_mathhubs`].
36pub fn mathhubs() -> &'static [&'static Path] {
37    MH.get_or_init(|| {
38        &*Box::leak(
39            default_mathhubs()
40                .into_iter()
41                .map(|p| &*Box::leak(p.into_boxed_path()))
42                .collect::<Vec<_>>()
43                .into_boxed_slice(),
44        )
45    })
46}
47
48/// Sets the mathhub directories used by tis run. May only be used *before* any call
49/// to `mathhubs`.
50///
51/// # Errors
52/// If already set
53#[allow(clippy::result_unit_err)]
54pub fn set_mathhubs(paths: impl IntoIterator<Item = PathBuf>) -> Result<(), ()> {
55    if MH.get().is_some() {
56        return Err(());
57    }
58    MH.get_or_init(|| {
59        &*Box::leak(
60            paths
61                .into_iter()
62                .map(|p| &*Box::leak(p.into_boxed_path()))
63                .collect::<Vec<_>>()
64                .into_boxed_slice(),
65        )
66    });
67    Ok(())
68}
69
70pub static MATHHUBS: std::sync::LazyLock<&'static [&'static Path]> = std::sync::LazyLock::new(
71    || {
72        if let Ok(f) = std::env::var("MATHHUB") {
73            return Box::leak(
74                f.split(',')
75                    .map(|s| &*PathBuf::from(s.trim()).leak())
76                    .collect::<Box<[_]>>(),
77            );
78        }
79        if let Some(d) = simple_home_dir::home_dir() {
80            let p = d.join(".mathhub").join("mathhub.path");
81            if let Ok(f) = std::fs::read_to_string(p) {
82                return Box::leak(
83                    f.split('\n')
84                        .map(|s| &*PathBuf::from(s.trim()).leak())
85                        .collect::<Box<[_]>>(),
86                );
87            }
88            return Box::leak(Box::new([&*d.join("MathHub").leak()]));
89        }
90        panic!(
91            "No MathHub directory found and default ~/MathHub not accessible!\n\
92    Please set the MATHHUB environment variable or create a file ~/.mathhub/mathhub.path containing \
93    the path to the MathHub directory."
94        )
95    },
96);
97
98pub fn load_all_archives() -> impl rayon::iter::ParallelIterator<Item = Archive> {
99    //impl orx_parallel::ParIter<Item = Archive> {
100    use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator};
101    use spliter::ParallelSpliterator;
102    crate::mathhub::MATHHUBS.par_iter().flat_map(|p| {
103        crate::archive_iter::ManifestIterator::new(p)
104            .par_split()
105            .into_par_iter()
106            .map(move |r| (p, r))
107            .filter_map(|(mh, p)| {
108                // SAFETY: manifest file is grandchild of root directory of archive
109                let parent = unsafe { p.parent().unwrap_unchecked().parent().unwrap_unchecked() };
110
111                let rel_path = parent.relative_to(mh)?;
112                match crate::manifest::parse_manifest(&p, rel_path) {
113                    Ok(r) => Some(r),
114                    Err(e) => {
115                        tracing::warn!("{e} in {rel_path}");
116                        None
117                    }
118                }
119            })
120            .map(|a| {
121                if let Archive::Local(a) = &a {
122                    a.update_sources();
123                }
124                a
125            })
126    })
127    /*
128    //use orx_parallel::IterIntoParIter;
129    crate::mathhub::MATHHUBS
130        .iter()
131        .flat_map(|p| crate::archive_iter::ManifestIterator::new(p).map(move |r| (p, r)))
132        .iter_into_par()
133        .filter_map(|(mh, p)| {
134            // SAFETY: manifest file is grandson of root directory of archive
135            let parent = unsafe { p.parent().unwrap_unchecked().parent().unwrap_unchecked() };
136            let Ok(diff) = parent.strip_prefix(mh) else {
137                return None;
138            };
139            crate::manifest::parse_manifest(&p, &diff.as_slash_str(), external_url)
140        })
141    */
142}
143
144#[test]
145fn all_archives() {
146    use crate::source_format;
147    use ftml_ontology::utils::time::measure;
148    use rayon::iter::*;
149    source_format!(STEX {
150        name: "stex",
151        file_extensions: &["tex", "ltx"],
152        description: "foo",
153        dependencies: |_| Vec::new(),
154        targets: &[]
155    });
156
157    let _ = tracing_subscriber::fmt().try_init();
158    let (i, t) = measure(|| load_all_archives().count());
159    tracing::info!("Loaded {i} archives in {t}");
160    tracing::info!("Memory: {}", ftml_uris::get_memory_state());
161}