use std::cmp::max;
use std::sync::Arc;
use crate::moonblade::error::EvaluationError;
use crate::moonblade::types::{BoundArguments, DynamicValue};
use super::FunctionResult;
pub fn len(mut args: BoundArguments) -> FunctionResult {
let arg = args.pop1();
Ok(DynamicValue::from(match arg {
DynamicValue::List(list) => list.len(),
DynamicValue::Map(map) => map.len(),
_ => arg.try_as_str()?.chars().count(),
}))
}
pub fn range(args: BoundArguments) -> FunctionResult {
let (start, stop, step): (i64, i64, i64) = if args.len() == 3 {
(
args.get(0).unwrap().try_as_i64()?,
args.get(1).unwrap().try_as_i64()?,
args.get(2).unwrap().try_as_i64()?,
)
} else if args.len() == 2 {
(
args.get(0).unwrap().try_as_i64()?,
args.get(1).unwrap().try_as_i64()?,
1,
)
} else {
(0, args.get(0).unwrap().try_as_i64()?, 1)
};
if step == 0 {
return Err(EvaluationError::Custom("step cannot be 0".to_string()));
}
let len = if step > 0 {
if start >= stop {
0
} else {
((stop - start - 1) / step + 1) as usize
}
} else if start <= stop {
0
} else {
((start - stop - 1) / (-step) + 1) as usize
};
let mut indices = Vec::with_capacity(len);
let mut current = start;
if step > 0 {
while current < stop {
indices.push(DynamicValue::from(current));
current += step;
}
} else {
while current > stop {
indices.push(DynamicValue::from(current));
current += step;
}
}
Ok(DynamicValue::from(indices))
}
pub fn repeat(args: BoundArguments) -> FunctionResult {
let (to_repeat_arg, times_arg) = args.get2();
let times = times_arg.try_as_usize()?;
if let DynamicValue::List(items) = to_repeat_arg {
let mut repeated = Vec::with_capacity(items.len() * times);
for _ in 0..times {
for item in items.iter() {
repeated.push(item.clone());
}
}
Ok(DynamicValue::from(repeated))
} else {
let to_repeat = to_repeat_arg.try_as_str()?;
let mut repeated = String::with_capacity(to_repeat.len() * times);
for _ in 0..times {
repeated.push_str(&to_repeat);
}
Ok(DynamicValue::from(repeated))
}
}
pub fn first(mut args: BoundArguments) -> FunctionResult {
let arg = args.pop1();
Ok(match arg {
DynamicValue::String(string) => DynamicValue::from(string.chars().next()),
DynamicValue::Bytes(bytes) => DynamicValue::from(
std::str::from_utf8(&bytes)
.map_err(|_| EvaluationError::UnicodeDecodeError)?
.chars()
.next(),
),
DynamicValue::List(list) => match list.first() {
None => DynamicValue::None,
Some(value) => value.clone(),
},
_ => return Err(EvaluationError::from_cast(&arg, "sequence")),
})
}
pub fn last(mut args: BoundArguments) -> FunctionResult {
let arg = args.pop1();
Ok(match arg {
DynamicValue::String(string) => DynamicValue::from(string.chars().next_back()),
DynamicValue::Bytes(bytes) => DynamicValue::from(
std::str::from_utf8(&bytes)
.map_err(|_| EvaluationError::UnicodeDecodeError)?
.chars()
.next_back(),
),
DynamicValue::List(list) => match list.last() {
None => DynamicValue::None,
Some(value) => value.clone(),
},
_ => return Err(EvaluationError::from_cast(&arg, "sequence")),
})
}
pub fn slice(args: BoundArguments) -> FunctionResult {
let target = args.get(0).unwrap();
if let DynamicValue::List(list) = target {
let mut lo = args.get(1).unwrap().try_as_i64()?;
let opt_hi = args.get(2);
let sublist: Vec<DynamicValue> = match opt_hi {
None => {
if lo < 0 {
let l = list.len();
lo = max(0, l as i64 + lo);
list[..lo as usize].to_vec()
} else if lo >= list.len() as i64 {
Vec::new()
} else {
list[..lo as usize].to_vec()
}
}
Some(hi_value) => {
let mut hi = hi_value.try_as_i64()?;
if lo >= list.len() as i64 {
Vec::new()
} else if lo < 0 {
let l = list.len();
lo = max(0, l as i64 + lo);
if hi < 0 {
hi = max(0, l as i64 + hi);
}
if hi <= lo {
Vec::new()
} else {
list[lo as usize..hi.min(list.len() as i64) as usize].to_vec()
}
} else {
if hi < 0 {
let l = list.len();
hi = max(0, l as i64 + hi);
}
if hi <= lo {
Vec::new()
} else {
list[lo as usize..hi.min(list.len() as i64) as usize].to_vec()
}
}
}
};
return Ok(DynamicValue::from(sublist));
}
let string = target.try_as_str()?;
let mut lo = args.get(1).unwrap().try_as_i64()?;
let opt_hi = args.get(2);
let chars = string.chars();
let substring: String = match opt_hi {
None => {
if lo < 0 {
let l = string.chars().count();
lo = max(0, l as i64 + lo);
chars.skip(lo as usize).collect()
} else {
chars.skip(lo as usize).collect()
}
}
Some(hi_value) => {
let mut hi = hi_value.try_as_i64()?;
if lo < 0 {
let l = string.chars().count();
lo = max(0, l as i64 + lo);
if hi < 0 {
hi = max(0, l as i64 + hi);
}
if hi <= lo {
"".to_string()
} else {
chars.skip(lo as usize).take((hi - lo) as usize).collect()
}
} else {
if hi < 0 {
let l = string.chars().count();
hi = max(0, l as i64 + hi);
}
chars.skip(lo as usize).take((hi - lo) as usize).collect()
}
}
};
Ok(DynamicValue::from(substring))
}
pub fn concat(args: BoundArguments) -> FunctionResult {
let mut args_iter = args.into_iter();
let first = args_iter.next().unwrap();
match first {
DynamicValue::List(list) => match Arc::try_unwrap(list) {
Ok(mut owned_list) => {
for arg in args_iter {
owned_list.push(arg);
}
Ok(DynamicValue::from(owned_list))
}
Err(borrowed_list) => {
let mut result = Vec::clone(&borrowed_list);
for arg in args_iter {
result.push(arg);
}
Ok(DynamicValue::from(result))
}
},
value => {
let first_part = value.try_as_str()?;
let mut result = String::with_capacity(first_part.len());
result.push_str(&first_part);
for arg in args_iter {
result.push_str(&arg.try_as_str()?);
}
Ok(DynamicValue::from(result))
}
}
}
pub fn compact(mut args: BoundArguments) -> FunctionResult {
let arg = args.pop1();
let list = arg.try_into_arc_list()?;
Ok(match Arc::try_unwrap(list) {
Err(borrowed_list) => DynamicValue::from(
borrowed_list
.iter()
.filter(|value| value.is_truthy())
.cloned()
.collect::<Vec<_>>(),
),
Ok(mut owned_list) => {
owned_list.retain(|v| v.is_truthy());
DynamicValue::from(owned_list)
}
})
}