Skip to content

Commit

Permalink
feat(pay): add support for basic serde of payer data
Browse files Browse the repository at this point in the history
  • Loading branch information
lsunsi committed Dec 17, 2023
1 parent d2b4892 commit 2598a98
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ pub struct Channel<'a> {
#[derive(Clone, Debug)]
pub struct Pay<'a> {
client: &'a reqwest::Client,
pub core: crate::pay::client::Entrypoint,
pub core: Box<crate::pay::client::Entrypoint>,
}

#[derive(Clone, Debug)]
Expand Down
4 changes: 2 additions & 2 deletions src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ fn resolve_address(s: &str) -> Result<url::Url, &'static str> {
#[derive(Debug)]
pub enum Entrypoint {
Channel(channel::client::Entrypoint),
Pay(pay::client::Entrypoint),
Pay(Box<pay::client::Entrypoint>),
Withdraw(withdraw::client::Entrypoint),
}

Expand All @@ -114,7 +114,7 @@ impl TryFrom<&[u8]> for Entrypoint {
Ok(Entrypoint::Channel(cr))
} else if tag.tag == pay::TAG {
let pr = s.try_into().map_err(|_| "deserialize data failed")?;
Ok(Entrypoint::Pay(pr))
Ok(Entrypoint::Pay(Box::new(pr)))
} else if tag.tag == withdraw::TAG {
let wr = s.try_into().map_err(|_| "deserialize data failed")?;
Ok(Entrypoint::Withdraw(wr))
Expand Down
50 changes: 50 additions & 0 deletions src/core/pay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,30 @@ pub struct Currency {
pub convertible: bool,
}

#[derive(Clone, Debug)]
pub struct Payer {
pub name: Option<PayerRequirement>,
pub pubkey: Option<PayerRequirement>,
pub identifier: Option<PayerRequirement>,
pub email: Option<PayerRequirement>,
pub auth: Option<PayerRequirementAuth>,
pub others: std::collections::HashMap<String, PayerRequirement>,
}

#[derive(Clone, Debug)]
pub struct PayerRequirement {
pub mandatory: bool,
}

#[derive(Clone, Debug)]
pub struct PayerRequirementAuth {
pub mandatory: bool,
pub k1: [u8; 32],
}

mod serde {
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Deserialize, Serialize)]
pub(super) struct Currency<'a> {
Expand All @@ -32,6 +54,34 @@ mod serde {
pub convertible: bool,
}

#[derive(Deserialize, Serialize)]
pub(super) struct Payer<'a> {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<PayerRequirement>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pubkey: Option<PayerRequirement>,
#[serde(skip_serializing_if = "Option::is_none")]
pub identifier: Option<PayerRequirement>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<PayerRequirement>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auth: Option<PayerRequirementAuth>,
#[serde(borrow, flatten)]
pub others: HashMap<&'a str, PayerRequirement>,
}

#[derive(Deserialize, Serialize)]
pub struct PayerRequirement {
pub mandatory: bool,
}

#[derive(Deserialize, Serialize)]
pub struct PayerRequirementAuth {
pub mandatory: bool,
#[serde(with = "hex::serde")]
pub k1: [u8; 32],
}

pub(super) mod amount {
use serde::{Deserialize, Deserializer, Serialize, Serializer};

Expand Down
93 changes: 92 additions & 1 deletion src/core/pay/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ pub struct Entrypoint {
pub min: u64,
pub max: u64,
pub currencies: Option<Vec<super::Currency>>,
pub payer: Option<super::Payer>,
}

#[allow(clippy::too_many_lines)]
impl TryFrom<&[u8]> for Entrypoint {
type Error = &'static str;

Expand All @@ -36,6 +38,37 @@ impl TryFrom<&[u8]> for Entrypoint {
.collect()
});

let payer = p.payer_data.map(|p| super::Payer {
name: p.name.map(|p| super::PayerRequirement {
mandatory: p.mandatory,
}),
pubkey: p.pubkey.map(|p| super::PayerRequirement {
mandatory: p.mandatory,
}),
identifier: p.identifier.map(|p| super::PayerRequirement {
mandatory: p.mandatory,
}),
email: p.email.map(|p| super::PayerRequirement {
mandatory: p.mandatory,
}),
auth: p.auth.map(|p| super::PayerRequirementAuth {
mandatory: p.mandatory,
k1: p.k1,
}),
others: p
.others
.into_iter()
.map(|(k, v)| {
(
String::from(k),
super::PayerRequirement {
mandatory: v.mandatory,
},
)
})
.collect(),
});

let metadata = serde_json::from_str::<Vec<(String, Value)>>(&p.metadata)
.map_err(|_| "deserialize metadata failed")?;

Expand Down Expand Up @@ -101,6 +134,7 @@ impl TryFrom<&[u8]> for Entrypoint {
jpeg,
png,
currencies,
payer,
})
}
}
Expand Down Expand Up @@ -195,7 +229,7 @@ mod ser {
}

