diff --git a/Cargo.lock b/Cargo.lock index 0fac8fb..35ca012 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1046,6 +1046,7 @@ dependencies = [ "secp256k1", "serde", "serde_json", + "serde_urlencoded", "thiserror", "tokio", "tokio-tungstenite", @@ -1766,6 +1767,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "sha-1" version = "0.10.0" diff --git a/Cargo.toml b/Cargo.toml index bb1588e..ff9624f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ http = { version = "0.2" } parse_duration = "2" rand = "0.8" const_format = "0.2.28" +serde_urlencoded = "0.7" [dev-dependencies] anyhow = "1" diff --git a/src/delegation.rs b/src/delegation.rs index 1cc57d1..8e93381 100644 --- a/src/delegation.rs +++ b/src/delegation.rs @@ -5,7 +5,7 @@ //use bitcoin_hashes::{sha256, Hash}; //use lazy_static::lazy_static; //use secp256k1::{schnorr, Secp256k1, VerifyOnly, XOnlyPublicKey}; -use serde::{Deserialize, Serialize}; +use serde::{Deserializer, Deserialize, Serialize}; //use serde_json::value::Value; //use serde_json::Number; //use std::collections::HashMap; @@ -33,6 +33,11 @@ use serde::{Deserialize, Serialize}; // different condition strings, since we do not support grouping or // "OR" logic. +// using serde_urldecode, we can get a serde data format from the +// condition string. We will then map that with a deserializer that +// maps to a ConditionQuery. + + #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] pub enum Field { Kind, @@ -48,16 +53,45 @@ pub enum Operator { } #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] +/// Values are the data associated with a restriction. For now, these can only be a single numbers. pub enum Value { Number(u64), - List(Vec), +} + +#[derive(Serialize, PartialEq, Eq, Debug, Clone)] +pub struct ConditionQuery { + pub(crate) conditions: Vec, +} + +impl <'de> Deserialize<'de> for ConditionQuery { + fn deserialize(d: D) -> Result + where + D: Deserializer<'de>, + { + // recv'd is an array of pairs. + let _recvd: Value = d.deserialize_seq + let cond_list = recvd.as_object().ok_or_else(|| { + serde::de::Error::invalid_type( + Unexpected::Other("reqfilter is not an object"), + &"a json object", + ) + })?; + + // all valid conditions will be put into this vec + let conditions : Vec = vec![]; + // loop through the parsed value, and identify if they are + // known attributes. unknown attributes will trigger failure, + // since we can't respect those restrictions. + Ok(ConditionQuery{conditions}) + } + } /// Parsed delegation statement #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] pub struct Delegation { pub(crate) pubkey: String, - pub(crate) conditions: Vec, + pub(crate) condition_query: ConditionQuery, pub(crate) signature: String, } @@ -68,5 +102,18 @@ pub struct Delegation { pub struct Condition { pub(crate) field: Field, pub(crate) operator: Operator, - pub(crate) value: Value, + pub(crate) values: Vec, +} + +#[cfg(test)] +mod tests { + use super::*; + + // parse condition strings + #[test] + fn parse_empty() { + // given an empty condition query, produce an empty vector + assert_eq!(Delegation::from(""), vec![]); + assert_eq!(serde_urlencoded::from_str::>(""), vec![]); + } }