use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use glob::glob;
use rand::prelude::*;
use regex::Regex;
use semver::{Version, VersionReq};
use target::{arch, endian, os, os_family, pointer_width};
use unicode_width::UnicodeWidthStr;
use crate::var::VarValue;
use crate::vm::Engine;
const LETTERS: &str = "0123456789abcdefghijklmnopqrstuvwxyz";
pub(crate) type FuncResult = Result<VarValue, String>;
enum CheckType {
IsFile,
IsDir,
Exists,
}
enum PathPart {
Stem,
Name,
Dir,
Ext,
}
enum SysPath {
Temp,
Home,
Docs,
Config,
}
enum Where {
All,
Left,
Right,
}
enum StrCase {
Up,
Low,
}
enum Semver {
Major,
Minor,
Patch,
}
enum SemverCmp {
Equal,
Greater,
Less,
}
pub(crate) fn run_func(name: &str, eng: &mut Engine, args: &[VarValue]) -> FuncResult {
let lowstr = name.to_lowercase();
match lowstr.as_str() {
"os" => Ok(VarValue::from(os())),
"family" | "platform" => Ok(VarValue::from(os_family())),
"bit" => Ok(VarValue::from(pointer_width())),
"arch" => Ok(VarValue::from(arch())),
"endian" => Ok(VarValue::from(endian())),
"is_file" | "is-file" | "isfile" => all_are(args, CheckType::IsFile),
"is_dir" | "is-dir" | "isdir" => all_are(args, CheckType::IsDir),
"exists" => all_are(args, CheckType::Exists),
"stem" => extract_part(args, PathPart::Stem),
"ext" => extract_part(args, PathPart::Ext),
"dir" => extract_part(args, PathPart::Dir),
"filename" => extract_part(args, PathPart::Name),
"add_ext" | "add-ext" => add_ext(args),
"with_ext" | "with-ext" => replace_ext(args),
"with_filename" | "with-filename" | "with_name" | "with-name" => replace_name(args),
"with_stem" | "with-stem" => replace_stem(args),
"join" => join_path(args),
"temp" | "temp_dir" | "temp-dir" => system_path(SysPath::Temp),
"home" | "home_dir" | "home-dir" | "user_dir" | "user-dir" => system_path(SysPath::Home),
"config" | "config_dir" | "config-dir" => system_path(SysPath::Config),
"documents" | "docs_dir" | "docs-dir" => system_path(SysPath::Docs),
"print" => print_all(args, false),
"println" => print_all(args, true),
"time" | "format-time" | "format_time" | "time-format" | "time_format" => format_time(args),
"trim" => trim_string(args, Where::All),
"trim_left" | "trim-left" | "trim_start" | "trim-start" => trim_string(args, Where::Left),
"trim_right" | "trim-right" | "trim_end" | "trim-end" => trim_string(args, Where::Right),
"starts-with" | "starts_with" => starts_with(args),
"ends-with" | "ends_with" => ends_with(args),
"lowcase" => change_case(args, StrCase::Low),
"upcase" => change_case(args, StrCase::Up),
"contains" => contains(args),
"replace" => replace(args),
"match" => match_regex(args),
"substr" => substr_regex(args),
"pad-center" | "pad_center" => pad(args, Where::All),
"pad-left" | "pad_left" => pad(args, Where::Left),
"pad-right" | "pad_right" => pad(args, Where::Right),
"field" | "fields" => fields(args),
"field-sep" | "fields-sep" | "field_sep" | "fields_sep" => fields_with_sep(args),
"rand-str" | "rand_str" => rand_string(args),
"inc" => increment(args),
"dec" => decrement(args),
"shell" => change_shell(eng, args),
"invoke-dir" | "invoke_dir" | "invokedir" => {
if eng.cwd_history.is_empty() {
return Ok(VarValue::from(eng.cwd.clone().to_string_lossy().to_string()));
}
Ok(VarValue::from(eng.cwd_history[0].clone().to_string_lossy().to_string()))
}
"set-env" | "set_env" | "setenv" => set_env_var(eng, args),
"del-env" | "del_env" | "delenv" => del_env_var(eng, args),
"clear-env" | "clear_env" | "clearenv" => eng.clear_env_vars(),
"glob" => globfiles(args),
"ver-inc" | "ver_inc" => semver_inc(args),
"ver-eq" | "ver_eq" => semver_equal(args),
"ver-gt" | "ver_gt" => semver_greater(args),
"ver-lt" | "ver_lt" => semver_less(args),
"ver-match" | "ver_match" => semver_match(args),
_ => Err(format!("function {} not found", name)),
}
}
fn change_shell(eng: &mut Engine, args: &[VarValue]) -> FuncResult {
let v: Vec<String> = args.iter().map(|v| v.to_string()).filter(|a| !a.is_empty()).collect();
eng.set_shell(v)
}
fn set_env_var(eng: &mut Engine, args: &[VarValue]) -> FuncResult {
let v: Vec<String> = args.iter().map(|v| v.to_string()).filter(|a| !a.is_empty()).collect();
let name = if v.is_empty() { String::new() } else { v[0].clone() };
let val = if v.len() > 1 { v[1].clone() } else { String::new() };
eng.set_env_var(name, val)
}
fn del_env_var(eng: &mut Engine, args: &[VarValue]) -> FuncResult {
let v: Vec<String> = args.iter().map(|v| v.to_string()).filter(|a| !a.is_empty()).collect();
let name = if v.is_empty() { String::new() } else { v[0].clone() };
eng.del_env_var(name)
}
fn all_are(args: &[VarValue], tp: CheckType) -> FuncResult {
if args.is_empty() {
return Ok(VarValue::Int(0));
}
for arg in args {
let s = arg.to_string();
let p = Path::new(&s);
let ok = match tp {
CheckType::IsFile => p.is_file(),
CheckType::IsDir => p.is_dir(),
CheckType::Exists => p.exists(),
};
if !ok {
return Ok(VarValue::Int(0));
}
}
Ok(VarValue::Int(1))
}
fn extract_part(args: &[VarValue], tp: PathPart) -> FuncResult {
if args.is_empty() {
return Ok(VarValue::Int(0));
}
let s = args[0].to_string();
let p = Path::new(&s);
let empty = OsStr::new("");
let empty_path = Path::new("");
match tp {
PathPart::Stem => Ok(VarValue::from(p.file_stem().unwrap_or(&empty).to_string_lossy().to_string())),
PathPart::Ext => Ok(VarValue::from(p.extension().unwrap_or(&empty).to_string_lossy().to_string())),
PathPart::Dir => Ok(VarValue::from(p.parent().unwrap_or(&empty_path).to_string_lossy().to_string())),
PathPart::Name => Ok(VarValue::from(p.file_name().unwrap_or(&empty).to_string_lossy().to_string())),
}
}
fn replace_ext(args: &[VarValue]) -> FuncResult {
if args.is_empty() {
return Err("path undefined".to_string());
}
if args.len() == 1 {
return Ok(args[0].clone());
}
let mut p = Path::new(&args[0].to_string()).to_owned();
let ext = if args.len() == 1 { String::new() } else { args[1].to_string() };
p.set_extension(ext);
Ok(VarValue::Str(p.to_string_lossy().to_string()))
}
fn add_ext(args: &[VarValue]) -> FuncResult {
if args.is_empty() {
return Err("path undefined".to_string());
}
if args.len() == 1 {
return Ok(args[0].clone());
}
let p = args[0].to_string();
let mut e = args[1].to_string();
if e.is_empty() {
return Ok(VarValue::Str(p));
}
if !e.starts_with('.') {
e = format!(".{}", e);
}
Ok(VarValue::Str(p + &e))
}
fn replace_name(args: &[VarValue]) -> FuncResult {
if args.is_empty() {
return Err("path undefined".to_string());
}
if args.len() == 1 {
return Err("new name undefined".to_string());
}
let mut p = Path::new(&args[0].to_string()).to_owned();
let new_name = Path::new(&args[1].to_string()).to_owned();
p.set_file_name(new_name);
Ok(VarValue::Str(p.to_string_lossy().to_string()))
}
fn replace_stem(args: &[VarValue]) -> FuncResult {
if args.is_empty() {
return Err("path undefined".to_string());
}
if args.len() == 1 {
return Err("new stem undefined".to_string());
}
let arg_str = args[0].to_string();
let p = Path::new(&arg_str);
let new_stem = args[1].to_string();
if new_stem.is_empty() {
return Err("new stem undefined".to_string());
}
let empty = OsStr::new("");
let empty_path = Path::new("");
let ext = p.extension().unwrap_or(&empty).to_string_lossy().to_string();
let dir = p.parent().unwrap_or(&empty_path);
let fname = if ext.is_empty() { new_stem } else { new_stem + "." + &ext };
Ok(VarValue::Str(dir.join(fname).to_string_lossy().to_string()))
}
fn join_path(args: &[VarValue]) -> FuncResult {
if args.is_empty() {
return Ok(VarValue::Str(String::new()));
}
if args.len() == 1 {
return Ok(args[0].clone());
}
let mut path = PathBuf::from(args[0].to_string());
for a in &args[1..] {
let astr = a.to_string();
let p = Path::new(&astr);
path = path.join(p);
}
Ok(VarValue::Str(path.to_string_lossy().to_string()))
}
fn system_path(pathtype: SysPath) -> FuncResult {
match pathtype {
SysPath::Temp => Ok(VarValue::Str(env::temp_dir().to_string_lossy().to_string())),
SysPath::Home => match dirs::home_dir() {
None => Err("user home directory undefined".to_string()),
Some(p) => Ok(VarValue::Str(p.to_string_lossy().to_string())),
},
SysPath::Config => match dirs::config_dir() {
None => Err("user configuration directory indefined".to_string()),
Some(p) => Ok(VarValue::Str(p.to_string_lossy().to_string())),
},
SysPath::Docs => match dirs::document_dir() {
None => Err("user document directory indefined".to_string()),
Some(p) => Ok(VarValue::Str(p.to_string_lossy().to_string())),
},
}
}
fn print_all(args: &[VarValue], add_new_line: bool) -> FuncResult {
for v in args.iter() {
print!("{}", v.to_string());
}
if add_new_line {
println!();
}
Ok(VarValue::Int(1))
}
fn format_time(args: &[VarValue]) -> FuncResult {
let now = chrono::Local::now();
let format = if args.is_empty() { "%Y%m%d-%H%M%S".to_string() } else { args[0].to_flat_string() };
let r = match format.to_lowercase().as_str() {
"2822" | "rfc2822" => now.to_rfc2822(),
"3339" | "rfc3339" => now.to_rfc3339(),
_ => now.format(&format).to_string(),
};
Ok(VarValue::Str(r))
}
fn trim_string(args: &[VarValue], dir: Where) -> FuncResult {
if args.is_empty() {
return Ok(VarValue::Str(String::new()));
}
let s = args[0].to_string();
if args.len() == 1 {
let st = match dir {
Where::All => s.trim(),
Where::Left => s.trim_start(),
Where::Right => s.trim_end(),
};
return Ok(VarValue::from(st));
}
let what = args[1].to_string().chars().next();
let c = match what {
None => return Ok(VarValue::Str(s)),
Some(cc) => cc,
};
let st = match dir {
Where::All => s.trim_matches(c),
Where::Left => s.trim_start_matches(c),
Where::Right => s.trim_end_matches(c),
};
Ok(VarValue::from(st))
}
fn starts_with(args: &[VarValue]) -> FuncResult {
if args.len() < 2 {
return Ok(VarValue::Int(1));
}
let s = args[0].to_string();
for a in args[1..].iter() {
let what = a.to_string();
if s.starts_with(&what) {
return Ok(VarValue::Int(1));
}
}
Ok(VarValue::Int(0))
}
fn ends_with(args: &[VarValue]) -> FuncResult {
if args.len() < 2 {
return Ok(VarValue::Int(1));
}
let s = args[0].to_string();
for a in args[1..].iter() {
let what = a.to_string();
if s.ends_with(&what) {
return Ok(VarValue::Int(1));
}
}
Ok(VarValue::Int(0))
}
fn change_case(args: &[VarValue], case: StrCase) -> FuncResult {
if args.is_empty() {
return Ok(VarValue::Str(String::new()));
}
let s = args[0].to_string();
let res = match case {
StrCase::Up => s.to_uppercase(),
StrCase::Low => s.to_lowercase(),
};
Ok(VarValue::Str(res))
}
fn contains(args: &[VarValue]) -> FuncResult {
if args.len() < 2 {
return Ok(VarValue::Int(1));
}
let s = args[0].to_string();
for a in args[1..].iter() {
let what = a.to_string();
if s.find(&what).is_some() {
return Ok(VarValue::Int(1));
}
}
Ok(VarValue::Int(0))
}
fn replace(args: &[VarValue]) -> FuncResult {
if args.len() < 2 {
return Err("requires at least two arguments".to_string());
}
let s = args[0].to_string();
let what = args[1].to_string();
let with = if args.len() > 2 { args[2].to_string() } else { String::new() };
Ok(VarValue::Str(s.replace(&what, &with)))
}
fn match_regex(args: &[VarValue]) -> FuncResult {
if args.len() < 2 {
return Ok(VarValue::from(1));
}
let s = args[0].to_string();
for a in args[1..].iter() {
let rx = a.to_string();
match Regex::new(&rx) {
Err(e) => return Err(e.to_string()),
Ok(r) => {
if r.is_match(&s) {
return Ok(VarValue::Int(1));
}
}
}
}
Ok(VarValue::Int(0))
}
fn substr_regex(args: &[VarValue]) -> FuncResult {
if args.is_empty() {
return Ok(VarValue::from(""));
}
if args.len() == 1 {
return Ok(args[0].clone());
}
let c_idx: usize = if args.len() < 3 { 0 } else { args[2].to_int() as usize };
let s = args[0].to_string();
let r = args[1].to_string();
let rx = match Regex::new(&r) {
Err(e) => return Err(e.to_string()),
Ok(rg) => rg,
};
let sub = match rx.captures(&s) {
None => VarValue::from(""),
Some(rs) => VarValue::from(rs.get(c_idx).map_or("", |st| st.as_str())),
};
Ok(sub)
}
fn pad(args: &[VarValue], loc: Where) -> FuncResult {
if args.len() < 3 {
return Err("requires three arguments".to_string());
}
let patt = args[1].to_string();
let patt_width = patt.width() as usize;
if patt_width == 0 {
return Err("pad string cannot be empty".to_string());
}
let l = args[2].to_int() as usize;
let s = args[0].to_string();
let orig_width = s.width() as usize;
if orig_width + patt_width >= l {
return Ok(VarValue::from(s));
}
let cnt = (l - orig_width) / patt_width;
let res = match loc {
Where::All => {
let right = cnt / 2;
let left = cnt - right;
patt.repeat(left) + &s + &patt.repeat(right)
}
Where::Left => patt.repeat(cnt) + &s,
Where::Right => s + &patt.repeat(cnt),
};
Ok(VarValue::from(res))
}
fn fields(args: &[VarValue]) -> FuncResult {
if args.len() < 2 {
return Err("requires at least two arguments".to_string());
}
let s = args[0].to_string();
let mut vals = Vec::new();
let flds: Vec<&str> = s.split_whitespace().collect();
for a in args[1..].iter() {
let idx = a.to_int() as usize;
if idx >= flds.len() {
vals.push(String::new());
} else {
vals.push(flds[idx].to_string());
}
}
if args.len() == 2 {
Ok(VarValue::from(vals.pop().unwrap())) } else {
Ok(VarValue::List(vals))
}
}
fn fields_with_sep(args: &[VarValue]) -> FuncResult {
if args.len() < 3 {
return Err("requires at least three arguments".to_string());
}
let s = args[0].to_string();
let sep = args[1].to_string();
if sep.is_empty() {
return Err("separator cannot be emtpy".to_string());
}
let mut vals = Vec::new();
let flds: Vec<&str> = s.split(&sep).collect();
for a in args[2..].iter() {
let idx = a.to_int() as usize;
if idx >= flds.len() {
vals.push(String::new());
} else {
vals.push(flds[idx].to_string());
}
}
if args.len() == 3 {
Ok(VarValue::from(vals.pop().unwrap())) } else {
Ok(VarValue::List(vals))
}
}
fn rand_string(args: &[VarValue]) -> FuncResult {
if args.is_empty() {
return Err("requires at least one argument".to_string());
}
let l = args[0].to_int();
if l <= 0 {
return Err("length must be greater than 0".to_string());
}
let ls = l as usize;
let chr: Vec<char> = if args.len() == 1 {
LETTERS.chars().collect()
} else {
let arg2 = args[1].to_string();
arg2.as_str().chars().collect()
};
let mx = chr.len();
if mx < 10 {
let r: String = chr.into_iter().collect();
return Err(format!("alphabet '{}' is too short: must have at least 10 characters", r));
}
let mut rng = thread_rng();
let mut c: Vec<char> = Vec::with_capacity(ls);
for _idx in 0..ls {
let cidx = rng.gen_range(0, mx) as usize;
c.push(chr[cidx]);
}
let s: String = c.into_iter().collect();
Ok(VarValue::from(s))
}
fn increment(args: &[VarValue]) -> FuncResult {
if args.is_empty() {
return Ok(VarValue::Int(1));
}
let mut val = args[0].to_int();
if args.len() == 1 {
return Ok(VarValue::Int(val + 1));
}
for a in args[1..].iter() {
let inc = a.to_int();
val += inc;
}
Ok(VarValue::Int(val))
}
fn decrement(args: &[VarValue]) -> FuncResult {
if args.is_empty() {
return Ok(VarValue::Int(-1));
}
let mut val = args[0].to_int();
if args.len() == 1 {
return Ok(VarValue::Int(val - 1));
}
for a in args[1..].iter() {
let inc = a.to_int();
val -= inc;
}
Ok(VarValue::Int(val))
}
fn globfiles(args: &[VarValue]) -> FuncResult {
let patt = if args.is_empty() { "*".to_owned() } else { args[0].to_string() };
let globtype = if args.len() > 1 { args[1].to_int() } else { 0 };
let mut v: Vec<String> = Vec::new();
let entries = match glob(&patt) {
Ok(p) => p,
Err(e) => return Err(e.to_string()),
};
for entry in entries {
if let Ok(p) = entry {
if globtype == 1 && !p.is_file() {
continue;
}
if globtype == 2 && !p.is_dir() {
continue;
}
let s = p.to_string_lossy();
v.push(s.to_string());
}
}
Ok(VarValue::List(v))
}
fn semver_cmp(args: &[VarValue], op: SemverCmp) -> FuncResult {
if args.is_empty() {
return match op {
SemverCmp::Equal => Ok(VarValue::from(1)),
_ => Ok(VarValue::from(0)),
};
}
if args.len() == 1 {
return match op {
SemverCmp::Less => Ok(VarValue::from(0)),
_ => Ok(VarValue::from(1)),
};
}
let v1 = match Version::parse(&args[0].to_string()) {
Ok(vr) => vr,
Err(e) => return Err(e.to_string()),
};
let v2 = match Version::parse(&args[1].to_string()) {
Ok(vr) => vr,
Err(e) => return Err(e.to_string()),
};
let res = match op {
SemverCmp::Equal => v1 == v2,
SemverCmp::Greater => v1 > v2,
SemverCmp::Less => v1 < v2,
};
Ok(VarValue::from(res))
}
fn semver_equal(args: &[VarValue]) -> FuncResult {
semver_cmp(args, SemverCmp::Equal)
}
fn semver_greater(args: &[VarValue]) -> FuncResult {
semver_cmp(args, SemverCmp::Greater)
}
fn semver_less(args: &[VarValue]) -> FuncResult {
semver_cmp(args, SemverCmp::Less)
}
fn semver_match(args: &[VarValue]) -> FuncResult {
if args.len() < 2 {
return Ok(VarValue::from(0));
}
let patt = match VersionReq::parse(&args[0].to_string()) {
Ok(vr) => vr,
Err(e) => return Err(e.to_string()),
};
let ver = match Version::parse(&args[1].to_string()) {
Ok(vr) => vr,
Err(e) => return Err(e.to_string()),
};
Ok(VarValue::from(patt.matches(&ver)))
}
fn semver_inc(args: &[VarValue]) -> FuncResult {
if args.is_empty() || (args.len() == 1 && args[0].to_string().is_empty()) {
return Ok(VarValue::from("0.0.1"));
}
let part = if args.len() < 2 {
Semver::Patch
} else {
match args[1].to_int() {
0 => Semver::Major,
1 => Semver::Minor,
2 => Semver::Patch,
num => return Err(format!("invalid semantic version part: {}", num)),
}
};
let mut v = match Version::parse(&args[0].to_string()) {
Ok(ver) => ver,
Err(e) => return Err(e.to_string()),
};
match part {
Semver::Major => v.increment_major(),
Semver::Minor => v.increment_minor(),
Semver::Patch => v.increment_patch(),
}
Ok(VarValue::from(format!("{}", v)))
}
#[cfg(test)]
mod path_test {
use super::*;
#[test]
fn extract() {
#[cfg(windows)]
let v = vec![VarValue::from("c:\\tmp\\file.abc")];
#[cfg(unix)]
let v = vec![VarValue::from("/tmp/file.abc")];
let r = extract_part(&v, PathPart::Ext);
assert_eq!(r, Ok(VarValue::from("abc")));
let r = extract_part(&v, PathPart::Stem);
assert_eq!(r, Ok(VarValue::from("file")));
let r = extract_part(&v, PathPart::Name);
assert_eq!(r, Ok(VarValue::from("file.abc")));
let r = extract_part(&v, PathPart::Dir);
#[cfg(windows)]
assert_eq!(r, Ok(VarValue::from("c:\\tmp")));
#[cfg(unix)]
assert_eq!(r, Ok(VarValue::from("/tmp")));
}
#[test]
fn change_ext() {
let v = vec![VarValue::from("file.abc"), VarValue::Str(String::new())];
let r = replace_ext(&v);
assert_eq!(r, Ok(VarValue::from("file")));
let v = vec![VarValue::from("file.abc"), VarValue::from("def")];
let r = replace_ext(&v);
assert_eq!(r, Ok(VarValue::from("file.def")));
let v = vec![VarValue::from("file.abc")];
let r = replace_ext(&v);
assert_eq!(r, Ok(VarValue::from("file.abc")));
}
#[test]
fn append_ext() {
let v = vec![VarValue::from("file.abc"), VarValue::Str(String::new())];
let r = add_ext(&v);
assert_eq!(r, Ok(VarValue::from("file.abc")));
let v = vec![VarValue::from("file.abc"), VarValue::from("def")];
let r = add_ext(&v);
assert_eq!(r, Ok(VarValue::from("file.abc.def")));
let v = vec![VarValue::from("file.abc")];
let r = add_ext(&v);
assert_eq!(r, Ok(VarValue::from("file.abc")));
}
#[test]
fn change_name() {
let v = vec![VarValue::from("file.abc"), VarValue::Str(String::new())];
let r = replace_name(&v);
assert_eq!(r, Ok(VarValue::from("")));
let v = vec![VarValue::from("file.abc"), VarValue::from("some.def")];
let r = replace_name(&v);
assert_eq!(r, Ok(VarValue::from("some.def")));
let v = vec![VarValue::from("file.abc")];
let r = replace_name(&v);
assert!(r.is_err());
}
#[test]
fn change_stem() {
let v = vec![VarValue::from("file.abc"), VarValue::Str(String::new())];
let r = replace_stem(&v);
assert!(r.is_err());
let v = vec![VarValue::from("file.abc"), VarValue::from("some.def")];
let r = replace_stem(&v);
assert_eq!(r, Ok(VarValue::from("some.def.abc")));
let v = vec![VarValue::from("file.abc"), VarValue::from("some")];
let r = replace_stem(&v);
assert_eq!(r, Ok(VarValue::from("some.abc")));
let v = vec![VarValue::from("file.abc")];
let r = replace_stem(&v);
assert!(r.is_err());
}
#[test]
fn trims() {
let v = vec![VarValue::from(" \n abc\t ")];
let r = trim_string(&v, Where::All);
assert_eq!(r, Ok(VarValue::from("abc")));
let r = trim_string(&v, Where::Left);
assert_eq!(r, Ok(VarValue::from("abc\t ")));
let r = trim_string(&v, Where::Right);
assert_eq!(r, Ok(VarValue::from(" \n abc")));
let v = vec![VarValue::from("++abc==="), VarValue::from("+")];
let r = trim_string(&v, Where::All);
assert_eq!(r, Ok(VarValue::from("abc===")));
let v = vec![VarValue::from("++abc==="), VarValue::from("=")];
let r = trim_string(&v, Where::All);
assert_eq!(r, Ok(VarValue::from("++abc")));
let v = vec![VarValue::from("++abc==="), VarValue::from("+")];
let r = trim_string(&v, Where::Left);
assert_eq!(r, Ok(VarValue::from("abc===")));
let r = trim_string(&v, Where::Right);
assert_eq!(r, Ok(VarValue::from("++abc===")));
}
#[test]
fn end_start() {
let v = vec![VarValue::from("testabc")];
let r = starts_with(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let r = ends_with(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let v = vec![VarValue::from("testabc"), VarValue::from("test")];
let r = starts_with(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let r = ends_with(&v);
assert_eq!(r, Ok(VarValue::Int(0)));
let v = vec![VarValue::from("testabc"), VarValue::from("abc")];
let r = starts_with(&v);
assert_eq!(r, Ok(VarValue::Int(0)));
let r = ends_with(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let v = vec![VarValue::from("testabc"), VarValue::from("xxx")];
let r = starts_with(&v);
assert_eq!(r, Ok(VarValue::Int(0)));
let r = ends_with(&v);
assert_eq!(r, Ok(VarValue::Int(0)));
let v = vec![VarValue::from("testabc"), VarValue::from("")];
let r = starts_with(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let r = ends_with(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let v = vec![VarValue::from("testabc"), VarValue::from("test"), VarValue::from("abc")];
let r = starts_with(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let r = ends_with(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
}
#[test]
fn up_low() {
let v = vec![VarValue::from("aBc DeF")];
let r = change_case(&v, StrCase::Low);
assert_eq!(r, Ok(VarValue::from("abc def")));
let r = change_case(&v, StrCase::Up);
assert_eq!(r, Ok(VarValue::from("ABC DEF")));
}
#[test]
fn contain() {
let v = vec![VarValue::from("aBc DeF")];
let r = contains(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let v = vec![VarValue::from("aBc DeF"), VarValue::from("Bc")];
let r = contains(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let v = vec![VarValue::from("aBc DeF"), VarValue::from("bc")];
let r = contains(&v);
assert_eq!(r, Ok(VarValue::Int(0)));
let v = vec![VarValue::from("aBc DeF"), VarValue::from("bc"), VarValue::from("eF")];
let r = contains(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
}
#[test]
fn replaces() {
let v = vec![VarValue::from("aBc DeF")];
let r = replace(&v);
assert!(r.is_err());
let v = vec![VarValue::from("abc def"), VarValue::from("bc")];
let r = replace(&v);
assert_eq!(r, Ok(VarValue::from("a def")));
let v = vec![VarValue::from("abc def"), VarValue::from("Bc")];
let r = replace(&v);
assert_eq!(r, Ok(VarValue::from("abc def")));
let v = vec![VarValue::from("abc def"), VarValue::from("bc"), VarValue::from("eFG")];
let r = replace(&v);
assert_eq!(r, Ok(VarValue::from("aeFG def")));
}
#[test]
fn matches() {
let v = vec![VarValue::from("aBc DeF")];
let r = match_regex(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let v = vec![VarValue::from("abc def"), VarValue::from("bc")];
let r = match_regex(&v);
assert_eq!(r, Ok(VarValue::from(1)));
let v = vec![VarValue::from("abc def"), VarValue::from("b.*e")];
let r = match_regex(&v);
assert_eq!(r, Ok(VarValue::from(1)));
let v = vec![VarValue::from("abc def"), VarValue::from("b.*g")];
let r = match_regex(&v);
assert_eq!(r, Ok(VarValue::from(0)));
let v = vec![VarValue::from("abc def"), VarValue::from("b.*g"), VarValue::from("d[mge]+")];
let r = match_regex(&v);
assert_eq!(r, Ok(VarValue::from(1)));
}
#[test]
fn substrings() {
let v = vec![VarValue::from("aBc DeF")];
let r = substr_regex(&v);
assert_eq!(r, Ok(VarValue::from("aBc DeF")));
let v = vec![VarValue::from("aBc DeF"), VarValue::from("c+.*e+")];
let r = substr_regex(&v);
assert_eq!(r, Ok(VarValue::from("c De")));
let v = vec![VarValue::from("aBc DeF"), VarValue::from("(c+).*(e+)")];
let r = substr_regex(&v);
assert_eq!(r, Ok(VarValue::from("c De")));
let v = vec![VarValue::from("aBc DeF"), VarValue::from("(c+).*(e+)"), VarValue::from(0)];
let r = substr_regex(&v);
assert_eq!(r, Ok(VarValue::from("c De")));
let v = vec![VarValue::from("aBc DeF"), VarValue::from("(c+).*(e+)"), VarValue::from(1)];
let r = substr_regex(&v);
assert_eq!(r, Ok(VarValue::from("c")));
let v = vec![VarValue::from("aBc DeF"), VarValue::from("(c+).*(e+)"), VarValue::from(2)];
let r = substr_regex(&v);
assert_eq!(r, Ok(VarValue::from("e")));
}
#[test]
fn pads() {
let v = vec![VarValue::from("abc")];
let r = pad(&v, Where::All);
assert!(r.is_err());
let v = vec![VarValue::from("abc"), VarValue::from("+=")];
let r = pad(&v, Where::All);
assert!(r.is_err());
let v = vec![VarValue::from("abc"), VarValue::from("")];
let r = pad(&v, Where::All);
assert!(r.is_err());
let v = vec![VarValue::from("abc"), VarValue::from("+="), VarValue::from("aa")];
let r = pad(&v, Where::All);
assert_eq!(r, Ok(VarValue::from("abc")));
let v = vec![VarValue::from("abc"), VarValue::from("+="), VarValue::from(0)];
let r = pad(&v, Where::All);
assert_eq!(r, Ok(VarValue::from("abc")));
let v = vec![VarValue::from("abc"), VarValue::from("+="), VarValue::from(2)];
let r = pad(&v, Where::All);
assert_eq!(r, Ok(VarValue::from("abc")));
let v = vec![VarValue::from("abc"), VarValue::from("+="), VarValue::from(10)];
let r = pad(&v, Where::All);
assert_eq!(r, Ok(VarValue::from("+=+=abc+=")));
let r = pad(&v, Where::Left);
assert_eq!(r, Ok(VarValue::from("+=+=+=abc")));
let r = pad(&v, Where::Right);
assert_eq!(r, Ok(VarValue::from("abc+=+=+=")));
let v = vec![VarValue::from("abc"), VarValue::from("+="), VarValue::from(11)];
let r = pad(&v, Where::All);
assert_eq!(r, Ok(VarValue::from("+=+=abc+=+=")));
}
#[test]
fn field() {
let v = vec![VarValue::from("abc def\tghi")];
let r = fields(&v);
assert!(r.is_err());
let v = vec![VarValue::from("abc def\tghi"), VarValue::from("s")];
let r = fields(&v);
assert_eq!(r, Ok(VarValue::from("abc")));
let v = vec![VarValue::from("abc def\tghi"), VarValue::from(8)];
let r = fields(&v);
assert_eq!(r, Ok(VarValue::from("")));
let v = vec![VarValue::from("abc def\tghi"), VarValue::from(1), VarValue::from(2), VarValue::from(1)];
let r = fields(&v);
assert_eq!(r, Ok(VarValue::List(vec!["def".to_string(), "ghi".to_string(), "def".to_string()])));
let v = vec![VarValue::from("abc daf\tahi")];
let r = fields_with_sep(&v);
assert!(r.is_err());
let v = vec![VarValue::from("abc daf\tahi"), VarValue::from("a")];
let r = fields_with_sep(&v);
assert!(r.is_err());
let v = vec![VarValue::from("abc daf\tahi"), VarValue::from("a"), VarValue::from(8)];
let r = fields_with_sep(&v);
assert_eq!(r, Ok(VarValue::from("")));
let v = vec![VarValue::from("abc daf\tahi"), VarValue::from("a"), VarValue::from(2), VarValue::from("1")];
let r = fields_with_sep(&v);
assert_eq!(r, Ok(VarValue::List(vec!["f\t".to_string(), "bc d".to_string()])));
let v = vec![VarValue::from("abc daf\tahi"), VarValue::from("a"), VarValue::from(1), VarValue::from("asd")];
let r = fields_with_sep(&v);
assert_eq!(r, Ok(VarValue::List(vec!["bc d".to_string(), String::new()])));
}
#[test]
fn rand() {
let v = vec![VarValue::from(10)];
let s1 = rand_string(&v);
let s2 = rand_string(&v);
assert_eq!(s1.clone().unwrap().to_string().len(), 10);
assert_eq!(s2.clone().unwrap().to_string().len(), 10);
assert_ne!(s1, s2);
let v = vec![VarValue::from(10), VarValue::from("0123456789")];
let s1 = rand_string(&v);
for chr in s1.unwrap().to_string().chars() {
assert!(chr >= '0' && chr <= '9');
}
}
#[test]
fn inc() {
let v: Vec<VarValue> = Vec::new();
let r = increment(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let v = vec![VarValue::from("abc")];
let r = increment(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let r = increment(&v);
assert_eq!(r, Ok(VarValue::Int(1)));
let v = vec![VarValue::from(10)];
let r = increment(&v);
assert_eq!(r, Ok(VarValue::Int(11)));
let v = vec![VarValue::from(10), VarValue::from(-3), VarValue::from(77)];
let r = increment(&v);
assert_eq!(r, Ok(VarValue::Int(84)));
}
#[test]
fn dec() {
let v: Vec<VarValue> = Vec::new();
let r = decrement(&v);
assert_eq!(r, Ok(VarValue::Int(-1)));
let v = vec![VarValue::from("abc")];
let r = decrement(&v);
assert_eq!(r, Ok(VarValue::Int(-1)));
let v = vec![VarValue::from(10)];
let r = decrement(&v);
assert_eq!(r, Ok(VarValue::Int(9)));
let v = vec![VarValue::from(10), VarValue::from(-3), VarValue::from(77)];
let r = decrement(&v);
assert_eq!(r, Ok(VarValue::Int(-64)));
}
#[test]
fn semver_incs() {
let v = vec![VarValue::from("1.2.3")];
let r = semver_inc(&v);
assert_eq!(r, Ok(VarValue::from("1.2.4")));
let v = vec![VarValue::from("1.2.3"), VarValue::from(0)];
let r = semver_inc(&v);
assert_eq!(r, Ok(VarValue::from("2.0.0")));
let v = vec![VarValue::from("1.2.3"), VarValue::from(1)];
let r = semver_inc(&v);
assert_eq!(r, Ok(VarValue::from("1.3.0")));
let v = vec![VarValue::from("1.2.3"), VarValue::from(2)];
let r = semver_inc(&v);
assert_eq!(r, Ok(VarValue::from("1.2.4")));
}
#[test]
fn semver_cmps() {
let v = vec![VarValue::from("1.2.3"), VarValue::from("1.2.3")];
let r = semver_equal(&v);
assert_eq!(r, Ok(VarValue::from(1)));
let r = semver_less(&v);
assert_eq!(r, Ok(VarValue::from(0)));
let r = semver_greater(&v);
assert_eq!(r, Ok(VarValue::from(0)));
let v = vec![VarValue::from("1.2.3"), VarValue::from("1.5.3")];
let r = semver_equal(&v);
assert_eq!(r, Ok(VarValue::from(0)));
let r = semver_less(&v);
assert_eq!(r, Ok(VarValue::from(1)));
let r = semver_greater(&v);
assert_eq!(r, Ok(VarValue::from(0)));
let v = vec![VarValue::from("1.2.3"), VarValue::from("0.5.3")];
let r = semver_equal(&v);
assert_eq!(r, Ok(VarValue::from(0)));
let r = semver_less(&v);
assert_eq!(r, Ok(VarValue::from(0)));
let r = semver_greater(&v);
assert_eq!(r, Ok(VarValue::from(1)));
}
#[test]
fn semver_matches() {
struct Task {
p: &'static str,
v: &'static str,
r: VarValue,
}
let tasks: Vec<Task> = vec![
Task { p: "1.2", v: "1.2.1", r: VarValue::from(1) },
Task { p: "1", v: "1.2.1", r: VarValue::from(1) },
Task { p: "2", v: "1.2.1", r: VarValue::from(0) },
Task { p: ">1.3", v: "1.2.1", r: VarValue::from(0) },
Task { p: ">1.3", v: "2.2.1", r: VarValue::from(1) },
Task { p: "<1.3", v: "1.2.1", r: VarValue::from(1) },
Task { p: "<1.3", v: "2.2.1", r: VarValue::from(0) },
];
for t in tasks.iter() {
let vc = vec![VarValue::from(t.p), VarValue::from(t.v)];
let res = semver_match(&vc);
assert_eq!(res, Ok(t.r.clone()));
}
}
}