use crate::types::Value;
pub fn exec_repeat(input: &Value, count: &Value) -> Value {
let s = match input {
Value::Null => return Value::Null,
Value::Text(t) => t.as_str().to_owned(),
v => v.to_string(),
};
let n = match count {
Value::Null => return Value::Null,
Value::Numeric(crate::Numeric::Integer(i)) => *i,
Value::Numeric(crate::Numeric::Float(f)) => {
let f: f64 = (*f).into();
f as i64
}
Value::Text(t) => t.as_str().parse::<i64>().unwrap_or(0),
_ => return Value::Null,
};
if n <= 0 {
return Value::build_text(String::new());
}
Value::build_text(s.repeat(n as usize))
}
pub fn exec_lpad(input: &Value, length: &Value, fill: Option<&Value>) -> Value {
pad(input, length, fill, true)
}
pub fn exec_rpad(input: &Value, length: &Value, fill: Option<&Value>) -> Value {
pad(input, length, fill, false)
}
fn pad(input: &Value, length: &Value, fill: Option<&Value>, on_left: bool) -> Value {
let s = match input {
Value::Null => return Value::Null,
Value::Text(t) => t.as_str().to_owned(),
v => v.to_string(),
};
let target_len = match length {
Value::Null => return Value::Null,
Value::Numeric(crate::Numeric::Integer(i)) => *i,
Value::Numeric(crate::Numeric::Float(f)) => {
let f: f64 = (*f).into();
f as i64
}
Value::Text(t) => t.as_str().parse::<i64>().unwrap_or(0),
_ => return Value::Null,
};
if target_len <= 0 {
return Value::build_text(String::new());
}
let target_len = target_len as usize;
let char_count = s.chars().count();
if char_count >= target_len {
return Value::build_text(s.chars().take(target_len).collect::<String>());
}
let fill_str = match fill {
None => " ".to_string(),
Some(Value::Null) => return Value::Null,
Some(Value::Text(t)) => t.as_str().to_owned(),
Some(v) => v.to_string(),
};
let fill_chars: Vec<char> = fill_str.chars().collect();
if fill_chars.is_empty() {
return Value::build_text(s);
}
let pad: String = fill_chars
.iter()
.cycle()
.take(target_len - char_count)
.collect();
Value::build_text(if on_left {
format!("{pad}{s}")
} else {
format!("{s}{pad}")
})
}
#[cfg(test)]
mod tests {
use super::*;
fn t(s: &str) -> Value {
Value::build_text(s.to_string())
}
fn i(n: i64) -> Value {
Value::from_i64(n)
}
#[test]
fn repeat_basic() {
assert_eq!(exec_repeat(&t("ab"), &i(3)), t("ababab"));
assert_eq!(exec_repeat(&t("x"), &i(0)), t(""));
assert_eq!(exec_repeat(&t("x"), &i(-1)), t(""));
assert_eq!(exec_repeat(&t(""), &i(5)), t(""));
}
#[test]
fn repeat_null() {
assert!(matches!(exec_repeat(&Value::Null, &i(3)), Value::Null));
assert!(matches!(exec_repeat(&t("x"), &Value::Null), Value::Null));
}
#[test]
fn lpad_basic() {
assert_eq!(exec_lpad(&t("abc"), &i(6), None), t(" abc"));
assert_eq!(exec_lpad(&t("abc"), &i(6), Some(&t("xy"))), t("xyxabc"));
assert_eq!(exec_lpad(&t("abcdef"), &i(3), None), t("abc"));
assert_eq!(exec_lpad(&t("abc"), &i(3), Some(&t("x"))), t("abc"));
}
#[test]
fn rpad_basic() {
assert_eq!(exec_rpad(&t("abc"), &i(6), None), t("abc "));
assert_eq!(exec_rpad(&t("abc"), &i(6), Some(&t("xy"))), t("abcxyx"));
assert_eq!(exec_rpad(&t("abcdef"), &i(3), None), t("abc"));
}
#[test]
fn pad_null() {
assert!(matches!(exec_lpad(&Value::Null, &i(3), None), Value::Null));
assert!(matches!(
exec_lpad(&t("x"), &Value::Null, None),
Value::Null
));
assert!(matches!(
exec_lpad(&t("x"), &i(3), Some(&Value::Null)),
Value::Null
));
}
#[test]
fn pad_empty_fill_returns_input() {
assert_eq!(exec_lpad(&t("abc"), &i(10), Some(&t(""))), t("abc"));
assert_eq!(exec_rpad(&t("abc"), &i(10), Some(&t(""))), t("abc"));
}
#[test]
fn pad_unicode_counts_characters_not_bytes() {
assert_eq!(exec_lpad(&t("aéc"), &i(4), None), t(" aéc"));
assert_eq!(exec_rpad(&t("aéc"), &i(4), None), t("aéc "));
}
}