#[inline]
pub(crate) fn is_digit(string: &str) -> bool {
string.chars().all(|c| c.is_ascii_digit())
}
#[inline]
pub(crate) fn is_exec(string: &str) -> bool {
string.starts_with("$ ")
}
#[inline]
pub(crate) fn is_garden(string: &str) -> bool {
string.starts_with(':')
}
#[inline]
pub(crate) fn is_group(string: &str) -> bool {
string.starts_with('%')
}
#[inline]
pub(crate) fn is_tree(string: &str) -> bool {
string.starts_with('@')
}
#[inline]
pub(crate) fn is_append_op(string: &str) -> bool {
string.ends_with('+')
}
#[inline]
pub(crate) fn is_replace_op(string: &str) -> bool {
string.ends_with('=')
}
#[inline]
pub(crate) fn is_graft(string: &str) -> bool {
string.contains("::")
}
#[inline]
pub(crate) fn is_eval_candidate(string: &str) -> bool {
string.contains('$')
}
#[inline]
pub(crate) fn is_git_dir(string: &str) -> bool {
string.len() > 4 && string.ends_with(".git") && !string.ends_with("/.git")
}
#[inline]
pub(crate) fn is_post_command(string: &str) -> bool {
string.ends_with('>')
}
#[inline]
pub(crate) fn is_pre_command(string: &str) -> bool {
string.ends_with('<')
}
#[inline]
pub fn is_pre_or_post_command(string: &str) -> bool {
is_pre_command(string) || is_post_command(string)
}
#[inline]
pub(crate) fn is_shebang(string: &str) -> bool {
string.starts_with("#!")
}
#[inline]
pub(crate) fn trim(string: &str) -> &str {
let needs_trim = is_group(string) || is_tree(string) || is_garden(string);
if !string.is_empty() && needs_trim {
&string[1..]
} else {
string
}
}
#[inline]
pub(crate) fn trim_exec(string: &str) -> &str {
let prefix = "$ ";
let prefix_len = prefix.len();
if string.len() >= prefix_len && string.starts_with(prefix) {
&string[prefix_len..]
} else {
string
}
}
#[inline]
pub fn trim_op_inplace(string: &mut String) {
let len = string.len();
if len > 1 {
string.remove(len - 1);
}
}
#[inline]
pub(crate) fn split_graft(string: &str) -> Option<(&str, &str)> {
string.split_once("::")
}
#[inline]
pub(crate) fn trim_graft(string: &str) -> Option<String> {
let (_before, after) = match split_graft(string) {
Some((before, after)) => (before, after),
None => return None,
};
let result;
if is_garden(string) {
result = string!(":") + after;
} else if is_group(string) {
result = string!("%") + after;
} else if is_tree(string) {
result = string!("@") + after;
} else {
result = after.to_string();
}
Some(result)
}
#[inline]
pub(crate) fn graft_basename(string: &str) -> Option<&str> {
let (before, _after) = match split_graft(string) {
Some((before, after)) => (before, after),
None => return None,
};
let result = if is_garden(string) || is_group(string) || is_tree(string) {
trim(before)
} else {
before
};
Some(result)
}
#[inline]
pub(crate) fn trim_shebang(string: &str) -> Option<&str> {
if is_shebang(string) {
Some(&string[2..])
} else {
None
}
}
pub(crate) fn split_shebang(string: &str) -> Option<(&str, &str)> {
if let Some(trimmed) = trim_shebang(string) {
trimmed.split_once('\n')
} else {
None
}
}
#[inline]
pub(crate) fn escape_shell_variables(string: &str) -> String {
let mut result = String::new();
let mut potential_variable = false;
for c in string.chars() {
if potential_variable {
if c.is_alphanumeric() || c == '_' {
result.push('$'); result.push(c);
} else if c == '$' {
result.push('$'); } else {
result.push(c);
}
potential_variable = false;
} else {
result.push(c);
potential_variable = c == '$';
}
}
result
}
#[inline]
pub(crate) fn bool_to_string(value: bool) -> String {
match value {
true => string!("true"),
false => string!("false"),
}
}
#[inline]
pub(crate) fn string_to_bool(value: &str) -> Option<bool> {
match value {
"true" | "1" => Some(true),
"false" | "0" => Some(false),
_ => None,
}
}
#[inline]
pub(crate) fn pre_command(name: &str) -> String {
format!("{name}<")
}
#[inline]
pub(crate) fn post_command(name: &str) -> String {
format!("{name}>")
}
#[cfg(test)]
mod tests {
#[test]
fn is_garden() {
assert!(super::is_garden(":garden"), ":garden is a garden");
assert!(!super::is_garden("garden"), "garden is not a garden");
}
#[test]
fn is_graft() {
assert!(super::is_graft("foo::bar"), "foo::bar is a graft");
assert!(!super::is_graft("foo"), "foo is not a graft");
}
#[test]
fn is_group() {
assert!(super::is_group("%group"), "%group is a group");
assert!(!super::is_group("group"), "group is not a group");
}
#[test]
fn is_tree() {
assert!(super::is_tree("@tree"), "@tree is a tree");
assert!(!super::is_tree("tree"), "tree is not a tree");
}
#[test]
fn is_git_dir() {
assert!(super::is_git_dir("tree.git"), "tree.git is a git dir");
assert!(
super::is_git_dir("/src/tree.git"),
"/src/tree.git is a git dir"
);
assert!(
!super::is_git_dir("src/tree/.git"),
"src/tree/.git is a git dir"
);
assert!(!super::is_git_dir(".git"), ".git is a git dir");
assert!(!super::is_git_dir("/.git"), "/.git is a git dir");
}
#[test]
fn split_graft_ok() {
let split = super::split_graft("foo::bar");
assert!(split.is_some(), "split_graft on foo::bar is ok");
assert_eq!(split, Some(("foo", "bar")));
}
#[test]
fn split_graft_nested_ok() {
let split = super::split_graft("@foo::bar::baz");
assert!(split.is_some(), "split_graft on @foo::bar::baz is ok");
assert_eq!(split, Some(("@foo", "bar::baz")));
}
#[test]
fn split_graft_empty() {
let split = super::split_graft("foo::");
assert!(split.is_some(), "split_graft on foo:: is ok");
assert_eq!(split, Some(("foo", "")));
}
#[test]
fn split_graft_not_found() {
let split = super::split_graft("foo");
assert!(split.is_none(), "split_graft on foo is None");
}
#[test]
fn trim_exec() {
assert_eq!("cmd", super::trim_exec("$ cmd"));
assert_eq!("$cmd", super::trim_exec("$cmd"));
assert_eq!("cmd", super::trim_exec("cmd"));
assert_eq!("", super::trim_exec("$ "));
assert_eq!("$", super::trim_exec("$"));
assert_eq!("", super::trim_exec(""));
}
#[test]
fn trim_graft() {
let value = super::trim_graft("foo::bar::baz");
assert!(value.is_some());
assert_eq!("bar::baz", value.unwrap());
let value = super::trim_graft("@foo::bar::baz");
assert!(value.is_some());
assert_eq!("@bar::baz", value.unwrap());
let value = super::trim_graft("%foo::bar::baz");
assert!(value.is_some());
assert_eq!("%bar::baz", value.unwrap());
let value = super::trim_graft(":foo::bar::baz");
assert!(value.is_some());
assert_eq!(":bar::baz", value.unwrap());
let value = super::trim_graft("foo::bar");
assert!(value.is_some());
assert_eq!("bar", value.unwrap());
let value = super::trim_graft("foo");
assert!(value.is_none());
}
#[test]
fn graft_basename() {
let value = super::graft_basename("foo");
assert!(value.is_none());
let value = super::graft_basename(":foo");
assert!(value.is_none());
let value = super::graft_basename("%foo");
assert!(value.is_none());
let value = super::graft_basename("@foo");
assert!(value.is_none());
let value = super::graft_basename("foo::bar");
assert!(value.is_some());
assert_eq!("foo", value.unwrap());
let value = super::graft_basename(":foo::bar");
assert!(value.is_some());
assert_eq!("foo", value.unwrap());
let value = super::graft_basename("%foo::bar");
assert!(value.is_some());
assert_eq!("foo", value.unwrap());
let value = super::graft_basename("@foo::bar");
assert!(value.is_some());
assert_eq!("foo", value.unwrap());
let value = super::graft_basename("foo::bar::baz");
assert!(value.is_some());
assert_eq!("foo", value.unwrap());
let value = super::graft_basename(":foo::bar::baz");
assert!(value.is_some());
assert_eq!("foo", value.unwrap());
let value = super::graft_basename("%foo::bar::baz");
assert!(value.is_some());
assert_eq!("foo", value.unwrap());
let value = super::graft_basename("@foo::bar::baz");
assert!(value.is_some());
assert_eq!("foo", value.unwrap());
}
#[test]
fn escape_shell_variables() {
let value = super::escape_shell_variables("$");
assert_eq!(value, "$");
let value = super::escape_shell_variables("$ ");
assert_eq!(value, "$ ");
let value = super::escape_shell_variables("$$");
assert_eq!(value, "$$");
let value = super::escape_shell_variables("$_");
assert_eq!(value, "$$_");
let value = super::escape_shell_variables("$a");
assert_eq!(value, "$$a");
let value = super::escape_shell_variables("$_a");
assert_eq!(value, "$$_a");
let value = super::escape_shell_variables("$ echo");
assert_eq!(value, "$ echo");
let value = super::escape_shell_variables("embedded $$ value");
assert_eq!(value, "embedded $$ value");
let value = super::escape_shell_variables("$variable");
assert_eq!(value, "$$variable");
let value = super::escape_shell_variables("$$variable");
assert_eq!(value, "$$variable");
let value = super::escape_shell_variables("${braces}${ignored}");
assert_eq!(value, "${braces}${ignored}");
let value = super::escape_shell_variables("$a ${b} $c $");
assert_eq!(value, "$$a ${b} $$c $");
let value = super::escape_shell_variables("echo $${value[@]:0:1}");
assert_eq!(value, "echo $${value[@]:0:1}");
}
#[test]
fn trim_and_split_shebang() {
assert!(super::is_shebang("#!test"));
let value = super::trim_shebang("#not-shebang\nvalue\n");
assert!(value.is_none());
let value = super::trim_shebang("#!test\nvalue\n");
assert!(value.is_some());
assert_eq!(value, Some("test\nvalue\n"));
let value = super::split_shebang("#not-shebang\nvalue\n");
assert!(value.is_none());
let value = super::split_shebang("#!test\nvalue\n");
assert_eq!(value, Some(("test", "value\n")));
let value = super::split_shebang("#comment\nvalue\n");
assert_eq!(value, None);
}
}