No longer depend on activitystreams
- Pull in primitives and traits that we need - Remove primitives that don't make sense - Limit the use of XsdAnyUri and similar primitives from public API - Add domain validation methods for ID and Actor fields - Add mutable borrows for Actor fields
This commit is contained in:
parent
ce69a70a92
commit
33c262461d
23 changed files with 2995 additions and 1438 deletions
|
@ -12,10 +12,12 @@ edition = "2018"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
activitystreams = { version = "0.6.2", default-features = false, features = ["kinds","primitives"] }
|
||||
chrono = "0.4"
|
||||
mime = "0.3"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
typed-builder = "0.6.0"
|
||||
thiserror = "1.0"
|
||||
url = "2.1"
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = "1.0"
|
||||
|
|
|
@ -4,6 +4,7 @@ use activitystreams_new::{
|
|||
prelude::*,
|
||||
uri,
|
||||
};
|
||||
use chrono::Duration;
|
||||
|
||||
fn main() -> Result<(), anyhow::Error> {
|
||||
let mut video = ApObject::new(Video::new());
|
||||
|
@ -14,7 +15,7 @@ fn main() -> Result<(), anyhow::Error> {
|
|||
.set_media_type("video/webm".parse()?)
|
||||
.set_url(uri!("https://example.com/@example/lions/video.webm"))
|
||||
.set_summary("A cool video".to_owned())
|
||||
.set_duration("PT4M20S".parse()?)
|
||||
.set_duration(Duration::minutes(4) + Duration::seconds(20))
|
||||
.set_shares(uri!("https://example.com/@example/lions/video.webm#shares"));
|
||||
|
||||
println!("Video, {:#?}", video);
|
||||
|
|
264
src/activity.rs
264
src/activity.rs
|
@ -26,11 +26,11 @@ use crate::{
|
|||
base::{AnyBase, AsBase, Base, Extends},
|
||||
markers,
|
||||
object::{ApObject, AsObject, Object},
|
||||
primitives::{OneOrMany, XsdAnyUri},
|
||||
primitives::OneOrMany,
|
||||
unparsed::{Unparsed, UnparsedMut, UnparsedMutExt},
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
use typed_builder::TypedBuilder;
|
||||
use url::Url;
|
||||
|
||||
pub mod kind {
|
||||
//! Kinds of activities defined by the spec
|
||||
|
@ -38,7 +38,36 @@ pub mod kind {
|
|||
//! These types exist only to be statically-typed versions of the associated string. e.g.
|
||||
//! `CreateType` -> `"Create"`
|
||||
|
||||
pub use activitystreams::activity::kind::*;
|
||||
use crate::kind;
|
||||
|
||||
kind!(AcceptType, Accept);
|
||||
kind!(AddType, Add);
|
||||
kind!(AnnounceType, Announce);
|
||||
kind!(ArriveType, Arrive);
|
||||
kind!(BlockType, Block);
|
||||
kind!(CreateType, Create);
|
||||
kind!(DeleteType, Delete);
|
||||
kind!(DislikeType, Dislike);
|
||||
kind!(FlagType, Flag);
|
||||
kind!(FollowType, Follow);
|
||||
kind!(IgnoreType, Ignore);
|
||||
kind!(InviteType, Invite);
|
||||
kind!(JoinType, Join);
|
||||
kind!(LeaveType, Leave);
|
||||
kind!(LikeType, Like);
|
||||
kind!(ListenType, Listen);
|
||||
kind!(MoveType, Move);
|
||||
kind!(OfferType, Offer);
|
||||
kind!(QuestionType, Question);
|
||||
kind!(ReadType, Read);
|
||||
kind!(RejectType, Reject);
|
||||
kind!(RemoveType, Remove);
|
||||
kind!(TentativeAcceptType, TentativeAccept);
|
||||
kind!(TentativeRejectType, TentativeReject);
|
||||
kind!(TravelType, Travel);
|
||||
kind!(UndoType, Undo);
|
||||
kind!(UpdateType, Update);
|
||||
kind!(ViewType, View);
|
||||
}
|
||||
|
||||
use self::kind::*;
|
||||
|
@ -445,7 +474,7 @@ pub trait ActorAndObjectRefExt: ActorAndObjectRef {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn actor_is(&self, id: &XsdAnyUri) -> bool {
|
||||
fn actor_is(&self, id: &Url) -> bool {
|
||||
self.actor().is_single_id(id)
|
||||
}
|
||||
|
||||
|
@ -551,7 +580,7 @@ pub trait ActorAndObjectRefExt: ActorAndObjectRef {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn object_is(&self, id: &XsdAnyUri) -> bool {
|
||||
fn object_is(&self, id: &Url) -> bool {
|
||||
self.object().is_single_id(id)
|
||||
}
|
||||
|
||||
|
@ -1562,7 +1591,7 @@ pub type Remove = ActorAndObjectOptOriginAndTarget<RemoveType>;
|
|||
/// Activity objects are specializations of the base Object type that provide information about
|
||||
/// actions that have either already occurred, are in the process of occurring, or may occur in the
|
||||
/// future.
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Activity<Kind> {
|
||||
/// Describes the result of the activity.
|
||||
|
@ -1573,24 +1602,22 @@ pub struct Activity<Kind> {
|
|||
/// - Range: Object | Link
|
||||
/// - Funcitonal: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub result: Option<OneOrMany<AnyBase>>,
|
||||
result: Option<OneOrMany<AnyBase>>,
|
||||
|
||||
/// Identifies one or more objects used (or to be used) in the completion of an Activity.
|
||||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Funcitonal: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub instrument: Option<OneOrMany<AnyBase>>,
|
||||
instrument: Option<OneOrMany<AnyBase>>,
|
||||
|
||||
/// base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: Object<Kind>,
|
||||
inner: Object<Kind>,
|
||||
}
|
||||
|
||||
/// Activity with actor and object properties
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ActorAndObject<Kind> {
|
||||
/// Describes one or more entities that either performed or are expected to perform the
|
||||
|
@ -1601,8 +1628,7 @@ pub struct ActorAndObject<Kind> {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub actor: OneOrMany<AnyBase>,
|
||||
actor: OneOrMany<AnyBase>,
|
||||
|
||||
/// When used within an Activity, describes the direct object of the activity.
|
||||
///
|
||||
|
@ -1611,19 +1637,18 @@ pub struct ActorAndObject<Kind> {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub object: OneOrMany<AnyBase>,
|
||||
object: OneOrMany<AnyBase>,
|
||||
|
||||
/// base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: Activity<Kind>,
|
||||
inner: Activity<Kind>,
|
||||
}
|
||||
|
||||
/// An IntransitiveActivity that indicates that the actor has arrived at the location.
|
||||
///
|
||||
/// The origin can be used to identify the context from which the actor originated. The target
|
||||
/// typically has no defined meaning.
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Arrive {
|
||||
/// Describes one or more entities that either performed or are expected to perform the
|
||||
|
@ -1634,8 +1659,7 @@ pub struct Arrive {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub actor: OneOrMany<AnyBase>,
|
||||
actor: OneOrMany<AnyBase>,
|
||||
|
||||
/// Describes an indirect object of the activity from which the activity is directed.
|
||||
///
|
||||
|
@ -1645,17 +1669,16 @@ pub struct Arrive {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub origin: OneOrMany<AnyBase>,
|
||||
origin: OneOrMany<AnyBase>,
|
||||
|
||||
/// base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: Activity<ArriveType>,
|
||||
inner: Activity<ArriveType>,
|
||||
}
|
||||
|
||||
/// A specialization of Offer in which the actor is extending an invitation for the object to the
|
||||
/// target.
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Invite {
|
||||
/// Describes one or more entities that either performed or are expected to perform the
|
||||
|
@ -1666,8 +1689,7 @@ pub struct Invite {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub actor: OneOrMany<AnyBase>,
|
||||
actor: OneOrMany<AnyBase>,
|
||||
|
||||
/// When used within an Activity, describes the direct object of the activity.
|
||||
///
|
||||
|
@ -1676,8 +1698,7 @@ pub struct Invite {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub object: OneOrMany<AnyBase>,
|
||||
object: OneOrMany<AnyBase>,
|
||||
|
||||
/// Describes the indirect object, or target, of the activity.
|
||||
///
|
||||
|
@ -1688,18 +1709,17 @@ pub struct Invite {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub target: OneOrMany<AnyBase>,
|
||||
target: OneOrMany<AnyBase>,
|
||||
|
||||
/// base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: Activity<InviteType>,
|
||||
inner: Activity<InviteType>,
|
||||
}
|
||||
|
||||
/// Indicates that the actor has deleted the object.
|
||||
///
|
||||
/// If specified, the origin indicates the context from which the object was deleted.
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Delete {
|
||||
/// Describes one or more entities that either performed or are expected to perform the
|
||||
|
@ -1710,8 +1730,7 @@ pub struct Delete {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub actor: OneOrMany<AnyBase>,
|
||||
actor: OneOrMany<AnyBase>,
|
||||
|
||||
/// When used within an Activity, describes the direct object of the activity.
|
||||
///
|
||||
|
@ -1720,8 +1739,7 @@ pub struct Delete {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub object: OneOrMany<AnyBase>,
|
||||
object: OneOrMany<AnyBase>,
|
||||
|
||||
/// Describes an indirect object of the activity from which the activity is directed.
|
||||
///
|
||||
|
@ -1732,16 +1750,15 @@ pub struct Delete {
|
|||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub origin: Option<OneOrMany<AnyBase>>,
|
||||
origin: Option<OneOrMany<AnyBase>>,
|
||||
|
||||
/// base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: Activity<DeleteType>,
|
||||
inner: Activity<DeleteType>,
|
||||
}
|
||||
|
||||
/// Activity with actor, object, and optional origin and target properties
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ActorAndObjectOptOriginAndTarget<Kind> {
|
||||
/// Describes one or more entities that either performed or are expected to perform the
|
||||
|
@ -1752,8 +1769,7 @@ pub struct ActorAndObjectOptOriginAndTarget<Kind> {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub actor: OneOrMany<AnyBase>,
|
||||
actor: OneOrMany<AnyBase>,
|
||||
|
||||
/// When used within an Activity, describes the direct object of the activity.
|
||||
///
|
||||
|
@ -1762,8 +1778,7 @@ pub struct ActorAndObjectOptOriginAndTarget<Kind> {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub object: OneOrMany<AnyBase>,
|
||||
object: OneOrMany<AnyBase>,
|
||||
|
||||
/// Describes an indirect object of the activity from which the activity is directed.
|
||||
///
|
||||
|
@ -1774,8 +1789,7 @@ pub struct ActorAndObjectOptOriginAndTarget<Kind> {
|
|||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub origin: Option<OneOrMany<AnyBase>>,
|
||||
origin: Option<OneOrMany<AnyBase>>,
|
||||
|
||||
/// Describes the indirect object, or target, of the activity.
|
||||
///
|
||||
|
@ -1787,16 +1801,15 @@ pub struct ActorAndObjectOptOriginAndTarget<Kind> {
|
|||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub target: Option<OneOrMany<AnyBase>>,
|
||||
target: Option<OneOrMany<AnyBase>>,
|
||||
|
||||
/// base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: Activity<Kind>,
|
||||
inner: Activity<Kind>,
|
||||
}
|
||||
|
||||
/// Activity with actor, object, and optional target properties
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ActorAndObjectOptTarget<Kind> {
|
||||
/// Describes one or more entities that either performed or are expected to perform the
|
||||
|
@ -1807,8 +1820,7 @@ pub struct ActorAndObjectOptTarget<Kind> {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub actor: OneOrMany<AnyBase>,
|
||||
actor: OneOrMany<AnyBase>,
|
||||
|
||||
/// When used within an Activity, describes the direct object of the activity.
|
||||
///
|
||||
|
@ -1817,8 +1829,7 @@ pub struct ActorAndObjectOptTarget<Kind> {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub object: OneOrMany<AnyBase>,
|
||||
object: OneOrMany<AnyBase>,
|
||||
|
||||
/// Describes the indirect object, or target, of the activity.
|
||||
///
|
||||
|
@ -1830,19 +1841,18 @@ pub struct ActorAndObjectOptTarget<Kind> {
|
|||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub target: Option<OneOrMany<AnyBase>>,
|
||||
target: Option<OneOrMany<AnyBase>>,
|
||||
|
||||
/// base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: Activity<Kind>,
|
||||
inner: Activity<Kind>,
|
||||
}
|
||||
|
||||
/// Indicates that the actor is traveling to target from origin.
|
||||
///
|
||||
/// Travel is an IntransitiveObject whose actor specifies the direct object. If the target or
|
||||
/// origin are not specified, either can be determined by context.
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Travel {
|
||||
/// Describes one or more entities that either performed or are expected to perform the
|
||||
|
@ -1853,8 +1863,7 @@ pub struct Travel {
|
|||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub actor: OneOrMany<AnyBase>,
|
||||
actor: OneOrMany<AnyBase>,
|
||||
|
||||
/// Describes an indirect object of the activity from which the activity is directed.
|
||||
///
|
||||
|
@ -1865,8 +1874,7 @@ pub struct Travel {
|
|||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub origin: Option<OneOrMany<AnyBase>>,
|
||||
origin: Option<OneOrMany<AnyBase>>,
|
||||
|
||||
/// Describes the indirect object, or target, of the activity.
|
||||
///
|
||||
|
@ -1878,12 +1886,11 @@ pub struct Travel {
|
|||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub target: Option<OneOrMany<AnyBase>>,
|
||||
target: Option<OneOrMany<AnyBase>>,
|
||||
|
||||
/// base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: Activity<TravelType>,
|
||||
inner: Activity<TravelType>,
|
||||
}
|
||||
|
||||
/// Represents a question being asked.
|
||||
|
@ -1894,7 +1901,7 @@ pub struct Travel {
|
|||
///
|
||||
/// Either of the anyOf and oneOf properties MAY be used to express possible answers, but a
|
||||
/// Question object MUST NOT have both properties.
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Question {
|
||||
/// Identifies an exclusive option for a Question.
|
||||
|
@ -1905,8 +1912,7 @@ pub struct Question {
|
|||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub one_of: Option<OneOrMany<AnyBase>>,
|
||||
one_of: Option<OneOrMany<AnyBase>>,
|
||||
|
||||
/// Identifies an inclusive option for a Question.
|
||||
///
|
||||
|
@ -1916,12 +1922,11 @@ pub struct Question {
|
|||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub any_of: Option<OneOrMany<AnyBase>>,
|
||||
any_of: Option<OneOrMany<AnyBase>>,
|
||||
|
||||
/// base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: Activity<QuestionType>,
|
||||
inner: Activity<QuestionType>,
|
||||
}
|
||||
|
||||
impl<Kind> Activity<Kind> {
|
||||
|
@ -1936,7 +1941,11 @@ impl<Kind> Activity<Kind> {
|
|||
where
|
||||
Kind: Default,
|
||||
{
|
||||
Activity::builder().inner(Object::new()).build()
|
||||
Activity {
|
||||
result: None,
|
||||
instrument: None,
|
||||
inner: Object::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(mut inner: Object<Kind>) -> Result<Self, serde_json::Error> {
|
||||
|
@ -1979,11 +1988,11 @@ impl<Kind> ActorAndObject<Kind> {
|
|||
U: Into<OneOrMany<AnyBase>>,
|
||||
Kind: Default,
|
||||
{
|
||||
Self::builder()
|
||||
.actor(actor)
|
||||
.object(object)
|
||||
.inner(Activity::new())
|
||||
.build()
|
||||
ActorAndObject {
|
||||
actor: actor.into(),
|
||||
object: object.into(),
|
||||
inner: Activity::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(object: Object<Kind>) -> Result<Self, serde_json::Error> {
|
||||
|
@ -2025,11 +2034,11 @@ impl Arrive {
|
|||
T: Into<OneOrMany<AnyBase>>,
|
||||
U: Into<OneOrMany<AnyBase>>,
|
||||
{
|
||||
Self::builder()
|
||||
.actor(actor)
|
||||
.origin(origin)
|
||||
.inner(Activity::new())
|
||||
.build()
|
||||
Arrive {
|
||||
actor: actor.into(),
|
||||
origin: origin.into(),
|
||||
inner: Activity::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(object: Object<ArriveType>) -> Result<Self, serde_json::Error> {
|
||||
|
@ -2072,12 +2081,12 @@ impl Invite {
|
|||
U: Into<OneOrMany<AnyBase>>,
|
||||
V: Into<OneOrMany<AnyBase>>,
|
||||
{
|
||||
Self::builder()
|
||||
.actor(actor)
|
||||
.object(object)
|
||||
.target(target)
|
||||
.inner(Activity::new())
|
||||
.build()
|
||||
Invite {
|
||||
actor: actor.into(),
|
||||
object: object.into(),
|
||||
target: target.into(),
|
||||
inner: Activity::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(object: Object<InviteType>) -> Result<Self, serde_json::Error> {
|
||||
|
@ -2125,11 +2134,12 @@ impl Delete {
|
|||
T: Into<OneOrMany<AnyBase>>,
|
||||
U: Into<OneOrMany<AnyBase>>,
|
||||
{
|
||||
Self::builder()
|
||||
.actor(actor)
|
||||
.object(object)
|
||||
.inner(Activity::new())
|
||||
.build()
|
||||
Delete {
|
||||
actor: actor.into(),
|
||||
object: object.into(),
|
||||
origin: None,
|
||||
inner: Activity::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(object: Object<DeleteType>) -> Result<Self, serde_json::Error> {
|
||||
|
@ -2181,11 +2191,13 @@ impl<Kind> ActorAndObjectOptOriginAndTarget<Kind> {
|
|||
U: Into<OneOrMany<AnyBase>>,
|
||||
Kind: Default,
|
||||
{
|
||||
Self::builder()
|
||||
.actor(actor)
|
||||
.object(object)
|
||||
.inner(Activity::new())
|
||||
.build()
|
||||
ActorAndObjectOptOriginAndTarget {
|
||||
actor: actor.into(),
|
||||
object: object.into(),
|
||||
origin: None,
|
||||
target: None,
|
||||
inner: Activity::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(object: Object<Kind>) -> Result<Self, serde_json::Error> {
|
||||
|
@ -2241,11 +2253,12 @@ impl<Kind> ActorAndObjectOptTarget<Kind> {
|
|||
U: Into<OneOrMany<AnyBase>>,
|
||||
Kind: Default,
|
||||
{
|
||||
Self::builder()
|
||||
.actor(actor)
|
||||
.object(object)
|
||||
.inner(Activity::new())
|
||||
.build()
|
||||
ActorAndObjectOptTarget {
|
||||
actor: actor.into(),
|
||||
object: object.into(),
|
||||
target: None,
|
||||
inner: Activity::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(object: Object<Kind>) -> Result<Self, serde_json::Error> {
|
||||
|
@ -2292,7 +2305,12 @@ impl Travel {
|
|||
where
|
||||
T: Into<OneOrMany<AnyBase>>,
|
||||
{
|
||||
Self::builder().actor(actor).inner(Activity::new()).build()
|
||||
Travel {
|
||||
actor: actor.into(),
|
||||
origin: None,
|
||||
target: None,
|
||||
inner: Activity::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(object: Object<TravelType>) -> Result<Self, serde_json::Error> {
|
||||
|
@ -2336,7 +2354,11 @@ impl Question {
|
|||
/// let activity = Question::new();
|
||||
/// ```
|
||||
pub fn new() -> Self {
|
||||
Question::builder().inner(Activity::new()).build()
|
||||
Question {
|
||||
one_of: None,
|
||||
any_of: None,
|
||||
inner: Activity::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(object: Object<QuestionType>) -> Result<Self, serde_json::Error> {
|
||||
|
@ -3189,11 +3211,11 @@ where
|
|||
Inner: AsActivity<Kind>,
|
||||
{
|
||||
fn activity_ref(&self) -> &Activity<Kind> {
|
||||
self.inner.activity_ref()
|
||||
self.inner().activity_ref()
|
||||
}
|
||||
|
||||
fn activity_mut(&mut self) -> &mut Activity<Kind> {
|
||||
self.inner.activity_mut()
|
||||
self.inner_mut().activity_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3202,19 +3224,19 @@ where
|
|||
Inner: ActorAndObjectRef,
|
||||
{
|
||||
fn actor_field_ref(&self) -> &OneOrMany<AnyBase> {
|
||||
self.inner.actor_field_ref()
|
||||
self.inner().actor_field_ref()
|
||||
}
|
||||
|
||||
fn object_field_ref(&self) -> &OneOrMany<AnyBase> {
|
||||
self.inner.object_field_ref()
|
||||
self.inner().object_field_ref()
|
||||
}
|
||||
|
||||
fn actor_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
|
||||
self.inner.actor_field_mut()
|
||||
self.inner_mut().actor_field_mut()
|
||||
}
|
||||
|
||||
fn object_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
|
||||
self.inner.object_field_mut()
|
||||
self.inner_mut().object_field_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3223,11 +3245,11 @@ where
|
|||
Inner: TargetRef,
|
||||
{
|
||||
fn target_field_ref(&self) -> &OneOrMany<AnyBase> {
|
||||
self.inner.target_field_ref()
|
||||
self.inner().target_field_ref()
|
||||
}
|
||||
|
||||
fn target_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
|
||||
self.inner.target_field_mut()
|
||||
self.inner_mut().target_field_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3236,11 +3258,11 @@ where
|
|||
Inner: OriginRef,
|
||||
{
|
||||
fn origin_field_ref(&self) -> &OneOrMany<AnyBase> {
|
||||
self.inner.origin_field_ref()
|
||||
self.inner().origin_field_ref()
|
||||
}
|
||||
|
||||
fn origin_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
|
||||
self.inner.origin_field_mut()
|
||||
self.inner_mut().origin_field_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3249,11 +3271,11 @@ where
|
|||
Inner: OptTargetRef,
|
||||
{
|
||||
fn target_field_ref(&self) -> &Option<OneOrMany<AnyBase>> {
|
||||
self.inner.target_field_ref()
|
||||
self.inner().target_field_ref()
|
||||
}
|
||||
|
||||
fn target_field_mut(&mut self) -> &mut Option<OneOrMany<AnyBase>> {
|
||||
self.inner.target_field_mut()
|
||||
self.inner_mut().target_field_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3262,11 +3284,11 @@ where
|
|||
Inner: OptOriginRef,
|
||||
{
|
||||
fn origin_field_ref(&self) -> &Option<OneOrMany<AnyBase>> {
|
||||
self.inner.origin_field_ref()
|
||||
self.inner().origin_field_ref()
|
||||
}
|
||||
|
||||
fn origin_field_mut(&mut self) -> &mut Option<OneOrMany<AnyBase>> {
|
||||
self.inner.origin_field_mut()
|
||||
self.inner_mut().origin_field_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3275,11 +3297,11 @@ where
|
|||
Inner: AsQuestion,
|
||||
{
|
||||
fn question_ref(&self) -> &Question {
|
||||
self.inner.question_ref()
|
||||
self.inner().question_ref()
|
||||
}
|
||||
|
||||
fn question_mut(&mut self) -> &mut Question {
|
||||
self.inner.question_mut()
|
||||
self.inner_mut().question_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
713
src/actor.rs
713
src/actor.rs
File diff suppressed because it is too large
Load diff
215
src/base.rs
215
src/base.rs
|
@ -29,11 +29,13 @@
|
|||
//! ```
|
||||
use crate::{
|
||||
either::Either,
|
||||
error::DomainError,
|
||||
markers,
|
||||
primitives::{AnyString, MimeMediaType, OneOrMany, XsdAnyUri},
|
||||
unparsed::{Unparsed, UnparsedMut},
|
||||
};
|
||||
use typed_builder::TypedBuilder;
|
||||
use mime::Mime;
|
||||
use url::Url;
|
||||
|
||||
/// Implements conversion between `Base<Kind>` and other ActivityStreams objects defined in this
|
||||
/// crate
|
||||
|
@ -244,6 +246,34 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Fetch the id for the current object, checking it against the provided domain
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::{object::Video, uri};
|
||||
/// # let mut video = Video::new();
|
||||
/// # video.set_id(uri!("https://example.com"));
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
/// assert_eq!(video.id("example.com")?, Some(&uri!("https://example.com")));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn id<'a>(&'a self, domain: &str) -> Result<Option<&'a Url>, DomainError>
|
||||
where
|
||||
Kind: 'a,
|
||||
{
|
||||
if let Some(unchecked) = self.id_unchecked() {
|
||||
if unchecked.domain() != Some(domain) {
|
||||
return Err(DomainError);
|
||||
}
|
||||
|
||||
return Ok(Some(unchecked));
|
||||
}
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Fetch the id for the current object
|
||||
///
|
||||
/// ```rust
|
||||
|
@ -252,15 +282,35 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
/// #
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
/// if let Some(id) = video.id() {
|
||||
/// if let Some(id) = video.id_unchecked() {
|
||||
/// println!("{:?}", id);
|
||||
/// }
|
||||
/// ```
|
||||
fn id<'a>(&'a self) -> Option<&'a XsdAnyUri>
|
||||
fn id_unchecked<'a>(&'a self) -> Option<&'a Url>
|
||||
where
|
||||
Kind: 'a,
|
||||
{
|
||||
self.base_ref().id.as_ref()
|
||||
self.base_ref().id.as_ref().map(|i| i.as_ref())
|
||||
}
|
||||
|
||||
/// Mutably borrow the ID from the current object
|
||||
///
|
||||
/// ```rust
|
||||
/// # use activitystreams_new::object::Video;
|
||||
/// # let mut video = Video::new();
|
||||
/// #
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
/// if let Some(id) = video.id_mut() {
|
||||
/// id.set_path("/actor");
|
||||
/// println!("{:?}", id);
|
||||
/// }
|
||||
/// ```
|
||||
fn id_mut<'a>(&'a mut self) -> Option<&'a mut Url>
|
||||
where
|
||||
Kind: 'a,
|
||||
{
|
||||
self.base_mut().id.as_mut().map(|i| i.as_mut())
|
||||
}
|
||||
|
||||
/// Check if the provided id is equal to the object's id
|
||||
|
@ -275,8 +325,8 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn is_id(&self, id: &XsdAnyUri) -> bool {
|
||||
self.id() == Some(id)
|
||||
fn is_id(&self, id: &Url) -> bool {
|
||||
self.id_unchecked() == Some(id)
|
||||
}
|
||||
|
||||
/// Set the id for the current object
|
||||
|
@ -294,8 +344,8 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn set_id(&mut self, id: XsdAnyUri) -> &mut Self {
|
||||
self.base_mut().id = Some(id);
|
||||
fn set_id(&mut self, id: Url) -> &mut Self {
|
||||
self.base_mut().id = Some(id.into());
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -311,8 +361,8 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
/// println!("{:?}", id);
|
||||
/// }
|
||||
/// ```
|
||||
fn take_id(&mut self) -> Option<XsdAnyUri> {
|
||||
self.base_mut().id.take()
|
||||
fn take_id(&mut self) -> Option<Url> {
|
||||
self.base_mut().id.take().map(|u| u.into_inner())
|
||||
}
|
||||
|
||||
/// Delete the id from the current object
|
||||
|
@ -324,9 +374,9 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
/// #
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
/// assert!(video.id().is_some());
|
||||
/// assert!(video.id_unchecked().is_some());
|
||||
/// video.delete_id();
|
||||
/// assert!(video.id().is_none());
|
||||
/// assert!(video.id_unchecked().is_none());
|
||||
/// ```
|
||||
fn delete_id(&mut self) -> &mut Self {
|
||||
self.base_mut().id = None;
|
||||
|
@ -386,7 +436,7 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
/// # let mut video = Video::new();
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
/// video.set_kind(VideoType);
|
||||
/// video.set_kind(VideoType::Video);
|
||||
/// ```
|
||||
fn set_kind(&mut self, kind: Kind) -> &mut Self {
|
||||
self.base_mut().kind = Some(kind);
|
||||
|
@ -414,7 +464,7 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
/// ```rust
|
||||
/// # use activitystreams_new::{object::{Video, kind::VideoType}};
|
||||
/// # let mut video = Video::new();
|
||||
/// # video.set_kind(VideoType);
|
||||
/// # video.set_kind(VideoType::Video);
|
||||
/// #
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
|
@ -439,11 +489,11 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
/// println!("{:?}", name);
|
||||
/// }
|
||||
/// ```
|
||||
fn name<'a>(&'a self) -> Option<&'a OneOrMany<AnyString>>
|
||||
fn name<'a>(&'a self) -> Option<OneOrMany<&'a AnyString>>
|
||||
where
|
||||
Kind: 'a,
|
||||
{
|
||||
self.base_ref().name.as_ref()
|
||||
self.base_ref().name.as_ref().map(|o| o.as_ref())
|
||||
}
|
||||
|
||||
/// Set the name for the current object
|
||||
|
@ -560,11 +610,11 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
/// println!("{:?}", media_type);
|
||||
/// }
|
||||
/// ```
|
||||
fn media_type<'a>(&'a self) -> Option<&'a MimeMediaType>
|
||||
fn media_type<'a>(&'a self) -> Option<&'a Mime>
|
||||
where
|
||||
Kind: 'a,
|
||||
{
|
||||
self.base_ref().media_type.as_ref()
|
||||
self.base_ref().media_type.as_ref().map(|m| m.as_ref())
|
||||
}
|
||||
|
||||
/// Set the media type for the current object
|
||||
|
@ -574,15 +624,15 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// use activitystreams_new::prelude::*;
|
||||
/// # use activitystreams_new::{object::Video, primitives::XsdAnyUri};
|
||||
/// # use activitystreams_new::object::Video;
|
||||
/// # let mut video = Video::new();
|
||||
///
|
||||
/// video.set_media_type("video/webm".parse()?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn set_media_type(&mut self, media_type: MimeMediaType) -> &mut Self {
|
||||
self.base_mut().media_type = Some(media_type);
|
||||
fn set_media_type(&mut self, media_type: Mime) -> &mut Self {
|
||||
self.base_mut().media_type = Some(media_type.into());
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -598,8 +648,8 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
/// println!("{:?}", media_type);
|
||||
/// }
|
||||
/// ```
|
||||
fn take_media_type(&mut self) -> Option<MimeMediaType> {
|
||||
self.base_mut().media_type.take()
|
||||
fn take_media_type(&mut self) -> Option<Mime> {
|
||||
self.base_mut().media_type.take().map(|m| m.into_inner())
|
||||
}
|
||||
|
||||
/// Delete the media type from the current object
|
||||
|
@ -635,11 +685,11 @@ pub trait BaseExt<Kind>: AsBase<Kind> {
|
|||
/// println!("{:?}", preview);
|
||||
/// }
|
||||
/// ```
|
||||
fn preview<'a>(&'a self) -> Option<&'a OneOrMany<AnyBase>>
|
||||
fn preview<'a>(&'a self) -> Option<OneOrMany<&'a AnyBase>>
|
||||
where
|
||||
Kind: 'a,
|
||||
{
|
||||
self.base_ref().preview.as_ref()
|
||||
self.base_ref().preview.as_ref().map(|o| o.as_ref())
|
||||
}
|
||||
|
||||
/// Set the preview for the current object
|
||||
|
@ -780,7 +830,7 @@ pub struct AnyBase(Either<IdOrBase, String>);
|
|||
/// Although the spec does not define a type more abstract that Object or Link, it does define
|
||||
/// fields present in both, so for the sake of "Everything derives from something," I've
|
||||
/// implemented a type.
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Base<Kind> {
|
||||
/// Identifies the context within which the object exists or an activity was performed.
|
||||
|
@ -794,8 +844,7 @@ pub struct Base<Kind> {
|
|||
#[serde(rename = "@context")]
|
||||
#[serde(alias = "context")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub context: Option<OneOrMany<AnyBase>>,
|
||||
context: Option<OneOrMany<AnyBase>>,
|
||||
|
||||
/// Provides the globally unique identifier for an Object or Link.
|
||||
///
|
||||
|
@ -810,8 +859,7 @@ pub struct Base<Kind> {
|
|||
/// When processing Activity Streams 1.0 documents and converting those to 2.0, implementations
|
||||
/// ought to treat id as an alias for the JSON-LD @id key word[.]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub id: Option<XsdAnyUri>,
|
||||
id: Option<XsdAnyUri>,
|
||||
|
||||
/// The `type` field
|
||||
///
|
||||
|
@ -839,8 +887,7 @@ pub struct Base<Kind> {
|
|||
#[serde(alias = "objectType")]
|
||||
#[serde(alias = "verb")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option))]
|
||||
pub kind: Option<Kind>,
|
||||
kind: Option<Kind>,
|
||||
|
||||
/// A simple, human-readable, plain-text name for the object.
|
||||
///
|
||||
|
@ -849,8 +896,7 @@ pub struct Base<Kind> {
|
|||
/// - Range: xsd:string | rdf:langString
|
||||
/// - Functional: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub name: Option<OneOrMany<AnyString>>,
|
||||
name: Option<OneOrMany<AnyString>>,
|
||||
|
||||
/// When used on an Object, identifies the MIME media type of the value of the content property.
|
||||
///
|
||||
|
@ -859,23 +905,20 @@ pub struct Base<Kind> {
|
|||
/// - Range: Mime Media Type
|
||||
/// - Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option))]
|
||||
pub media_type: Option<MimeMediaType>,
|
||||
media_type: Option<MimeMediaType>,
|
||||
|
||||
/// Identifies an entity that provides a preview of this object.
|
||||
///
|
||||
/// - Range: Object | Link
|
||||
/// - Functional: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub preview: Option<OneOrMany<AnyBase>>,
|
||||
preview: Option<OneOrMany<AnyBase>>,
|
||||
|
||||
/// Any additional data present on the object if parsed from JSON
|
||||
///
|
||||
/// This is used to extend the Base into other kinds of objects and links
|
||||
#[serde(flatten)]
|
||||
#[builder(default)]
|
||||
pub unparsed: Unparsed,
|
||||
unparsed: Unparsed,
|
||||
}
|
||||
|
||||
impl Base<serde_json::Value> {
|
||||
|
@ -902,7 +945,15 @@ impl<Kind> Base<Kind> {
|
|||
where
|
||||
Kind: Default,
|
||||
{
|
||||
Base::builder().kind(Kind::default()).build()
|
||||
Base {
|
||||
context: None,
|
||||
id: None,
|
||||
kind: Some(Kind::default()),
|
||||
name: None,
|
||||
media_type: None,
|
||||
preview: None,
|
||||
unparsed: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Extend the Base into any other ActivityStreams type provided in this crate
|
||||
|
@ -1116,9 +1167,11 @@ impl AnyBase {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn id(&self) -> Option<&XsdAnyUri> {
|
||||
self.as_xsd_any_uri()
|
||||
.or_else(|| self.as_base().and_then(|base| base.id.as_ref()))
|
||||
pub fn id(&self) -> Option<&Url> {
|
||||
self.as_xsd_any_uri().or_else(|| {
|
||||
self.as_base()
|
||||
.and_then(|base| base.id.as_ref().map(|i| i.as_ref()))
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if the current object's id matches the provided id
|
||||
|
@ -1138,7 +1191,7 @@ impl AnyBase {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn is_id(&self, id: &XsdAnyUri) -> bool {
|
||||
pub fn is_id(&self, id: &Url) -> bool {
|
||||
self.id() == Some(id)
|
||||
}
|
||||
|
||||
|
@ -1153,7 +1206,7 @@ impl AnyBase {
|
|||
/// # };
|
||||
/// # let mut video = Video::new();
|
||||
/// #
|
||||
/// video.set_kind(VideoType);
|
||||
/// video.set_kind(VideoType::Video);
|
||||
///
|
||||
/// let any_base = AnyBase::from_extended(video)?;
|
||||
///
|
||||
|
@ -1180,7 +1233,7 @@ impl AnyBase {
|
|||
/// # };
|
||||
/// # let mut video = Video::new();
|
||||
/// #
|
||||
/// video.set_kind(VideoType);
|
||||
/// video.set_kind(VideoType::Video);
|
||||
///
|
||||
/// let any_base = AnyBase::from_extended(video)?;
|
||||
///
|
||||
|
@ -1204,7 +1257,7 @@ impl AnyBase {
|
|||
/// # };
|
||||
/// # let mut video = Video::new();
|
||||
/// #
|
||||
/// video.set_kind(VideoType);
|
||||
/// video.set_kind(VideoType::Video);
|
||||
///
|
||||
/// let any_base = AnyBase::from_extended(video)?;
|
||||
///
|
||||
|
@ -1229,7 +1282,7 @@ impl AnyBase {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_xsd_any_uri(&self) -> Option<&XsdAnyUri> {
|
||||
pub fn as_xsd_any_uri(&self) -> Option<&Url> {
|
||||
self.0.as_ref().left().and_then(|l| l.as_xsd_any_uri())
|
||||
}
|
||||
|
||||
|
@ -1277,7 +1330,7 @@ impl AnyBase {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn take_xsd_any_uri(self) -> Option<XsdAnyUri> {
|
||||
pub fn take_xsd_any_uri(self) -> Option<Url> {
|
||||
self.0.left().and_then(|l| l.id())
|
||||
}
|
||||
|
||||
|
@ -1327,7 +1380,7 @@ impl AnyBase {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn set_xsd_any_uri(&mut self, id: XsdAnyUri) {
|
||||
pub fn set_xsd_any_uri(&mut self, id: Url) {
|
||||
self.0 = Either::Left(IdOrBase::from_xsd_any_uri(id));
|
||||
}
|
||||
|
||||
|
@ -1382,7 +1435,7 @@ impl AnyBase {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn from_xsd_any_uri(id: XsdAnyUri) -> Self {
|
||||
pub fn from_xsd_any_uri(id: Url) -> Self {
|
||||
AnyBase(Either::Left(IdOrBase::from_xsd_any_uri(id)))
|
||||
}
|
||||
|
||||
|
@ -1415,24 +1468,24 @@ impl AnyBase {
|
|||
}
|
||||
|
||||
impl IdOrBase {
|
||||
fn as_xsd_any_uri(&self) -> Option<&XsdAnyUri> {
|
||||
self.0.as_ref().left()
|
||||
fn as_xsd_any_uri(&self) -> Option<&Url> {
|
||||
self.0.as_ref().left().map(|u| u.as_ref())
|
||||
}
|
||||
|
||||
fn as_base(&self) -> Option<&Base<serde_json::Value>> {
|
||||
self.0.as_ref().right().map(|b| b.as_ref())
|
||||
}
|
||||
|
||||
fn id(self) -> Option<XsdAnyUri> {
|
||||
self.0.left()
|
||||
fn id(self) -> Option<Url> {
|
||||
self.0.left().map(|u| u.into_inner())
|
||||
}
|
||||
|
||||
fn base(self) -> Option<Base<serde_json::Value>> {
|
||||
self.0.right().map(|b| *b)
|
||||
}
|
||||
|
||||
fn from_xsd_any_uri(id: XsdAnyUri) -> Self {
|
||||
IdOrBase(Either::Left(id))
|
||||
fn from_xsd_any_uri(id: Url) -> Self {
|
||||
IdOrBase(Either::Left(id.into()))
|
||||
}
|
||||
|
||||
fn from_base(base: Base<serde_json::Value>) -> Self {
|
||||
|
@ -1445,17 +1498,17 @@ impl OneOrMany<AnyBase> {
|
|||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::{base::Base, primitives::OneOrMany, uri};
|
||||
/// # use activitystreams_new::{base::{Base, BaseExt}, primitives::OneOrMany, uri};
|
||||
/// # let mut base = Base::<String>::new();
|
||||
/// # let id = uri!("https://example.com");
|
||||
/// # base.id = Some(id.clone());
|
||||
/// # base.set_id(id.clone());
|
||||
/// # let base = OneOrMany::from_base(base.into_generic()?.into());
|
||||
/// #
|
||||
/// assert!(base.as_single_id() == Some(&id));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_single_id(&self) -> Option<&XsdAnyUri> {
|
||||
pub fn as_single_id(&self) -> Option<&Url> {
|
||||
self.as_one().and_then(|one| one.id())
|
||||
}
|
||||
|
||||
|
@ -1463,17 +1516,17 @@ impl OneOrMany<AnyBase> {
|
|||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::{base::Base, primitives::OneOrMany, uri};
|
||||
/// # use activitystreams_new::{base::{Base, BaseExt}, primitives::OneOrMany, uri};
|
||||
/// # let mut base = Base::<String>::new();
|
||||
/// # let id = uri!("https://example.com");
|
||||
/// # base.id = Some(id.clone());
|
||||
/// # base.set_id(id.clone());
|
||||
/// # let base = OneOrMany::from_base(base.into_generic()?.into());
|
||||
/// #
|
||||
/// assert!(base.is_single_id(&id));
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn is_single_id(&self, id: &XsdAnyUri) -> bool {
|
||||
pub fn is_single_id(&self, id: &Url) -> bool {
|
||||
self.as_single_id() == Some(id)
|
||||
}
|
||||
|
||||
|
@ -1481,9 +1534,9 @@ impl OneOrMany<AnyBase> {
|
|||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::{base::Base, primitives::OneOrMany};
|
||||
/// # let mut base = Base::new();
|
||||
/// # base.kind = Some(String::from("Person"));
|
||||
/// # use activitystreams_new::{base::{Base, BaseExt}, primitives::OneOrMany};
|
||||
/// # let mut base = Base::<String>::new();
|
||||
/// # base.set_kind(String::from("Person"));
|
||||
/// # let base = OneOrMany::from_base(base.into_generic()?.into());
|
||||
/// #
|
||||
/// assert!(base.as_single_kind_str() == Some("Person"));
|
||||
|
@ -1499,9 +1552,9 @@ impl OneOrMany<AnyBase> {
|
|||
/// This returns None if the kind is not present, or not a String
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::{base::Base, primitives::OneOrMany};
|
||||
/// # let mut base = Base::new();
|
||||
/// # base.kind = Some(String::from("Person"));
|
||||
/// # use activitystreams_new::{base::{Base, BaseExt}, primitives::OneOrMany};
|
||||
/// # let mut base = Base::<String>::new();
|
||||
/// # base.set_kind(String::from("Person"));
|
||||
/// # let base = OneOrMany::from_base(base.into_generic()?.into());
|
||||
/// #
|
||||
/// assert!(base.as_single_kind_str() == Some("Person"));
|
||||
|
@ -1518,9 +1571,9 @@ impl OneOrMany<AnyBase> {
|
|||
///
|
||||
/// ```
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::{base::Base, primitives::OneOrMany};
|
||||
/// # use activitystreams_new::{base::{Base, BaseExt}, primitives::OneOrMany};
|
||||
/// # let mut base = Base::new();
|
||||
/// # base.kind = Some(String::from("Person"));
|
||||
/// # base.set_kind(String::from("Person"));
|
||||
/// # let base = OneOrMany::from_base(base.into_generic()?.into());
|
||||
/// #
|
||||
/// assert!(base.is_single_kind("Person"));
|
||||
|
@ -1544,7 +1597,7 @@ impl OneOrMany<AnyBase> {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_single_xsd_any_uri(&self) -> Option<&XsdAnyUri> {
|
||||
pub fn as_single_xsd_any_uri(&self) -> Option<&Url> {
|
||||
self.as_one().and_then(|inner| inner.as_xsd_any_uri())
|
||||
}
|
||||
|
||||
|
@ -1588,7 +1641,7 @@ impl OneOrMany<AnyBase> {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn single_xsd_any_uri(self) -> Option<XsdAnyUri> {
|
||||
pub fn single_xsd_any_uri(self) -> Option<Url> {
|
||||
self.one().and_then(|inner| inner.take_xsd_any_uri())
|
||||
}
|
||||
|
||||
|
@ -1629,7 +1682,7 @@ impl OneOrMany<AnyBase> {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn from_xsd_any_uri(id: XsdAnyUri) -> Self {
|
||||
pub fn from_xsd_any_uri(id: Url) -> Self {
|
||||
OneOrMany(Either::Left(AnyBase::from_xsd_any_uri(id)))
|
||||
}
|
||||
|
||||
|
@ -1670,7 +1723,7 @@ impl OneOrMany<AnyBase> {
|
|||
///
|
||||
/// assert!(one.as_single_xsd_any_uri().is_some());
|
||||
/// ```
|
||||
pub fn set_single_xsd_any_uri(&mut self, id: XsdAnyUri) -> &mut Self {
|
||||
pub fn set_single_xsd_any_uri(&mut self, id: Url) -> &mut Self {
|
||||
self.0 = Either::Left(AnyBase::from_xsd_any_uri(id));
|
||||
self
|
||||
}
|
||||
|
@ -1719,7 +1772,7 @@ impl OneOrMany<AnyBase> {
|
|||
/// many.add_xsd_any_uri(security())
|
||||
/// .add_xsd_any_uri(context());
|
||||
/// ```
|
||||
pub fn add_xsd_any_uri(&mut self, id: XsdAnyUri) -> &mut Self {
|
||||
pub fn add_xsd_any_uri(&mut self, id: Url) -> &mut Self {
|
||||
self.add(AnyBase::from_xsd_any_uri(id))
|
||||
}
|
||||
|
||||
|
@ -1786,8 +1839,8 @@ impl From<Base<serde_json::Value>> for AnyBase {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<XsdAnyUri> for AnyBase {
|
||||
fn from(id: XsdAnyUri) -> Self {
|
||||
impl From<Url> for AnyBase {
|
||||
fn from(id: Url) -> Self {
|
||||
Self::from_xsd_any_uri(id)
|
||||
}
|
||||
}
|
||||
|
@ -1804,8 +1857,8 @@ impl From<Base<serde_json::Value>> for OneOrMany<AnyBase> {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<XsdAnyUri> for OneOrMany<AnyBase> {
|
||||
fn from(xsd_any_uri: XsdAnyUri) -> Self {
|
||||
impl From<Url> for OneOrMany<AnyBase> {
|
||||
fn from(xsd_any_uri: Url) -> Self {
|
||||
Self::from_xsd_any_uri(xsd_any_uri)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
//! ]);
|
||||
//!
|
||||
//! collection
|
||||
//! .set_total_items(1)
|
||||
//! .set_total_items(1u64)
|
||||
//! .set_current(uri!("https://example.com/notes/1234"))
|
||||
//! .set_first(uri!("https://example.com/notes/1234"))
|
||||
//! .set_last(uri!("https://example.com/notes/1234"))
|
||||
|
@ -27,11 +27,10 @@ use crate::{
|
|||
base::{AnyBase, AsBase, Base, Extends},
|
||||
markers,
|
||||
object::{ApObject, AsObject, Object},
|
||||
primitives::{OneOrMany, XsdNonNegativeInteger},
|
||||
primitives::OneOrMany,
|
||||
unparsed::{Unparsed, UnparsedMut, UnparsedMutExt},
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
pub mod kind {
|
||||
//! Kinds of collections defined by the spec
|
||||
|
@ -39,7 +38,12 @@ pub mod kind {
|
|||
//! These types exist only to be statically-typed versions of the associated string. e.g.
|
||||
//! `CollectionType` -> `"Collection"`
|
||||
|
||||
pub use activitystreams::collection::kind::*;
|
||||
use crate::kind;
|
||||
|
||||
kind!(CollectionType, Collection);
|
||||
kind!(OrderedCollectionType, OrderedCollection);
|
||||
kind!(CollectionPageType, CollectionPage);
|
||||
kind!(OrderedCollectionPageType, OrderedCollectionPage);
|
||||
}
|
||||
|
||||
use self::kind::*;
|
||||
|
@ -178,11 +182,11 @@ pub trait CollectionExt<Kind>: AsCollection<Kind> {
|
|||
/// println!("{:?}", total_items);
|
||||
/// }
|
||||
/// ```
|
||||
fn total_items<'a>(&'a self) -> Option<&'a XsdNonNegativeInteger>
|
||||
fn total_items<'a>(&'a self) -> Option<u64>
|
||||
where
|
||||
Kind: 'a,
|
||||
{
|
||||
self.collection_ref().total_items.as_ref()
|
||||
self.collection_ref().total_items
|
||||
}
|
||||
|
||||
/// Set the total_items for the current object
|
||||
|
@ -194,11 +198,11 @@ pub trait CollectionExt<Kind>: AsCollection<Kind> {
|
|||
/// # let mut collection = UnorderedCollection::new(vec![context().into()]);
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
/// collection.set_total_items(5);
|
||||
/// collection.set_total_items(5u64);
|
||||
/// ```
|
||||
fn set_total_items<T>(&mut self, total_items: T) -> &mut Self
|
||||
where
|
||||
T: Into<XsdNonNegativeInteger>,
|
||||
T: Into<u64>,
|
||||
{
|
||||
self.collection_mut().total_items = Some(total_items.into());
|
||||
self
|
||||
|
@ -215,7 +219,7 @@ pub trait CollectionExt<Kind>: AsCollection<Kind> {
|
|||
/// println!("{:?}", total_items);
|
||||
/// }
|
||||
/// ```
|
||||
fn take_total_items(&mut self) -> Option<XsdNonNegativeInteger> {
|
||||
fn take_total_items(&mut self) -> Option<u64> {
|
||||
self.collection_mut().total_items.take()
|
||||
}
|
||||
|
||||
|
@ -224,7 +228,7 @@ pub trait CollectionExt<Kind>: AsCollection<Kind> {
|
|||
/// ```rust
|
||||
/// # use activitystreams_new::{context, collection::UnorderedCollection};
|
||||
/// # let mut collection = UnorderedCollection::new(vec![context().into()]);
|
||||
/// # collection.set_total_items(5);
|
||||
/// # collection.set_total_items(5u64);
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
/// assert!(collection.total_items().is_some());
|
||||
|
@ -713,8 +717,8 @@ pub trait OrderedCollectionPageExt: AsOrderedCollectionPage {
|
|||
/// println!("{:?}", start_index);
|
||||
/// }
|
||||
/// ```
|
||||
fn start_index(&self) -> Option<&XsdNonNegativeInteger> {
|
||||
self.ordered_collection_page_ref().start_index.as_ref()
|
||||
fn start_index(&self) -> Option<u64> {
|
||||
self.ordered_collection_page_ref().start_index
|
||||
}
|
||||
|
||||
/// Set the start_index for the current object
|
||||
|
@ -726,11 +730,11 @@ pub trait OrderedCollectionPageExt: AsOrderedCollectionPage {
|
|||
/// # let mut collection = OrderedCollectionPage::new(vec![context().into()]);
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
/// collection.set_start_index(5);
|
||||
/// collection.set_start_index(5u64);
|
||||
/// ```
|
||||
fn set_start_index<T>(&mut self, start_index: T) -> &mut Self
|
||||
where
|
||||
T: Into<XsdNonNegativeInteger>,
|
||||
T: Into<u64>,
|
||||
{
|
||||
self.ordered_collection_page_mut().start_index = Some(start_index.into());
|
||||
self
|
||||
|
@ -747,7 +751,7 @@ pub trait OrderedCollectionPageExt: AsOrderedCollectionPage {
|
|||
/// println!("{:?}", start_index);
|
||||
/// }
|
||||
/// ```
|
||||
fn take_start_index(&mut self) -> Option<XsdNonNegativeInteger> {
|
||||
fn take_start_index(&mut self) -> Option<u64> {
|
||||
self.ordered_collection_page_mut().start_index.take()
|
||||
}
|
||||
|
||||
|
@ -756,7 +760,7 @@ pub trait OrderedCollectionPageExt: AsOrderedCollectionPage {
|
|||
/// ```rust
|
||||
/// # use activitystreams_new::{context, collection::OrderedCollectionPage};
|
||||
/// # let mut collection = OrderedCollectionPage::new(vec![context().into()]);
|
||||
/// # collection.set_start_index(5);
|
||||
/// # collection.set_start_index(5u64);
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
/// assert!(collection.start_index().is_some());
|
||||
|
@ -795,15 +799,14 @@ pub type UnorderedCollectionPage = CollectionPage<CollectionPageType>;
|
|||
/// used to identify a Collection whose items are always ordered. In the JSON serialization, the
|
||||
/// unordered items of a Collection are represented using the items property while ordered items
|
||||
/// are represented using the orderedItems property.
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Collection<Kind> {
|
||||
/// Identifies the items contained in a collection. The items might be ordered or unordered.
|
||||
///
|
||||
/// - Range: Object | Link | Ordered List of [ Object | Link ]
|
||||
/// - Functional: false
|
||||
#[builder(setter(into))]
|
||||
pub items: OneOrMany<AnyBase>,
|
||||
items: OneOrMany<AnyBase>,
|
||||
|
||||
/// A non-negative integer specifying the total number of objects contained by the logical view
|
||||
/// of the collection.
|
||||
|
@ -814,8 +817,7 @@ pub struct Collection<Kind> {
|
|||
/// - Range: xsd:nonNegativeInteger
|
||||
/// - Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub total_items: Option<XsdNonNegativeInteger>,
|
||||
total_items: Option<u64>,
|
||||
|
||||
/// In a paged Collection, indicates the page that contains the most recently updated member
|
||||
/// items.
|
||||
|
@ -823,28 +825,25 @@ pub struct Collection<Kind> {
|
|||
/// - Range: CollectionPage | Link
|
||||
/// - Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub current: Option<AnyBase>,
|
||||
current: Option<AnyBase>,
|
||||
|
||||
/// In a paged Collection, indicates the furthest preceeding page of items in the collection.
|
||||
///
|
||||
/// - Range: CollectionPage | Link
|
||||
/// - Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub first: Option<AnyBase>,
|
||||
first: Option<AnyBase>,
|
||||
|
||||
/// In a paged Collection, indicates the furthest proceeding page of the collection.
|
||||
///
|
||||
/// - Range: CollectionPage | Link
|
||||
/// - Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub last: Option<AnyBase>,
|
||||
last: Option<AnyBase>,
|
||||
|
||||
/// Base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: Object<Kind>,
|
||||
inner: Object<Kind>,
|
||||
}
|
||||
|
||||
/// Used to represent distinct subsets of items from a Collection.
|
||||
|
@ -853,7 +852,7 @@ pub struct Collection<Kind> {
|
|||
/// implementation to serialize every item contained by a Collection using the items (or
|
||||
/// ordered_items) property alone. In such cases, the items within a Collection can be divided into
|
||||
/// distinct subsets or "pages". A page is identified using the CollectionPage type.
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CollectionPage<Kind> {
|
||||
/// Identifies the Collection to which a CollectionPage objects items belong.
|
||||
|
@ -861,32 +860,29 @@ pub struct CollectionPage<Kind> {
|
|||
/// - Range: Collection | Link
|
||||
/// - Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub part_of: Option<AnyBase>,
|
||||
part_of: Option<AnyBase>,
|
||||
|
||||
/// In a paged Collection, indicates the next page of items.
|
||||
///
|
||||
/// - Range: CollectionPage | Link
|
||||
/// - Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub next: Option<AnyBase>,
|
||||
next: Option<AnyBase>,
|
||||
|
||||
/// In a paged Collection, identifies the previous page of items.
|
||||
///
|
||||
/// - Range: CollectionPage | Link
|
||||
/// - Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub prev: Option<AnyBase>,
|
||||
prev: Option<AnyBase>,
|
||||
|
||||
/// Base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: Collection<Kind>,
|
||||
inner: Collection<Kind>,
|
||||
}
|
||||
|
||||
/// Used to represent ordered subsets of items from an OrderedCollection.
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OrderedCollectionPage {
|
||||
/// A non-negative integer value identifying the relative position within the logical view of a strictly ordered collection.
|
||||
|
@ -894,12 +890,11 @@ pub struct OrderedCollectionPage {
|
|||
/// - Range: xsd:nonNegativeInteger
|
||||
/// - Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub start_index: Option<XsdNonNegativeInteger>,
|
||||
start_index: Option<u64>,
|
||||
|
||||
/// Base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: CollectionPage<OrderedCollectionPageType>,
|
||||
inner: CollectionPage<OrderedCollectionPageType>,
|
||||
}
|
||||
|
||||
impl<Kind> Collection<Kind> {
|
||||
|
@ -915,7 +910,14 @@ impl<Kind> Collection<Kind> {
|
|||
T: Into<OneOrMany<AnyBase>>,
|
||||
Kind: Default,
|
||||
{
|
||||
Self::builder().items(items).inner(Object::new()).build()
|
||||
Collection {
|
||||
items: items.into(),
|
||||
total_items: None,
|
||||
current: None,
|
||||
first: None,
|
||||
last: None,
|
||||
inner: Object::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(mut inner: Object<Kind>) -> Result<Self, serde_json::Error> {
|
||||
|
@ -969,9 +971,12 @@ impl<Kind> CollectionPage<Kind> {
|
|||
T: Into<OneOrMany<AnyBase>>,
|
||||
Kind: Default,
|
||||
{
|
||||
let collection = Collection::new(items);
|
||||
|
||||
CollectionPage::builder().inner(collection).build()
|
||||
CollectionPage {
|
||||
part_of: None,
|
||||
next: None,
|
||||
prev: None,
|
||||
inner: Collection::new(items),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(object: Object<Kind>) -> Result<Self, serde_json::Error> {
|
||||
|
@ -1018,11 +1023,10 @@ impl OrderedCollectionPage {
|
|||
where
|
||||
T: Into<OneOrMany<AnyBase>>,
|
||||
{
|
||||
let collection_page = CollectionPage::new(items);
|
||||
|
||||
OrderedCollectionPage::builder()
|
||||
.inner(collection_page)
|
||||
.build()
|
||||
OrderedCollectionPage {
|
||||
start_index: None,
|
||||
inner: CollectionPage::new(items),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(object: Object<OrderedCollectionPageType>) -> Result<Self, serde_json::Error> {
|
||||
|
@ -1295,11 +1299,11 @@ where
|
|||
Inner: AsCollection<Kind>,
|
||||
{
|
||||
fn collection_ref(&self) -> &Collection<Kind> {
|
||||
self.inner.collection_ref()
|
||||
self.inner().collection_ref()
|
||||
}
|
||||
|
||||
fn collection_mut(&mut self) -> &mut Collection<Kind> {
|
||||
self.inner.collection_mut()
|
||||
self.inner_mut().collection_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1308,11 +1312,11 @@ where
|
|||
Inner: AsCollectionPage<Kind>,
|
||||
{
|
||||
fn collection_page_ref(&self) -> &CollectionPage<Kind> {
|
||||
self.inner.collection_page_ref()
|
||||
self.inner().collection_page_ref()
|
||||
}
|
||||
|
||||
fn collection_page_mut(&mut self) -> &mut CollectionPage<Kind> {
|
||||
self.inner.collection_page_mut()
|
||||
self.inner_mut().collection_page_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1321,11 +1325,11 @@ where
|
|||
Inner: AsOrderedCollectionPage,
|
||||
{
|
||||
fn ordered_collection_page_ref(&self) -> &OrderedCollectionPage {
|
||||
self.inner.ordered_collection_page_ref()
|
||||
self.inner().ordered_collection_page_ref()
|
||||
}
|
||||
|
||||
fn ordered_collection_page_mut(&mut self) -> &mut OrderedCollectionPage {
|
||||
self.inner.ordered_collection_page_mut()
|
||||
self.inner_mut().ordered_collection_page_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,4 +30,22 @@ impl<L, R> Either<L, R> {
|
|||
Either::Right(ref r) => Either::Right(r),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut(&mut self) -> Either<&mut L, &mut R> {
|
||||
match self {
|
||||
Either::Left(ref mut l) => Either::Left(l),
|
||||
Either::Right(ref mut r) => Either::Right(r),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map<F1, F2, L2, R2>(self, f1: F1, f2: F2) -> Either<L2, R2>
|
||||
where
|
||||
F1: Fn(L) -> L2,
|
||||
F2: Fn(R) -> R2,
|
||||
{
|
||||
match self {
|
||||
Either::Left(l) => Either::Left((f1)(l)),
|
||||
Either::Right(r) => Either::Right((f2)(r)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
3
src/error.rs
Normal file
3
src/error.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
#[error("URL did not match expected domain")]
|
||||
pub struct DomainError;
|
70
src/lib.rs
70
src/lib.rs
|
@ -143,9 +143,9 @@
|
|||
//!
|
||||
//! For fields with more specific bounds, like `id`,
|
||||
//! ```rust,ignore
|
||||
//! fn id(&self) -> Option<&XsdAnyUri>;
|
||||
//! fn set_id(&mut self, XsdAnyUri) -> &mut Self;
|
||||
//! fn take_id(&self) -> Option<XsdAnyUri>;
|
||||
//! fn id(&self) -> Option<&Url>;
|
||||
//! fn set_id(&mut self, Url) -> &mut Self;
|
||||
//! fn take_id(&self) -> Option<Url>;
|
||||
//! fn delete_id(&mut self) -> &mut Self;
|
||||
//! ```
|
||||
//!
|
||||
|
@ -207,6 +207,7 @@
|
|||
//! prelude::*,
|
||||
//! uri,
|
||||
//! };
|
||||
//! use chrono::Duration;
|
||||
//!
|
||||
//! fn main() -> Result<(), anyhow::Error> {
|
||||
//! let mut video = ApObject::new(Video::new());
|
||||
|
@ -217,7 +218,7 @@
|
|||
//! .set_media_type("video/webm".parse()?)
|
||||
//! .set_url(uri!("https://example.com/@example/lions/video.webm"))
|
||||
//! .set_summary("A cool video")
|
||||
//! .set_duration("PT4M20S".parse()?)
|
||||
//! .set_duration(Duration::minutes(4) + Duration::seconds(20))
|
||||
//! .set_shares(uri!("https://example.com/@example/lions/video.webm#shares"));
|
||||
//!
|
||||
//! println!("Video, {:#?}", video);
|
||||
|
@ -299,33 +300,30 @@ pub mod actor;
|
|||
pub mod base;
|
||||
pub mod collection;
|
||||
mod either;
|
||||
pub mod error;
|
||||
pub mod link;
|
||||
mod macros;
|
||||
pub mod markers;
|
||||
pub mod object;
|
||||
pub mod primitives;
|
||||
pub mod unparsed;
|
||||
|
||||
pub use activitystreams::{context, public, security};
|
||||
pub extern crate chrono;
|
||||
pub extern crate mime;
|
||||
pub extern crate url;
|
||||
|
||||
pub mod markers {
|
||||
//! Marker traits for bounding methods
|
||||
//!
|
||||
//! ```rust
|
||||
//! use activitystreams_new::{base::BaseExt, markers::Activity};
|
||||
//!
|
||||
//! /// Applies the name "hi" to any given activity
|
||||
//! fn manipulator<T, Kind>(mut some_type: T) -> T
|
||||
//! where
|
||||
//! T: Activity + BaseExt<Kind>,
|
||||
//! {
|
||||
//! some_type.set_name("hi");
|
||||
//!
|
||||
//! some_type
|
||||
//! }
|
||||
//! ```
|
||||
pub fn context() -> url::Url {
|
||||
"https://www.w3.org/ns/activitystreams".parse().unwrap()
|
||||
}
|
||||
|
||||
pub use activitystreams::{
|
||||
Activity, Actor, Base, Collection, CollectionPage, IntransitiveActivity, Link, Object,
|
||||
};
|
||||
pub fn public() -> url::Url {
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
.parse()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn security() -> url::Url {
|
||||
"https://w3id.org/security/v1".parse().unwrap()
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
|
@ -343,6 +341,7 @@ pub mod prelude {
|
|||
//! security,
|
||||
//! uri,
|
||||
//! };
|
||||
//! use chrono::Duration;
|
||||
//!
|
||||
//! let mut person = ApActor::new(
|
||||
//! uri!("http://localhost:8080/inbox"),
|
||||
|
@ -370,7 +369,7 @@ pub mod prelude {
|
|||
//! .set_media_type("video/webm".parse()?)
|
||||
//! .set_summary("A cool video")
|
||||
//! .set_preview(preview.into_any_base()?)
|
||||
//! .set_duration("PT4M20S".parse()?)
|
||||
//! .set_duration(Duration::minutes(4) + Duration::seconds(20))
|
||||
//! .set_shares(uri!("http://localhost:8080/video.webm#shares"));
|
||||
//!
|
||||
//! let mut activity = Create::new(
|
||||
|
@ -397,24 +396,3 @@ pub mod prelude {
|
|||
object::{ApObjectExt, ObjectExt, PlaceExt, ProfileExt, RelationshipExt, TombstoneExt},
|
||||
};
|
||||
}
|
||||
|
||||
/// A macro to shorten the `string.parse::<XsdAnyUri>()?` calls inevitably made in downstream code
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::uri;
|
||||
///
|
||||
/// fn fallible() -> Result<(), anyhow::Error> {
|
||||
/// let my_uri = uri!("https://example.com");
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), anyhow::Error> { fallible() }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! uri {
|
||||
( $x:expr ) => {{
|
||||
use activitystreams_new::primitives::XsdAnyUri;
|
||||
|
||||
$x.parse::<XsdAnyUri>()?
|
||||
}};
|
||||
}
|
||||
|
|
72
src/link.rs
72
src/link.rs
|
@ -23,11 +23,11 @@
|
|||
use crate::{
|
||||
base::{AsBase, Base, Extends},
|
||||
markers,
|
||||
primitives::{OneOrMany, XsdAnyUri, XsdNonNegativeInteger},
|
||||
primitives::{OneOrMany, XsdAnyUri},
|
||||
unparsed::{Unparsed, UnparsedMut, UnparsedMutExt},
|
||||
};
|
||||
use std::convert::TryFrom;
|
||||
use typed_builder::TypedBuilder;
|
||||
use url::Url;
|
||||
|
||||
pub mod kind {
|
||||
//! Kinds of links defined by the spec
|
||||
|
@ -35,7 +35,9 @@ pub mod kind {
|
|||
//! These types exist only to be statically-typed versions of the associated string. e.g.
|
||||
//! `MentionType` -> `"Mention"`
|
||||
|
||||
pub use activitystreams::link::kind::MentionType;
|
||||
use crate::kind;
|
||||
|
||||
kind!(MentionType, Mention);
|
||||
}
|
||||
|
||||
use self::kind::MentionType;
|
||||
|
@ -67,11 +69,11 @@ pub trait LinkExt<Kind>: AsLink<Kind> {
|
|||
///
|
||||
/// let mention_href = mention.href();
|
||||
/// ```
|
||||
fn href<'a>(&'a self) -> Option<&'a XsdAnyUri>
|
||||
fn href<'a>(&'a self) -> Option<&'a Url>
|
||||
where
|
||||
Kind: 'a,
|
||||
{
|
||||
self.link_ref().href.as_ref()
|
||||
self.link_ref().href.as_ref().map(|u| u.as_ref())
|
||||
}
|
||||
|
||||
/// Set the href for the current object
|
||||
|
@ -89,8 +91,8 @@ pub trait LinkExt<Kind>: AsLink<Kind> {
|
|||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn set_href(&mut self, href: XsdAnyUri) -> &mut Self {
|
||||
self.link_mut().href = Some(href);
|
||||
fn set_href(&mut self, href: Url) -> &mut Self {
|
||||
self.link_mut().href = Some(href.into());
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -106,8 +108,8 @@ pub trait LinkExt<Kind>: AsLink<Kind> {
|
|||
/// println!("{:?}", href);
|
||||
/// }
|
||||
/// ```
|
||||
fn take_href(&mut self) -> Option<XsdAnyUri> {
|
||||
self.link_mut().href.take()
|
||||
fn take_href(&mut self) -> Option<Url> {
|
||||
self.link_mut().href.take().map(|u| u.into_inner())
|
||||
}
|
||||
|
||||
/// Delete the href from the current object
|
||||
|
@ -335,11 +337,11 @@ pub trait LinkExt<Kind>: AsLink<Kind> {
|
|||
/// println!("{:?}", height);
|
||||
/// }
|
||||
/// ```
|
||||
fn height<'a>(&'a self) -> Option<&'a XsdNonNegativeInteger>
|
||||
fn height<'a>(&'a self) -> Option<u64>
|
||||
where
|
||||
Kind: 'a,
|
||||
{
|
||||
self.link_ref().height.as_ref()
|
||||
self.link_ref().height
|
||||
}
|
||||
|
||||
/// Set the height for the current object
|
||||
|
@ -352,11 +354,11 @@ pub trait LinkExt<Kind>: AsLink<Kind> {
|
|||
/// #
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
/// mention.set_height(5);
|
||||
/// mention.set_height(5u64);
|
||||
/// ```
|
||||
fn set_height<T>(&mut self, height: T) -> &mut Self
|
||||
where
|
||||
T: Into<XsdNonNegativeInteger>,
|
||||
T: Into<u64>,
|
||||
{
|
||||
self.link_mut().height = Some(height.into());
|
||||
self
|
||||
|
@ -374,7 +376,7 @@ pub trait LinkExt<Kind>: AsLink<Kind> {
|
|||
/// println!("{:?}", height);
|
||||
/// }
|
||||
/// ```
|
||||
fn take_height(&mut self) -> Option<XsdNonNegativeInteger> {
|
||||
fn take_height(&mut self) -> Option<u64> {
|
||||
self.link_mut().height.take()
|
||||
}
|
||||
|
||||
|
@ -383,7 +385,7 @@ pub trait LinkExt<Kind>: AsLink<Kind> {
|
|||
/// ```rust
|
||||
/// # use activitystreams_new::link::Mention;
|
||||
/// # let mut mention = Mention::new();
|
||||
/// # mention.set_height(5);
|
||||
/// # mention.set_height(5u64);
|
||||
/// #
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
|
@ -408,11 +410,11 @@ pub trait LinkExt<Kind>: AsLink<Kind> {
|
|||
/// println!("{:?}", width);
|
||||
/// }
|
||||
/// ```
|
||||
fn width<'a>(&'a self) -> Option<&'a XsdNonNegativeInteger>
|
||||
fn width<'a>(&'a self) -> Option<u64>
|
||||
where
|
||||
Kind: 'a,
|
||||
{
|
||||
self.link_ref().width.as_ref()
|
||||
self.link_ref().width
|
||||
}
|
||||
|
||||
/// Set the width for the current object
|
||||
|
@ -425,11 +427,11 @@ pub trait LinkExt<Kind>: AsLink<Kind> {
|
|||
/// #
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
/// mention.set_width(5);
|
||||
/// mention.set_width(5u64);
|
||||
/// ```
|
||||
fn set_width<T>(&mut self, width: T) -> &mut Self
|
||||
where
|
||||
T: Into<XsdNonNegativeInteger>,
|
||||
T: Into<u64>,
|
||||
{
|
||||
self.link_mut().width = Some(width.into());
|
||||
self
|
||||
|
@ -447,7 +449,7 @@ pub trait LinkExt<Kind>: AsLink<Kind> {
|
|||
/// println!("{:?}", width);
|
||||
/// }
|
||||
/// ```
|
||||
fn take_width(&mut self) -> Option<XsdNonNegativeInteger> {
|
||||
fn take_width(&mut self) -> Option<u64> {
|
||||
self.link_mut().width.take()
|
||||
}
|
||||
|
||||
|
@ -456,7 +458,7 @@ pub trait LinkExt<Kind>: AsLink<Kind> {
|
|||
/// ```rust
|
||||
/// # use activitystreams_new::link::Mention;
|
||||
/// # let mut mention = Mention::new();
|
||||
/// # mention.set_width(5);
|
||||
/// # mention.set_width(5u64);
|
||||
/// #
|
||||
/// use activitystreams_new::prelude::*;
|
||||
///
|
||||
|
@ -492,7 +494,7 @@ pub type Mention = Link<MentionType>;
|
|||
/// object might have multiple such visual representations -- multiple screenshots, for instance,
|
||||
/// or the same image at different resolutions. In Activity Streams 2.0, there are essentially
|
||||
/// three ways of describing such references.
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, TypedBuilder)]
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Link<Kind> {
|
||||
/// The target resource pointed to by a Link.
|
||||
|
@ -500,8 +502,7 @@ pub struct Link<Kind> {
|
|||
/// - Range: xsd:anyUri
|
||||
/// - Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option))]
|
||||
pub href: Option<XsdAnyUri>,
|
||||
href: Option<XsdAnyUri>,
|
||||
|
||||
/// Hints as to the language used by the target resource.
|
||||
///
|
||||
|
@ -510,8 +511,7 @@ pub struct Link<Kind> {
|
|||
/// - Range: [BCP47] Language Tag
|
||||
/// - Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option))]
|
||||
pub hreflang: Option<String>,
|
||||
hreflang: Option<String>,
|
||||
|
||||
/// A link relation associated with a Link.
|
||||
///
|
||||
|
@ -522,28 +522,25 @@ pub struct Link<Kind> {
|
|||
/// - Range: [RFC5988] or [HTML5] Link Relation
|
||||
/// - Functional: false
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub rel: Option<OneOrMany<String>>,
|
||||
rel: Option<OneOrMany<String>>,
|
||||
|
||||
/// On a Link, specifies a hint as to the rendering height in device-independent pixels of the linked resource.
|
||||
///
|
||||
/// - Range: xsd:nonNegativeInteger
|
||||
/// - Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub height: Option<XsdNonNegativeInteger>,
|
||||
height: Option<u64>,
|
||||
|
||||
/// On a Link, specifies a hint as to the rendering width in device-independent pixels of the linked resource.
|
||||
///
|
||||
/// Range: xsd:nonNegativeInteger
|
||||
/// Functional: true
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
#[builder(default, setter(strip_option, into))]
|
||||
pub width: Option<XsdNonNegativeInteger>,
|
||||
width: Option<u64>,
|
||||
|
||||
/// Base fields and unparsed json ends up here
|
||||
#[serde(flatten)]
|
||||
pub inner: Base<Kind>,
|
||||
inner: Base<Kind>,
|
||||
}
|
||||
|
||||
impl<Kind> Link<Kind> {
|
||||
|
@ -558,7 +555,14 @@ impl<Kind> Link<Kind> {
|
|||
where
|
||||
Kind: Default,
|
||||
{
|
||||
Link::builder().inner(Base::new()).build()
|
||||
Link {
|
||||
href: None,
|
||||
hreflang: None,
|
||||
rel: None,
|
||||
height: None,
|
||||
width: None,
|
||||
inner: Base::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn extending(mut inner: Base<Kind>) -> Result<Self, serde_json::Error> {
|
||||
|
|
69
src/macros.rs
Normal file
69
src/macros.rs
Normal file
|
@ -0,0 +1,69 @@
|
|||
/// Generate an enum implementing serde's Serialize and Deserialize with a single variant
|
||||
///
|
||||
/// This is useful for describing constants
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// use activitystreams_new::kind;
|
||||
///
|
||||
/// kind!(CustomType, Custom);
|
||||
///
|
||||
/// #[derive(serde::Deserialize)]
|
||||
/// struct MyStruct {
|
||||
/// #[serde(rename = "type")]
|
||||
/// kind: CustomType,
|
||||
/// }
|
||||
///
|
||||
/// let s: MyStruct = serde_json::from_str(r#"{"type":"Custom"}"#)?;
|
||||
///
|
||||
/// assert_eq!(s.kind, CustomType::Custom);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! kind {
|
||||
($x:ident, $y:ident) => {
|
||||
#[derive(
|
||||
Clone,
|
||||
Debug,
|
||||
Eq,
|
||||
Hash,
|
||||
Ord,
|
||||
PartialEq,
|
||||
PartialOrd,
|
||||
serde::Deserialize,
|
||||
serde::Serialize,
|
||||
)]
|
||||
/// A type stand-in for the constant $y, deriving serde traits
|
||||
pub enum $x {
|
||||
$y,
|
||||
}
|
||||
|
||||
impl Default for $x {
|
||||
fn default() -> Self {
|
||||
$x::$y
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// A macro to shorten the `string.parse::<Url>()?` calls inevitably made in downstream code
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::uri;
|
||||
///
|
||||
/// fn fallible() -> Result<(), anyhow::Error> {
|
||||
/// let my_uri = uri!("https://example.com");
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// # fn main() -> Result<(), anyhow::Error> { fallible() }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! uri {
|
||||
( $x:expr ) => {{
|
||||
use activitystreams_new::url::Url;
|
||||
|
||||
$x.parse::<Url>()?
|
||||
}};
|
||||
}
|
89
src/markers.rs
Normal file
89
src/markers.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
//! Marker traits for bounding methods
|
||||
//!
|
||||
//! ```rust
|
||||
//! use activitystreams_new::{base::BaseExt, markers::Activity};
|
||||
//!
|
||||
//! /// Applies the name "hi" to any given activity
|
||||
//! fn manipulator<T, Kind>(mut some_type: T) -> T
|
||||
//! where
|
||||
//! T: Activity + BaseExt<Kind>,
|
||||
//! {
|
||||
//! some_type.set_name("hi");
|
||||
//!
|
||||
//! some_type
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
/// The lowermost trait of the trait structure
|
||||
///
|
||||
/// Base exists solely so Object and Link can have impls that don't potentially conflict
|
||||
pub trait Base {}
|
||||
|
||||
/// Describes an object of any kind.
|
||||
///
|
||||
/// The Object type serves as the base type for most of the other kinds of objects defined in the
|
||||
/// Activity Vocabulary, including other Core types such as `Activity`, `IntransitiveActivity`,
|
||||
/// `Collection` and `OrderedCollection`.
|
||||
pub trait Object: Base {}
|
||||
|
||||
/// A Link is an indirect, qualified reference to a resource identified by a URL.
|
||||
///
|
||||
/// The fundamental model for links is established by
|
||||
/// [[RFC5988](https://tools.ietf.org/html/rfc5988)]. Many of the properties defined by the
|
||||
/// Activity Vocabulary allow values that are either instances of Object or Link. When a Link is
|
||||
/// used, it establishes a qualified relation connecting the subject (the containing object) to the
|
||||
/// resource identified by the href. Properties of the Link are properties of the reference as
|
||||
/// opposed to properties of the resource.
|
||||
pub trait Link: Base {}
|
||||
|
||||
/// A Collection is a subtype of `Object` that represents ordered or unordered sets of `Object` or
|
||||
/// `Link` instances.
|
||||
///
|
||||
/// The items within a Collection can be ordered or unordered. The OrderedCollection type MAY be
|
||||
/// used to identify a Collection whose items are always ordered. In the JSON serialization, the
|
||||
/// unordered items of a Collection are represented using the items property while ordered items
|
||||
/// are represented using the orderedItems property.
|
||||
pub trait Collection: Object {}
|
||||
|
||||
/// Used to represent distinct subsets of items from a Collection.
|
||||
///
|
||||
/// A `Collection` can contain a large number of items. Often, it becomes impractical for an
|
||||
/// implementation to serialize every item contained by a `Collection` using the items (or
|
||||
/// `ordered_items`) property alone. In such cases, the items within a `Collection` can be divided
|
||||
/// into distinct subsets or "pages". A page is identified using the `CollectionPage` type.
|
||||
pub trait CollectionPage: Collection {}
|
||||
|
||||
/// `Actor` types are `Object` types that are capable of performing activities.
|
||||
///
|
||||
/// This specification intentionally defines `Actors` in only the most generalized way, stopping
|
||||
/// short of defining semantically specific properties for each. All Actor objects are
|
||||
/// specializations of `Object` and inherit all of the core properties common to all Objects.
|
||||
/// External vocabularies can be used to express additional detail not covered by the Activity
|
||||
/// Vocabulary. VCard [[vcard-rdf](https://www.w3.org/TR/vcard-rdf/) SHOULD be used to provide
|
||||
/// additional metadata for `Person`, `Group`, and `Organization` instances.
|
||||
///
|
||||
/// While implementations are free to introduce new types of Actors beyond those defined by the
|
||||
/// Activity Vocabulary, interoperability issues can arise when applications rely too much on
|
||||
/// extension types that are not recognized by other implementations. Care should be taken to not
|
||||
/// unduly overlap with or duplicate the existing `Actor` types.
|
||||
///
|
||||
/// When an implementation uses an extension type that overlaps with a core vocabulary type, the
|
||||
/// implementation MUST also specify the core vocabulary type. For instance, some vocabularies
|
||||
/// (e.g. VCard) define their own types for describing people. An implementation that wishes, for
|
||||
/// example, to use a `vcard:Individual` as an `Actor` MUST also identify that `Actor` as a
|
||||
/// `Person`.
|
||||
pub trait Actor: Object {}
|
||||
|
||||
/// An Activity is a subtype of `Object` that describes some form of action that may happen, is
|
||||
/// currently happening, or has already happened.
|
||||
///
|
||||
/// The `Activity` type itself serves as an abstract base type for all types of activities. It is
|
||||
/// important to note that the `Activity` type itself does not carry any specific semantics about
|
||||
/// the kind of action being taken.
|
||||
pub trait Activity: Object {}
|
||||
|
||||
/// Instances of `IntransitiveActivity` are a subtype of `Activity` representing intransitive
|
||||
/// actions.
|
||||
///
|
||||
/// The `object` property is therefore inappropriate for these activities.
|
||||
pub trait IntransitiveActivity: Activity {}
|
436
src/object.rs
436
src/object.rs
File diff suppressed because it is too large
Load diff
|
@ -1,796 +0,0 @@
|
|||
//! Types creating the base for most ActivityStreams fields.
|
||||
//!
|
||||
//! These types are not themselves defined by ActivityStreams, but are referenced by the
|
||||
//! specification.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use activitystreams_new::primitives::{AnyString, OneOrMany, Unit};
|
||||
//!
|
||||
//! let any_string = AnyString::from_xsd_string("hey");
|
||||
//!
|
||||
//! let one_or_many = OneOrMany::<i32>::from_one(1234);
|
||||
//!
|
||||
//! let cm = Unit::centimeters();
|
||||
//! ```
|
||||
use crate::either::Either;
|
||||
|
||||
pub use activitystreams::primitives::{
|
||||
MimeMediaType, MimeMediaTypeError, RdfLangString, XsdAnyUri, XsdAnyUriError, XsdDateTime,
|
||||
XsdDateTimeError, XsdDuration, XsdDurationError, XsdFloat, XsdFloatError,
|
||||
XsdNonNegativeInteger,
|
||||
};
|
||||
|
||||
use activitystreams::primitives::Length;
|
||||
|
||||
/// A type representing any kind of string
|
||||
///
|
||||
/// In the ActivityStreams specification, string types are often defined as either an xsd:String or
|
||||
/// and rdf:langString. The AnyString type represents this union.
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct AnyString(Either<String, RdfLangString>);
|
||||
|
||||
/// A type representing units of length
|
||||
///
|
||||
/// It can be any of the following
|
||||
/// - Centimeters
|
||||
/// - Meters
|
||||
/// - Kilometers
|
||||
/// - Inches
|
||||
/// - Feet
|
||||
/// - A custom value
|
||||
#[derive(
|
||||
Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
|
||||
)]
|
||||
#[serde(transparent)]
|
||||
pub struct Unit(Either<Length, String>);
|
||||
|
||||
/// A type representing at least one value
|
||||
///
|
||||
/// When translated to JSON, it can represent the following structures:
|
||||
/// ```json
|
||||
/// {
|
||||
/// "key": value
|
||||
/// }
|
||||
/// ```
|
||||
/// ```json
|
||||
/// {
|
||||
/// "key": [],
|
||||
/// }
|
||||
/// ```
|
||||
/// ```json
|
||||
/// {
|
||||
/// "key": [value, ...]
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct OneOrMany<T>(pub(crate) Either<T, Vec<T>>);
|
||||
|
||||
impl AnyString {
|
||||
/// Borrow the AnyString as an &str
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::AnyString;
|
||||
/// # let any_string = AnyString::from_xsd_string("hi");
|
||||
/// #
|
||||
/// let s_borrow = any_string
|
||||
/// .as_xsd_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_xsd_string(&self) -> Option<&str> {
|
||||
self.0.as_ref().left().map(|l| l.as_str())
|
||||
}
|
||||
|
||||
/// Borrow the AnyString as an RdfLangString
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::{AnyString, RdfLangString};
|
||||
/// # let any_string = AnyString::from_rdf_lang_string(RdfLangString {
|
||||
/// # value: "hi".into(),
|
||||
/// # language: "en".into(),
|
||||
/// # });
|
||||
/// #
|
||||
/// let s_borrow = any_string
|
||||
/// .as_rdf_lang_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_rdf_lang_string(&self) -> Option<&RdfLangString> {
|
||||
self.0.as_ref().right()
|
||||
}
|
||||
|
||||
/// Take the AnyString as a String
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::AnyString;
|
||||
/// # let any_string = AnyString::from_xsd_string("hi");
|
||||
/// #
|
||||
/// let xsd_string = any_string
|
||||
/// .xsd_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn xsd_string(self) -> Option<String> {
|
||||
self.0.left()
|
||||
}
|
||||
|
||||
/// Take the AnyString as an RdfLangString
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::{AnyString, RdfLangString};
|
||||
/// # let any_string = AnyString::from_rdf_lang_string(RdfLangString {
|
||||
/// # value: "hi".into(),
|
||||
/// # language: "en".into(),
|
||||
/// # });
|
||||
/// #
|
||||
/// let rdf_lang_string = any_string
|
||||
/// .rdf_lang_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn rdf_lang_string(self) -> Option<RdfLangString> {
|
||||
self.0.right()
|
||||
}
|
||||
|
||||
/// Create a new AnyString from an `Into<String>`
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::AnyString;
|
||||
///
|
||||
/// let any_string = AnyString::from_xsd_string("hi");
|
||||
/// ```
|
||||
pub fn from_xsd_string<T>(string: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
AnyString(Either::Left(string.into()))
|
||||
}
|
||||
|
||||
/// Create a new AnyString from an RdfLangString
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{AnyString, RdfLangString};
|
||||
///
|
||||
/// let any_string = AnyString::from_rdf_lang_string(RdfLangString {
|
||||
/// value: "hi".into(),
|
||||
/// language: "en".into(),
|
||||
/// });
|
||||
/// ```
|
||||
pub fn from_rdf_lang_string<T>(string: T) -> Self
|
||||
where
|
||||
T: Into<RdfLangString>,
|
||||
{
|
||||
AnyString(Either::Right(string.into()))
|
||||
}
|
||||
|
||||
/// Replace the contents of self with a String
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{AnyString, RdfLangString};
|
||||
///
|
||||
/// let mut any_string = AnyString::from_rdf_lang_string(RdfLangString {
|
||||
/// value: "hi".into(),
|
||||
/// language: "en".into(),
|
||||
/// });
|
||||
///
|
||||
/// any_string.set_xsd_string("hi");
|
||||
///
|
||||
/// assert!(any_string.as_xsd_string().is_some());
|
||||
/// ```
|
||||
pub fn set_xsd_string<T>(&mut self, string: T)
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.0 = Either::Left(string.into());
|
||||
}
|
||||
|
||||
/// Replace the contents of self with an RdfLangString
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{AnyString, RdfLangString};
|
||||
///
|
||||
/// let mut any_string = AnyString::from_xsd_string("hi");
|
||||
///
|
||||
/// any_string.set_rdf_lang_string(RdfLangString {
|
||||
/// value: "hi".into(),
|
||||
/// language: "en".into(),
|
||||
/// });
|
||||
///
|
||||
/// assert!(any_string.as_rdf_lang_string().is_some());
|
||||
/// ```
|
||||
pub fn set_rdf_lang_string<T>(&mut self, string: T)
|
||||
where
|
||||
T: Into<RdfLangString>,
|
||||
{
|
||||
self.0 = Either::Right(string.into());
|
||||
}
|
||||
}
|
||||
|
||||
impl OneOrMany<AnyString> {
|
||||
/// Try to borrow a single String from the current object
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::{OneOrMany, AnyString};
|
||||
/// # let string = OneOrMany::<AnyString>::from_xsd_string("Hey");
|
||||
/// string
|
||||
/// .as_single_xsd_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_single_xsd_string(&self) -> Option<&str> {
|
||||
self.as_one()
|
||||
.and_then(|any_string| any_string.as_xsd_string())
|
||||
}
|
||||
|
||||
/// Try to borrow a single RdfLangString from the current object
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::{OneOrMany, RdfLangString};
|
||||
/// # let string = OneOrMany::from_rdf_lang_string(RdfLangString {
|
||||
/// # value: "hi".into(),
|
||||
/// # language: "en".into(),
|
||||
/// # });
|
||||
/// string
|
||||
/// .as_single_rdf_lang_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_single_rdf_lang_string(&self) -> Option<&RdfLangString> {
|
||||
self.as_one()
|
||||
.and_then(|any_string| any_string.as_rdf_lang_string())
|
||||
}
|
||||
|
||||
/// Try to take a single String from the current object
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::{OneOrMany, AnyString};
|
||||
/// # let string = OneOrMany::<AnyString>::from_xsd_string("Hey");
|
||||
/// string
|
||||
/// .single_xsd_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn single_xsd_string(self) -> Option<String> {
|
||||
self.one().and_then(|any_string| any_string.xsd_string())
|
||||
}
|
||||
|
||||
/// Try to take a single RdfLangString from the current object
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::{OneOrMany, RdfLangString};
|
||||
/// # let string = OneOrMany::from_rdf_lang_string(RdfLangString {
|
||||
/// # value: "hi".into(),
|
||||
/// # language: "en".into(),
|
||||
/// # });
|
||||
/// string
|
||||
/// .single_rdf_lang_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn single_rdf_lang_string(self) -> Option<RdfLangString> {
|
||||
self.one()
|
||||
.and_then(|any_string| any_string.rdf_lang_string())
|
||||
}
|
||||
|
||||
/// Create the object from a single String
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{OneOrMany, AnyString};
|
||||
///
|
||||
/// let string = OneOrMany::<AnyString>::from_xsd_string("hi");
|
||||
/// ```
|
||||
pub fn from_xsd_string<T>(string: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
Self::from_one(AnyString::from_xsd_string(string))
|
||||
}
|
||||
|
||||
/// Create the object from a single RdfLangString
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{OneOrMany, RdfLangString};
|
||||
///
|
||||
/// let string = OneOrMany::from_rdf_lang_string(RdfLangString {
|
||||
/// value: "hi".into(),
|
||||
/// language: "en".into(),
|
||||
/// });
|
||||
/// ```
|
||||
pub fn from_rdf_lang_string<T>(string: T) -> Self
|
||||
where
|
||||
T: Into<RdfLangString>,
|
||||
{
|
||||
Self::from_one(AnyString::from_rdf_lang_string(string))
|
||||
}
|
||||
|
||||
/// Add a String to the object, appending to whatever is currently included
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{OneOrMany, AnyString};
|
||||
///
|
||||
/// let mut string = OneOrMany::<AnyString>::from_xsd_string("Hello");
|
||||
///
|
||||
/// string
|
||||
/// .add_xsd_string("Hey")
|
||||
/// .add_xsd_string("hi");
|
||||
/// ```
|
||||
pub fn add_xsd_string<T>(&mut self, string: T) -> &mut Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.add(string.into())
|
||||
}
|
||||
|
||||
/// Add an RdfLangString to the object, appending to whatever is currently included
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{AnyString, OneOrMany, RdfLangString};
|
||||
///
|
||||
/// let mut string = OneOrMany::<AnyString>::from_xsd_string("Hello");
|
||||
///
|
||||
/// string
|
||||
/// .add_rdf_lang_string(RdfLangString {
|
||||
/// value: "Hey".into(),
|
||||
/// language: "en".into(),
|
||||
/// })
|
||||
/// .add_rdf_lang_string(RdfLangString {
|
||||
/// value: "hi".into(),
|
||||
/// language: "en".into(),
|
||||
/// });
|
||||
/// ```
|
||||
pub fn add_rdf_lang_string<T>(&mut self, string: T) -> &mut Self
|
||||
where
|
||||
T: Into<RdfLangString>,
|
||||
{
|
||||
self.add(string.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OneOrMany<T> {
|
||||
/// Get a reference to a single value
|
||||
///
|
||||
/// ```rust
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let value = OneOrMany::from_one(1);
|
||||
/// if let Some(v) = value.as_one() {
|
||||
/// println!("{:?}", v);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn as_one(&self) -> Option<&T> {
|
||||
self.0.as_ref().left()
|
||||
}
|
||||
|
||||
/// Take a single value
|
||||
///
|
||||
/// ```rust
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let value = OneOrMany::from_one(1);
|
||||
/// if let Some(v) = value.one() {
|
||||
/// println!("{:?}", v);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn one(self) -> Option<T> {
|
||||
self.0.left()
|
||||
}
|
||||
|
||||
/// Get a slice of values
|
||||
///
|
||||
/// ```rust
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let value = OneOrMany::from_many(vec![1, 2, 3]);
|
||||
/// if let Some(v) = value.as_many() {
|
||||
/// for item in v.iter() {
|
||||
/// println!("{:?}", item);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn as_many(&self) -> Option<&[T]> {
|
||||
self.0.as_ref().right().map(|v| v.as_ref())
|
||||
}
|
||||
|
||||
/// Take a Vec of values
|
||||
///
|
||||
/// ```rust
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let value = OneOrMany::from_many(vec![1, 2, 3]);
|
||||
/// if let Some(v) = value.many() {
|
||||
/// for item in v.into_iter() {
|
||||
/// println!("{:?}", item);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn many(self) -> Option<Vec<T>> {
|
||||
self.0.right()
|
||||
}
|
||||
|
||||
/// Consume the type, returning a vec
|
||||
///
|
||||
/// ```rust
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let value = OneOrMany::from_many(vec![1, 2, 3]);
|
||||
/// for item in value.unwrap_to_vec() {
|
||||
/// println!("{:?}", item);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn unwrap_to_vec(self) -> Vec<T> {
|
||||
match self.0 {
|
||||
Either::Left(t) => vec![t],
|
||||
Either::Right(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
/// Produce a new object from one value
|
||||
///
|
||||
/// ```
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
/// let v = OneOrMany::from_one(1234);
|
||||
/// ```
|
||||
pub fn from_one(t: T) -> Self {
|
||||
OneOrMany(Either::Left(t))
|
||||
}
|
||||
|
||||
/// Produce a new object from a vec of values
|
||||
///
|
||||
/// ```
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
/// let v = OneOrMany::from_many(vec![1, 2, 3, 4]);
|
||||
/// ```
|
||||
pub fn from_many(items: Vec<T>) -> Self {
|
||||
OneOrMany(Either::Right(items))
|
||||
}
|
||||
|
||||
/// Overwrite the contents with a single value
|
||||
///
|
||||
/// ```
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let mut value = OneOrMany::from_many(vec![1, 2, 3]);
|
||||
/// value.set_one(3);
|
||||
///
|
||||
/// assert!(value.as_one().is_some());
|
||||
/// ```
|
||||
pub fn set_one<U>(&mut self, u: U) -> &mut Self
|
||||
where
|
||||
U: Into<T>,
|
||||
{
|
||||
self.0 = Either::Left(u.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Overwrite the contents with vec of values
|
||||
///
|
||||
/// ```
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let mut value = OneOrMany::from_one(1234);
|
||||
/// value.set_many(vec![1, 2, 3, 4]);
|
||||
///
|
||||
/// assert!(value.as_many().is_some());
|
||||
/// ```
|
||||
pub fn set_many<U>(&mut self, items: impl IntoIterator<Item = U>) -> &mut Self
|
||||
where
|
||||
U: Into<T>,
|
||||
{
|
||||
self.0 = Either::Right(items.into_iter().map(Into::into).collect());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a value to the object
|
||||
///
|
||||
/// This appends the value to the existing vec, or converts the single value into a vec, and
|
||||
/// then appends the new value
|
||||
///
|
||||
/// ```
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
/// let mut value = OneOrMany::from_one(1234);
|
||||
/// value.add(4321);
|
||||
///
|
||||
/// assert!(value.as_many().is_some());
|
||||
/// ```
|
||||
pub fn add<U>(&mut self, u: U) -> &mut Self
|
||||
where
|
||||
U: Into<T>,
|
||||
{
|
||||
let mut v = match std::mem::replace(&mut self.0, Either::Right(vec![])) {
|
||||
Either::Left(one) => vec![one],
|
||||
Either::Right(v) => v,
|
||||
};
|
||||
v.push(u.into());
|
||||
self.0 = Either::Right(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add many values to the object
|
||||
///
|
||||
/// This appends the values to the existing vec, or converts the single value into a vec, and
|
||||
/// then appends the new values
|
||||
///
|
||||
/// ```
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
/// let mut value = OneOrMany::from_one(1234);
|
||||
/// value.add_many(vec![4321, 2345]);
|
||||
///
|
||||
/// assert!(value.as_many().is_some());
|
||||
/// ```
|
||||
pub fn add_many<U>(&mut self, items: impl IntoIterator<Item = U>) -> &mut Self
|
||||
where
|
||||
U: Into<T>,
|
||||
{
|
||||
let mut v = match std::mem::replace(&mut self.0, Either::Right(vec![])) {
|
||||
Either::Left(one) => vec![one],
|
||||
Either::Right(v) => v,
|
||||
};
|
||||
v.extend(items.into_iter().map(Into::into));
|
||||
self.0 = Either::Right(v);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Unit {
|
||||
/// Create a new unit measuring Centimeters
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// Unit::centimeters();
|
||||
/// ```
|
||||
pub fn centimeters() -> Self {
|
||||
Unit(Either::Left(Length::Centimeters))
|
||||
}
|
||||
|
||||
/// Check if the unit is Centimeters
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::centimeters().is_centimeters());
|
||||
/// ```
|
||||
pub fn is_centimeters(&self) -> bool {
|
||||
self.0
|
||||
.as_ref()
|
||||
.left()
|
||||
.map(|l| l.is_centimeters())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Create a new unit measuring Meters
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// Unit::meters();
|
||||
/// ```
|
||||
pub fn meters() -> Self {
|
||||
Unit(Either::Left(Length::Meters))
|
||||
}
|
||||
|
||||
/// Check if the unit is Meters
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::meters().is_meters());
|
||||
/// ```
|
||||
pub fn is_meters(&self) -> bool {
|
||||
self.0
|
||||
.as_ref()
|
||||
.left()
|
||||
.map(|l| l.is_meters())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Create a new unit measuring Kilometers
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// Unit::kilometers();
|
||||
/// ```
|
||||
pub fn kilometers() -> Self {
|
||||
Unit(Either::Left(Length::Kilometers))
|
||||
}
|
||||
|
||||
/// Check if the unit is Kilometers
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::kilometers().is_kilometers());
|
||||
/// ```
|
||||
pub fn is_kilometers(&self) -> bool {
|
||||
self.0
|
||||
.as_ref()
|
||||
.left()
|
||||
.map(|l| l.is_kilometers())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Create a new unit measuring Feet
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// Unit::feet();
|
||||
/// ```
|
||||
pub fn feet() -> Self {
|
||||
Unit(Either::Left(Length::Feet))
|
||||
}
|
||||
|
||||
/// Check if the unit is Feet
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::feet().is_feet());
|
||||
/// ```
|
||||
pub fn is_feet(&self) -> bool {
|
||||
self.0.as_ref().left().map(|l| l.is_feet()).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Create a new unit measuring Inches
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// Unit::inches();
|
||||
/// ```
|
||||
pub fn inches() -> Self {
|
||||
Unit(Either::Left(Length::Inches))
|
||||
}
|
||||
|
||||
/// Check if the unit is Inches
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::inches().is_inches());
|
||||
/// ```
|
||||
pub fn is_inches(&self) -> bool {
|
||||
self.0
|
||||
.as_ref()
|
||||
.left()
|
||||
.map(|l| l.is_inches())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Create a new custom unit
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// Unit::custom("Yards");
|
||||
/// ```
|
||||
pub fn custom<T>(string: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
Unit(Either::Right(string.into()))
|
||||
}
|
||||
|
||||
/// Check if a unit is custom
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::custom("Yards").is_custom());
|
||||
/// ```
|
||||
pub fn is_custom(&self) -> bool {
|
||||
self.as_custom().is_some()
|
||||
}
|
||||
|
||||
/// Fetch a custom unit
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::custom("Yards").as_custom() == Some("Yards"));
|
||||
/// ```
|
||||
pub fn as_custom(&self) -> Option<&str> {
|
||||
self.0.as_ref().right().map(|r| r.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Unit {
|
||||
fn default() -> Self {
|
||||
Self::meters()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Unit {
|
||||
type Err = std::convert::Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let unit = match s {
|
||||
"cm" => Self::centimeters(),
|
||||
"m" => Self::meters(),
|
||||
"km" => Self::kilometers(),
|
||||
"inches" => Self::inches(),
|
||||
"feet" => Self::feet(),
|
||||
other => Self::custom(other),
|
||||
};
|
||||
|
||||
Ok(unit)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Unit {
|
||||
fn from(s: String) -> Self {
|
||||
match s.parse() {
|
||||
Ok(u) => u,
|
||||
Err(e) => match e {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Unit {
|
||||
fn from(s: &str) -> Self {
|
||||
match s.parse() {
|
||||
Ok(u) => u,
|
||||
Err(e) => match e {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for OneOrMany<T> {
|
||||
fn from(t: T) -> Self {
|
||||
OneOrMany::from_one(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Vec<T>> for OneOrMany<T> {
|
||||
fn from(t: Vec<T>) -> Self {
|
||||
OneOrMany::from_many(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for AnyString {
|
||||
fn from(s: &str) -> Self {
|
||||
AnyString::from_xsd_string(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for AnyString {
|
||||
fn from(s: String) -> Self {
|
||||
AnyString::from_xsd_string(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RdfLangString> for AnyString {
|
||||
fn from(s: RdfLangString) -> Self {
|
||||
AnyString::from_rdf_lang_string(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for OneOrMany<AnyString> {
|
||||
fn from(s: &str) -> Self {
|
||||
OneOrMany::<AnyString>::from_xsd_string(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for OneOrMany<AnyString> {
|
||||
fn from(s: String) -> Self {
|
||||
OneOrMany::<AnyString>::from_xsd_string(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RdfLangString> for OneOrMany<AnyString> {
|
||||
fn from(s: RdfLangString) -> Self {
|
||||
OneOrMany::<AnyString>::from_rdf_lang_string(s)
|
||||
}
|
||||
}
|
345
src/primitives/any_string.rs
Normal file
345
src/primitives/any_string.rs
Normal file
|
@ -0,0 +1,345 @@
|
|||
use crate::{
|
||||
either::Either,
|
||||
primitives::{OneOrMany, RdfLangString},
|
||||
};
|
||||
|
||||
/// A type representing any kind of string
|
||||
///
|
||||
/// In the ActivityStreams specification, string types are often defined as either an xsd:String or
|
||||
/// and rdf:langString. The AnyString type represents this union.
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct AnyString(Either<String, RdfLangString>);
|
||||
|
||||
impl AnyString {
|
||||
/// Borrow the AnyString as an &str
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::AnyString;
|
||||
/// # let any_string = AnyString::from_xsd_string("hi");
|
||||
/// #
|
||||
/// let s_borrow = any_string
|
||||
/// .as_xsd_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_xsd_string(&self) -> Option<&str> {
|
||||
self.0.as_ref().left().map(|l| l.as_str())
|
||||
}
|
||||
|
||||
/// Borrow the AnyString as an RdfLangString
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::{AnyString, RdfLangString};
|
||||
/// # let any_string = AnyString::from_rdf_lang_string(RdfLangString {
|
||||
/// # value: "hi".into(),
|
||||
/// # language: "en".into(),
|
||||
/// # });
|
||||
/// #
|
||||
/// let s_borrow = any_string
|
||||
/// .as_rdf_lang_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_rdf_lang_string(&self) -> Option<&RdfLangString> {
|
||||
self.0.as_ref().right()
|
||||
}
|
||||
|
||||
/// Take the AnyString as a String
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::AnyString;
|
||||
/// # let any_string = AnyString::from_xsd_string("hi");
|
||||
/// #
|
||||
/// let xsd_string = any_string
|
||||
/// .xsd_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn xsd_string(self) -> Option<String> {
|
||||
self.0.left()
|
||||
}
|
||||
|
||||
/// Take the AnyString as an RdfLangString
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::{AnyString, RdfLangString};
|
||||
/// # let any_string = AnyString::from_rdf_lang_string(RdfLangString {
|
||||
/// # value: "hi".into(),
|
||||
/// # language: "en".into(),
|
||||
/// # });
|
||||
/// #
|
||||
/// let rdf_lang_string = any_string
|
||||
/// .rdf_lang_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn rdf_lang_string(self) -> Option<RdfLangString> {
|
||||
self.0.right()
|
||||
}
|
||||
|
||||
/// Create a new AnyString from an `Into<String>`
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::AnyString;
|
||||
///
|
||||
/// let any_string = AnyString::from_xsd_string("hi");
|
||||
/// ```
|
||||
pub fn from_xsd_string<T>(string: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
AnyString(Either::Left(string.into()))
|
||||
}
|
||||
|
||||
/// Create a new AnyString from an RdfLangString
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{AnyString, RdfLangString};
|
||||
///
|
||||
/// let any_string = AnyString::from_rdf_lang_string(RdfLangString {
|
||||
/// value: "hi".into(),
|
||||
/// language: "en".into(),
|
||||
/// });
|
||||
/// ```
|
||||
pub fn from_rdf_lang_string<T>(string: T) -> Self
|
||||
where
|
||||
T: Into<RdfLangString>,
|
||||
{
|
||||
AnyString(Either::Right(string.into()))
|
||||
}
|
||||
|
||||
/// Replace the contents of self with a String
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{AnyString, RdfLangString};
|
||||
///
|
||||
/// let mut any_string = AnyString::from_rdf_lang_string(RdfLangString {
|
||||
/// value: "hi".into(),
|
||||
/// language: "en".into(),
|
||||
/// });
|
||||
///
|
||||
/// any_string.set_xsd_string("hi");
|
||||
///
|
||||
/// assert!(any_string.as_xsd_string().is_some());
|
||||
/// ```
|
||||
pub fn set_xsd_string<T>(&mut self, string: T)
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.0 = Either::Left(string.into());
|
||||
}
|
||||
|
||||
/// Replace the contents of self with an RdfLangString
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{AnyString, RdfLangString};
|
||||
///
|
||||
/// let mut any_string = AnyString::from_xsd_string("hi");
|
||||
///
|
||||
/// any_string.set_rdf_lang_string(RdfLangString {
|
||||
/// value: "hi".into(),
|
||||
/// language: "en".into(),
|
||||
/// });
|
||||
///
|
||||
/// assert!(any_string.as_rdf_lang_string().is_some());
|
||||
/// ```
|
||||
pub fn set_rdf_lang_string<T>(&mut self, string: T)
|
||||
where
|
||||
T: Into<RdfLangString>,
|
||||
{
|
||||
self.0 = Either::Right(string.into());
|
||||
}
|
||||
}
|
||||
|
||||
impl OneOrMany<AnyString> {
|
||||
/// Try to borrow a single String from the current object
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::{OneOrMany, AnyString};
|
||||
/// # let string = OneOrMany::<AnyString>::from_xsd_string("Hey");
|
||||
/// string
|
||||
/// .as_single_xsd_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_single_xsd_string(&self) -> Option<&str> {
|
||||
self.as_one()
|
||||
.and_then(|any_string| any_string.as_xsd_string())
|
||||
}
|
||||
|
||||
/// Try to borrow a single RdfLangString from the current object
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::{OneOrMany, RdfLangString};
|
||||
/// # let string = OneOrMany::from_rdf_lang_string(RdfLangString {
|
||||
/// # value: "hi".into(),
|
||||
/// # language: "en".into(),
|
||||
/// # });
|
||||
/// string
|
||||
/// .as_single_rdf_lang_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn as_single_rdf_lang_string(&self) -> Option<&RdfLangString> {
|
||||
self.as_one()
|
||||
.and_then(|any_string| any_string.as_rdf_lang_string())
|
||||
}
|
||||
|
||||
/// Try to take a single String from the current object
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::{OneOrMany, AnyString};
|
||||
/// # let string = OneOrMany::<AnyString>::from_xsd_string("Hey");
|
||||
/// string
|
||||
/// .single_xsd_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn single_xsd_string(self) -> Option<String> {
|
||||
self.one().and_then(|any_string| any_string.xsd_string())
|
||||
}
|
||||
|
||||
/// Try to take a single RdfLangString from the current object
|
||||
///
|
||||
/// ```rust
|
||||
/// # fn main() -> Result<(), anyhow::Error> {
|
||||
/// # use activitystreams_new::primitives::{OneOrMany, RdfLangString};
|
||||
/// # let string = OneOrMany::from_rdf_lang_string(RdfLangString {
|
||||
/// # value: "hi".into(),
|
||||
/// # language: "en".into(),
|
||||
/// # });
|
||||
/// string
|
||||
/// .single_rdf_lang_string()
|
||||
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn single_rdf_lang_string(self) -> Option<RdfLangString> {
|
||||
self.one()
|
||||
.and_then(|any_string| any_string.rdf_lang_string())
|
||||
}
|
||||
|
||||
/// Create the object from a single String
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{OneOrMany, AnyString};
|
||||
///
|
||||
/// let string = OneOrMany::<AnyString>::from_xsd_string("hi");
|
||||
/// ```
|
||||
pub fn from_xsd_string<T>(string: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
Self::from_one(AnyString::from_xsd_string(string))
|
||||
}
|
||||
|
||||
/// Create the object from a single RdfLangString
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{OneOrMany, RdfLangString};
|
||||
///
|
||||
/// let string = OneOrMany::from_rdf_lang_string(RdfLangString {
|
||||
/// value: "hi".into(),
|
||||
/// language: "en".into(),
|
||||
/// });
|
||||
/// ```
|
||||
pub fn from_rdf_lang_string<T>(string: T) -> Self
|
||||
where
|
||||
T: Into<RdfLangString>,
|
||||
{
|
||||
Self::from_one(AnyString::from_rdf_lang_string(string))
|
||||
}
|
||||
|
||||
/// Add a String to the object, appending to whatever is currently included
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{OneOrMany, AnyString};
|
||||
///
|
||||
/// let mut string = OneOrMany::<AnyString>::from_xsd_string("Hello");
|
||||
///
|
||||
/// string
|
||||
/// .add_xsd_string("Hey")
|
||||
/// .add_xsd_string("hi");
|
||||
/// ```
|
||||
pub fn add_xsd_string<T>(&mut self, string: T) -> &mut Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
self.add(string.into())
|
||||
}
|
||||
|
||||
/// Add an RdfLangString to the object, appending to whatever is currently included
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::{AnyString, OneOrMany, RdfLangString};
|
||||
///
|
||||
/// let mut string = OneOrMany::<AnyString>::from_xsd_string("Hello");
|
||||
///
|
||||
/// string
|
||||
/// .add_rdf_lang_string(RdfLangString {
|
||||
/// value: "Hey".into(),
|
||||
/// language: "en".into(),
|
||||
/// })
|
||||
/// .add_rdf_lang_string(RdfLangString {
|
||||
/// value: "hi".into(),
|
||||
/// language: "en".into(),
|
||||
/// });
|
||||
/// ```
|
||||
pub fn add_rdf_lang_string<T>(&mut self, string: T) -> &mut Self
|
||||
where
|
||||
T: Into<RdfLangString>,
|
||||
{
|
||||
self.add(string.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for AnyString {
|
||||
fn from(s: &str) -> Self {
|
||||
AnyString::from_xsd_string(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for AnyString {
|
||||
fn from(s: String) -> Self {
|
||||
AnyString::from_xsd_string(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RdfLangString> for AnyString {
|
||||
fn from(s: RdfLangString) -> Self {
|
||||
AnyString::from_rdf_lang_string(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for OneOrMany<AnyString> {
|
||||
fn from(s: &str) -> Self {
|
||||
OneOrMany::<AnyString>::from_xsd_string(s.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for OneOrMany<AnyString> {
|
||||
fn from(s: String) -> Self {
|
||||
OneOrMany::<AnyString>::from_xsd_string(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RdfLangString> for OneOrMany<AnyString> {
|
||||
fn from(s: RdfLangString) -> Self {
|
||||
OneOrMany::<AnyString>::from_rdf_lang_string(s)
|
||||
}
|
||||
}
|
39
src/primitives/mod.rs
Normal file
39
src/primitives/mod.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
//! Types creating the base for most ActivityStreams fields.
|
||||
//!
|
||||
//! These types are not themselves defined by ActivityStreams, but are referenced by the
|
||||
//! specification.
|
||||
//!
|
||||
//! ```rust
|
||||
//! use activitystreams_new::primitives::{AnyString, OneOrMany, Unit};
|
||||
//!
|
||||
//! let any_string = AnyString::from_xsd_string("hey");
|
||||
//!
|
||||
//! let one_or_many = OneOrMany::<i32>::from_one(1234);
|
||||
//!
|
||||
//! let cm = Unit::centimeters();
|
||||
//! ```
|
||||
|
||||
mod any_string;
|
||||
mod one_or_many;
|
||||
mod rdf_lang_string;
|
||||
mod serde_parse;
|
||||
mod unit;
|
||||
mod xsd_datetime;
|
||||
mod xsd_duration;
|
||||
|
||||
pub use self::{
|
||||
any_string::AnyString,
|
||||
one_or_many::OneOrMany,
|
||||
rdf_lang_string::RdfLangString,
|
||||
unit::Unit,
|
||||
xsd_datetime::XsdDateTime,
|
||||
xsd_duration::{XsdDuration, XsdDurationError},
|
||||
};
|
||||
|
||||
use self::serde_parse::SerdeParse;
|
||||
|
||||
/// An alias for the url::Url struct with serde compatibility
|
||||
pub type XsdAnyUri = SerdeParse<url::Url>;
|
||||
|
||||
/// An alias for the mime::Mime struct with serde compatibility
|
||||
pub(crate) type MimeMediaType = SerdeParse<mime::Mime>;
|
292
src/primitives/one_or_many.rs
Normal file
292
src/primitives/one_or_many.rs
Normal file
|
@ -0,0 +1,292 @@
|
|||
use crate::either::Either;
|
||||
|
||||
/// A type representing at least one value
|
||||
///
|
||||
/// When translated to JSON, it can represent the following structures:
|
||||
/// ```json
|
||||
/// {
|
||||
/// "key": value
|
||||
/// }
|
||||
/// ```
|
||||
/// ```json
|
||||
/// {
|
||||
/// "key": [],
|
||||
/// }
|
||||
/// ```
|
||||
/// ```json
|
||||
/// {
|
||||
/// "key": [value, ...]
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct OneOrMany<T>(pub(crate) Either<T, Vec<T>>);
|
||||
|
||||
impl<T> OneOrMany<T> {
|
||||
/// Create a OneOrMany referencing the existing one
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
///
|
||||
/// let value = OneOrMany::from_one(String::from("hi"));
|
||||
/// let value_ref = value.as_ref();
|
||||
///
|
||||
/// assert_eq!(value_ref.one(), Some(&String::from("hi")));
|
||||
/// ```
|
||||
pub fn as_ref(&self) -> OneOrMany<&T> {
|
||||
OneOrMany(self.0.as_ref().map(|l| l, |r| r.iter().collect()))
|
||||
}
|
||||
|
||||
/// Map the value inside the OneOrMany from T to U
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
///
|
||||
/// let value = OneOrMany::from_one("Jake from StateFarm");
|
||||
/// let new_value = value.map(|s| format!("Hi, {}", s));
|
||||
///
|
||||
/// assert_eq!(new_value.one(), Some(String::from("Hi, Jake from StateFarm")));
|
||||
/// ```
|
||||
pub fn map<F, U>(self, f: F) -> OneOrMany<U>
|
||||
where
|
||||
F: Fn(T) -> U + Copy,
|
||||
{
|
||||
OneOrMany(self.0.map(f, |v| v.into_iter().map(f).collect()))
|
||||
}
|
||||
|
||||
/// Create a OneOrMany mutably referencing the existing one
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
///
|
||||
/// let mut value = OneOrMany::from_one(5);
|
||||
/// let value_mut = value.as_mut();
|
||||
/// ```
|
||||
pub fn as_mut(&mut self) -> OneOrMany<&mut T> {
|
||||
OneOrMany(self.0.as_mut().map(|l| l, |r| r.iter_mut().collect()))
|
||||
}
|
||||
|
||||
/// Get a reference to a single value
|
||||
///
|
||||
/// ```rust
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let value = OneOrMany::from_one(1);
|
||||
/// if let Some(v) = value.as_one() {
|
||||
/// println!("{:?}", v);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn as_one(&self) -> Option<&T> {
|
||||
self.0.as_ref().left()
|
||||
}
|
||||
|
||||
/// Borrow one as mutable
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
///
|
||||
/// let mut value = OneOrMany::from_one(1);
|
||||
///
|
||||
/// if let Some(i) = value.one_mut() {
|
||||
/// *i += 1;
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(value.one(), Some(2));
|
||||
/// ```
|
||||
pub fn one_mut(&mut self) -> Option<&mut T> {
|
||||
self.0.as_mut().left()
|
||||
}
|
||||
|
||||
/// Take a single value
|
||||
///
|
||||
/// ```rust
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let value = OneOrMany::from_one(1);
|
||||
/// if let Some(v) = value.one() {
|
||||
/// println!("{:?}", v);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn one(self) -> Option<T> {
|
||||
self.0.left()
|
||||
}
|
||||
|
||||
/// Get a slice of values
|
||||
///
|
||||
/// ```rust
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let value = OneOrMany::from_many(vec![1, 2, 3]);
|
||||
/// if let Some(v) = value.as_many() {
|
||||
/// for item in v.iter() {
|
||||
/// println!("{:?}", item);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn as_many(&self) -> Option<&[T]> {
|
||||
self.0.as_ref().right().map(|v| v.as_ref())
|
||||
}
|
||||
|
||||
/// Borrow many as mutable
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
///
|
||||
/// let mut value = OneOrMany::from_many(vec![1, 2, 3]);
|
||||
///
|
||||
/// if let Some(v) = value.many_mut() {
|
||||
/// for i in v.iter_mut() {
|
||||
/// *i += 3;
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(value.many(), Some(vec![4, 5, 6]));
|
||||
/// ```
|
||||
pub fn many_mut(&mut self) -> Option<&mut [T]> {
|
||||
self.0.as_mut().right().map(|v| v.as_mut())
|
||||
}
|
||||
|
||||
/// Take a Vec of values
|
||||
///
|
||||
/// ```rust
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let value = OneOrMany::from_many(vec![1, 2, 3]);
|
||||
/// if let Some(v) = value.many() {
|
||||
/// for item in v.into_iter() {
|
||||
/// println!("{:?}", item);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn many(self) -> Option<Vec<T>> {
|
||||
self.0.right()
|
||||
}
|
||||
|
||||
/// Consume the type, returning a vec
|
||||
///
|
||||
/// ```rust
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let value = OneOrMany::from_many(vec![1, 2, 3]);
|
||||
/// for item in value.unwrap_to_vec() {
|
||||
/// println!("{:?}", item);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn unwrap_to_vec(self) -> Vec<T> {
|
||||
match self.0 {
|
||||
Either::Left(t) => vec![t],
|
||||
Either::Right(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
/// Produce a new object from one value
|
||||
///
|
||||
/// ```
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
/// let v = OneOrMany::from_one(1234);
|
||||
/// ```
|
||||
pub fn from_one(t: T) -> Self {
|
||||
OneOrMany(Either::Left(t))
|
||||
}
|
||||
|
||||
/// Produce a new object from a vec of values
|
||||
///
|
||||
/// ```
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
/// let v = OneOrMany::from_many(vec![1, 2, 3, 4]);
|
||||
/// ```
|
||||
pub fn from_many(items: Vec<T>) -> Self {
|
||||
OneOrMany(Either::Right(items))
|
||||
}
|
||||
|
||||
/// Overwrite the contents with a single value
|
||||
///
|
||||
/// ```
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let mut value = OneOrMany::from_many(vec![1, 2, 3]);
|
||||
/// value.set_one(3);
|
||||
///
|
||||
/// assert!(value.as_one().is_some());
|
||||
/// ```
|
||||
pub fn set_one<U>(&mut self, u: U) -> &mut Self
|
||||
where
|
||||
U: Into<T>,
|
||||
{
|
||||
self.0 = Either::Left(u.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Overwrite the contents with vec of values
|
||||
///
|
||||
/// ```
|
||||
/// # use activitystreams_new::primitives::OneOrMany;
|
||||
/// # let mut value = OneOrMany::from_one(1234);
|
||||
/// value.set_many(vec![1, 2, 3, 4]);
|
||||
///
|
||||
/// assert!(value.as_many().is_some());
|
||||
/// ```
|
||||
pub fn set_many<U>(&mut self, items: impl IntoIterator<Item = U>) -> &mut Self
|
||||
where
|
||||
U: Into<T>,
|
||||
{
|
||||
self.0 = Either::Right(items.into_iter().map(Into::into).collect());
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a value to the object
|
||||
///
|
||||
/// This appends the value to the existing vec, or converts the single value into a vec, and
|
||||
/// then appends the new value
|
||||
///
|
||||
/// ```
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
/// let mut value = OneOrMany::from_one(1234);
|
||||
/// value.add(4321);
|
||||
///
|
||||
/// assert!(value.as_many().is_some());
|
||||
/// ```
|
||||
pub fn add<U>(&mut self, u: U) -> &mut Self
|
||||
where
|
||||
U: Into<T>,
|
||||
{
|
||||
let mut v = match std::mem::replace(&mut self.0, Either::Right(vec![])) {
|
||||
Either::Left(one) => vec![one],
|
||||
Either::Right(v) => v,
|
||||
};
|
||||
v.push(u.into());
|
||||
self.0 = Either::Right(v);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add many values to the object
|
||||
///
|
||||
/// This appends the values to the existing vec, or converts the single value into a vec, and
|
||||
/// then appends the new values
|
||||
///
|
||||
/// ```
|
||||
/// use activitystreams_new::primitives::OneOrMany;
|
||||
/// let mut value = OneOrMany::from_one(1234);
|
||||
/// value.add_many(vec![4321, 2345]);
|
||||
///
|
||||
/// assert!(value.as_many().is_some());
|
||||
/// ```
|
||||
pub fn add_many<U>(&mut self, items: impl IntoIterator<Item = U>) -> &mut Self
|
||||
where
|
||||
U: Into<T>,
|
||||
{
|
||||
let mut v = match std::mem::replace(&mut self.0, Either::Right(vec![])) {
|
||||
Either::Left(one) => vec![one],
|
||||
Either::Right(v) => v,
|
||||
};
|
||||
v.extend(items.into_iter().map(Into::into));
|
||||
self.0 = Either::Right(v);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for OneOrMany<T> {
|
||||
fn from(t: T) -> Self {
|
||||
OneOrMany::from_one(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Vec<T>> for OneOrMany<T> {
|
||||
fn from(t: Vec<T>) -> Self {
|
||||
OneOrMany::from_many(t)
|
||||
}
|
||||
}
|
42
src/primitives/rdf_lang_string.rs
Normal file
42
src/primitives/rdf_lang_string.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* This file is part of ActivityStreams.
|
||||
*
|
||||
* Copyright © 2020 Riley Trautman
|
||||
*
|
||||
* ActivityStreams is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ActivityStreams is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/// The rdf.langString type extends xs.string, and represents a language tagged string in RDF.
|
||||
#[derive(
|
||||
Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
|
||||
)]
|
||||
pub struct RdfLangString {
|
||||
/// The content of the langstring
|
||||
///
|
||||
/// Represented in json as "@value"
|
||||
#[serde(rename = "@value")]
|
||||
pub value: String,
|
||||
|
||||
/// The language identifier
|
||||
///
|
||||
/// Represented in json as "@language"
|
||||
#[serde(rename = "@language")]
|
||||
pub language: String,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for RdfLangString {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}:{}", self.language, self.value)
|
||||
}
|
||||
}
|
89
src/primitives/serde_parse.rs
Normal file
89
src/primitives/serde_parse.rs
Normal file
|
@ -0,0 +1,89 @@
|
|||
/// A struct that wraps a type implementing FromStr and Display implements Serde's Deserialize and
|
||||
/// Serialize
|
||||
#[derive(Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub struct SerdeParse<T>(T);
|
||||
|
||||
impl<T> SerdeParse<T> {
|
||||
/// Extract the inner item from SerdeParse
|
||||
pub fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for SerdeParse<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::ops::DerefMut for SerdeParse<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for SerdeParse<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<T> for SerdeParse<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::str::FromStr for SerdeParse<T>
|
||||
where
|
||||
T: std::str::FromStr,
|
||||
{
|
||||
type Err = <T as std::str::FromStr>::Err;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
T::from_str(s).map(SerdeParse)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::fmt::Display for SerdeParse<T>
|
||||
where
|
||||
T: std::fmt::Display,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> serde::ser::Serialize for SerdeParse<T>
|
||||
where
|
||||
T: std::fmt::Display,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::ser::Serializer,
|
||||
{
|
||||
self.0.to_string().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> serde::de::Deserialize<'de> for SerdeParse<T>
|
||||
where
|
||||
T: std::str::FromStr,
|
||||
T::Err: std::fmt::Display,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::de::Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
s.parse().map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for SerdeParse<T> {
|
||||
fn from(t: T) -> Self {
|
||||
SerdeParse(t)
|
||||
}
|
||||
}
|
342
src/primitives/unit.rs
Normal file
342
src/primitives/unit.rs
Normal file
|
@ -0,0 +1,342 @@
|
|||
use crate::either::Either;
|
||||
|
||||
/// A type representing units of length
|
||||
///
|
||||
/// It can be any of the following
|
||||
/// - Centimeters
|
||||
/// - Meters
|
||||
/// - Kilometers
|
||||
/// - Inches
|
||||
/// - Feet
|
||||
/// - A custom value
|
||||
#[derive(
|
||||
Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
|
||||
)]
|
||||
#[serde(transparent)]
|
||||
pub struct Unit(Either<Length, String>);
|
||||
|
||||
impl Unit {
|
||||
/// Create a new unit measuring Centimeters
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// Unit::centimeters();
|
||||
/// ```
|
||||
pub fn centimeters() -> Self {
|
||||
Unit(Either::Left(Length::Centimeters))
|
||||
}
|
||||
|
||||
/// Check if the unit is Centimeters
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::centimeters().is_centimeters());
|
||||
/// ```
|
||||
pub fn is_centimeters(&self) -> bool {
|
||||
self.0
|
||||
.as_ref()
|
||||
.left()
|
||||
.map(|l| l.is_centimeters())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Create a new unit measuring Meters
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// Unit::meters();
|
||||
/// ```
|
||||
pub fn meters() -> Self {
|
||||
Unit(Either::Left(Length::Meters))
|
||||
}
|
||||
|
||||
/// Check if the unit is Meters
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::meters().is_meters());
|
||||
/// ```
|
||||
pub fn is_meters(&self) -> bool {
|
||||
self.0
|
||||
.as_ref()
|
||||
.left()
|
||||
.map(|l| l.is_meters())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Create a new unit measuring Kilometers
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// Unit::kilometers();
|
||||
/// ```
|
||||
pub fn kilometers() -> Self {
|
||||
Unit(Either::Left(Length::Kilometers))
|
||||
}
|
||||
|
||||
/// Check if the unit is Kilometers
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::kilometers().is_kilometers());
|
||||
/// ```
|
||||
pub fn is_kilometers(&self) -> bool {
|
||||
self.0
|
||||
.as_ref()
|
||||
.left()
|
||||
.map(|l| l.is_kilometers())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Create a new unit measuring Feet
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// Unit::feet();
|
||||
/// ```
|
||||
pub fn feet() -> Self {
|
||||
Unit(Either::Left(Length::Feet))
|
||||
}
|
||||
|
||||
/// Check if the unit is Feet
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::feet().is_feet());
|
||||
/// ```
|
||||
pub fn is_feet(&self) -> bool {
|
||||
self.0.as_ref().left().map(|l| l.is_feet()).unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Create a new unit measuring Inches
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// Unit::inches();
|
||||
/// ```
|
||||
pub fn inches() -> Self {
|
||||
Unit(Either::Left(Length::Inches))
|
||||
}
|
||||
|
||||
/// Check if the unit is Inches
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::inches().is_inches());
|
||||
/// ```
|
||||
pub fn is_inches(&self) -> bool {
|
||||
self.0
|
||||
.as_ref()
|
||||
.left()
|
||||
.map(|l| l.is_inches())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
/// Create a new custom unit
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// Unit::custom("Yards");
|
||||
/// ```
|
||||
pub fn custom<T>(string: T) -> Self
|
||||
where
|
||||
T: Into<String>,
|
||||
{
|
||||
Unit(Either::Right(string.into()))
|
||||
}
|
||||
|
||||
/// Check if a unit is custom
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::custom("Yards").is_custom());
|
||||
/// ```
|
||||
pub fn is_custom(&self) -> bool {
|
||||
self.as_custom().is_some()
|
||||
}
|
||||
|
||||
/// Fetch a custom unit
|
||||
///
|
||||
/// ```rust
|
||||
/// use activitystreams_new::primitives::Unit;
|
||||
///
|
||||
/// assert!(Unit::custom("Yards").as_custom() == Some("Yards"));
|
||||
/// ```
|
||||
pub fn as_custom(&self) -> Option<&str> {
|
||||
self.0.as_ref().right().map(|r| r.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Unit {
|
||||
fn default() -> Self {
|
||||
Self::meters()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Unit {
|
||||
type Err = std::convert::Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let unit = match s {
|
||||
"cm" => Self::centimeters(),
|
||||
"m" => Self::meters(),
|
||||
"km" => Self::kilometers(),
|
||||
"inches" => Self::inches(),
|
||||
"feet" => Self::feet(),
|
||||
other => Self::custom(other),
|
||||
};
|
||||
|
||||
Ok(unit)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Unit {
|
||||
fn from(s: String) -> Self {
|
||||
match s.parse() {
|
||||
Ok(u) => u,
|
||||
Err(e) => match e {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Unit {
|
||||
fn from(s: &str) -> Self {
|
||||
match s.parse() {
|
||||
Ok(u) => u,
|
||||
Err(e) => match e {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of units of length that represent valid units for certain ActivityStreams objects
|
||||
#[derive(
|
||||
Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
|
||||
)]
|
||||
#[serde(untagged)]
|
||||
enum Length {
|
||||
#[serde(rename = "cm")]
|
||||
Centimeters,
|
||||
|
||||
#[serde(rename = "feet")]
|
||||
Feet,
|
||||
|
||||
#[serde(rename = "inches")]
|
||||
Inches,
|
||||
|
||||
#[serde(rename = "km")]
|
||||
Kilometers,
|
||||
|
||||
#[serde(rename = "m")]
|
||||
Meters,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
#[error("Could not parse units")]
|
||||
/// The error type produced when a Length cannot be parsed
|
||||
struct LengthError;
|
||||
|
||||
impl Length {
|
||||
fn is_centimeters(&self) -> bool {
|
||||
match self {
|
||||
Length::Centimeters => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_feet(&self) -> bool {
|
||||
match self {
|
||||
Length::Feet => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_inches(&self) -> bool {
|
||||
match self {
|
||||
Length::Inches => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_kilometers(&self) -> bool {
|
||||
match self {
|
||||
Length::Kilometers => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_meters(&self) -> bool {
|
||||
match self {
|
||||
Length::Meters => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Length {
|
||||
fn default() -> Self {
|
||||
Length::Meters
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Length {
|
||||
type Err = LengthError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"cm" => Ok(Length::Centimeters),
|
||||
"feet" => Ok(Length::Feet),
|
||||
"inches" => Ok(Length::Inches),
|
||||
"km" => Ok(Length::Kilometers),
|
||||
"m" => Ok(Length::Meters),
|
||||
_ => Err(LengthError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<&str> for Length {
|
||||
type Error = LengthError;
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
s.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<&mut str> for Length {
|
||||
type Error = LengthError;
|
||||
|
||||
fn try_from(s: &mut str) -> Result<Self, Self::Error> {
|
||||
s.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<String> for Length {
|
||||
type Error = LengthError;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
s.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Length {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Length::Centimeters => write!(f, "cm"),
|
||||
Length::Feet => write!(f, "feet"),
|
||||
Length::Inches => write!(f, "inches"),
|
||||
Length::Kilometers => write!(f, "km"),
|
||||
Length::Meters => write!(f, "meters"),
|
||||
}
|
||||
}
|
||||
}
|
138
src/primitives/xsd_datetime.rs
Normal file
138
src/primitives/xsd_datetime.rs
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* This file is part of ActivityStreams.
|
||||
*
|
||||
* Copyright © 2020 Riley Trautman
|
||||
*
|
||||
* ActivityStreams is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ActivityStreams is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/// The type xsd:dateTime represents a specific date and time in the format
|
||||
/// CCYY-MM-DDThh:mm:ss.sss, which is a concatenation of the date and time forms, separated by a
|
||||
/// literal letter "T".
|
||||
///
|
||||
/// All of the same rules that apply to the date and time types are applicable
|
||||
/// to xsd:dateTime as well.
|
||||
///
|
||||
/// An optional time zone expression may be added at the end of the value. The letter Z is used to
|
||||
/// indicate Coordinated Universal Time (UTC). All other time zones are represented by their
|
||||
/// difference from Coordinated Universal Time in the format +hh:mm, or -hh:mm. These values may
|
||||
/// range from -14:00 to 14:00. For example, US Eastern Standard Time, which is five hours behind
|
||||
/// UTC, is represented as -05:00. If no time zone value is present, it is considered unknown; it
|
||||
/// is not assumed to be UTC.
|
||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct XsdDateTime(pub chrono::DateTime<chrono::FixedOffset>);
|
||||
|
||||
impl XsdDateTime {
|
||||
/// Create a XsdDateTime from a chrono::DateTime
|
||||
pub fn new(d: chrono::DateTime<chrono::FixedOffset>) -> Self {
|
||||
XsdDateTime(d)
|
||||
}
|
||||
|
||||
/// Extract the chrono::DateTime from XsdDateTime
|
||||
pub fn into_inner(self) -> chrono::DateTime<chrono::FixedOffset> {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Borrow the underlying `chrono::DateTime<chrono::FixedOffset>`
|
||||
pub fn as_datetime(&self) -> &chrono::DateTime<chrono::FixedOffset> {
|
||||
self.as_ref()
|
||||
}
|
||||
|
||||
/// Mutably borrow the underlying `chrono::DateTime<chrono::FixedOffset>`
|
||||
pub fn as_datetime_mut(&mut self) -> &mut chrono::DateTime<chrono::FixedOffset> {
|
||||
self.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<chrono::DateTime<chrono::FixedOffset>> for XsdDateTime {
|
||||
fn from(d: chrono::DateTime<chrono::FixedOffset>) -> Self {
|
||||
XsdDateTime(d)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<XsdDateTime> for chrono::DateTime<chrono::FixedOffset> {
|
||||
fn from(d: XsdDateTime) -> Self {
|
||||
d.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<chrono::DateTime<chrono::FixedOffset>> for XsdDateTime {
|
||||
fn as_ref(&self) -> &chrono::DateTime<chrono::FixedOffset> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<chrono::DateTime<chrono::FixedOffset>> for XsdDateTime {
|
||||
fn as_mut(&mut self) -> &mut chrono::DateTime<chrono::FixedOffset> {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<String> for XsdDateTime {
|
||||
type Error = chrono::format::ParseError;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
s.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<&str> for XsdDateTime {
|
||||
type Error = chrono::format::ParseError;
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
s.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<&mut str> for XsdDateTime {
|
||||
type Error = chrono::format::ParseError;
|
||||
|
||||
fn try_from(s: &mut str) -> Result<Self, Self::Error> {
|
||||
s.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for XsdDateTime {
|
||||
type Err = chrono::format::ParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(XsdDateTime(chrono::DateTime::parse_from_rfc3339(s)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for XsdDateTime {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let s = self.0.to_rfc3339();
|
||||
std::fmt::Display::fmt(&s, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::Serialize for XsdDateTime {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::ser::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::de::Deserialize<'de> for XsdDateTime {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::de::Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
s.parse().map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
240
src/primitives/xsd_duration.rs
Normal file
240
src/primitives/xsd_duration.rs
Normal file
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* This file is part of ActivityStreams.
|
||||
*
|
||||
* Copyright © 2020 Riley Trautman
|
||||
*
|
||||
* ActivityStreams is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ActivityStreams is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/// The type xsd:duration represents a duration of time expressed as a number of years, months,
|
||||
/// days, hours, minutes, and seconds.
|
||||
///
|
||||
/// The format of xsd:duration is PnYnMnDTnHnMnS, where P is a literal value that starts the
|
||||
/// expression, nY is the number of years followed by a literal Y, nM is the number of months
|
||||
/// followed by a literal M, nD is the number of days followed by a literal D, T is a literal value
|
||||
/// that separates the date and time, nH is the number of hours followed by a literal H, nM is the
|
||||
/// number of minutes followed by a literal M, and nS is the number of seconds followed by a
|
||||
/// literal S. The following rules apply to xsd:duration values:
|
||||
///
|
||||
/// - Any of these numbers and corresponding designators may be absent if they are equal to 0, but
|
||||
/// at least one number and designator must appear.
|
||||
/// - The numbers may be any unsigned integer, with the exception of the number of seconds, which
|
||||
/// may be an unsigned decimal number.
|
||||
/// - If a decimal point appears in the number of seconds, there must be at least one digit after
|
||||
/// the decimal point.
|
||||
/// - A minus sign may appear before the P to specify a negative duration.
|
||||
/// - If no time items (hour, minute, second) are present, the letter T must not appear.
|
||||
///
|
||||
/// ### Note
|
||||
///
|
||||
/// This implementation converts Months to Days by multiplying by 31, and converts Years to days by
|
||||
/// multiplying by 365. If this is an issue for your application, look into specifying days
|
||||
/// directly.
|
||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
pub struct XsdDuration(pub chrono::Duration);
|
||||
|
||||
/// The error type produced when an XsdDuration cannot be parsed
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
#[error("Error parsing Duration")]
|
||||
pub struct XsdDurationError;
|
||||
|
||||
impl XsdDuration {
|
||||
/// Create a new XsdDuration from a chrono::Duration
|
||||
pub fn new(duration: chrono::Duration) -> Self {
|
||||
XsdDuration(duration)
|
||||
}
|
||||
|
||||
/// Extract the chrono::Duration from an XsdDuration
|
||||
pub fn into_inner(self) -> chrono::Duration {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Borrow the underlying `chrono::Duration`
|
||||
pub fn as_duration(&self) -> &chrono::Duration {
|
||||
self.as_ref()
|
||||
}
|
||||
|
||||
/// Mutably borrow the underlying `chrono::Duration`
|
||||
pub fn as_duration_mut(&mut self) -> &mut chrono::Duration {
|
||||
self.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<chrono::Duration> for XsdDuration {
|
||||
fn from(d: chrono::Duration) -> Self {
|
||||
XsdDuration(d)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<XsdDuration> for chrono::Duration {
|
||||
fn from(d: XsdDuration) -> Self {
|
||||
d.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<chrono::Duration> for XsdDuration {
|
||||
fn as_ref(&self) -> &chrono::Duration {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMut<chrono::Duration> for XsdDuration {
|
||||
fn as_mut(&mut self) -> &mut chrono::Duration {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<String> for XsdDuration {
|
||||
type Error = XsdDurationError;
|
||||
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
s.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<&str> for XsdDuration {
|
||||
type Error = XsdDurationError;
|
||||
|
||||
fn try_from(s: &str) -> Result<Self, Self::Error> {
|
||||
s.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<&mut str> for XsdDuration {
|
||||
type Error = XsdDurationError;
|
||||
|
||||
fn try_from(s: &mut str) -> Result<Self, Self::Error> {
|
||||
s.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for XsdDuration {
|
||||
type Err = XsdDurationError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.find('P') != Some(0) {
|
||||
return Err(XsdDurationError);
|
||||
}
|
||||
|
||||
let s = s.trim_start_matches('P');
|
||||
|
||||
let negative = Some(0) == s.find('-');
|
||||
let s = s.trim_start_matches('-');
|
||||
|
||||
let (large, small) = if let Some(index) = s.find('T') {
|
||||
let (l, s) = s.split_at(index);
|
||||
(l, s.trim_start_matches('T'))
|
||||
} else {
|
||||
(s, "")
|
||||
};
|
||||
|
||||
let (years, large) = parse_next(large, 'Y')?;
|
||||
let (months, large) = parse_next(large, 'M')?;
|
||||
let (days, _) = parse_next(large, 'D')?;
|
||||
|
||||
let (hours, small) = parse_next(small, 'H')?;
|
||||
let (minutes, small) = parse_next(small, 'M')?;
|
||||
let (seconds, _) = parse_next(small, 'S')?;
|
||||
|
||||
let mut duration = chrono::Duration::days(365 * years);
|
||||
duration = duration + chrono::Duration::days(31 * months);
|
||||
duration = duration + chrono::Duration::days(days);
|
||||
duration = duration + chrono::Duration::hours(hours);
|
||||
duration = duration + chrono::Duration::minutes(minutes);
|
||||
duration = duration + chrono::Duration::seconds(seconds);
|
||||
|
||||
duration = if negative { duration * -1 } else { duration };
|
||||
|
||||
Ok(XsdDuration(duration))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_next(s: &str, c: char) -> Result<(i64, &str), XsdDurationError> {
|
||||
let res = if let Some(index) = s.find(c) {
|
||||
let (beginning, end) = s.split_at(index);
|
||||
let i = beginning.parse().map_err(|_| XsdDurationError)?;
|
||||
(i, end.trim_start_matches(c))
|
||||
} else {
|
||||
(0, s)
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
impl std::fmt::Display for XsdDuration {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let (s, mut duration) = if chrono::Duration::seconds(0) > self.0 {
|
||||
("P-".to_string(), self.0 * -1)
|
||||
} else {
|
||||
("P".to_string(), self.0)
|
||||
};
|
||||
|
||||
let s = if duration.num_days() > 0 {
|
||||
format!("{}{}D", s, duration.num_days())
|
||||
} else {
|
||||
s
|
||||
};
|
||||
|
||||
duration = duration - chrono::Duration::days(duration.num_days());
|
||||
|
||||
let s = if duration.num_seconds() > 0 {
|
||||
format!("{}T", s)
|
||||
} else {
|
||||
s
|
||||
};
|
||||
|
||||
let s = if duration.num_hours() > 0 {
|
||||
format!("{}{}H", s, duration.num_hours())
|
||||
} else {
|
||||
s
|
||||
};
|
||||
|
||||
duration = duration - chrono::Duration::hours(duration.num_hours());
|
||||
|
||||
let s = if duration.num_minutes() > 0 {
|
||||
format!("{}{}M", s, duration.num_minutes())
|
||||
} else {
|
||||
s
|
||||
};
|
||||
|
||||
duration = duration - chrono::Duration::minutes(duration.num_minutes());
|
||||
|
||||
let s = if duration.num_seconds() > 0 {
|
||||
format!("{}{}S", s, duration.num_seconds())
|
||||
} else {
|
||||
s
|
||||
};
|
||||
|
||||
std::fmt::Display::fmt(&s, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::Serialize for XsdDuration {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::ser::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> serde::de::Deserialize<'de> for XsdDuration {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::de::Deserializer<'de>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
s.parse().map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
|
@ -23,11 +23,12 @@
|
|||
//! prelude::*,
|
||||
//! primitives::*,
|
||||
//! unparsed::*,
|
||||
//! url::Url,
|
||||
//! };
|
||||
//!
|
||||
//! /// First, we'll define our public key types
|
||||
//!
|
||||
//! #[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||
//! #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
|
||||
//! #[serde(rename_all = "camelCase")]
|
||||
//! pub struct PublicKeyValues {
|
||||
//! pub id: XsdAnyUri,
|
||||
|
@ -167,30 +168,30 @@
|
|||
//! /// And now create helper methods
|
||||
//! pub trait PublicKeyExt<Inner>: AsPublicKey<Inner> {
|
||||
//! /// Borrow the public key's ID
|
||||
//! fn key_id<'a>(&'a self) -> &'a XsdAnyUri
|
||||
//! fn key_id<'a>(&'a self) -> &'a Url
|
||||
//! where
|
||||
//! Inner: 'a,
|
||||
//! {
|
||||
//! &self.public_key_ref().public_key.id
|
||||
//! &self.public_key_ref().public_key.id.as_ref()
|
||||
//! }
|
||||
//!
|
||||
//! /// Set the public key's ID
|
||||
//! fn set_key_id(&mut self, id: XsdAnyUri) -> &mut Self {
|
||||
//! self.public_key_mut().public_key.id = id;
|
||||
//! fn set_key_id(&mut self, id: Url) -> &mut Self {
|
||||
//! self.public_key_mut().public_key.id = id.into();
|
||||
//! self
|
||||
//! }
|
||||
//!
|
||||
//! /// Borrow the public key's Owner
|
||||
//! fn key_owner<'a>(&'a self) -> &'a XsdAnyUri
|
||||
//! fn key_owner<'a>(&'a self) -> &'a Url
|
||||
//! where
|
||||
//! Inner: 'a,
|
||||
//! {
|
||||
//! &self.public_key_ref().public_key.owner
|
||||
//! &self.public_key_ref().public_key.owner.as_ref()
|
||||
//! }
|
||||
//!
|
||||
//! /// Set the public key's Owner
|
||||
//! fn set_key_owner(&mut self, owner: XsdAnyUri) -> &mut Self {
|
||||
//! self.public_key_mut().public_key.owner = owner;
|
||||
//! fn set_key_owner(&mut self, owner: Url) -> &mut Self {
|
||||
//! self.public_key_mut().public_key.owner = owner.into();
|
||||
//! self
|
||||
//! }
|
||||
//!
|
||||
|
@ -220,24 +221,33 @@
|
|||
//!
|
||||
//!
|
||||
//! /// Now that eveything is implemented, we can use it like so:
|
||||
//! use activitystreams_new::actor::{kind::PersonType, Person};
|
||||
//! use activitystreams_new::{actor::{kind::PersonType, Person}, uri};
|
||||
//!
|
||||
//! pub type ExtendedPerson = PublicKey<ApActor<Person>>;
|
||||
//!
|
||||
//! impl ExtendedPerson {
|
||||
//! pub fn new(inbox: XsdAnyUri) -> Self {
|
||||
//! pub fn new(inbox: Url, mut owner: Url) -> Self {
|
||||
//! let id = owner.clone();
|
||||
//! owner.set_fragment(Some("main-key"));
|
||||
//! PublicKey {
|
||||
//! public_key: Default::default(),
|
||||
//! public_key: PublicKeyValues {
|
||||
//! id: id.into(),
|
||||
//! owner: owner.into(),
|
||||
//! public_key_pem: String::new(),
|
||||
//! },
|
||||
//! inner: ApActor::new(inbox, Person::new()),
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! fn main() -> Result<(), anyhow::Error> {
|
||||
//! let mut extended_person = ExtendedPerson::new("https://example.com/user/inbox".parse()?);
|
||||
//! let mut extended_person = ExtendedPerson::new(
|
||||
//! uri!("https://example.com/user/inbox"),
|
||||
//! uri!("https://example.com/user"),
|
||||
//! );
|
||||
//!
|
||||
//! extended_person
|
||||
//! .set_kind(PersonType)
|
||||
//! .set_kind(PersonType::Person)
|
||||
//! .set_id("https://example.com/user".parse()?)
|
||||
//! .set_name("Demo User")
|
||||
//! .set_preferred_username("user")
|
||||
|
|
Loading…
Reference in a new issue