Skip to content

Commit

Permalink
refactor: centralize generic callback response in core
Browse files Browse the repository at this point in the history
  • Loading branch information
lsunsi committed Dec 14, 2023
1 parent c9c3a6d commit 7bf8847
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 200 deletions.
24 changes: 9 additions & 15 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl Channel<'_> {
&self,
remoteid: &str,
private: bool,
) -> Result<crate::channel::client::CallbackResponse, &'static str> {
) -> Result<crate::CallbackResponse, &'static str> {
let callback = self.core.accept(remoteid, private);

let response = self
Expand All @@ -87,17 +87,14 @@ impl Channel<'_> {
.await
.map_err(|_| "request failed")?;

let text = response.text().await.map_err(|_| "body failed")?;
text.parse().map_err(|_| "parse failed")
let bytes = response.bytes().await.map_err(|_| "body failed")?;
(&bytes as &[u8]).try_into().map_err(|_| "parse failed")
}

/// # Errors
///
/// Returns errors on network or deserialization failures.
pub async fn cancel(
&self,
remoteid: &str,
) -> Result<crate::channel::client::CallbackResponse, &'static str> {
pub async fn cancel(&self, remoteid: &str) -> Result<crate::CallbackResponse, &'static str> {
let callback = self.core.cancel(remoteid);

let response = self
Expand All @@ -107,8 +104,8 @@ impl Channel<'_> {
.await
.map_err(|_| "request failed")?;

let text = response.text().await.map_err(|_| "body failed")?;
text.parse().map_err(|_| "parse failed")
let bytes = response.bytes().await.map_err(|_| "body failed")?;
(&bytes as &[u8]).try_into().map_err(|_| "parse failed")
}
}

Expand Down Expand Up @@ -139,10 +136,7 @@ impl Withdraw<'_> {
/// # Errors
///
/// Returns errors on network or deserialization failures.
pub async fn submit(
&self,
pr: &str,
) -> Result<crate::withdraw::client::CallbackResponse, &'static str> {
pub async fn submit(&self, pr: &str) -> Result<crate::CallbackResponse, &'static str> {
let callback = self.core.submit(pr);

let response = self
Expand All @@ -152,7 +146,7 @@ impl Withdraw<'_> {
.await
.map_err(|_| "request failed")?;

let text = response.text().await.map_err(|_| "body failed")?;
text.parse().map_err(|_| "parse failed")
let bytes = response.bytes().await.map_err(|_| "body failed")?;
(&bytes as &[u8]).try_into().map_err(|_| "parse failed")
}
}
67 changes: 67 additions & 0 deletions src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,44 @@ impl TryFrom<&[u8]> for Entrypoint {
}
}

#[derive(Clone, Debug)]
pub enum CallbackResponse {
Error { reason: String },
Ok,
}

#[derive(serde::Serialize, serde::Deserialize)]
#[serde(tag = "status", rename_all = "UPPERCASE")]
enum CallbackResponseSerde {
Error { reason: String },
Ok,
}

impl TryFrom<&[u8]> for CallbackResponse {
type Error = &'static str;

fn try_from(s: &[u8]) -> Result<Self, &'static str> {
serde_json::from_slice::<CallbackResponseSerde>(s)
.map_err(|_| "deserialize failed")
.map(|a| match a {
CallbackResponseSerde::Error { reason } => CallbackResponse::Error { reason },
CallbackResponseSerde::Ok => CallbackResponse::Ok,
})
}
}

impl TryFrom<CallbackResponse> for Vec<u8> {
type Error = &'static str;

fn try_from(c: CallbackResponse) -> Result<Self, Self::Error> {
serde_json::to_vec(&match c {
CallbackResponse::Error { reason } => CallbackResponseSerde::Error { reason },
CallbackResponse::Ok => CallbackResponseSerde::Ok,
})
.map_err(|_| "deserialize failed")
}
}

#[cfg(test)]
mod tests {
#[test]
Expand Down Expand Up @@ -210,4 +248,33 @@ mod tests {
panic!("expected resolved url");
};
}

#[test]
fn callback_response_parse() {
assert!(matches!(
(br#"{ "status": "OK" }"# as &[u8]).try_into().unwrap(),
super::CallbackResponse::Ok
));

assert!(matches!(
(br#"{ "status": "ERROR", "reason": "razao" }"# as &[u8]).try_into().unwrap(),
super::CallbackResponse::Error { reason } if reason == "razao"
));
}

#[test]
fn callback_response_render() {
assert_eq!(
<Vec::<u8>>::try_from(super::CallbackResponse::Ok).unwrap(),
br#"{"status":"OK"}"#
);

assert_eq!(
<Vec::<u8>>::try_from(super::CallbackResponse::Error {
reason: String::from("razao")
})
.unwrap(),
br#"{"status":"ERROR","reason":"razao"}"#
);
}
}
37 changes: 0 additions & 37 deletions src/core/channel/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,30 +91,6 @@ impl std::fmt::Display for Callback<'_> {
}
}

#[derive(Clone, Debug)]
pub enum CallbackResponse {
Error { reason: String },
Ok,
}

