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:
asonix 2020-06-19 22:03:43 -05:00
parent ce69a70a92
commit 33c262461d
23 changed files with 2995 additions and 1438 deletions

View file

@ -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"

View file

@ -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);

View file

@ -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()
}
}

File diff suppressed because it is too large Load diff

View file

@ -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)
}
}

View file

@ -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()
}
}

View file

@ -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
View file

@ -0,0 +1,3 @@
#[derive(Clone, Debug, thiserror::Error)]
#[error("URL did not match expected domain")]
pub struct DomainError;

View file

@ -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>()?
}};
}

View file

@ -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
View 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
View 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 {}

File diff suppressed because it is too large Load diff

View file

@ -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)
}
}

View 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
View 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>;

View 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)
}
}

View 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)
}
}

View 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
View 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"),
}
}
}

View 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)
}
}

View 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)
}
}

View file

@ -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")