use std::fmt;
#[derive(Clone, PartialEq, Eq, Default)]
pub struct Raw(Box<str>);
impl fmt::Debug for Raw {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Raw({} bytes)", self.0.len())
}
}
impl Raw {
pub fn from_text(body: impl Into<Box<str>>) -> Self {
Self(body.into())
}
#[must_use]
pub fn from_json(value: &serde_json::Value) -> Self {
Self(value.to_string().into_boxed_str())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[must_use]
pub fn json(&self) -> Option<serde_json::Value> {
serde_json::from_str(&self.0).ok()
}
#[must_use]
pub fn text_at(&self, pointer: &str) -> Option<String> {
self.json()?
.pointer(pointer)?
.as_str()
.map(ToOwned::to_owned)
}
}
impl fmt::Display for Raw {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl From<&serde_json::Value> for Raw {
fn from(value: &serde_json::Value) -> Self {
Self::from_json(value)
}
}
#[cfg(test)]
mod tests {
use super::Raw;
#[test]
fn a_json_body_is_readable_by_pointer_and_as_a_value() {
let raw = Raw::from_text(r#"{"transactionDetail":{"currencyCode":"TRY"}}"#);
assert_eq!(
raw.text_at("/transactionDetail/currencyCode").as_deref(),
Some("TRY")
);
assert!(raw.json().is_some());
assert!(!raw.is_empty());
}
#[test]
fn a_body_that_is_not_json_is_still_kept() {
let raw = Raw::from_text("<result><status>ok</status></result>");
assert!(raw.json().is_none());
assert!(raw.text_at("/status").is_none());
assert!(raw.as_str().starts_with("<result>"));
}
#[test]
fn a_pointer_at_something_that_is_not_a_string_finds_nothing() {
let raw = Raw::from_text(r#"{"amount":1499,"nested":{"a":1}}"#);
assert!(raw.text_at("/amount").is_none());
assert!(raw.text_at("/nested").is_none());
assert!(raw.text_at("/missing").is_none());
}
#[test]
fn an_empty_body_is_empty_and_not_json() {
let raw = Raw::default();
assert!(raw.is_empty());
assert!(raw.json().is_none());
}
#[test]
fn debug_shows_the_length_and_not_the_body() {
let raw = Raw::from_text(r#"{"iban":"TR330006100519786457841326"}"#);
let shown = format!("{raw:?}");
assert!(!shown.contains("TR33"), "the body reached Debug: {shown}");
assert_eq!(shown, format!("Raw({} bytes)", raw.as_str().len()));
assert_eq!(
raw.text_at("/iban").as_deref(),
Some("TR330006100519786457841326")
);
}
}