nushell/crates/nu-protocol/src/value/range.rs

225 lines
6.0 KiB
Rust
Raw Normal View History

2021-10-01 07:11:49 +02:00
use serde::{Deserialize, Serialize};
2021-09-12 13:12:53 +02:00
use std::cmp::Ordering;
2021-09-08 04:26:57 +02:00
/// A Range is an iterator over integers.
use crate::{
ast::{RangeInclusion, RangeOperator},
*,
};
2021-09-08 04:26:57 +02:00
2021-10-01 07:11:49 +02:00
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2021-09-08 04:26:57 +02:00
pub struct Range {
pub from: Value,
pub incr: Value,
2021-09-08 04:26:57 +02:00
pub to: Value,
pub inclusion: RangeInclusion,
}
impl Range {
pub fn new(
expr_span: Span,
from: Value,
next: Value,
to: Value,
operator: &RangeOperator,
) -> Result<Range, ShellError> {
// Select from & to values if they're not specified
// TODO: Replace the placeholder values with proper min/max based on data type
let from = if let Value::Nothing { .. } = from {
Value::Int {
val: 0i64,
span: Span::unknown(),
}
} else {
from
};
let to = if let Value::Nothing { .. } = to {
if let Ok(Value::Bool { val: true, .. }) = next.lt(expr_span, &from) {
Value::Int {
val: -100i64,
span: Span::unknown(),
}
} else {
Value::Int {
val: 100i64,
span: Span::unknown(),
}
}
} else {
to
};
// Check if the range counts up or down
let moves_up = matches!(from.lte(expr_span, &to), Ok(Value::Bool { val: true, .. }));
// Convert the next value into the inctement
let incr = if let Value::Nothing { .. } = next {
if moves_up {
Value::Int {
val: 1i64,
span: Span::unknown(),
}
} else {
Value::Int {
val: -1i64,
span: Span::unknown(),
}
}
} else {
next.sub(operator.next_op_span, &from)?
};
let zero = Value::Int {
val: 0i64,
span: Span::unknown(),
};
// Increment must be non-zero, otherwise we iterate forever
if matches!(incr.eq(expr_span, &zero), Ok(Value::Bool { val: true, .. })) {
return Err(ShellError::CannotCreateRange(expr_span));
}
// If to > from, then incr > 0, otherwise we iterate forever
if let (Value::Bool { val: true, .. }, Value::Bool { val: false, .. }) = (
to.gt(operator.span, &from)?,
incr.gt(operator.next_op_span, &zero)?,
) {
return Err(ShellError::CannotCreateRange(expr_span));
}
// If to < from, then incr < 0, otherwise we iterate forever
if let (Value::Bool { val: true, .. }, Value::Bool { val: false, .. }) = (
to.lt(operator.span, &from)?,
incr.lt(operator.next_op_span, &zero)?,
) {
return Err(ShellError::CannotCreateRange(expr_span));
}
Ok(Range {
from,
incr,
to,
inclusion: operator.inclusion,
})
}
pub fn moves_up(&self) -> bool {
self.from <= self.to
}
pub fn is_end_inclusive(&self) -> bool {
matches!(self.inclusion, RangeInclusion::Inclusive)
}
pub fn contains(&self, item: &Value) -> bool {
match (item.partial_cmp(&self.from), item.partial_cmp(&self.to)) {
(Some(Ordering::Greater | Ordering::Equal), Some(Ordering::Less)) => self.moves_up(),
(Some(Ordering::Less | Ordering::Equal), Some(Ordering::Greater)) => self.moves_up(),
(Some(_), Some(Ordering::Equal)) => self.is_end_inclusive(),
(_, _) => false,
}
}
}
2021-09-08 04:26:57 +02:00
impl IntoIterator for Range {
type Item = Value;
type IntoIter = RangeIterator;
fn into_iter(self) -> Self::IntoIter {
let span = self.from.span();
RangeIterator::new(self, span)
}
}
pub struct RangeIterator {
curr: Value,
end: Value,
span: Span,
is_end_inclusive: bool,
moves_up: bool,
incr: Value,
2021-09-08 04:26:57 +02:00
done: bool,
}
impl RangeIterator {
pub fn new(range: Range, span: Span) -> RangeIterator {
let moves_up = range.moves_up();
let is_end_inclusive = range.is_end_inclusive();
2021-09-08 04:26:57 +02:00
let start = match range.from {
Value::Nothing { .. } => Value::Int { val: 0, span },
x => x,
};
let end = match range.to {
Value::Nothing { .. } => Value::Int {
val: i64::MAX,
span,
},
x => x,
};
RangeIterator {
moves_up,
2021-09-08 04:26:57 +02:00
curr: start,
end,
span,
is_end_inclusive,
2021-09-08 04:26:57 +02:00
done: false,
incr: range.incr,
2021-09-08 04:26:57 +02:00
}
}
2021-09-12 13:12:53 +02:00
}
2021-09-08 04:26:57 +02:00
impl Iterator for RangeIterator {
type Item = Value;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
let ordering = if matches!(self.end, Value::Nothing { .. }) {
2021-09-12 13:12:53 +02:00
Some(Ordering::Less)
2021-09-08 04:26:57 +02:00
} else {
self.curr.partial_cmp(&self.end)
2021-09-08 04:26:57 +02:00
};
2021-09-12 13:12:53 +02:00
let ordering = if let Some(ord) = ordering {
ord
} else {
self.done = true;
return Some(Value::Error {
error: ShellError::CannotCreateRange(self.span),
});
};
2021-09-08 04:26:57 +02:00
let desired_ordering = if self.moves_up {
Ordering::Less
} else {
Ordering::Greater
};
2021-09-08 04:26:57 +02:00
if (ordering == desired_ordering) || (self.is_end_inclusive && ordering == Ordering::Equal)
2021-09-08 04:26:57 +02:00
{
let next_value = self.curr.add(self.span, &self.incr);
2021-09-08 04:26:57 +02:00
let mut next = match next_value {
Ok(result) => result,
2021-09-08 04:26:57 +02:00
Err(error) => {
self.done = true;
return Some(Value::Error { error });
}
};
std::mem::swap(&mut self.curr, &mut next);
Some(next)
} else {
None
}
}
}