feat: improved Node API

This commit is contained in:
əlemi 2024-03-19 06:49:02 +01:00
parent 67c3e80cf6
commit 106380d3b7
Signed by: alemi
GPG key ID: A4895B84D311642C
2 changed files with 59 additions and 26 deletions

View file

@ -1,14 +1,4 @@
#[derive(Debug, thiserror::Error)] use super::Object;
pub enum NodeResolutionError {
#[error("error fetching object: {0}")]
FetchError(#[from] reqwest::Error),
#[error("empty array")]
EmptyArray,
#[error("field not present")]
Empty,
}
pub enum Node<T> { pub enum Node<T> {
Array(Vec<Node<T>>), Array(Vec<Node<T>>),
@ -31,7 +21,13 @@ impl<T> Node<T> {
match self { match self {
Node::Empty | Node::Link(_) => None, Node::Empty | Node::Link(_) => None,
Node::Object(x) => Some(x), Node::Object(x) => Some(x),
Node::Array(v) => v.first(), Node::Array(v) => match v.iter().find_map(|x| match x {
Node::Object(x) => Some(x),
_ => None,
}) {
Some(x) => Some(x),
None => None,
},
} }
} }
@ -40,7 +36,17 @@ impl<T> Node<T> {
Node::Empty | Node::Link(_) => None, Node::Empty | Node::Link(_) => None,
Node::Object(x) => Some(vec![x]), Node::Object(x) => Some(vec![x]),
Node::Array(v) => Node::Array(v) =>
Some(v.iter().map(|x| &x).collect()), Some(v.iter().filter_map(|x| match x {
Node::Object(x) => Some(x),
_ => None,
}).collect()),
}
}
pub fn is_empty(&self) -> bool {
match self {
Node::Empty | Node::Link(_) => true,
Node::Object(_) | Node::Array(_) => false,
} }
} }
@ -54,6 +60,35 @@ impl<T> Node<T> {
} }
} }
impl<T> Node<T>
where
T : Object
{
pub fn id(&self) -> Option<&str> {
match self {
Node::Empty => None,
Node::Link(uri) => Some(uri.href()),
Node::Object(obj) => obj.id(),
Node::Array(arr) => arr.first()?.id(),
}
}
}
impl From<Option<&str>> for Node<serde_json::Value> {
fn from(value: Option<&str>) -> Self {
match value {
Some(x) => Node::Link(Box::new(x.to_string())),
None => Node::Empty,
}
}
}
impl From<&str> for Node<serde_json::Value> {
fn from(value: &str) -> Self {
Node::Link(Box::new(value.to_string()))
}
}
impl From<serde_json::Value> for Node<serde_json::Value> { impl From<serde_json::Value> for Node<serde_json::Value> {
fn from(value: serde_json::Value) -> Self { fn from(value: serde_json::Value) -> Self {
match value { match value {
@ -65,7 +100,7 @@ impl From<serde_json::Value> for Node<serde_json::Value> {
serde_json::Value::Array(arr) => Node::Array( serde_json::Value::Array(arr) => Node::Array(
arr arr
.into_iter() .into_iter()
.filter_map(|x| Self::new(x).ok()) .map(Self::from)
.collect() .collect()
), ),
_ => Node::Empty, _ => Node::Empty,
@ -86,7 +121,12 @@ impl Node<serde_json::Value>{
} }
} }
pub trait NodeExtractor {
pub(crate) trait NodeExtractor {
fn node(&self, id: &str) -> Node<serde_json::Value>; fn node(&self, id: &str) -> Node<serde_json::Value>;
fn node_vec(&self, id: &str) -> Node<serde_json::Value>; fn node_vec(&self, id: &str) -> Node<serde_json::Value>;
} }
@ -95,26 +135,18 @@ impl NodeExtractor for serde_json::Value {
fn node(&self, id: &str) -> Node<serde_json::Value> { fn node(&self, id: &str) -> Node<serde_json::Value> {
match self.get(id) { match self.get(id) {
None => Node::Empty, None => Node::Empty,
Some(x) => match Node::new(x.clone()) { Some(x) => Node::from(x.clone()),
Err(e) => Node::Empty,
Ok(x) => x,
}
} }
} }
fn node_vec(&self, id: &str) -> Node<serde_json::Value> { fn node_vec(&self, id: &str) -> Node<serde_json::Value> {
match self.get(id) { match self.get(id) {
None => Node::Empty, None => Node::Empty,
Some(x) => match Node::many(x.clone()) { Some(x) => Node::from(x.clone()),
Err(e) => Node::Empty,
Ok(x) => x,
}
} }
} }
} }
pub(crate) trait InsertStr { pub(crate) trait InsertStr {
fn insert_str(&mut self, k: &str, v: Option<&str>); fn insert_str(&mut self, k: &str, v: Option<&str>);
fn insert_timestr(&mut self, k: &str, t: Option<chrono::DateTime<chrono::Utc>>); fn insert_timestr(&mut self, k: &str, t: Option<chrono::DateTime<chrono::Utc>>);
@ -133,7 +165,7 @@ impl InsertStr for serde_json::Map<String, serde_json::Value> {
fn insert_timestr(&mut self, k: &str, t: Option<chrono::DateTime<chrono::Utc>>) { fn insert_timestr(&mut self, k: &str, t: Option<chrono::DateTime<chrono::Utc>>) {
if let Some(published) = t { if let Some(published) = t {
self.insert( self.insert(
"published".to_string(), k.to_string(),
serde_json::Value::String(published.to_rfc3339()), serde_json::Value::String(published.to_rfc3339()),
); );
} }

View file

@ -70,6 +70,7 @@ pub trait Object : super::Base {
impl Object for serde_json::Value { impl Object for serde_json::Value {
fn object_type(&self) -> Option<ObjectType> { fn object_type(&self) -> Option<ObjectType> {
use super::Base;
match self.base_type() { match self.base_type() {
Some(super::BaseType::Object(o)) => Some(o), Some(super::BaseType::Object(o)) => Some(o),
_ => None, _ => None,