mod de {
use super::super::serde::Currency;
use super::super::serde::{Currency, Payer};
use serde::Deserialize;
use std::collections::BTreeMap;
use url::Url;
Expand All @@ -212,6 +246,8 @@ mod de {
pub comment_allowed: Option<u64>,
#[serde(borrow)]
pub currencies: Option<Vec<Currency<'a>>>,
#[serde(rename = "payerData")]
pub payer_data: Option<Payer<'a>>,
}

#[derive(Deserialize)]
Expand Down Expand Up @@ -252,6 +288,7 @@ mod tests {
assert!(parsed.identifier.is_none());
assert!(parsed.email.is_none());
assert!(parsed.currencies.is_none());
assert!(parsed.payer.is_none());
}

#[test]
Expand Down Expand Up @@ -368,6 +405,60 @@ mod tests {
assert!(!currencies[1].convertible);
}

#[test]
fn entrypoint_parse_payer() {
use super::super::{PayerRequirement, PayerRequirementAuth};

let input = r#"{
"callback": "https://yuri?o=callback",
"metadata": "[[\"text/plain\", \"boneco do steve magal\"],[\"text/crazy\", \"πŸ‘‹πŸ‡§πŸ‡΄πŸ’Ύ\"]]",
"maxSendable": 315,
"minSendable": 314,
"payerData": {
"name": { "mandatory": true },
"pubkey": { "mandatory": true },
"identifier": { "mandatory": false },
"email": { "mandatory": true },
"auth": { "mandatory": true, "k1": "3132333132333231333132333132333132333132333132333331323132333132" },
"outro": { "mandatory": false }
}
}"#;

let parsed: super::Entrypoint = input.as_bytes().try_into().expect("parse");
let payer = parsed.payer.unwrap();

assert!(matches!(payer.name.unwrap(), PayerRequirement { mandatory } if mandatory));
assert!(matches!(payer.pubkey.unwrap(), PayerRequirement { mandatory } if mandatory));
assert!(matches!(payer.identifier.unwrap(), PayerRequirement { mandatory } if !mandatory));
assert!(matches!(payer.email.unwrap(), PayerRequirement { mandatory } if mandatory));
assert!(
matches!(payer.auth.unwrap(), PayerRequirementAuth { mandatory, k1 } if mandatory && &k1 == b"12312321312312312312312331212312")
);

assert_eq!(payer.others.len(), 1);
assert!(
matches!(payer.others.get("outro").unwrap(), PayerRequirement { mandatory } if !mandatory)
);

let input = r#"{
"callback": "https://yuri?o=callback",
"metadata": "[[\"text/plain\", \"boneco do steve magal\"],[\"text/crazy\", \"πŸ‘‹πŸ‡§πŸ‡΄πŸ’Ύ\"]]",
"maxSendable": 315,
"minSendable": 314,
"payerData": {}
}"#;

let parsed: super::Entrypoint = input.as_bytes().try_into().expect("parse");
let payer = parsed.payer.unwrap();

assert!(payer.name.is_none());
assert!(payer.pubkey.is_none());
assert!(payer.identifier.is_none());
assert!(payer.email.is_none());
assert!(payer.auth.is_none());
assert_eq!(payer.others.len(), 0);
}

#[test]
fn callback_render_base() {
let input = r#"{
Expand Down
Loading

0 comments on commit 2598a98

Please sign in to comment.