wip, feat: upend lang supports provenance & timestamp matches

feat/lang-upgrades
Tomáš Mládek 2023-07-26 20:45:36 +02:00
parent 37a038b8b8
commit 68740399e7
2 changed files with 94 additions and 17 deletions

View File

@ -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()
}))
);

View File

@ -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)?,