141 lines
4.6 KiB
Rust
141 lines
4.6 KiB
Rust
use anyhow::*;
|
|
use extend::ext;
|
|
use itertools::Itertools;
|
|
use std::path::Path;
|
|
|
|
#[macro_export]
|
|
macro_rules! impl_try_from {
|
|
($typ:ty {
|
|
$(
|
|
for $for:ty => |$arg:ident| $code:expr
|
|
);*;
|
|
}) => {
|
|
$(impl TryFrom<$typ> for $for {
|
|
type Error = anyhow::Error;
|
|
|
|
fn try_from($arg: $typ) -> Result<Self> {
|
|
$code
|
|
}
|
|
})*
|
|
};
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! try_logging_errors {
|
|
($context:expr => $code:block) => {{
|
|
let result: Result<_> = try { $code };
|
|
if let Err(err) = result {
|
|
eprintln!("[{}:{}] Error while {}: {:?}", ::std::file!(), ::std::line!(), $context, err);
|
|
}
|
|
}};
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! print_result_err {
|
|
($context:expr, $result:expr $(,)?) => {{
|
|
if let Err(err) = $result {
|
|
eprintln!("[{}:{}] Error {}: {:?}", ::std::file!(), ::std::line!(), $context, err);
|
|
}
|
|
}};
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! loop_select {
|
|
($($body:tt)*) => {
|
|
loop {
|
|
::tokio::select! {
|
|
$($body)*
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Joins two paths while keeping it somewhat pretty.
|
|
/// If the second path is absolute, this will just return the second path.
|
|
/// If it is relative, it will return the second path joined onto the first path, removing any `./` if present.
|
|
/// TODO this is not yet perfect, as it will still leave ../ and multiple ./ etc,... check for a Path::simplify or something.
|
|
pub fn join_path_pretty<P: AsRef<std::path::Path>, P2: AsRef<std::path::Path>>(a: P, b: P2) -> std::path::PathBuf {
|
|
let a = a.as_ref();
|
|
let b = b.as_ref();
|
|
if b.is_absolute() {
|
|
b.to_path_buf()
|
|
} else {
|
|
a.parent().unwrap().join(b.strip_prefix("./").unwrap_or(&b))
|
|
}
|
|
}
|
|
|
|
/// extends a hashmap, returning a list of keys that already where present in the hashmap.
|
|
pub fn extend_safe<K: std::cmp::Eq + std::hash::Hash + Clone, V, T: IntoIterator<Item = (K, V)>>(
|
|
a: &mut std::collections::HashMap<K, V>,
|
|
b: T,
|
|
) -> Vec<K> {
|
|
b.into_iter().filter_map(|(k, v)| a.insert(k.clone(), v).map(|_| k.clone())).collect()
|
|
}
|
|
|
|
/// read an scss file, replace all environment variable references within it and
|
|
/// then parse it into css.
|
|
pub fn parse_scss_from_file(path: &Path) -> Result<String> {
|
|
let config_dir = path.parent().context("Given SCSS file has no parent directory?!")?;
|
|
let scss_file_content =
|
|
std::fs::read_to_string(path).with_context(|| format!("Given SCSS File Doesnt Exist! {}", path.display()))?;
|
|
let file_content = replace_env_var_references(scss_file_content);
|
|
let grass_config = grass::Options::default().load_path(config_dir);
|
|
grass::from_string(file_content, &grass_config).map_err(|err| anyhow!("Encountered SCSS parsing error: {:?}", err))
|
|
}
|
|
|
|
#[ext(pub, name = StringExt)]
|
|
impl<T: AsRef<str>> T {
|
|
/// check if the string is empty after removing all linebreaks and trimming
|
|
/// whitespace
|
|
fn is_blank(self) -> bool {
|
|
self.as_ref().replace('\n', "").trim().is_empty()
|
|
}
|
|
|
|
/// trim all lines in a string
|
|
fn trim_lines(self) -> String {
|
|
self.as_ref().lines().map(|line| line.trim()).join("\n")
|
|
}
|
|
}
|
|
|
|
pub fn parse_duration(s: &str) -> Result<std::time::Duration> {
|
|
use std::time::Duration;
|
|
if s.ends_with("ms") {
|
|
Ok(Duration::from_millis(s.trim_end_matches("ms").parse()?))
|
|
} else if s.ends_with('s') {
|
|
Ok(Duration::from_secs(s.trim_end_matches('s').parse()?))
|
|
} else if s.ends_with('m') {
|
|
Ok(Duration::from_secs(s.trim_end_matches('m').parse::<u64>()? * 60))
|
|
} else if s.ends_with('h') {
|
|
Ok(Duration::from_secs(s.trim_end_matches('h').parse::<u64>()? * 60 * 60))
|
|
} else {
|
|
Err(anyhow!("unrecognized time format: {}", s))
|
|
}
|
|
}
|
|
|
|
/// Replace all env-var references of the format `"something ${foo}"` in a string
|
|
/// by the actual env-variables. If the env-var isn't found, will replace the
|
|
/// reference with an empty string.
|
|
pub fn replace_env_var_references(input: String) -> String {
|
|
lazy_static::lazy_static! {
|
|
static ref ENV_VAR_PATTERN: regex::Regex = regex::Regex::new(r"\$\{([^\s]*)\}").unwrap();
|
|
}
|
|
ENV_VAR_PATTERN
|
|
.replace_all(&input, |var_name: ®ex::Captures| std::env::var(var_name.get(1).unwrap().as_str()).unwrap_or_default())
|
|
.into_owned()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::replace_env_var_references;
|
|
use std;
|
|
|
|
#[test]
|
|
fn test_replace_env_var_references() {
|
|
let scss = "$test: ${USER};";
|
|
|
|
assert_eq!(
|
|
replace_env_var_references(String::from(scss)),
|
|
format!("$test: {};", std::env::var("USER").unwrap_or_default())
|
|
)
|
|
}
|
|
}
|