erg_common/
lib.rs

1//! provides utilities for parser, compiler, and vm crate.
2use std::fmt;
3use std::path::{Component, Path, PathBuf};
4
5pub mod cache;
6pub mod config;
7pub mod consts;
8pub mod datetime;
9pub mod dict;
10pub mod env;
11pub mod erg_util;
12pub mod error;
13pub mod fresh;
14pub mod fxhash;
15pub mod help_messages;
16pub mod io;
17pub mod lang;
18pub mod levenshtein;
19pub mod macros;
20pub mod opcode;
21pub mod opcode308;
22pub mod opcode309;
23pub mod opcode310;
24pub mod opcode311;
25pub mod pathutil;
26pub mod python_util;
27pub mod random;
28pub mod serialize;
29pub mod set;
30pub mod shared;
31pub mod spawn;
32pub mod stdin;
33pub mod str;
34pub mod style;
35pub mod traits;
36pub mod triple;
37pub mod tsort;
38pub mod vfs;
39
40use consts::CASE_SENSITIVE;
41
42use crate::set::Set;
43pub use crate::str::Str;
44pub use crate::triple::Triple;
45
46pub type ArcArray<T> = std::sync::Arc<[T]>;
47
48pub fn open_read(filename: &str) -> std::io::Result<String> {
49    let f = std::fs::File::open(filename)?;
50    read_file(f)
51}
52
53pub fn read_file(mut f: std::fs::File) -> std::io::Result<String> {
54    let mut s = "".to_string();
55    std::io::Read::read_to_string(&mut f, &mut s)?;
56    Ok(s)
57}
58
59pub fn fmt_vec<T: fmt::Display>(v: &[T]) -> String {
60    fmt_iter(v.iter())
61}
62
63pub fn fmt_slice<T: fmt::Display>(v: &[T]) -> String {
64    fmt_iter(v.iter())
65}
66
67pub fn fmt_vec_split_with<T: fmt::Display>(v: &[T], splitter: &str) -> String {
68    fmt_iter_split_with(v.iter(), splitter)
69}
70
71pub fn fmt_set_split_with<T: fmt::Display + std::hash::Hash>(s: &Set<T>, splitter: &str) -> String {
72    fmt_iter_split_with(s.iter(), splitter)
73}
74
75pub fn debug_fmt_iter<T: fmt::Debug, I: Iterator<Item = T>>(iter: I) -> String {
76    let mut s = iter.fold("".to_string(), |sum, elem| format!("{sum}{elem:?}, "));
77    s.pop();
78    s.pop();
79    s
80}
81
82pub fn fmt_iter<T: fmt::Display, I: Iterator<Item = T>>(iter: I) -> String {
83    let mut s = iter.fold("".to_string(), |sum, elem| sum + &elem.to_string() + ", ");
84    s.pop();
85    s.pop();
86    s
87}
88
89pub fn fmt_iter_split_with<T: fmt::Display, I: Iterator<Item = T>>(i: I, splitter: &str) -> String {
90    let mut s = i.fold("".to_string(), |sum, elem| {
91        sum + &elem.to_string() + splitter
92    });
93    for _ in 0..splitter.len() {
94        s.pop();
95    }
96    s
97}
98
99pub fn fmt_indent(s: String, depth: usize) -> String {
100    let indent = " ".repeat(depth);
101    s.split('\n').map(|s| indent.clone() + s).collect()
102}
103
104/// If you want to get a hash consisting of multiple objects, pass it as a tuple or array
105pub fn get_hash<T: std::hash::Hash>(t: &T) -> usize {
106    let mut s = fxhash::FxHasher::default();
107    t.hash(&mut s);
108    let res = std::hash::Hasher::finish(&s);
109    if cfg!(target_pointer_width = "64") {
110        res as usize
111    } else {
112        (res % usize::MAX as u64) as usize
113    }
114}
115
116/// \r\n (Windows), \r (old MacOS) -> \n (Unix)
117#[inline]
118pub fn normalize_newline(src: &str) -> String {
119    src.replace("\r\n", "\n").replace('\r', "\n")
120}
121
122/// cut \n
123#[inline]
124pub fn chomp(src: &str) -> String {
125    normalize_newline(src).replace('\n', "")
126}
127
128pub fn try_map<T, U, E, F, I>(i: I, f: F) -> Result<Vec<U>, E>
129where
130    F: Fn(T) -> Result<U, E>,
131    I: Iterator<Item = T>,
132{
133    let mut v = vec![];
134    for x in i {
135        let y = f(x)?;
136        v.push(y);
137    }
138    Ok(v)
139}
140
141pub fn try_map_mut<T, U, E, F, I>(i: I, mut f: F) -> Result<Vec<U>, E>
142where
143    F: FnMut(T) -> Result<U, E>,
144    I: Iterator<Item = T>,
145{
146    let mut v = vec![];
147    for x in i {
148        let y = f(x)?;
149        v.push(y);
150    }
151    Ok(v)
152}
153
154pub fn failable_map_mut<T, U, E, F, I>(i: I, mut f: F) -> Result<Vec<U>, (Vec<U>, Vec<E>)>
155where
156    F: FnMut(T) -> Result<U, (U, E)>,
157    I: Iterator<Item = T>,
158{
159    let mut v = vec![];
160    let mut errs = vec![];
161    for x in i {
162        match f(x) {
163            Ok(y) => {
164                v.push(y);
165            }
166            Err((y, e)) => {
167                v.push(y);
168                errs.push(e);
169            }
170        }
171    }
172    if errs.is_empty() {
173        Ok(v)
174    } else {
175        Err((v, errs))
176    }
177}
178
179pub fn unique_in_place<T: Eq + std::hash::Hash + Clone>(v: &mut Vec<T>) {
180    let mut uniques = Set::new();
181    v.retain(|e| uniques.insert(e.clone()));
182}
183
184/// at least, this is necessary for Windows and macOS
185pub fn normalize_path(path: PathBuf) -> PathBuf {
186    let verbatim_replaced = path.to_string_lossy().replace("\\\\?\\", "");
187    let lower = if !CASE_SENSITIVE {
188        verbatim_replaced.to_lowercase()
189    } else {
190        verbatim_replaced
191    };
192    PathBuf::from(lower)
193}
194
195pub fn cheap_canonicalize_path(path: &Path) -> PathBuf {
196    let mut components = path.components().peekable();
197    let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
198        components.next();
199        PathBuf::from(c.as_os_str())
200    } else {
201        PathBuf::new()
202    };
203
204    for component in components {
205        match component {
206            Component::Prefix(..) => unreachable!(),
207            Component::RootDir => {
208                ret.push(component.as_os_str());
209            }
210            Component::CurDir => {}
211            Component::ParentDir => {
212                ret.pop();
213            }
214            Component::Normal(c) => {
215                ret.push(c);
216            }
217        }
218    }
219    ret
220}
221
222/// ```
223/// use erg_common::trim_eliminate_top_indent;
224/// let code = r#"
225///     def foo():
226///         pass
227/// "#;
228/// let expected = r#"def foo():
229///     pass"#;
230/// assert_eq!(trim_eliminate_top_indent(code.to_string()), expected);
231/// ```
232pub fn trim_eliminate_top_indent(code: String) -> String {
233    let code = code.trim_matches(|c| c == '\n' || c == '\r');
234    if !code.starts_with(' ') {
235        return code.to_string();
236    }
237    let indent = code.chars().take_while(|c| *c == ' ').count();
238    let mut result = String::new();
239    for line in code.lines() {
240        if line.len() > indent {
241            result.push_str(line[indent..].trim_end());
242        } else {
243            result.push_str(line.trim_end());
244        }
245        result.push('\n');
246    }
247    if !result.is_empty() {
248        result.pop();
249    }
250    result
251}
252
253pub fn deepen_indent(code: String) -> String {
254    let mut result = String::new();
255    for line in code.lines() {
256        result.push_str("    ");
257        result.push_str(line);
258        result.push('\n');
259    }
260    if !result.is_empty() {
261        result.pop();
262    }
263    result
264}