add (some) lang tests, implement "in" logic

feat/vaults
Tomáš Mládek 2022-02-02 17:13:11 +01:00
parent cb22756a47
commit 6aa804584d
No known key found for this signature in database
GPG Key ID: ED21612889E75EC5
1 changed files with 212 additions and 8 deletions

View File

@ -11,7 +11,7 @@ use std::borrow::Borrow;
use std::convert::TryFrom;
use std::str::FromStr;
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum QueryComponent<T>
where
T: FromStr,
@ -22,33 +22,33 @@ where
Any,
}
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub struct EntryQuery {
pub entity: QueryComponent<Address>,
pub attribute: QueryComponent<String>,
pub value: QueryComponent<EntryValue>,
}
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum QueryPart {
Matches(EntryQuery),
Type(String),
}
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum QueryQualifier {
And,
Or,
Not,
}
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub struct MultiQuery {
pub qualifier: QueryQualifier,
pub queries: NonEmpty<Box<Query>>,
}
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum Query {
SingleQuery(QueryPart),
MultiQuery(MultiQuery),
@ -291,12 +291,31 @@ impl Query {
))?;
match first {
EntryValue::Number(_) => todo!(),
EntryValue::Number(_) => subqueries.push(Box::new(
data::value_num.eq_any(
q_values
.iter()
.map(|v| {
if let EntryValue::Number(n) = v {
Ok(*n)
} else {
Err(anyhow!("IN queries fidj oisdjfo isdjfoi jsdoifj sodijf oisdjf oij must not combine numeric and string values! ({v} is not a number)"))
}
})
.collect::<Result<Vec<f64>>>()?,
),
)),
_ => subqueries.push(Box::new(
data::value_str.eq_any(
q_values
.iter()
.map(|v| v.to_string())
.map(|v| {
if let EntryValue::Number(_) = v {
Err(anyhow!("IN queries must not combine numeric and string values! (Found {v})"))
} else {
v.to_string()
}
})
.collect::<Result<Vec<String>>>()?,
),
)),
@ -363,3 +382,188 @@ impl Query {
}
}
}
#[cfg(test)]
mod test {
use super::*;
use anyhow::Result;
#[test]
fn test_matches() -> Result<()> {
let query = "(matches ? ? ?)".parse::<Query>()?;
assert_eq!(
query,
Query::SingleQuery(QueryPart::Matches(EntryQuery {
entity: QueryComponent::Any,
attribute: QueryComponent::Any,
value: QueryComponent::Any
}))
);
query.to_sqlite_predicates()?;
let address = Address::Url(String::from("https://upendproject.net"));
let query = format!("(matches \"{address}\" ? ?)").parse::<Query>()?;
assert_eq!(
query,
Query::SingleQuery(QueryPart::Matches(EntryQuery {
entity: QueryComponent::Exact(address),
attribute: QueryComponent::Any,
value: QueryComponent::Any
}))
);
query.to_sqlite_predicates()?;
let query = "(matches ? \"FOO\" ?)".parse::<Query>()?;
assert_eq!(
query,
Query::SingleQuery(QueryPart::Matches(EntryQuery {
entity: QueryComponent::Any,
attribute: QueryComponent::Exact(String::from("FOO")),
value: QueryComponent::Any
}))
);
query.to_sqlite_predicates()?;
let value = EntryValue::Number(1337.93);
let query = format!("(matches ? ? \"{}\")", value.to_string().unwrap()).parse::<Query>()?;
assert_eq!(
query,
Query::SingleQuery(QueryPart::Matches(EntryQuery {
entity: QueryComponent::Any,
attribute: QueryComponent::Any,
value: QueryComponent::Exact(value)
}))
);
query.to_sqlite_predicates()?;
Ok(())
}
#[test]
fn test_in_parse() -> Result<()> {
let query = "(matches ? (in \"FOO\" \"BAR\") ?)".parse::<Query>()?;
assert_eq!(
query,
Query::SingleQuery(QueryPart::Matches(EntryQuery {
entity: QueryComponent::Any,
attribute: QueryComponent::In(vec!("FOO".to_string(), "BAR".to_string())),
value: QueryComponent::Any
}))
);
query.to_sqlite_predicates()?;
let values = vec![
EntryValue::String("FOO".to_string()),
EntryValue::String("BAR".to_string()),
];
let query = format!(
"(matches ? ? (in {}))",
values
.iter()
.map(|v| format!("\"{}\"", v.to_string().unwrap()))
.collect::<Vec<String>>()
.join(" ")
)
.parse::<Query>()?;
assert_eq!(
query,
Query::SingleQuery(QueryPart::Matches(EntryQuery {
entity: QueryComponent::Any,
attribute: QueryComponent::Any,
value: QueryComponent::In(values)
}))
);
query.to_sqlite_predicates()?;
let values = vec![EntryValue::Number(1337.93), EntryValue::Number(1968.12)];
let query = format!(
"(matches ? ? (in {}))",
values
.iter()
.map(|v| format!("\"{}\"", v.to_string().unwrap()))
.collect::<Vec<String>>()
.join(" ")
)
.parse::<Query>()?;
assert_eq!(
query,
Query::SingleQuery(QueryPart::Matches(EntryQuery {
entity: QueryComponent::Any,
attribute: QueryComponent::Any,
value: QueryComponent::In(values)
}))
);
query.to_sqlite_predicates()?;
// Invalid queries
let values = vec![
EntryValue::String("FOO".to_string()),
EntryValue::Number(1337.93),
];
let query = format!(
"(matches ? ? (in {}))",
values
.iter()
.map(|v| format!("\"{}\"", v.to_string().unwrap()))
.collect::<Vec<String>>()
.join(" ")
)
.parse::<Query>()?;
assert_eq!(
query,
Query::SingleQuery(QueryPart::Matches(EntryQuery {
entity: QueryComponent::Any,
attribute: QueryComponent::Any,
value: QueryComponent::In(values)
}))
);
query
.to_sqlite_predicates()
.err()
.ok_or(anyhow!("Failed to reject mixed query."))?;
let values = vec![
EntryValue::Number(1337.93),
EntryValue::String("FOO".to_string()),
];
let query = format!(
"(matches ? ? (in {}))",
values
.iter()
.map(|v| format!("\"{}\"", v.to_string().unwrap()))
.collect::<Vec<String>>()
.join(" ")
)
.parse::<Query>()?;
assert_eq!(
query,
Query::SingleQuery(QueryPart::Matches(EntryQuery {
entity: QueryComponent::Any,
attribute: QueryComponent::Any,
value: QueryComponent::In(values)
}))
);
query
.to_sqlite_predicates()
.err()
.ok_or(anyhow!("Failed to reject mixed query."))?;
Ok(())
}
#[test]
fn test_contains() -> Result<()> {
let query = "(matches ? ? (contains \"foo\"))".parse::<Query>()?;
assert_eq!(
query,
Query::SingleQuery(QueryPart::Matches(EntryQuery {
entity: QueryComponent::Any,
attribute: QueryComponent::Any,
value: QueryComponent::Contains("foo".to_string())
}))
);
query.to_sqlite_predicates()?;
Ok(())
}
}