Add ObjectPath query language
Some checks failed
CI / Typos (push) Successful in 19s
CI / Build and test (push) Failing after 40s
CI / Clippy (push) Failing after 53s

This commit is contained in:
2026-03-05 21:35:07 -08:00
parent 0053ed3a69
commit a9e402bc83
11 changed files with 657 additions and 48 deletions

View File

@@ -130,12 +130,7 @@ impl DbFtsIndex {
// Try paths in order, using the first value we find
'outer: for path in field.path.as_slice() {
let segments = path
.split('.')
.map(|x| Label::new(x).unwrap_or_else(|| panic!("wtf {x}")))
.collect::<Vec<_>>();
let val = match extractor.query(&segments)? {
let val = match extractor.query(path)? {
Some(x) => x,
None => return Ok(None),
};
@@ -145,7 +140,7 @@ impl DbFtsIndex {
trace!(
message = "Skipping field, is null",
field = field_name.to_string(),
path,
?path,
// value = ?val
);
continue;
@@ -170,7 +165,7 @@ impl DbFtsIndex {
debug!(
message = "Skipping field, is array with more than one element",
field = field_name.to_string(),
path,
?path,
//value = ?val
);
continue 'outer;
@@ -178,7 +173,7 @@ impl DbFtsIndex {
debug!(
message = "Skipping field, is empty array",
field = field_name.to_string(),
path,
?path,
//value = ?val
);
continue 'outer;
@@ -188,7 +183,7 @@ impl DbFtsIndex {
trace!(
message = "Skipping field, is null",
field = field_name.to_string(),
path,
?path,
//value = ?val
);
continue 'outer;
@@ -197,7 +192,7 @@ impl DbFtsIndex {
trace!(
message = "Skipping field, is object",
field = field_name.to_string(),
path,
?path,
//value = ?val
);
continue 'outer;

View File

@@ -71,12 +71,13 @@ fn find_latest_modified(dir: &Path) -> Result<Option<DateTime<Utc>>, std::io::Er
});
}
} else if metadata.is_dir()
&& let Some(dir_latest) = find_latest_modified(&path)? {
latest = Some(match latest {
Some(prev) if prev > dir_latest => prev,
_ => dir_latest,
});
}
&& let Some(dir_latest) = find_latest_modified(&path)?
{
latest = Some(match latest {
Some(prev) if prev > dir_latest => prev,
_ => dir_latest,
});
}
}
return Ok(latest);
@@ -107,12 +108,13 @@ fn find_earliest_modified(dir: &Path) -> Result<Option<DateTime<Utc>>, std::io::
});
}
} else if metadata.is_dir()
&& let Some(dir_earliest) = find_earliest_modified(&path)? {
earliest = Some(match earliest {
Some(prev) if prev < dir_earliest => prev,
_ => dir_earliest,
});
}
&& let Some(dir_earliest) = find_earliest_modified(&path)?
{
earliest = Some(match earliest {
Some(prev) if prev < dir_earliest => prev,
_ => dir_earliest,
});
}
}
return Ok(earliest);

View File

@@ -1,6 +1,6 @@
use std::rc::Rc;
use pile_config::Label;
use pile_config::objectpath::{ObjectPath, PathSegment};
use serde_json::{Map, Value};
use smartstring::{LazyCompact, SmartString};
@@ -32,17 +32,40 @@ impl<I: Item> Clone for PileValue<'_, I> {
}
impl<'a, I: Item> PileValue<'a, I> {
pub fn query(&'a self, query: &[Label]) -> Result<Option<&'a Self>, std::io::Error> {
pub fn query(&'a self, query: &ObjectPath) -> Result<Option<&'a Self>, std::io::Error> {
let mut out = Some(self);
for q in query {
out = match &out {
None => return Ok(None),
Some(Self::Null) => None,
Some(Self::Array(_)) => None,
Some(Self::String(_)) => None,
Some(Self::Extractor(e)) => e.field(q)?,
};
for s in &query.segments {
match s {
PathSegment::Root => out = Some(self),
PathSegment::Field(field) => {
out = match &out {
None => return Ok(None),
Some(Self::Null) => None,
Some(Self::Array(_)) => None,
Some(Self::String(_)) => None,
Some(Self::Extractor(e)) => e.field(field)?,
}
}
PathSegment::Index(idx) => {
out = match &out {
None => return Ok(None),
Some(Self::Null) => None,
Some(Self::Array(v)) => {
let idx = if *idx >= 0 {
usize::try_from(*idx).ok()
} else {
usize::try_from(v.len() as i64 - idx).ok()
};
idx.and_then(|idx| v.get(idx))
}
Some(Self::String(_)) => None,
Some(Self::Extractor(_)) => None,
}
}
}
}
return Ok(out);