fix(apb): actually link href is not guaranteed

This commit is contained in:
əlemi 2024-07-06 04:25:06 +02:00
parent c3e319d5a9
commit 2b4fb3bd62
Signed by: alemi
GPG key ID: A4895B84D311642C
7 changed files with 42 additions and 35 deletions

View file

@ -102,7 +102,7 @@ impl<T : super::Base> Node<T> {
pub fn id(&self) -> crate::Field<&str> {
match self {
Node::Empty => Err(crate::FieldErr("id")),
Node::Link(uri) => Ok(uri.href()),
Node::Link(uri) => uri.href(),
Node::Object(obj) => obj.id(),
Node::Array(arr) => arr.front().map(|x| x.id()).ok_or(crate::FieldErr("id"))?,
}
@ -111,7 +111,7 @@ impl<T : super::Base> Node<T> {
pub fn all_ids(&self) -> Vec<String> {
match self {
Node::Empty => vec![],
Node::Link(uri) => vec![uri.href().to_string()],
Node::Link(uri) => uri.href().map(|x| vec![x.to_string()]).unwrap_or_default(),
Node::Object(x) => x.id().map_or(vec![], |x| vec![x.to_string()]),
Node::Array(x) => x.iter().filter_map(|x| Some(x.id().ok()?.to_string())).collect()
}
@ -236,7 +236,7 @@ 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::Link(l) => serde_json::Value::String(l.href().unwrap_or_default().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()),

View file

@ -19,7 +19,7 @@ crate::strenum! {
pub trait Link : crate::Base {
fn link_type(&self) -> Field<LinkType> { Err(FieldErr("type")) }
fn href(&self) -> &str;
fn href(&self) -> Field<&str>;
fn rel(&self) -> Field<&str> { Err(FieldErr("rel")) }
fn media_type(&self) -> Field<&str> { Err(FieldErr("mediaType")) } // also in obj
fn name(&self) -> Field<&str> { Err(FieldErr("name")) } // also in obj
@ -31,7 +31,7 @@ pub trait Link : crate::Base {
pub trait LinkMut : crate::BaseMut {
fn set_link_type(self, val: Option<LinkType>) -> Self;
fn set_href(self, href: &str) -> Self;
fn set_href(self, href: Option<&str>) -> Self;
fn set_rel(self, val: Option<&str>) -> Self;
fn set_media_type(self, val: Option<&str>) -> Self; // also in obj
fn set_name(self, val: Option<&str>) -> Self; // also in obj
@ -42,19 +42,19 @@ pub trait LinkMut : crate::BaseMut {
}
impl Link for String {
fn href(&self) -> &str {
self
fn href(&self) -> Field<&str> {
Ok(self)
}
}
#[cfg(feature = "unstructured")]
impl Link for serde_json::Value {
// TODO this can fail, but it should never do!
fn href(&self) -> &str {
fn href(&self) -> Field<&str> {
if self.is_string() {
self.as_str().unwrap_or("")
self.as_str().ok_or(FieldErr("href"))
} else {
self.get("href").map(|x| x.as_str().unwrap_or("")).unwrap_or("")
self.get("href").and_then(|x| x.as_str()).ok_or(FieldErr("href"))
}
}
@ -70,15 +70,18 @@ impl Link for serde_json::Value {
#[cfg(feature = "unstructured")]
impl LinkMut for serde_json::Value {
fn set_href(mut self, href: &str) -> Self {
fn set_href(mut self, href: Option<&str>) -> Self {
match &mut self {
serde_json::Value::Object(map) => {
map.insert(
match href {
Some(href) => map.insert(
"href".to_string(),
serde_json::Value::String(href.to_string())
);
),
None => map.remove("href"),
};
},
x => *x = serde_json::Value::String(href.to_string()),
x => *x = serde_json::Value::String(href.unwrap_or_default().to_string()),
}
self
}

View file

@ -31,7 +31,7 @@ impl<T: apb::Base> From<apb::Node<T>> for Audience {
Audience(
match value {
apb::Node::Empty => vec![],
apb::Node::Link(l) => vec![l.href().to_string()],
apb::Node::Link(l) => l.href().map(|x| vec![x.to_string()]).unwrap_or_default(),
apb::Node::Object(o) => if let Ok(id) = o.id() { vec![id.to_string()] } else { vec![] },
apb::Node::Array(arr) => arr.into_iter().filter_map(|l| l.id().str()).collect(),
}

View file

@ -12,7 +12,7 @@ impl RichMention {
use apb::LinkMut;
apb::new()
.set_link_type(Some(apb::LinkType::Mention))
.set_href(&self.id)
.set_href(Some(&self.id))
.set_name(Some(&self.fqn))
}
}

View file

@ -427,12 +427,14 @@ pub trait Fetchable : Sync + Send {
impl Fetchable for apb::Node<serde_json::Value> {
async fn fetch(&mut self, ctx: &crate::Context) -> Result<&mut Self, PullError> {
if let apb::Node::Link(uri) = self {
*self = crate::Context::request(Method::GET, uri.href(), None, ctx.base(), ctx.pkey(), ctx.domain())
if let Ok(href) = uri.href() {
*self = crate::Context::request(Method::GET, href, None, ctx.base(), ctx.pkey(), ctx.domain())
.await?
.json::<serde_json::Value>()
.await?
.into();
}
}
Ok(self)
}

View file

@ -77,7 +77,7 @@ impl Normalizer for crate::Context {
},
Node::Link(l) => crate::model::attachment::ActiveModel {
internal: sea_orm::ActiveValue::NotSet,
url: Set(l.href().to_string()),
url: Set(l.href().unwrap_or_default().to_string()),
object: Set(object_model.internal),
document_type: Set(apb::DocumentType::Page),
name: Set(l.name().str()),
@ -96,7 +96,8 @@ impl Normalizer for crate::Context {
Node::Empty | Node::Object(_) | Node::Array(_) => {},
Node::Link(l) => match l.link_type() {
Ok(apb::LinkType::Mention) => {
if let Some(internal) = crate::model::actor::Entity::ap_to_internal(l.href(), tx).await? {
if let Ok(href) = l.href() {
if let Some(internal) = crate::model::actor::Entity::ap_to_internal(href, tx).await? {
let model = crate::model::mention::ActiveModel {
internal: NotSet,
object: Set(object_model.internal),
@ -106,10 +107,11 @@ impl Normalizer for crate::Context {
.exec(tx)
.await?;
}
}
},
Ok(apb::LinkType::Hashtag) => {
let hashtag = l.name()
.unwrap_or_else(|_| l.href().split('/').last().unwrap_or_default())
.unwrap_or_else(|_| l.href().unwrap_or_default().split('/').last().unwrap_or_default()) // TODO maybe just fail?
.replace('#', "");
let model = crate::model::hashtag::ActiveModel {
internal: NotSet,

View file

@ -72,7 +72,7 @@ pub fn Object(
})
},
Ok(apb::LinkType::Mention) => {
let uid = apb::Link::href(link.as_ref());
let uid = apb::Link::href(link.as_ref()).unwrap_or_default();
let mention = apb::Link::name(link.as_ref()).unwrap_or_default().replacen('@', "", 1);
let (username, domain) = if let Some((username, server)) = mention.split_once('@') {
(username.to_string(), server.to_string())