wip, feat: upend lang supports provenance & timestamp matches
parent
37a038b8b8
commit
68740399e7
108
base/src/lang.rs
108
base/src/lang.rs
|
@ -1,6 +1,7 @@
|
|||
use crate::addressing::Address;
|
||||
use crate::entry::EntryValue;
|
||||
use crate::error::UpEndError;
|
||||
use chrono::NaiveDateTime;
|
||||
use nonempty::NonEmpty;
|
||||
use std::borrow::Borrow;
|
||||
use std::convert::TryFrom;
|
||||
|
@ -15,7 +16,7 @@ impl From<&str> for Attribute {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub enum PatternQueryComponent<T>
|
||||
where
|
||||
T: TryFrom<lexpr::Value>,
|
||||
|
@ -24,14 +25,23 @@ where
|
|||
In(Vec<T>),
|
||||
Contains(String),
|
||||
Variable(Option<String>),
|
||||
#[default]
|
||||
Discard,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Provenance(pub String);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Timestamp(pub NaiveDateTime);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Default)]
|
||||
pub struct PatternQuery {
|
||||
pub entity: PatternQueryComponent<Address>,
|
||||
pub attribute: PatternQueryComponent<Attribute>,
|
||||
pub value: PatternQueryComponent<EntryValue>,
|
||||
pub provenance: Option<PatternQueryComponent<Provenance>>,
|
||||
pub timestamp: Option<PatternQueryComponent<Timestamp>>,
|
||||
}
|
||||
|
||||
impl TryFrom<lexpr::Value> for Address {
|
||||
|
@ -88,6 +98,42 @@ impl TryFrom<lexpr::Value> for Attribute {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<lexpr::Value> for Provenance {
|
||||
type Error = UpEndError;
|
||||
|
||||
fn try_from(value: lexpr::Value) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
lexpr::Value::String(str) => Ok(Provenance(str.to_string())),
|
||||
_ => Err(UpEndError::QueryParseError(
|
||||
"Can only convert to provenance from string.".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<lexpr::Value> for Timestamp {
|
||||
type Error = UpEndError;
|
||||
|
||||
fn try_from(value: lexpr::Value) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
lexpr::Value::Number(num) => {
|
||||
if let Some(num) = num.as_i64() {
|
||||
Ok(Timestamp(NaiveDateTime::from_timestamp_opt(num, 0).ok_or(
|
||||
UpEndError::QueryParseError("Couldn't parse timestamp.".into()),
|
||||
)?))
|
||||
} else {
|
||||
Err(UpEndError::QueryParseError(
|
||||
"Couldn't parse number as i64.".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => Err(UpEndError::QueryParseError(
|
||||
"Can only convert to attribute from string.".into(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum QueryPart {
|
||||
Matches(PatternQuery),
|
||||
|
@ -193,19 +239,34 @@ impl TryFrom<&lexpr::Value> for Query {
|
|||
match symbol.borrow() {
|
||||
"matches" => {
|
||||
let (cons_vec, _) = value.clone().into_vec();
|
||||
if let [_, entity, attribute, value] = &cons_vec[..] {
|
||||
if let [_, entity, attribute, value, rest @ ..] = &cons_vec[..] {
|
||||
let entity = parse_component::<Address>(entity)?;
|
||||
let attribute = parse_component::<Attribute>(attribute)?;
|
||||
let value = parse_component::<EntryValue>(value)?;
|
||||
let (provenance, timestamp) = match rest {
|
||||
[] => (None, None),
|
||||
[provenance] => {
|
||||
(Some(parse_component::<Provenance>(provenance)?), None)
|
||||
}
|
||||
[provenance, timestamp] => (
|
||||
Some(parse_component::<Provenance>(provenance)?),
|
||||
Some(parse_component::<Timestamp>(timestamp)?),
|
||||
),
|
||||
_ => Err(UpEndError::QueryParseError(
|
||||
"Malformed expression: Too many arguments for `matches`."
|
||||
.into(),
|
||||
))?,
|
||||
};
|
||||
Ok(Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity,
|
||||
attribute,
|
||||
value,
|
||||
provenance,
|
||||
timestamp,
|
||||
})))
|
||||
} else {
|
||||
Err(UpEndError::QueryParseError(
|
||||
"Malformed expression: Wrong number of arguments to 'matches'."
|
||||
.into(),
|
||||
"Malformed expression: Not enough arguments for `matches`.".into(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -315,7 +376,8 @@ mod test {
|
|||
Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::Variable(None),
|
||||
value: PatternQueryComponent::Variable(None)
|
||||
value: PatternQueryComponent::Variable(None),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
@ -326,7 +388,8 @@ mod test {
|
|||
Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: PatternQueryComponent::Exact(address),
|
||||
attribute: PatternQueryComponent::Variable(None),
|
||||
value: PatternQueryComponent::Variable(None)
|
||||
value: PatternQueryComponent::Variable(None),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
@ -336,7 +399,8 @@ mod test {
|
|||
Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::Exact("FOO".into()),
|
||||
value: PatternQueryComponent::Variable(None)
|
||||
value: PatternQueryComponent::Variable(None),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
@ -347,7 +411,8 @@ mod test {
|
|||
Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::Variable(None),
|
||||
value: PatternQueryComponent::Exact(value)
|
||||
value: PatternQueryComponent::Exact(value),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
@ -362,7 +427,8 @@ mod test {
|
|||
Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: PatternQueryComponent::Variable(Some("a".into())),
|
||||
attribute: PatternQueryComponent::Variable(Some("b".into())),
|
||||
value: PatternQueryComponent::Variable(None)
|
||||
value: PatternQueryComponent::Variable(None),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
@ -377,7 +443,8 @@ mod test {
|
|||
Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::In(vec!("FOO".into(), "BAR".into())),
|
||||
value: PatternQueryComponent::Variable(None)
|
||||
value: PatternQueryComponent::Variable(None),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
@ -388,7 +455,8 @@ mod test {
|
|||
Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::Variable(None),
|
||||
value: PatternQueryComponent::In(values)
|
||||
value: PatternQueryComponent::In(values),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
@ -399,7 +467,8 @@ mod test {
|
|||
Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::Variable(None),
|
||||
value: PatternQueryComponent::In(values)
|
||||
value: PatternQueryComponent::In(values),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
@ -412,7 +481,8 @@ mod test {
|
|||
Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::Variable(None),
|
||||
value: PatternQueryComponent::In(values)
|
||||
value: PatternQueryComponent::In(values),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
@ -424,7 +494,8 @@ mod test {
|
|||
Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::Variable(None),
|
||||
value: PatternQueryComponent::In(values)
|
||||
value: PatternQueryComponent::In(values),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
@ -439,7 +510,8 @@ mod test {
|
|||
Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: PatternQueryComponent::Contains("foo".to_string()),
|
||||
attribute: PatternQueryComponent::Variable(None),
|
||||
value: PatternQueryComponent::Variable(None)
|
||||
value: PatternQueryComponent::Variable(None),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
@ -450,6 +522,7 @@ mod test {
|
|||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::Contains("foo".to_string()),
|
||||
value: PatternQueryComponent::Variable(None),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
@ -459,7 +532,8 @@ mod test {
|
|||
Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::Variable(None),
|
||||
value: PatternQueryComponent::Contains("foo".to_string())
|
||||
value: PatternQueryComponent::Contains("foo".to_string()),
|
||||
..Default::default()
|
||||
}))
|
||||
);
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@ pub fn list_roots(connection: &UpEndConnection) -> Result<Vec<Address>> {
|
|||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::Exact(ATTR_IN.into()),
|
||||
value: PatternQueryComponent::Exact((*HIER_ROOT_ADDR).clone().into()),
|
||||
..Default::default()
|
||||
})))
|
||||
}
|
||||
|
||||
|
@ -109,6 +110,7 @@ pub fn fetch_or_create_dir(
|
|||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::Exact(ATTR_LABEL.into()),
|
||||
value: PatternQueryComponent::Exact(String::from(directory.clone()).into()),
|
||||
..Default::default()
|
||||
})))?;
|
||||
|
||||
let parent_has: Vec<Address> = match parent.clone() {
|
||||
|
@ -117,6 +119,7 @@ pub fn fetch_or_create_dir(
|
|||
entity: PatternQueryComponent::Variable(None),
|
||||
attribute: PatternQueryComponent::Exact(ATTR_IN.into()),
|
||||
value: PatternQueryComponent::Exact(parent.into()),
|
||||
..Default::default()
|
||||
}),
|
||||
))?,
|
||||
None => list_roots(connection)?,
|
||||
|
|
Loading…
Reference in New Issue