impl std::str::FromStr for CallbackResponse {
type Err = &'static str;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let map = serde_json::from_str::<std::collections::BTreeMap<String, String>>(s)
.map_err(|_| "bad json")?;

match map.get("status").map(|s| s as &str) {
Some("OK") => Ok(CallbackResponse::Ok),
Some("ERROR") => {
let reason = String::from(map.get("reason").ok_or("error without reason")?);
Ok(CallbackResponse::Error { reason })
}
_ => Err("bad status field"),
}
}
}

mod de {
use serde::Deserialize;
use url::Url;
Expand Down Expand Up @@ -184,17 +160,4 @@ mod tests {
"https://yuri/?o=callback&k1=caum&remoteid=idremoto&cancel=1"
);
}

#[test]
fn callback_response_parse() {
assert!(matches!(
r#"{ "status": "OK" }"#.parse().unwrap(),
super::CallbackResponse::Ok
));

assert!(matches!(
r#"{ "status": "ERROR", "reason": "razao" }"#.parse().unwrap(),
super::CallbackResponse::Error { reason } if reason == "razao"
));
}
}
40 changes: 0 additions & 40 deletions src/core/channel/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,30 +63,6 @@ impl<'a> TryFrom<&'a str> for Callback {
}
}

#[derive(Clone, Debug)]
pub enum CallbackResponse {
Error { reason: String },
Ok,
}

impl std::fmt::Display for CallbackResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut map = std::collections::BTreeMap::new();

match self {
CallbackResponse::Error { reason } => {
map.insert("status", "ERROR");
map.insert("reason", reason);
}
CallbackResponse::Ok => {
map.insert("status", "OK");
}
}

f.write_str(&serde_json::to_string(&map).map_err(|_| std::fmt::Error)?)
}
}

mod ser {
use serde::Serialize;
use url::Url;
Expand Down Expand Up @@ -169,20 +145,4 @@ mod tests {
let parsed: Result<super::Callback, _> = input.try_into();
assert!(parsed.is_err());
}

#[test]
fn callback_response_render() {
assert_eq!(
super::CallbackResponse::Ok.to_string(),
r#"{"status":"OK"}"#
);

assert_eq!(
super::CallbackResponse::Error {
reason: String::from("razao")
}
.to_string(),
r#"{"reason":"razao","status":"ERROR"}"#
);
}
}
37 changes: 0 additions & 37 deletions src/core/withdraw/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,30 +73,6 @@ impl std::fmt::Display for Callback<'_> {
}
}

#[derive(Clone, Debug)]
pub enum CallbackResponse {
Error { reason: String },
Ok,
}

impl std::str::FromStr for CallbackResponse {
type Err = &'static str;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let map = serde_json::from_str::<std::collections::BTreeMap<String, String>>(s)
.map_err(|_| "bad json")?;

match map.get("status").map(|s| s as &str) {
Some("OK") => Ok(CallbackResponse::Ok),
Some("ERROR") => {
let reason = String::from(map.get("reason").ok_or("error without reason")?);
Ok(CallbackResponse::Error { reason })
}
_ => Err("bad status field"),
}
}
}

mod de {
use serde::Deserialize;
use url::Url;
Expand Down Expand Up @@ -170,17 +146,4 @@ mod tests {
"https://yuri/?o=callback&k1=caum&pr=pierre"
);
}

#[test]
fn callback_response_parse() {
assert!(matches!(
r#"{ "status": "OK" }"#.parse().unwrap(),
super::CallbackResponse::Ok
));

assert!(matches!(
r#"{ "status": "ERROR", "reason": "razao" }"#.parse().unwrap(),
super::CallbackResponse::Error { reason } if reason == "razao"
));
}
}
41 changes: 0 additions & 41 deletions src/core/withdraw/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,31 +56,6 @@ impl<'a> TryFrom<&'a str> for Callback {
}
}

#[derive(Clone, Debug)]
pub enum CallbackResponse {
Error { reason: String },
Ok,
}

impl std::fmt::Display for CallbackResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut map = std::collections::BTreeMap::new();

match self {
CallbackResponse::Error { reason } => {
map.insert("status", "ERROR");
map.insert("reason", reason);
}
CallbackResponse::Ok => {
map.insert("status", "OK");
}
}

let ser = serde_json::to_string(&map).map_err(|_| std::fmt::Error)?;
f.write_str(&ser)
}
}

mod ser {
use serde::Serialize;
use url::Url;
Expand Down Expand Up @@ -125,20 +100,4 @@ mod tests {
assert_eq!(parsed.pr, "pierre");
assert_eq!(parsed.k1, "caum");
}

#[test]
fn callback_response_render() {
assert_eq!(
super::CallbackResponse::Ok.to_string(),
r#"{"status":"OK"}"#
);

assert_eq!(
super::CallbackResponse::Error {
reason: String::from("razao")
}
.to_string(),
r#"{"reason":"razao","status":"ERROR"}"#
);
}
}
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#![cfg_attr(all(doc, docsrs), feature(doc_auto_cfg))]

mod core;
pub use core::{auth, channel, pay, resolve, withdraw, Entrypoint, Resolved};
pub use core::{auth, channel, pay, resolve, withdraw, CallbackResponse, Entrypoint, Resolved};

#[cfg(feature = "client")]
pub mod client;
Expand Down
Loading

0 comments on commit 7bf8847

Please sign in to comment.