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
use std::{
    fs::{self, File},
    io::{self, Write},
    path::Path,
};

/// If the file path does not exist, then it will create the file and import the relevant module.
///
/// If it already exists, then it determines if the module in question exists in `mod.rs`. If it does, it does nothing; otherwise, it appends the relevant statements.
///
/// Note: the detection step is not strict, it does not exclude comments.
///
/// If the file path is **src/assets/localisation.rs**, then it will detect **src/assets/mod.rs**, append `mod localisation;` and `use glossa::LangID;` etc to **mod.rs**.
pub fn append_to_l10n_mod<P: AsRef<Path>>(path: P) -> io::Result<()> {
    let path = path.as_ref();

    let previous = path
        .parent()
        .expect("Invalid path");

    // Create all directories under the parent directory if they do not exist
    fs::create_dir_all(previous)?;

    let mod_stmt = get_mod_stem_name(path);

    let mod_rs = previous.join("mod.rs");

    let mut mod_file = File::options()
        .create(true)
        .append(true)
        .open(&mod_rs)?;

    let mut write_to_mod_rs =
        |contents: &str| -> io::Result<()> { writeln!(mod_file, "{}", contents) };

    let mod_rs_content = || fs::read_to_string(&mod_rs);
    let not_contains =
        |s| -> io::Result<bool> { Ok(!mod_rs_content()?.contains(s)) };

    // `mod localisation;` -> `pub(crate) mod localisation;`
    let pub_crate_mod_stmt = format!("pub(crate) {mod_stmt}");

    // lang_id_consts
    match mod_rs.exists() {
        false => {
            use to_be_imported::*;
            for s in [
                USE_MAP,
                USE_ID,
                USE_MAP,
                USE_CONSTS,
                "\n",
                &pub_crate_mod_stmt,
            ] {
                write_to_mod_rs(s)?;
            }
        }

        true => {
            // const CELL: &str = "OnceCell";
            const MAP: &str = "HashMap";
            const ID: &str = "LangID";
            const ID_CONSTS: &str = "lang_id_consts";

            for s in [
                &mod_stmt, //
                // CELL,
                MAP, ID, ID_CONSTS,
            ] {
                if not_contains(s)? {
                    use to_be_imported::*;
                    write_to_mod_rs(match s {
                        // CELL => USE_CELL,
                        MAP => USE_MAP,
                        ID => USE_ID,
                        ID_CONSTS => USE_CONSTS,
                        s if s == mod_stmt => &pub_crate_mod_stmt,
                        _ => "",
                    })?;
                }
            }
        }
    }
    mod_file.flush()
}

/// Original dependencies(modules):
///
/// ```no_run
/// const USE_MAP: &str = "use ahash::HashMap;";
/// const USE_ID: &str = "use lang_id::LangID;";
/// const USE_CONSTS: &str = "use lang_id::consts as lang_id_consts;";
/// ```
mod to_be_imported {
    // Modules for glossa re-export:
    // const USE_CELL: &str = "use once_cell::sync::OnceCell;";
    // pub(super) const USE_CELL: &str = "use glossa::assets::OnceCell;";
    pub(super) const USE_MAP: &str = "use glossa::assets::HashMap;";
    pub(super) const USE_ID: &str = "use glossa::LangID;";
    pub(super) const USE_CONSTS: &str = "use glossa::assets::lang_id_consts;";
}

/// Gets the module name by file path
///
/// name: `mod mod_name;`
/// If the file path is **assets/localisation.rs**, then the name is `mod localisation;`
fn get_mod_stem_name(path: &Path) -> String {
    format!(
        "mod {};",
        path.file_stem()
            .expect("Invalid rs file")
            .to_str()
            .unwrap_or_else(|| panic!("Invalid file name"))
    )
}