cst/
lib.rs

1use std::env;
2use std::fs::File;
3use std::io::prelude::*;
4use std::path::Path;
5use substring::Substring;
6use walkdir::WalkDir;
7
8#[cfg(windows)]
9const LINE_ENDING: &'static str = "\r\n";
10#[cfg(not(windows))]
11const LINE_ENDING: &'static str = "\n";
12
13const CARET: char = '^';
14
15/// Converts the content into a vector of strings, each string separated by the last caret in the file.
16// Unfortunately, it doesn't separate comments yet but the get_entry() ignores them, anyway.
17fn normalize_entries<S: Into<String>>(content: S) -> Vec<String> {
18    let cst_ending = format!("{}{}", CARET, LINE_ENDING);
19    let entries = content
20        .into()
21        .trim_end()
22        .trim_end_matches(CARET)
23        .split(cst_ending.as_str())
24        .map(|s| s.to_string())
25        .collect::<Vec<String>>();
26
27    return entries;
28}
29
30/// Parses a specific entry from a string.
31///
32/// # Example
33/// ```rust
34/// use cst::get_entry;
35///
36/// let input = "1 ^The quick brown fox jumps over the lazy dog.^";
37/// let expect = "The quick brown fox jumps over the lazy dog.";
38/// let entry = get_entry(input, 1);
39/// assert_eq!(entry.unwrap(), expect);
40/// ```
41pub fn get_entry<S: Into<String>>(content: S, key: usize) -> Option<String> {
42    let entries = normalize_entries(content);
43
44    // Find the entry
45    for entry in entries {
46        if entry.contains(&key.to_string()) {
47            let start_index = entry.find(CARET)?;
48            let line = entry.substring(start_index + 1, entry.len());
49            return Some(line.to_string());
50        }
51    }
52
53    // No entry found.
54    return None;
55}
56
57pub struct UIText {
58    language: String,
59}
60
61/// The UIText is a wrapper around the get_entry() function that recursively searches directories for a file with the given language and key.
62///
63/// # Example
64/// ```rust
65/// use cst::UIText;
66///
67/// let expect = "The quick brown fox jumps over the lazy dog.";
68/// let ui_text = UIText::new("example"); // uitext/example.dir
69/// let entry = ui_text.get_text(101, 1); // Entry 1 of _101_[name].cst
70///
71/// assert_eq!(entry.unwrap(), expect);
72/// ```
73impl UIText {
74    pub fn new<S: Into<String>>(language: S) -> UIText {
75        UIText {
76            language: language.into(),
77        }
78    }
79
80    pub fn get_text(&self, id: usize, key: usize) -> Option<String> {
81        let language_dir = format!(
82            "{}/uitext/{}.dir/",
83            env::current_dir().unwrap().display(),
84            self.language
85        );
86
87        for entry in WalkDir::new(&language_dir)
88            .sort_by_file_name()
89            .into_iter()
90            .filter_map(|e| e.ok())
91        {
92            let f_name = entry.file_name().to_string_lossy();
93            let find_id = format!("_{}_", id);
94            if f_name.contains(&find_id) && f_name.ends_with(".cst") {
95                let mut open_file =
96                    match File::open(Path::new(format!("{}/{}", language_dir, f_name).as_str())) {
97                        Err(_) => panic!("couldn't open {}", f_name),
98                        Ok(file) => file,
99                    };
100                let mut contents = String::new();
101                match open_file.read_to_string(&mut contents) {
102                    Ok(_) => return Some(get_entry(contents, key)?),
103                    Err(why) => panic!("couldn't read {}: {}", f_name, why),
104                }
105            }
106        }
107        return None;
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use crate::{get_entry, normalize_entries, UIText};
114
115    #[cfg(windows)]
116    const LINE_ENDING: &'static str = "\r\n";
117    #[cfg(not(windows))]
118    const LINE_ENDING: &'static str = "\n";
119
120    #[test]
121    fn get_uitext() {
122        let ui_text = UIText::new("lorem");
123        let expected = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin ac dictum orci, at tincidunt nulla. Donec aliquet, eros non interdum posuere, ipsum sapien molestie nunc, nec facilisis libero ipsum et risus. In sed lorem vel ipsum placerat viverra.".to_string();
124        dbg!(ui_text.get_text(101, 1));
125        assert_eq!(ui_text.get_text(101, 1).unwrap(), expected);
126    }
127
128    #[test]
129    fn is_normalized() {
130        let example = format!(
131            "1 ^The quick brown fox^{}2 ^jumps over the lazy dog^{}",
132            LINE_ENDING, LINE_ENDING
133        );
134        let input = normalize_entries(example);
135        let expected = [
136            "1 ^The quick brown fox".to_string(),
137            "2 ^jumps over the lazy dog".to_string(),
138        ];
139        assert_eq!(input, expected);
140    }
141
142    #[test]
143    fn test_entry() {
144        let example = format!(
145            "# comment{}1 ^The quick brown fox^{}2 ^jumps over the lazy dog^{}",
146            LINE_ENDING, LINE_ENDING, LINE_ENDING
147        );
148        let expected = "jumps over the lazy dog".to_string();
149        dbg!(get_entry(&example, 2));
150        assert_eq!(get_entry(example, 2).unwrap(), expected);
151    }
152}