|
1 | | -use crate::SyntaxShape; |
| 1 | +use crate::{SyntaxShape, ast::PathMember}; |
2 | 2 | use serde::{Deserialize, Serialize}; |
3 | | -use std::fmt::Display; |
| 3 | +use std::{borrow::Cow, fmt::Display}; |
4 | 4 | #[cfg(test)] |
5 | 5 | use strum_macros::EnumIter; |
6 | 6 |
|
@@ -35,6 +35,76 @@ pub enum Type { |
35 | 35 | Table(Box<[(String, Type)]>), |
36 | 36 | } |
37 | 37 |
|
| 38 | +fn follow_cell_path_recursive<'a>( |
| 39 | + current: Cow<'a, Type>, |
| 40 | + path_members: &mut dyn Iterator<Item = &'a PathMember>, |
| 41 | +) -> Option<Cow<'a, Type>> { |
| 42 | + let Some(first) = path_members.next() else { |
| 43 | + return Some(current); |
| 44 | + }; |
| 45 | + match (current.as_ref(), first) { |
| 46 | + (Type::Record(fields), PathMember::String { val, .. }) => { |
| 47 | + let idx = fields.iter().position(|(name, _)| name == val)?; |
| 48 | + let next = match current { |
| 49 | + Cow::Borrowed(Type::Record(f)) => Cow::Borrowed(&f[idx].1), |
| 50 | + Cow::Owned(Type::Record(f)) => Cow::Owned(f[idx].1.to_owned()), |
| 51 | + _ => unreachable!(), |
| 52 | + }; |
| 53 | + follow_cell_path_recursive(next, path_members) |
| 54 | + } |
| 55 | + |
| 56 | + // Table to Record (Int) |
| 57 | + (Type::Table(f), PathMember::Int { .. }) => { |
| 58 | + follow_cell_path_recursive(Cow::Owned(Type::Record(f.clone())), path_members) |
| 59 | + } |
| 60 | + |
| 61 | + // Table to List (String) |
| 62 | + (Type::Table(fields), PathMember::String { val, .. }) => { |
| 63 | + let (_, sub_type) = fields.iter().find(|(name, _)| name == val)?; |
| 64 | + let list_type = Type::List(Box::new(sub_type.clone())); |
| 65 | + follow_cell_path_recursive(Cow::Owned(list_type), path_members) |
| 66 | + } |
| 67 | + |
| 68 | + (Type::List(_), PathMember::Int { .. }) => { |
| 69 | + let next = match current { |
| 70 | + Cow::Borrowed(Type::List(i)) => Cow::Borrowed(i.as_ref()), |
| 71 | + Cow::Owned(Type::List(i)) => Cow::Owned(*i), |
| 72 | + _ => unreachable!(), |
| 73 | + }; |
| 74 | + follow_cell_path_recursive(next, path_members) |
| 75 | + } |
| 76 | + |
| 77 | + // List of Records indexed by key names |
| 78 | + (Type::List(_), PathMember::String { .. }) => { |
| 79 | + let next = match current { |
| 80 | + Cow::Borrowed(Type::List(i)) => Cow::Borrowed(i.as_ref()), |
| 81 | + Cow::Owned(Type::List(i)) => Cow::Owned(*i), |
| 82 | + _ => unreachable!(), |
| 83 | + }; |
| 84 | + |
| 85 | + let mut found_int_member = false; |
| 86 | + let mut new_iter = std::iter::once(first).chain(path_members).filter(|pm| { |
| 87 | + let first_int = !found_int_member && matches!(pm, PathMember::Int { .. }); |
| 88 | + if first_int { |
| 89 | + found_int_member = true; |
| 90 | + } |
| 91 | + !first_int |
| 92 | + }); |
| 93 | + let inner_ty = follow_cell_path_recursive(next, &mut new_iter); |
| 94 | + |
| 95 | + // If there's no int path member, need to wrap in a List type |
| 96 | + // e.g. [{foo: bar}].foo -> [bar], list<record<foo: string>> -> list<string> |
| 97 | + if found_int_member { |
| 98 | + inner_ty |
| 99 | + } else { |
| 100 | + inner_ty.map(|inner_ty| Cow::Owned(Type::List(Box::new(inner_ty.into_owned())))) |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + _ => None, |
| 105 | + } |
| 106 | +} |
| 107 | + |
38 | 108 | impl Type { |
39 | 109 | pub fn list(inner: Type) -> Self { |
40 | 110 | Self::List(Box::new(inner)) |
@@ -294,6 +364,10 @@ impl Type { |
294 | 364 | Type::Glob => String::from("glob"), |
295 | 365 | } |
296 | 366 | } |
| 367 | + |
| 368 | + pub fn follow_cell_path<'a>(&'a self, path_members: &'a [PathMember]) -> Option<Cow<'a, Self>> { |
| 369 | + follow_cell_path_recursive(Cow::Borrowed(self), &mut path_members.iter()) |
| 370 | + } |
297 | 371 | } |
298 | 372 |
|
299 | 373 | impl Display for Type { |
|
0 commit comments