ColumnPath creation flexibility. (#2674)

This commit is contained in:
Andrés N. Robalino
2020-10-15 17:25:17 -05:00
committed by GitHub
parent bf2363947b
commit 791e07650d
9 changed files with 259 additions and 28 deletions

View File

@ -16,13 +16,13 @@ use crate::value::dict::Dictionary;
use crate::value::iter::{RowValueIter, TableValueIter};
use crate::value::primitive::Primitive;
use crate::value::range::{Range, RangeInclusion};
use crate::{ColumnPath, PathMember};
use crate::ColumnPath;
use bigdecimal::BigDecimal;
use bigdecimal::FromPrimitive;
use chrono::{DateTime, Utc};
use indexmap::IndexMap;
use nu_errors::ShellError;
use nu_source::{AnchorLocation, HasSpan, Span, Spanned, Tag};
use nu_source::{AnchorLocation, HasSpan, Span, Spanned, SpannedItem, Tag};
use num_bigint::BigInt;
use num_traits::ToPrimitive;
use serde::{Deserialize, Serialize};
@ -169,10 +169,10 @@ impl UntaggedValue {
}
/// Helper for creating column-path values
pub fn column_path(s: Vec<impl Into<PathMember>>) -> UntaggedValue {
UntaggedValue::Primitive(Primitive::ColumnPath(ColumnPath::new(
s.into_iter().map(|p| p.into()).collect(),
)))
pub fn column_path(s: &str) -> UntaggedValue {
let s = s.to_string().spanned_unknown();
UntaggedValue::Primitive(Primitive::ColumnPath(ColumnPath::build(&s)))
}
/// Helper for creating integer values

View File

@ -1,9 +1,15 @@
use derive_new::new;
use getset::Getters;
use nu_source::{b, span_for_spanned_list, DebugDocBuilder, HasFallibleSpan, PrettyDebug, Span};
use nu_source::{
b, span_for_spanned_list, DebugDocBuilder, HasFallibleSpan, PrettyDebug, Span, Spanned,
SpannedItem,
};
use num_bigint::BigInt;
use serde::{Deserialize, Serialize};
use crate::hir::{Expression, Literal, Member, SpannedExpression};
use nu_errors::ParseError;
/// A PathMember that has yet to be spanned so that it can be used in later processing
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum UnspannedPathMember {
@ -65,6 +71,23 @@ impl ColumnPath {
pub fn last(&self) -> Option<&PathMember> {
self.iter().last()
}
pub fn build(text: &Spanned<String>) -> ColumnPath {
if let (
SpannedExpression {
expr: Expression::Literal(Literal::ColumnPath(path)),
span: _,
},
_,
) = parse(&text)
{
ColumnPath {
members: path.iter().map(|member| member.to_path_member()).collect(),
}
} else {
ColumnPath { members: vec![] }
}
}
}
impl PrettyDebug for ColumnPath {
@ -111,3 +134,71 @@ impl PathMember {
}
}
}
fn parse(raw_column_path: &Spanned<String>) -> (SpannedExpression, Option<ParseError>) {
let mut delimiter = '.';
let mut inside_delimiter = false;
let mut output = vec![];
let mut current_part = String::new();
let mut start_index = 0;
let mut last_index = 0;
for (idx, c) in raw_column_path.item.char_indices() {
last_index = idx;
if inside_delimiter {
if c == delimiter {
inside_delimiter = false;
}
} else if c == '\'' || c == '"' || c == '`' {
inside_delimiter = true;
delimiter = c;
} else if c == '.' {
let part_span = Span::new(
raw_column_path.span.start() + start_index,
raw_column_path.span.start() + idx,
);
if let Ok(row_number) = current_part.parse::<u64>() {
output.push(Member::Int(BigInt::from(row_number), part_span));
} else {
let trimmed = trim_quotes(&current_part);
output.push(Member::Bare(trimmed.clone().spanned(part_span)));
}
current_part.clear();
// Note: I believe this is safe because of the delimiter we're using, but if we get fancy with
// unicode we'll need to change this
start_index = idx + '.'.len_utf8();
continue;
}
current_part.push(c);
}
if !current_part.is_empty() {
let part_span = Span::new(
raw_column_path.span.start() + start_index,
raw_column_path.span.start() + last_index + 1,
);
if let Ok(row_number) = current_part.parse::<u64>() {
output.push(Member::Int(BigInt::from(row_number), part_span));
} else {
let current_part = trim_quotes(&current_part);
output.push(Member::Bare(current_part.spanned(part_span)));
}
}
(
SpannedExpression::new(Expression::simple_column_path(output), raw_column_path.span),
None,
)
}
fn trim_quotes(input: &str) -> String {
let mut chars = input.chars();
match (chars.next(), chars.next_back()) {
(Some('\''), Some('\'')) => chars.collect(),
(Some('"'), Some('"')) => chars.collect(),
(Some('`'), Some('`')) => chars.collect(),
_ => input.to_string(),
}
}