2024-04-06 19:16:15 +02:00
|
|
|
/// ActivityPub object node, representing either nothing, something, a link to something or
|
|
|
|
/// multiple things
|
2024-04-06 16:56:13 +02:00
|
|
|
pub enum Node<T : super::Base> {
|
2024-05-19 21:18:04 +02:00
|
|
|
Array(std::collections::VecDeque<Node<T>>), // TODO would be cool to make it Box<[T]> so that Node is just a ptr
|
2024-04-06 16:56:13 +02:00
|
|
|
Object(Box<T>),
|
2024-04-29 20:01:39 +02:00
|
|
|
Link(Box<dyn crate::Link + Sync + Send>), // TODO feature flag to toggle these maybe?
|
2024-04-06 16:56:13 +02:00
|
|
|
Empty,
|
|
|
|
}
|
|
|
|
|
2024-05-19 21:18:04 +02:00
|
|
|
// TODO convert in a from_residual (iirc?) so that in rust nightly we can do ?
|
2024-04-06 16:56:13 +02:00
|
|
|
impl<T : super::Base> From<Option<T>> for Node<T> {
|
|
|
|
fn from(value: Option<T>) -> Self {
|
|
|
|
match value {
|
|
|
|
Some(x) => Node::Object(Box::new(x)),
|
|
|
|
None => Node::Empty,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-17 02:23:56 +02:00
|
|
|
impl<T : super::Base> Iterator for Node<T> {
|
2024-04-06 16:56:13 +02:00
|
|
|
type Item = T;
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
2024-05-17 02:23:56 +02:00
|
|
|
match std::mem::replace(self, Self::Empty) {
|
|
|
|
Self::Empty => None,
|
|
|
|
Self::Object(res) => Some(*res),
|
|
|
|
Self::Link(lnk) => {
|
|
|
|
*self = Self::Link(lnk);
|
|
|
|
None
|
|
|
|
},
|
|
|
|
Self::Array(mut arr) => {
|
2024-05-19 21:18:04 +02:00
|
|
|
let mut out = None;
|
|
|
|
while let Some(res) = arr.pop_front() {
|
|
|
|
if let Some(inner) = res.extract() {
|
|
|
|
out = Some(inner);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2024-05-17 02:23:56 +02:00
|
|
|
*self = Self::Array(arr);
|
2024-05-19 21:18:04 +02:00
|
|
|
out
|
2024-05-17 02:23:56 +02:00
|
|
|
}
|
|
|
|
}
|
2024-04-06 16:56:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T : super::Base> Node<T> {
|
2024-05-19 21:18:04 +02:00
|
|
|
/// return reference to embedded object (or first if many are present)
|
2024-04-06 16:56:13 +02:00
|
|
|
pub fn get(&self) -> Option<&T> {
|
|
|
|
match self {
|
|
|
|
Node::Empty | Node::Link(_) => None,
|
|
|
|
Node::Object(x) => Some(x),
|
2024-05-19 21:18:04 +02:00
|
|
|
Node::Array(v) => v.iter().filter_map(|x| x.get()).next(),
|
2024-04-06 16:56:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-19 21:18:04 +02:00
|
|
|
/// return reference to embedded object (or first if many are present)
|
2024-04-06 16:56:13 +02:00
|
|
|
pub fn extract(self) -> Option<T> {
|
|
|
|
match self {
|
|
|
|
Node::Empty | Node::Link(_) => None,
|
|
|
|
Node::Object(x) => Some(*x),
|
2024-05-19 21:18:04 +02:00
|
|
|
Node::Array(mut v) => v.pop_front()?.extract(),
|
2024-04-06 16:56:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-06 19:16:15 +02:00
|
|
|
/// true only if Node is empty
|
2024-04-23 23:27:01 +02:00
|
|
|
pub fn is_nothing(&self) -> bool {
|
2024-04-06 16:56:13 +02:00
|
|
|
matches!(self, Node::Empty)
|
|
|
|
}
|
|
|
|
|
2024-04-06 19:16:15 +02:00
|
|
|
/// true only if Node is link
|
2024-04-06 16:56:13 +02:00
|
|
|
pub fn is_link(&self) -> bool {
|
|
|
|
matches!(self, Node::Link(_))
|
|
|
|
}
|
|
|
|
|
2024-04-06 19:16:15 +02:00
|
|
|
/// true only if Node contains one embedded object
|
2024-04-06 16:56:13 +02:00
|
|
|
pub fn is_object(&self) -> bool {
|
|
|
|
matches!(self, Node::Object(_))
|
|
|
|
}
|
|
|
|
|
2024-04-06 19:16:15 +02:00
|
|
|
/// true only if Node contains many embedded objects
|
2024-04-06 16:56:13 +02:00
|
|
|
pub fn is_array(&self) -> bool {
|
|
|
|
matches!(self, Node::Array(_))
|
|
|
|
}
|
|
|
|
|
2024-04-23 23:27:01 +02:00
|
|
|
/// true only if Node is empty
|
|
|
|
pub fn is_empty(&self) -> bool {
|
|
|
|
self.len() == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-04-06 19:16:15 +02:00
|
|
|
/// returns number of contained items (links count as items for len)
|
2024-04-06 16:56:13 +02:00
|
|
|
pub fn len(&self) -> usize {
|
|
|
|
match self {
|
|
|
|
Node::Empty => 0,
|
|
|
|
Node::Link(_) => 1,
|
|
|
|
Node::Object(_) => 1,
|
|
|
|
Node::Array(v) => v.len(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-06 19:16:15 +02:00
|
|
|
/// returns id of object: url for link, id for object, None if empty or array
|
2024-04-06 16:56:13 +02:00
|
|
|
pub fn id(&self) -> Option<String> {
|
|
|
|
match self {
|
2024-05-13 16:32:23 +02:00
|
|
|
Node::Empty => None,
|
2024-04-06 16:56:13 +02:00
|
|
|
Node::Link(uri) => Some(uri.href().to_string()),
|
2024-05-13 16:32:23 +02:00
|
|
|
Node::Object(obj) => Some(obj.id()?.to_string()),
|
|
|
|
Node::Array(arr) => Some(arr.front()?.id()?.to_string()),
|
2024-04-06 16:56:13 +02:00
|
|
|
}
|
|
|
|
}
|
2024-05-19 21:18:04 +02:00
|
|
|
|
|
|
|
pub fn ids(&self) -> Vec<String> {
|
|
|
|
match self {
|
|
|
|
Node::Empty => vec![],
|
|
|
|
Node::Link(uri) => vec![uri.href().to_string()],
|
|
|
|
Node::Object(x) => x.id().map_or(vec![], |x| vec![x.to_string()]),
|
|
|
|
Node::Array(x) => x.iter().filter_map(Self::id).collect()
|
|
|
|
}
|
|
|
|
}
|
2024-05-20 01:56:32 +02:00
|
|
|
|
|
|
|
pub fn flat(self) -> Vec<Node<T>> {
|
|
|
|
match self {
|
|
|
|
Node::Empty => vec![],
|
|
|
|
Node::Link(_) | Node::Object(_) => vec![self],
|
|
|
|
// i think AP disallows array of arrays so no need to make this recursive
|
|
|
|
Node::Array(arr) => arr.into()
|
|
|
|
}
|
|
|
|
}
|
2024-04-06 16:56:13 +02:00
|
|
|
}
|
|
|
|
|
2024-04-06 18:04:14 +02:00
|
|
|
#[cfg(feature = "unstructured")]
|
2024-04-06 16:56:13 +02:00
|
|
|
impl Node<serde_json::Value> {
|
|
|
|
pub fn link(uri: String) -> Self {
|
|
|
|
Node::Link(Box::new(uri))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn links(uris: Vec<String>) -> Self {
|
|
|
|
Node::Array(
|
|
|
|
uris
|
|
|
|
.into_iter()
|
2024-05-19 21:18:04 +02:00
|
|
|
.map(Node::link)
|
2024-04-06 16:56:13 +02:00
|
|
|
.collect()
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn maybe_link(uri: Option<String>) -> Self {
|
|
|
|
match uri {
|
|
|
|
Some(uri) => Node::Link(Box::new(uri)),
|
|
|
|
None => Node::Empty,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn object(x: serde_json::Value) -> Self {
|
|
|
|
Node::Object(Box::new(x))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn maybe_object(x: Option<serde_json::Value>) -> Self {
|
|
|
|
match x {
|
|
|
|
Some(x) => Node::Object(Box::new(x)),
|
|
|
|
None => Node::Empty,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn array(values: Vec<serde_json::Value>) -> Self {
|
2024-05-19 21:18:04 +02:00
|
|
|
Node::Array(
|
|
|
|
std::collections::VecDeque::from_iter(
|
|
|
|
values.into_iter()
|
|
|
|
.map(Node::object)
|
|
|
|
)
|
|
|
|
)
|
2024-04-06 16:56:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "fetch")]
|
|
|
|
pub async fn fetch(&mut self) -> reqwest::Result<&mut Self> {
|
|
|
|
if let Node::Link(link) = self {
|
|
|
|
*self = reqwest::Client::new()
|
|
|
|
.get(link.href())
|
|
|
|
.header("Accept", "application/json")
|
|
|
|
.send()
|
|
|
|
.await?
|
|
|
|
.json::<serde_json::Value>()
|
|
|
|
.await?
|
|
|
|
.into();
|
|
|
|
}
|
|
|
|
Ok(self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-06 18:04:14 +02:00
|
|
|
#[cfg(feature = "unstructured")]
|
2024-04-06 16:56:13 +02:00
|
|
|
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,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-06 18:04:14 +02:00
|
|
|
#[cfg(feature = "unstructured")]
|
2024-04-06 16:56:13 +02:00
|
|
|
impl From<&str> for Node<serde_json::Value> {
|
|
|
|
fn from(value: &str) -> Self {
|
|
|
|
Node::Link(Box::new(value.to_string()))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-06 18:04:14 +02:00
|
|
|
#[cfg(feature = "unstructured")]
|
2024-04-06 16:56:13 +02:00
|
|
|
impl From<serde_json::Value> for Node<serde_json::Value> {
|
|
|
|
fn from(value: serde_json::Value) -> Self {
|
|
|
|
match value {
|
|
|
|
serde_json::Value::String(uri) => Node::Link(Box::new(uri)),
|
2024-05-19 21:18:04 +02:00
|
|
|
serde_json::Value::Array(arr) => Node::Array(
|
|
|
|
std::collections::VecDeque::from_iter(
|
|
|
|
arr.into_iter()
|
|
|
|
.map(Node::from)
|
|
|
|
)
|
|
|
|
),
|
2024-04-06 16:56:13 +02:00
|
|
|
serde_json::Value::Object(_) => match value.get("href") {
|
|
|
|
None => Node::Object(Box::new(value)),
|
|
|
|
Some(_) => Node::Link(Box::new(value)),
|
|
|
|
},
|
|
|
|
_ => Node::Empty,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-23 00:19:28 +02:00
|
|
|
#[cfg(feature = "unstructured")]
|
|
|
|
impl From<Node<serde_json::Value>> for serde_json::Value {
|
|
|
|
fn from(value: Node<serde_json::Value>) -> Self {
|
|
|
|
match value {
|
|
|
|
Node::Empty => serde_json::Value::Null,
|
|
|
|
Node::Link(l) => serde_json::Value::String(l.href().to_string()), // TODO there could be more
|
|
|
|
Node::Object(o) => *o,
|
|
|
|
Node::Array(arr) =>
|
|
|
|
serde_json::Value::Array(arr.into_iter().map(|x| x.into()).collect()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|