From afafe24d510ea5d1741f033f98e3481fb1581f33 Mon Sep 17 00:00:00 2001 From: asonix Date: Sat, 25 Jul 2020 16:59:15 -0500 Subject: [PATCH] Copy in -new and -ext --- Cargo.toml | 43 +- activitystreams-derive/Cargo.toml | 22 - activitystreams-derive/README.md | 121 - activitystreams-derive/src/lib.rs | 1521 ----- .../.gitignore | 2 +- activitystreams-ext/Cargo.toml | 20 + activitystreams-ext/Dockerfile.arm64v8 | 6 + .../LICENSE | 0 activitystreams-ext/README.md | 104 + activitystreams-ext/build.sh | 54 + activitystreams-ext/examples/public_key.rs | 71 + activitystreams-ext/src/ext1.rs | 201 + activitystreams-ext/src/ext2.rs | 201 + activitystreams-ext/src/ext3.rs | 204 + activitystreams-ext/src/ext4.rs | 207 + activitystreams-ext/src/lib.rs | 375 ++ src/activity.rs | 3343 ++++++++++ src/activity/kind.rs | 134 - src/activity/mod.rs | 52 - src/activity/properties.rs | 624 -- src/activity/types.rs | 881 --- src/actor.rs | 1559 +++++ src/actor/kind.rs | 41 - src/actor/mod.rs | 54 - src/actor/properties.rs | 192 - src/actor/types.rs | 119 - src/base.rs | 1873 ++++++ src/collection.rs | 1338 ++++ src/collection/kind.rs | 37 - src/collection/mod.rs | 59 - src/collection/properties.rs | 226 - src/collection/types.rs | 142 - src/either.rs | 51 + src/endpoint.rs | 121 - src/error.rs | 3 + src/ext.rs | 384 -- src/lib.rs | 528 +- src/link.rs | 663 ++ src/link/kind.rs | 26 - src/link/mod.rs | 41 - src/link/properties.rs | 236 - src/link/types.rs | 40 - src/macros.rs | 85 + src/markers.rs | 89 + src/object.rs | 5540 +++++++++++++++++ src/object/kind.rs | 81 - src/object/mod.rs | 73 - src/object/properties.rs | 764 --- src/object/types.rs | 284 - src/primitives/any_string.rs | 345 + src/primitives/length.rs | 139 - src/primitives/mime_media_type.rs | 114 - src/primitives/mod.rs | 82 +- src/primitives/one_or_many.rs | 292 + src/primitives/rdf_lang_string.rs | 6 +- src/primitives/serde_parse.rs | 89 + src/primitives/unit.rs | 342 + src/primitives/xsd_any_uri.rs | 169 - src/primitives/xsd_datetime.rs | 29 +- src/primitives/xsd_duration.rs | 12 +- src/primitives/xsd_float.rs | 114 - src/primitives/xsd_non_negative_float.rs | 121 - src/primitives/xsd_non_negative_integer.rs | 111 - src/primitives/xsd_string.rs | 124 - src/unparsed.rs | 325 + 65 files changed, 17686 insertions(+), 7563 deletions(-) delete mode 100644 activitystreams-derive/Cargo.toml delete mode 100644 activitystreams-derive/README.md delete mode 100644 activitystreams-derive/src/lib.rs rename {activitystreams-derive => activitystreams-ext}/.gitignore (63%) create mode 100644 activitystreams-ext/Cargo.toml create mode 100644 activitystreams-ext/Dockerfile.arm64v8 rename {activitystreams-derive => activitystreams-ext}/LICENSE (100%) create mode 100644 activitystreams-ext/README.md create mode 100755 activitystreams-ext/build.sh create mode 100644 activitystreams-ext/examples/public_key.rs create mode 100644 activitystreams-ext/src/ext1.rs create mode 100644 activitystreams-ext/src/ext2.rs create mode 100644 activitystreams-ext/src/ext3.rs create mode 100644 activitystreams-ext/src/ext4.rs create mode 100644 activitystreams-ext/src/lib.rs create mode 100644 src/activity.rs delete mode 100644 src/activity/kind.rs delete mode 100644 src/activity/mod.rs delete mode 100644 src/activity/properties.rs delete mode 100644 src/activity/types.rs create mode 100644 src/actor.rs delete mode 100644 src/actor/kind.rs delete mode 100644 src/actor/mod.rs delete mode 100644 src/actor/properties.rs delete mode 100644 src/actor/types.rs create mode 100644 src/base.rs create mode 100644 src/collection.rs delete mode 100644 src/collection/kind.rs delete mode 100644 src/collection/mod.rs delete mode 100644 src/collection/properties.rs delete mode 100644 src/collection/types.rs create mode 100644 src/either.rs delete mode 100644 src/endpoint.rs create mode 100644 src/error.rs delete mode 100644 src/ext.rs create mode 100644 src/link.rs delete mode 100644 src/link/kind.rs delete mode 100644 src/link/mod.rs delete mode 100644 src/link/properties.rs delete mode 100644 src/link/types.rs create mode 100644 src/macros.rs create mode 100644 src/markers.rs create mode 100644 src/object.rs delete mode 100644 src/object/kind.rs delete mode 100644 src/object/mod.rs delete mode 100644 src/object/properties.rs delete mode 100644 src/object/types.rs create mode 100644 src/primitives/any_string.rs delete mode 100644 src/primitives/length.rs delete mode 100644 src/primitives/mime_media_type.rs create mode 100644 src/primitives/one_or_many.rs create mode 100644 src/primitives/serde_parse.rs create mode 100644 src/primitives/unit.rs delete mode 100644 src/primitives/xsd_any_uri.rs delete mode 100644 src/primitives/xsd_float.rs delete mode 100644 src/primitives/xsd_non_negative_float.rs delete mode 100644 src/primitives/xsd_non_negative_integer.rs delete mode 100644 src/primitives/xsd_string.rs create mode 100644 src/unparsed.rs diff --git a/Cargo.toml b/Cargo.toml index db1eea7..e80b760 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,39 +1,28 @@ [package] name = "activitystreams" -description = "Activity Streams in Rust" -version = "0.6.2" +version = "0.7.0-alpha.0" license = "GPL-3.0" authors = ["asonix "] -repository = "https://git.asonix.dog/Aardwolf/activitystreams" +repository = "https://git.asonix.dog/asonix/activitystreams-sketch" +documentation = "https://activitystreams-new.asonix.dog" readme = "README.md" keywords = ["activitystreams", "activitypub"] edition = "2018" -[features] -default = ["types"] -derive = ["activitystreams-derive", "serde", "serde_json"] -kinds = ["derive", "serde"] -primitives = ["chrono", "mime", "serde", "thiserror", "url"] -types = ["derive", "kinds", "primitives"] - -[dependencies] -activitystreams-derive = { version = "0.6.0", path = "activitystreams-derive", optional = true} -chrono = { version = "0.4", optional = true } -mime = { version = "0.3", optional = true } -serde = { version = "1.0", features = ["derive"], optional = true } -serde_json = { version = "1.0", optional = true } -thiserror = { version = "1.0", optional = true } -url = { version = "2.1", optional = true } - -[dev-dependencies] -anyhow = "1.0" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" - -[profile.dev.package.activitystreams-derive] -opt-level = 3 +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [workspace] members = [ - "activitystreams-derive", + "activitystreams-ext" ] + +[dependencies] +chrono = "0.4" +mime = "0.3" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +thiserror = "1.0" +url = { version = "2.1", features = ["serde"] } + +[dev-dependencies] +anyhow = "1.0" diff --git a/activitystreams-derive/Cargo.toml b/activitystreams-derive/Cargo.toml deleted file mode 100644 index b454e82..0000000 --- a/activitystreams-derive/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "activitystreams-derive" -description = "Derive macros for activitystreams" -version = "0.6.1" -license = "GPL-3.0" -authors = ["asonix "] -repository = "https://git.asonix.dog/Aardwolf/activitystreams" -readme = "README.md" -keywords = ["activitystreams", "activitypub"] -edition = "2018" - -[dependencies] -quote = "1.0" -syn = { version = "1.0", features = ["full"] } -proc-macro2 = "1.0" - -[dev-dependencies] -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" - -[lib] -proc-macro = true diff --git a/activitystreams-derive/README.md b/activitystreams-derive/README.md deleted file mode 100644 index 6382062..0000000 --- a/activitystreams-derive/README.md +++ /dev/null @@ -1,121 +0,0 @@ -# ActivityStreams Derive -__derive macros for ActivityStreams__ - -- [Read the documentation on docs.rs](https://docs.rs/activitystreams-derive) -- [Find the crate on crates.io](https://crates.io/crates/activitystreams-derive) -- [Hit me up on Mastodon](https://asonix.dog/@asonix) - -## Usage -Add the required crates to your `Cargo.toml` -```toml -# Cargo.toml - -activitystreams = "0.6.0" -serde = { version = "1.0", features = ["derive"] } -``` - -And then in your project -```rust -// derive macros -use activitystreams::{ - properties, - PropRefs, - UnitString -}; -// traits -use activitystreams::Object; -// properties -use activitystreams::object::properties::ObjectProperties; - -/// Using the UnitString derive macro -/// -/// This macro implements Serialize and Deserialize for the given type, making this type -/// represent the string "SomeKind" in JSON. -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(SomeKind)] -pub struct MyKind; - -properties! { - My { - docs [ - "Using the properties macro", - "", - "This macro generates getters and setters for the associated fields.", - ], - - kind { - docs [ - "Use the UnitString MyKind to enforce the type of the object by \"SomeKind\"", - "", - "Rename to/from 'type' when serializing/deserializing", - ], - - types [ - MyKind, - ], - functional, - required, - rename("type"), - }, - - required_key { - docs [ - "Derive getters and setters for required_key with String type.", - "", - "In the Activity Streams spec, 'functional' means there can only be one item for", - "this key. This means all fields not labeled 'functional' can also be", - "serialized/deserialized as Vec.", - "", - "'required' here means that the field must be present, otherwise, it's" - "represented as an Option", - ], - types [ - String, - ], - functional, - required, - }, - } -} - -#[derive(Clone, Default, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -pub struct My { - /// Derive AsRef and AsMut - #[serde(flatten)] - #[prop_refs] - my_properties: MyProperties, - - /// Derive AsRef and AsMut - /// - /// as well as the Object trait - #[serde(flatten)] - #[prop_refs] - properties: ObjectProperties, -} - -fn main() -> Result<(), Box> { - let mut my = My::default(); - - let mprops: &mut MyProperties = my.as_mut(); - mprops.set_required_key("Hello")?; - - let mprops: &MyProperties = my.as_ref(); - assert_eq!(mprops.get_required_key(), "Hello"); - Ok(()) -} -``` - -## Contributing -Feel free to open issues for anything you find an issue with. Please note that any contributed code will be licensed under the GPLv3. - -## License - -Copyright © 2020 Riley Trautman - -ActivityStreams Derive 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 Derive 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. This file is part of ActivityStreams Derive. - -You should have received a copy of the GNU General Public License along with ActivityStreams Derive. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). diff --git a/activitystreams-derive/src/lib.rs b/activitystreams-derive/src/lib.rs deleted file mode 100644 index 52cb6d5..0000000 --- a/activitystreams-derive/src/lib.rs +++ /dev/null @@ -1,1521 +0,0 @@ -/* - * This file is part of ActivityStreams Derive. - * - * Copyright © 2020 Riley Trautman - * - * ActivityStreams Derive 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 Derive 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 Derive. If not, see . - */ - -//! Derive macros for Activity Streams -//! -//! ## Examples -//! -//! First, add `serde` and `activitystreams-derive` to your Cargo.toml -//! ```toml -//! activitystreams-derive = "0.5.0" -//! # or activitystreams = "0.5.0" -//! serde = { version = "1.0", features = ["derive"] } -//! ``` -//! -//! ```rust -//! use activitystreams_derive::{properties, UnitString}; -//! // or activitystreams::{properties, UnitString}; -//! use serde_json::Value; -//! -//! /// Using the UnitString derive macro -//! /// -//! /// This macro implements Serialize and Deserialize for the given type, making this type -//! /// represent the string "SomeKind" in JSON. -//! #[derive(Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd, UnitString)] -//! #[unit_string(SomeKind)] -//! pub struct MyKind; -//! -//! /// Using the properties macro -//! /// -//! /// This macro generates getters and setters for the associated fields. -//! properties! { -//! My { -//! context { -//! types [ -//! String, -//! ], -//! rename("@context"), -//! }, -//! kind { -//! types [ -//! MyKind, -//! ], -//! functional, -//! required, -//! rename("type"), -//! }, -//! required_key { -//! types [ -//! Value, -//! ], -//! functional, -//! required, -//! alias [ -//! "someKey", -//! "existingKey", -//! "woooKey", -//! ], -//! }, -//! } -//! } -//! -//! fn main () -> Result<(), Box> { -//! let s = r#"{ -//! "@context": "http://www.w3c.org/ns#activitystreams", -//! "type": "SomeKind", -//! "woooKey": { -//! "key": "value" -//! } -//! }"#; -//! -//! let m: MyProperties = serde_json::from_str(s)?; -//! assert_eq!(&MyKind, m.get_kind()); -//! Ok(()) -//! } -//! ``` - -extern crate proc_macro; - -use proc_macro::TokenStream; -use proc_macro2::TokenTree; -use quote::{quote, ToTokens}; -use syn::{ - braced, bracketed, parenthesized, - parse::{Parse, ParseStream, Peek}, - parse_macro_input, - punctuated::Punctuated, - token, Attribute, Data, DeriveInput, Fields, Ident, LitStr, Result, Token, Type, -}; - -/// Generate a type with default extensions -/// -/// This derive -/// ```ignore -/// use activitystreams::{extensions::Ext, Extensible}; -/// -/// #[derive(Clone, Debug, Default, Extensible)] -/// #[extension(MyExtension)] -/// #[extension(MyOtherExtension)] -/// pub struct MyType; -/// ``` -/// -/// Produces this code -/// ```ignore -/// impl MyType { -/// pub fn full() -> Ext, OtherExtension> { -/// Default::default() -/// } -/// } -/// ``` -#[proc_macro_derive(Extensible, attributes(extension))] -pub fn extensible(input: TokenStream) -> TokenStream { - let input: DeriveInput = syn::parse(input).unwrap(); - - let name = input.ident; - - let kind: proc_macro2::TokenStream = input - .attrs - .iter() - .filter_map(move |attr| { - if attr - .path - .segments - .last() - .map(|segment| segment.ident == "extension") - .unwrap_or(false) - { - Some(from_value(attr.clone())) - } else { - None - } - }) - .fold(quote! {#name}, |acc, ident| { - quote! { Ext<#acc, #ident> } - }); - - let tokens = quote! { - impl #name { - /// Generate a fully extended type - /// - /// This effect can be achieved with `Self::new().extend(SomeExtension::default())` - pub fn full() -> #kind { - Default::default() - } - } - }; - - tokens.into() -} - -/// Derive implementations for activitystreams objects -/// -/// ```ignore -/// #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, PropRefs)] -/// #[prop_refs(Object)] -/// pub struct MyStruct { -/// /// Derive AsRef and AsMut delegating to `my_field` -/// #[prop_refs] -/// my_field: MyProperties, -/// -/// /// Derive the above, plus Object (activitystreams) -/// #[prop_refs] -/// obj_field: ObjectProperties, -/// } -/// ``` -#[proc_macro_derive(PropRefs, attributes(prop_refs))] -pub fn ref_derive(input: TokenStream) -> TokenStream { - let input: DeriveInput = syn::parse(input).unwrap(); - - let name = input.ident; - - let data = match input.data { - Data::Struct(s) => s, - _ => panic!("Can only derive for structs"), - }; - - let fields = match data.fields { - Fields::Named(fields) => fields, - _ => panic!("Can only derive for named fields"), - }; - - let name2 = name.clone(); - let base_impl = quote! { - impl Base for #name {} - - impl #name { - /// Create from default - pub fn new() -> Self { - Default::default() - } - } - - impl std::convert::TryFrom<#name> for BaseBox { - type Error = std::io::Error; - - fn try_from(s: #name) -> Result { - BaseBox::from_concrete(s) - } - } - }; - let trait_impls: proc_macro2::TokenStream = input - .attrs - .iter() - .filter_map(move |attr| { - if attr - .path - .segments - .last() - .map(|segment| segment.ident == "prop_refs") - .unwrap_or(false) - { - let object = from_value(attr.clone()); - let name = name2.clone(); - let box_name = Ident::new(&format!("{}Box", object), name.span()); - - Some(quote! { - impl #object for #name {} - - impl std::convert::TryFrom<#name> for #box_name { - type Error = std::io::Error; - - fn try_from(s: #name) -> Result { - #box_name::from_concrete(s) - } - } - }) - } else { - None - } - }) - .collect(); - - let tokens: proc_macro2::TokenStream = fields - .named - .iter() - .filter_map(|field| { - let our_attr = field.attrs.iter().find(|attribute| { - attribute - .path - .segments - .last() - .map(|segment| segment.ident == "prop_refs") - .unwrap_or(false) - }); - - our_attr.map(move |_| (field.ident.clone().unwrap(), field.ty.clone())) - }) - .map(move |(ident, ty)| { - let name = name.clone(); - quote! { - impl AsRef<#ty> for #name { - fn as_ref(&self) -> &#ty { - &self.#ident - } - } - - impl AsMut<#ty> for #name { - fn as_mut(&mut self) -> &mut #ty { - &mut self.#ident - } - } - } - }) - .collect(); - - let full = quote! { - #base_impl - #trait_impls - #tokens - }; - - full.into() -} - -/// Derive a wrapper type based on serde_json::Value to contain any possible trait type -/// -/// The following code -/// ```ignore -/// #[wrapper_type] -/// pub trait Object {} -/// ``` -/// produces the following type -/// ```ignore -/// pub struct ObjectBox(pub serde_json::Value); -/// -/// impl ObjectBox { -/// pub fn from_concrete(t: T) -> Result -/// where -/// T: Object + serde::ser::Serialize; -/// -/// pub fn into_concrete(self) -> Result -/// where -/// T: Object + serde::de::DeserializeOwned; -/// -/// pub fn is_type(&self, kind: impl std::fmt::Display) -> bool; -/// -/// pub fn type(&self) -> Option<&str>; -/// } -#[proc_macro_attribute] -pub fn wrapper_type(_: TokenStream, input: TokenStream) -> TokenStream { - let input: syn::ItemTrait = syn::parse(input).unwrap(); - let trait_name = input.ident.clone(); - let type_name = Ident::new(&format!("{}Box", trait_name), trait_name.span()); - - let doc_line = to_doc(&format!("A wrapper type around a generic `{}`", trait_name)); - let tokens = quote! { - #input - - #doc_line - #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] - #[serde(transparent)] - pub struct #type_name(serde_json::Map); - - impl #type_name { - /// Coerce a concrete type into this wrapper type - /// - /// This is done automatically via TryFrom in proprties setter methods - pub fn from_concrete(t: T) -> Result - where - T: #trait_name + serde::ser::Serialize, - { - match serde_json::to_value(t)? { - serde_json::Value::Object(map) => Ok(#type_name(map)), - _ => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, "Not an object")), - } - } - - /// Attempt to deserialize the wrapper type to a concrete type - /// - /// Before this method is called, the type should be verified via the `kind` or - /// `is_kind` methods - pub fn into_concrete(self) -> Result - where - T: #trait_name + serde::de::DeserializeOwned, - { - Ok(serde_json::from_value(serde_json::Value::Object(self.0))?) - } - - /// Return whether the given wrapper type is expected. - /// - /// For example - /// ```ignore - /// use activitystreams::object::{ - /// kind::ImageType, - /// apub::Image, - /// }; - /// if my_wrapper_type.is_kind(ImageType) { - /// let image = my_wrapper_type.into_concrete::()?; - /// ... - /// } - /// ``` - pub fn is_kind(&self, kind: impl std::fmt::Display) -> bool { - self.0["type"] == kind.to_string() - } - - /// Return the kind of wrapper type, if present - /// - /// Example - /// ```ignore - /// match my_wrapper_type.kind() { - /// Some("Image") => { - /// let image = my_wrapper_type.into_concrete::()?; - /// ... - /// } - /// _ => ..., - /// } - /// ``` - pub fn kind(&self) -> Option<&str> { - match self.0["type"] { - serde_json::Value::String(ref s) => Some(s), - _ => None, - } - } - } - }; - - tokens.into() -} - -/// Derive implementations Serialize and Deserialize for a constant string Struct type -/// -/// ```ignore -/// /// Derive Serialize and Deserialize such that MyStruct becomes the "MyType" string -/// #[derive(Clone, Debug, UnitString)] -/// #[unit_string(MyType)] -/// pub struct MyStruct; -/// -/// // usage -/// let _: HashMap = serde_json::from_str(r#"{"type":"MyType"}"#)?; -/// ``` -#[proc_macro_derive(UnitString, attributes(unit_string))] -pub fn unit_string(input: TokenStream) -> TokenStream { - let input: DeriveInput = syn::parse(input).unwrap(); - - let name = input.ident; - - let attr = input - .attrs - .iter() - .find(|attribute| { - attribute - .path - .segments - .last() - .map(|segment| segment.ident == "unit_string") - .unwrap_or(false) - }) - .unwrap() - .clone(); - - let visitor_name = from_value(attr); - let value = format!("{}", visitor_name); - - let serialize = quote! { - impl ::serde::ser::Serialize for #name { - fn serialize(&self, serializer: S) -> Result - where - S: ::serde::ser::Serializer, - { - serializer.serialize_str(#value) - } - } - }; - - let expecting = quote! { - fn expecting(&self, formatter: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(formatter, "The string '{}'", #value) - } - }; - - let visit = quote! { - fn visit_str(self, v: &str) -> Result - where - E: ::serde::de::Error, - { - if v == #value { - Ok(#name) - } else { - Err(::serde::de::Error::custom("Invalid type")) - } - } - }; - - let visitor = quote! { - struct #visitor_name; - - impl<'de> ::serde::de::Visitor<'de> for #visitor_name { - type Value = #name; - - #expecting - - #visit - } - }; - - let deserialize = quote! { - impl<'de> ::serde::de::Deserialize<'de> for #name { - fn deserialize(deserializer: D) -> Result<#name, D::Error> - where - D: ::serde::de::Deserializer<'de>, - { - deserializer.deserialize_str(#visitor_name) - } - } - }; - - let display = quote! { - impl std::fmt::Display for #name { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", #value) - } - } - }; - - let c = quote! { - #serialize - #visitor - #deserialize - #display - }; - - c.into() -} - -fn from_value(attr: Attribute) -> Ident { - let group = attr - .tokens - .into_iter() - .filter_map(|token_tree| match token_tree { - TokenTree::Group(group) => Some(group), - _ => None, - }) - .next() - .unwrap(); - - group - .stream() - .into_iter() - .filter_map(|token_tree| match token_tree { - TokenTree::Ident(ident) => Some(ident), - _ => None, - }) - .next() - .unwrap() -} - -fn to_doc(s: &str) -> proc_macro2::TokenStream { - format!("/// {}", s).parse().unwrap() -} - -fn many_docs(v: &[String]) -> proc_macro2::TokenStream { - v.iter() - .map(|d| { - let d = to_doc(d); - quote! { - #d - } - }) - .collect() -} - -/// Generate structs and enums for activitystreams objects -/// -/// ```rust -/// use activitystreams_derive::properties; -/// -/// #[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)] -/// pub struct MyStruct; -/// -/// properties! { -/// Hello { -/// docs [ "Defining the HelloProperties struct" ], -/// -/// field { -/// types [ String ], -/// }, -/// -/// other_field { -/// docs [ -/// "field documentation", -/// "is cool", -/// ], -/// types [ -/// String, -/// MyStruct, -/// ], -/// functional, -/// required, -/// rename("@other_field"), -/// alias [ -/// "@second_field", -/// "another_field", -/// ], -/// }, -/// } -/// } -/// -/// let _ = HelloProperties::default(); -/// ``` -#[proc_macro] -pub fn properties(tokens: TokenStream) -> TokenStream { - let Properties { name, docs, fields } = parse_macro_input!(tokens as Properties); - - let docs: proc_macro2::TokenStream = many_docs(&docs); - - let name = Ident::new(&format!("{}Properties", name), name.span()); - - let (fields, deps): (Vec<_>, Vec<_>) = fields.iter().filter_map(|field| { - if field.description.types.is_empty() { - return None; - } - - let fname = field.name.clone(); - let fdocs: proc_macro2::TokenStream = many_docs(&field.description.docs); - - let (ty, deps) = if field.description.types.len() == 1 { - let ty = Ident::new(&field.description.types.first().unwrap().to_token_stream().to_string(), fname.span()); - if field.description.functional { - (ty, None) - } else { - let enum_ty = Ident::new(&camelize(&format!("{}_{}_enum", name, fname)), fname.span()); - let doc_lines = many_docs(&[ - format!("Variations for the `{}` field from `{}", fname, name), - String::new(), - format!("`{}` isn't functional, meaning it can be represented as either a single `{}` or a vector of `{}`.", fname, ty, ty), - ]); - let deps = quote! { - #doc_lines - #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] - #[serde(rename_all = "camelCase")] - #[serde(untagged)] - pub enum #enum_ty { - Array(Vec<#ty>), - Term(#ty), - } - - impl Default for #enum_ty { - fn default() -> Self { - #enum_ty::Array(Vec::new()) - } - } - - impl From<#ty> for #enum_ty { - fn from(t: #ty) -> Self { - #enum_ty::Term(t) - } - } - - impl From> for #enum_ty { - fn from(v: Vec<#ty>) -> Self { - #enum_ty::Array(v) - } - } - }; - - (enum_ty, Some(deps)) - } - } else { - let ty = Ident::new(&camelize(&format!("{}_{}_enum", name, fname)), fname.span()); - - let v_tokens: proc_macro2::TokenStream = field - .description - .types - .iter() - .map(|v_ty| { - quote! { - #v_ty(#v_ty), - } - }) - .collect(); - - let first_type = field.description.types.iter().next().unwrap().clone(); - - let deps = if !field.description.functional { - let term_ty = Ident::new(&camelize(&format!("{}_{}_term_enum", name, fname)), fname.span()); - - let from_tokens: proc_macro2::TokenStream = field - .description - .types - .iter() - .map(|v_ty| { - quote! { - impl From<#v_ty> for #term_ty { - fn from(item: #v_ty) -> #term_ty { - #term_ty::#v_ty(item) - } - } - } - }) - .collect(); - - let term_doc_lines = many_docs(&[ - format!("Terminating variations for the `{}` field from `{}`", fname, name), - String::new(), - format!("Since {} can be one of multiple types, this enum represents all possibilities of {}", fname, fname), - ]); - let doc_lines = many_docs(&[ - format!("Non-Terminating variations for the `{}` field from `{}`", fname, name), - String::new(), - format!("`{}` isn't functional, meaning it can be represented as either a single `{}` or a vector of `{}`", fname, term_ty, term_ty), - ]); - quote! { - #term_doc_lines - #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] - #[serde(rename_all = "camelCase")] - #[serde(untagged)] - pub enum #term_ty { - #v_tokens - } - - #doc_lines - #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] - #[serde(rename_all = "camelCase")] - #[serde(untagged)] - pub enum #ty { - Array(Vec<#term_ty>), - Term(#term_ty), - } - - impl Default for #ty { - fn default() -> Self { - #ty::Array(Vec::new()) - } - } - - impl From<#term_ty> for #ty { - fn from(term: #term_ty) -> Self { - #ty::Term(term) - } - } - - impl From> for #ty { - fn from(v: Vec<#term_ty>) -> Self { - #ty::Array(v) - } - } - - #from_tokens - } - } else { - let from_tokens: proc_macro2::TokenStream = field - .description - .types - .iter() - .map(|v_ty| { - quote! { - impl From<#v_ty> for #ty { - fn from(item: #v_ty) -> #ty { - #ty::#v_ty(item) - } - } - } - }) - .collect(); - - let doc_lines = many_docs(&[ - format!("Variations for the `{}` field from `{}`", fname, name), - String::new(), - format!("`{}` isn't functional, meaning it can only be represented as a single `{}`", fname, ty), - String::new(), - format!("This enum's variants represent all valid types to construct a `{}`", fname), - ]); - quote! { - #doc_lines - #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] - #[serde(rename_all = "camelCase")] - #[serde(untagged)] - pub enum #ty { - #v_tokens - } - - impl Default for #ty { - fn default() -> Self { - #ty::#first_type(Default::default()) - } - } - - #from_tokens - } - }; - - (ty, Some(deps)) - }; - - let alias_tokens: proc_macro2::TokenStream = field.description.aliases.iter().map(|alias| quote!{ - #[serde(alias = #alias)] - }).collect(); - let rename_tokens: proc_macro2::TokenStream = field.description.rename.iter().map(|rename| quote!{ - #[serde(rename = #rename)] - }).collect(); - - let field_tokens = if field.description.required { - quote! { - pub #fname: #ty, - } - } else { - quote! { - #[serde(skip_serializing_if = "Option::is_none")] - pub #fname: Option<#ty>, - } - }; - - let field_tokens = quote!{ - #fdocs - #rename_tokens - #alias_tokens - #field_tokens - }; - - let fns = if field.description.types.len() == 1 { - let v_ty = field.description.types.first().unwrap().clone(); - - let set_ident = - Ident::new(&format!("set_{}", fname), fname.span()); - let get_ident = - Ident::new(&format!("get_{}", fname), fname.span()); - - let enum_ty = Ident::new(&camelize(&format!("{}_{}_enum", name, fname)), fname.span()); - - let set_many_ident = - Ident::new(&format!("set_many_{}", pluralize(fname.to_string())), fname.span()); - let get_many_ident = - Ident::new(&format!("get_many_{}", pluralize(fname.to_string())), fname.span()); - let add_ident = Ident::new(&format!("add_{}", fname.to_string()), fname.span()); - - if field.description.required { - if field.description.functional { - let doc_line = to_doc(&format!("Set `{}` with a type that can be cnoverted into a `{}`", fname, v_ty.to_token_stream())); - let set = quote! { - #doc_line - pub fn #set_ident(&mut self, item: T) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - use std::convert::TryInto; - self.#fname = item.try_into()?; - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Get the `{}` as `{}`", fname, v_ty.to_token_stream())); - let get = quote! { - #doc_line - pub fn #get_ident(&self) -> &#v_ty { - &self.#fname - } - }; - - quote!{ - #get - #set - } - } else { - let doc_line = to_doc(&format!("Set `{}` with a type that can be converted into a `{}`", fname, v_ty.to_token_stream())); - let set = quote! { - #doc_line - pub fn #set_ident(&mut self, item: T) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - use std::convert::TryInto; - self.#fname = #enum_ty::Term(item.try_into()?); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Get the `{}` as `{}`", fname, v_ty.to_token_stream())); - let get = quote! { - #doc_line - /// - /// This returns `None` when there is more than one item - pub fn #get_ident(&self) -> Option<&#v_ty> { - match self.#fname { - #enum_ty::Term(ref term) => Some(term), - _ => None, - } - } - }; - - let doc_line = to_doc(&format!("Set the `{}` with a vector of types that can be converted into `{}`s", fname, v_ty.to_token_stream())); - let set_many = quote! { - #doc_line - pub fn #set_many_ident(&mut self, item: Vec) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - let item: Vec<#v_ty> = item.into_iter().map(std::convert::TryInto::try_into).collect::, _>>()?; - self.#fname = #enum_ty::Array(item); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Add a type that can be converted into a `{}` to the `{}` vec", v_ty.to_token_stream(), fname)); - let add = quote! { - #doc_line - pub fn #add_ident(&mut self, item: T) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - let item = item.try_into()?; - - let new_vec = match self.#fname { - #enum_ty::Array(items) => { - let mut new_vec = Vec::new(); - new_vec.extend(&items) - new_vec.push(item); - new_vec - } - #enum_ty::Term(old_item) => { - let mut new_vec = Vec::new(); - new_vec.push(old_item.clone()); - new_vec.push(item); - new_vec - } - }; - - self.#fname = #enum_ty::Array(new_vec); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Get the `{}` as a slice of `{}`", fname, v_ty.to_token_stream())); - let get_many = quote! { - #doc_line - /// - /// This returns `None` if - /// - There is only one element - pub fn #get_many_ident(&self) -> Option<&[#v_ty]> { - match self.#fname { - #enum_ty::Array(ref array) => Some(array), - _ => None, - } - } - }; - - quote! { - #get - #set - #get_many - #set_many - #add - } - } - } else if field.description.functional { - let doc_line = to_doc(&format!("Set the `{}` with a type that can be converted into `{}`", fname, v_ty.to_token_stream())); - let set = quote! { - #doc_line - pub fn #set_ident(&mut self, item: T) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - use std::convert::TryInto; - self.#fname = Some(item.try_into()?); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Get `{}` as a `{}`", fname, v_ty.to_token_stream())); - let get = quote! { - #doc_line - /// - /// This returns `None` if there is no value present - pub fn #get_ident(&self) -> Option<&#v_ty> { - self.#fname.as_ref() - } - }; - - quote!{ - #get - #set - } - } else { - let doc_line = to_doc(&format!("Set the `{}` with a type that can be converted into `{}`", fname, v_ty.to_token_stream())); - let set = quote! { - #doc_line - pub fn #set_ident(&mut self, item: T) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - use std::convert::TryInto; - self.#fname = Some(#enum_ty::Term(item.try_into()?)); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Get `{}` as a `{}`", fname, v_ty.to_token_stream())); - let get = quote! { - #doc_line - /// - /// This returns `None` if - /// - There is no value present - /// - There is more than one value present - pub fn #get_ident(&self) -> Option<&#v_ty> { - match self.#fname { - Some(#enum_ty::Term(ref term)) => Some(term), - _ => None, - } - } - }; - - let doc_line = to_doc(&format!("Set the `{}` with a vector of types that can be converted into `{}`s", fname, v_ty.to_token_stream())); - let set_many = quote! { - #doc_line - pub fn #set_many_ident(&mut self, item: Vec) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - let item: Vec<#v_ty> = item.into_iter().map(std::convert::TryInto::try_into).collect::, _>>()?; - self.#fname = Some(#enum_ty::Array(item)); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Add a type that can be converted into a `{}` to the `{}` vec", v_ty.to_token_stream(), fname)); - let add = quote! { - #doc_line - pub fn #add_ident(&mut self, item: T) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - let item = item.try_into()?; - - let new_vec = match self.#fname.take() { - Some(#enum_ty::Array(mut items)) => { - items.push(item); - items - } - Some(#enum_ty::Term(old_item)) => { - let mut new_vec = Vec::new(); - new_vec.push(old_item.clone()); - new_vec.push(item); - new_vec - } - None => { - let mut new_vec = Vec::new(); - new_vec.push(item); - new_vec - } - }; - - self.#fname = Some(#enum_ty::Array(new_vec)); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Get `{}` as a slice of `{}`s", fname, v_ty.to_token_stream())); - let get_many = quote! { - #doc_line - /// - /// This returns `None` if - /// - There is no value present - /// - There is only one value present - pub fn #get_many_ident(&self) -> Option<&[#v_ty]> { - match self.#fname { - Some(#enum_ty::Array(ref a)) => Some(a), - _ => None, - } - } - }; - - quote! { - #get - #set - #get_many - #set_many - #add - } - } - } else if field.description.functional { - let tokens: proc_macro2::TokenStream = field - .description - .types - .iter() - .map(|v_ty| { - let set_ident = - Ident::new(&format!("set_{}_{}", fname, snakize(&v_ty.to_token_stream().to_string())), fname.span()); - let get_ident = - Ident::new(&format!("get_{}_{}", fname, snakize(&v_ty.to_token_stream().to_string())), fname.span()); - - if field.description.required { - let doc_line = to_doc(&format!("Set the `{}` with a type that can be converted into `{}`", fname, v_ty.to_token_stream())); - let set = quote! { - #doc_line - pub fn #set_ident(&mut self, item: T) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - use std::convert::TryInto; - let item: #v_ty = item.try_into()?; - self.#fname = item.into(); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Get `{}` as a slice of `{}`s", fname, v_ty.to_token_stream())); - let get = quote! { - #doc_line - /// - /// This returns `None` if - /// - The requested type is not the stored type - pub fn #get_ident(&self) -> Option<&#v_ty> { - match self.#fname { - #ty::#v_ty(ref term) => Some(term), - _ => None, - } - } - }; - - quote! { - #get - #set - } - } else { - let doc_line = to_doc(&format!("Set `{}` with a value that can be converted into `{}`", fname, v_ty.to_token_stream())); - let set = quote! { - #doc_line - pub fn #set_ident(&mut self, item: T) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - use std::convert::TryInto; - let item: #v_ty = item.try_into()?; - self.#fname = Some(item.into()); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Get `{}` as a `{}`", fname, v_ty.to_token_stream())); - let get = quote! { - #doc_line - /// - /// This returns `None` if - /// - There is no value present - /// - The requested type is not the stored type - pub fn #get_ident(&self) -> Option<&#v_ty> { - match self.#fname { - Some(#ty::#v_ty(ref term)) => Some(term), - _ => None, - } - } - }; - - quote! { - #get - #set - } - } - }) - .collect(); - - quote! { - #tokens - } - } else { - let term_ty = Ident::new(&camelize(&format!("{}_{}_term_enum", name, fname)), fname.span()); - let tokens: proc_macro2::TokenStream = field - .description - .types - .iter() - .map(|v_ty| { - let set_ident = - Ident::new(&format!("set_{}_{}", fname, snakize(&v_ty.to_token_stream().to_string())), fname.span()); - let get_ident = - Ident::new(&format!("get_{}_{}", fname, snakize(&v_ty.to_token_stream().to_string())), fname.span()); - - let set_many_ident = - Ident::new(&format!("set_many_{}_{}", fname, pluralize(snakize(&v_ty.to_token_stream().to_string()))), fname.span()); - let get_many_ident = - Ident::new(&format!("get_many_{}_{}", fname, pluralize(snakize(&v_ty.to_token_stream().to_string()))), fname.span()); - - if field.description.required { - let doc_line = to_doc(&format!("Set `{}` with a value that can be converted into `{}`", fname, v_ty.to_token_stream())); - let set = quote! { - #doc_line - pub fn #set_ident(&mut self, item: T) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - use std::convert::TryInto; - let item: #v_ty = item.try_into()?; - let item: #term_ty = item.into(); - self.#fname = item.into(); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Get the `{}` as a `{}`", fname, v_ty.to_token_stream())); - let get = quote! { - #doc_line - /// - /// This returns `None` if - /// - There is more than one value present - /// - The requested type is not the stored type - pub fn #get_ident(&self) -> Option<&#v_ty> { - match self.#fname { - #ty::Term(#term_ty::#v_ty(ref term)) => Some(term), - _ => None, - } - } - }; - - let doc_line = to_doc(&format!("Set `{}` from a vec of items that can be converted into `{}`s", fname, v_ty.to_token_stream())); - let set_many = quote! { - #doc_line - pub fn #set_many_ident(&mut self, item: Vec) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - let item: Vec<#v_ty> = item.into_iter().map(std::convert::TryInto::try_into).collect::, _>>()?; - let item: Vec<#term_ty> = item.into_iter().map(Into::into).collect(); - self.#fname = item.into(); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Get `{}` as a vec of `&{}`s", fname, v_ty.to_token_stream())); - let get_many = quote! { - #doc_line - /// - /// This returns `None` if - /// - There is only one value present - /// - /// The returned vec will be empty if no values match the requested - /// type, but values are present. - pub fn #get_many_ident(&self) -> Option> { - match self.#fname { - #ty::Array(ref array) => Some(array.iter().filter_map(|i| match i { - #term_ty::#v_ty(item) => Some(item), - _ => None, - })), - _ => None, - } - } - }; - - quote! { - #get - #set - #get_many - #set_many - } - } else { - let doc_line = to_doc(&format!("Set `{}` from a value that can be converted into `{}`", fname, v_ty.to_token_stream())); - let set = quote! { - #doc_line - pub fn #set_ident(&mut self, item: T) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - use std::convert::TryInto; - let item: #v_ty = item.try_into()?; - let item: #term_ty = item.into(); - self.#fname = Some(item.into()); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Get `{}` as a `{}`", fname, v_ty.to_token_stream())); - let get = quote! { - #doc_line - /// - /// This returns `None` if - /// - There is no value present - /// - There is more than one value present - /// - The requested type is not stored type - pub fn #get_ident(&self) -> Option<&#v_ty> { - match self.#fname { - Some(#ty::Term(#term_ty::#v_ty(ref term))) => Some(term), - _ => None, - } - } - }; - - let doc_line = to_doc(&format!("Set `{}` from a vec of items that can be converted into `{}`s", fname, v_ty.to_token_stream())); - let set_many = quote! { - #doc_line - pub fn #set_many_ident(&mut self, item: Vec) -> Result<&mut Self, >::Error> - where - T: std::convert::TryInto<#v_ty>, - { - let item: Vec<#v_ty> = item.into_iter().map(std::convert::TryInto::try_into).collect::, _>>()?; - let item: Vec<#term_ty> = item.into_iter().map(Into::into).collect(); - self.#fname = Some(item.into()); - Ok(self) - } - }; - - let doc_line = to_doc(&format!("Get `{}` as a slice of `{}`s", fname, term_ty.to_token_stream())); - let get_many = quote! { - #doc_line - /// - /// This returns `None` if - /// - There is no value present - /// - There is only one value present - pub fn #get_many_ident(&self) -> Option> { - match self.#fname { - Some(#ty::Array(ref array)) => Some(array.iter().filter_map(|i| match i { - #term_ty::#v_ty(item) => Some(item), - _ => None, - })), - _ => None, - } - } - }; - - quote! { - #get - #set - #get_many - #set_many - } - } - }) - .collect(); - - let delete = if !field.description.required { - let delete_ident = - Ident::new(&format!("delete_{}", fname), fname.span()); - - let doc_line = to_doc(&format!("Set the `{}` field to `None`", fname)); - quote! { - #doc_line - pub fn #delete_ident(&mut self) -> &mut Self { - self.#fname = None; - self - } - } - } else { - quote! {} - }; - - quote! { - #tokens - - #delete - } - }; - - Some(((field_tokens, fns), deps)) - }).unzip(); - - let (field_tokens, fn_tokens): (proc_macro2::TokenStream, proc_macro2::TokenStream) = - fields.into_iter().unzip(); - let deps_tokens: proc_macro2::TokenStream = deps.into_iter().filter_map(|d| d).collect(); - - let q = quote! { - #docs - #[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)] - #[serde(rename_all = "camelCase")] - pub struct #name { - #field_tokens - } - - impl #name { - #fn_tokens - } - - #deps_tokens - }; - q.into() -} - -mod kw { - syn::custom_keyword!(types); - syn::custom_keyword!(functional); - syn::custom_keyword!(required); - syn::custom_keyword!(rename); - syn::custom_keyword!(alias); - syn::custom_keyword!(docs); -} - -struct Properties { - name: Ident, - docs: Vec, - fields: Punctuated, -} - -struct Field { - name: Ident, - description: Description, -} - -struct Description { - docs: Vec, - types: Punctuated, - functional: bool, - required: bool, - rename: Option, - aliases: Vec, -} - -impl Parse for Properties { - fn parse(input: ParseStream) -> Result { - let name: Ident = input.parse()?; - - let content; - let _: token::Brace = braced!(content in input); - - let docs = parse_string_array::<_, kw::docs>(&&content, kw::docs)?; - - let fields = Punctuated::::parse_terminated(&content)?; - - Ok(Properties { name, docs, fields }) - } -} - -impl Parse for Field { - fn parse(input: ParseStream) -> Result { - let name: Ident = input.parse()?; - - let content; - let _: token::Brace = braced!(content in input); - - let description = content.parse()?; - - Ok(Field { name, description }) - } -} - -impl Parse for Description { - fn parse(input: ParseStream) -> Result { - let docs = parse_string_array::<_, kw::docs>(&input, kw::docs)?; - - let lookahead = input.lookahead1(); - if !lookahead.peek(kw::types) { - return Err(lookahead.error()); - } - input.parse::()?; - - let content; - let _: token::Bracket = bracketed!(content in input); - let types = Punctuated::::parse_terminated(&content)?; - optional_comma(&input)?; - - let functional = parse_kw::<_, kw::functional>(&input, kw::functional)?; - let required = parse_kw::<_, kw::required>(&input, kw::required)?; - let rename = parse_string_group::<_, kw::rename>(&input, kw::rename)?; - let aliases = parse_string_array::<_, kw::alias>(&input, kw::alias)?; - - Ok(Description { - docs, - types, - functional, - required, - rename, - aliases, - }) - } -} - -fn parse_kw(input: ParseStream, t: T) -> Result { - let lookahead = input.lookahead1(); - if lookahead.peek(t) { - input.parse::()?; - optional_comma(&input)?; - - return Ok(true); - } - - Ok(false) -} - -fn parse_string_array(input: ParseStream, t: T) -> Result> { - let lookahead = input.lookahead1(); - if lookahead.peek(t) { - input.parse::()?; - let content; - bracketed!(content in input); - - let docs = Punctuated::::parse_terminated(&content)?; - optional_comma(&input)?; - Ok(docs.into_iter().map(|d| d.value()).collect()) - } else { - Ok(vec![]) - } -} - -fn parse_string_group( - input: ParseStream, - t: T, -) -> Result> { - let lookahead = input.lookahead1(); - if lookahead.peek(t) { - input.parse::()?; - let content; - parenthesized!(content in input); - let s: LitStr = content.parse()?; - optional_comma(&input)?; - - return Ok(Some(s.value())); - } - - Ok(None) -} - -fn optional_comma(input: ParseStream) -> Result<()> { - let lookahead = input.lookahead1(); - if lookahead.peek(Token![,]) { - input.parse::()?; - } - Ok(()) -} - -fn camelize(s: &str) -> String { - let (s, _) = s - .chars() - .fold((String::new(), true), |(mut acc, should_upper), c| { - if c == '_' { - (acc, true) - } else { - if should_upper { - acc += &c.to_uppercase().to_string(); - } else { - acc += &c.to_string(); - } - - (acc, false) - } - }); - - s -} - -fn snakize(s: &str) -> String { - s.chars().fold(String::new(), |mut acc, c| { - if c.is_uppercase() && !acc.is_empty() { - acc += "_"; - acc += &c.to_lowercase().to_string(); - } else if c.is_uppercase() { - acc += &c.to_lowercase().to_string(); - } else { - acc += &c.to_string(); - } - acc - }) -} - -fn pluralize(s: String) -> String { - if s.ends_with('x') { - s + "es" - } else if s.ends_with('s') { - s - } else { - s + "s" - } -} diff --git a/activitystreams-derive/.gitignore b/activitystreams-ext/.gitignore similarity index 63% rename from activitystreams-derive/.gitignore rename to activitystreams-ext/.gitignore index 6936990..bb23f66 100644 --- a/activitystreams-derive/.gitignore +++ b/activitystreams-ext/.gitignore @@ -1,3 +1,3 @@ /target -**/*.rs.bk +/artifacts Cargo.lock diff --git a/activitystreams-ext/Cargo.toml b/activitystreams-ext/Cargo.toml new file mode 100644 index 0000000..e22bd2c --- /dev/null +++ b/activitystreams-ext/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "activitystreams-ext" +version = "0.1.0" +license = "GPL-3.0" +authors = ["asonix "] +repository = "https://git.asonix.dog/asonix/activitystreams-ext" +documentation = "https://activitystreams-ext.asonix.dog" +readme = "README.md" +keywords = ["activitystreams", "activitypub"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +activitystreams = { path = ".." } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + +[dev-dependencies] +anyhow = "1.0" diff --git a/activitystreams-ext/Dockerfile.arm64v8 b/activitystreams-ext/Dockerfile.arm64v8 new file mode 100644 index 0000000..71fb670 --- /dev/null +++ b/activitystreams-ext/Dockerfile.arm64v8 @@ -0,0 +1,6 @@ +FROM arm64v8/nginx:mainline-alpine + +COPY html/ /usr/share/nginx/html/ + +RUN chown -R nginx:nginx /usr/share/nginx/html + diff --git a/activitystreams-derive/LICENSE b/activitystreams-ext/LICENSE similarity index 100% rename from activitystreams-derive/LICENSE rename to activitystreams-ext/LICENSE diff --git a/activitystreams-ext/README.md b/activitystreams-ext/README.md new file mode 100644 index 0000000..c1b9c8e --- /dev/null +++ b/activitystreams-ext/README.md @@ -0,0 +1,104 @@ +# ActivityStreams Ext +_This crate provides Ext1, Ext2, Ext3, and Ext4 for adding extensions to ActivityStreams types_ + +- Find the code on [git.asonix.dog](https://git.asonix.dog/asonix/activitystreams-ext) +- Read the docs on [activitystreams-ext.asonix.dog](https://activitystreams-ext.asonix.dog) +- Join the matrix channel at [#activitypub:asonix.dog](https://matrix.to/#/!fAEcHyTUdAaKCzIKCt:asonix.dog?via=asonix.dog&via=matrix.org&via=t2bot.io) +- Hit me up on [mastodon](https://asonix.dog/@asonix) + +## Usage + +First, add ActivityStreams to your dependencies +```toml +[dependencies] +activitystreams-new = { git = "https://git.asonix.dog/asonix/activitystreams-sketch", branch = "main" } +activitystreams-ext = { git = "https://git.asonix.dog/asonix/activitystreams-ext", branch = "main" } +``` + +For an example, we'll implement a PublicKey extension and demonstrate usage with Ext1 +```rust +use activitystreams_ext::{Ext1, UnparsedExtension}; +use activitystreams_new::{ + actor::{ApActor, Person}, + context, + prelude::*, + primitives::XsdAnyUri, + security, + unparsed::UnparsedMutExt, +}; + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PublicKey { + public_key: PublicKeyInner, +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PublicKeyInner { + id: XsdAnyUri, + owner: XsdAnyUri, + public_key_pem: String, +} + +impl UnparsedExtension for PublicKey +where + U: UnparsedMutExt, +{ + type Error = serde_json::Error; + + fn try_from_unparsed(unparsed_mut: &mut U) -> Result { + Ok(PublicKey { + public_key: unparsed_mut.remove("publicKey")?, + }) + } + + fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> { + unparsed_mut.insert("publicKey", self.public_key)?; + Ok(()) + } +} + +pub type ExtendedPerson = Ext1, PublicKey>; + +fn main() -> Result<(), anyhow::Error> { + let actor = ApActor::new( + "http://in.box".parse()?, + "http://out.box".parse()?, + Person::new(), + ); + + let mut person = Ext1::new( + actor, + PublicKey { + public_key: PublicKeyInner { + id: "http://key.id".parse()?, + owner: "http://owner.id".parse()?, + public_key_pem: "asdfasdfasdf".to_owned(), + }, + }, + ); + + person.set_context(context()).add_context(security()); + + let any_base = person.into_any_base()?; + println!("any_base: {:#?}", any_base); + let person = ExtendedPerson::from_any_base(any_base)?; + + println!("person: {:#?}", person); + Ok(()) +} +``` + +## Contributing +Feel free to open issues for anything you find an issue with. Please note that any contributed code will be licensed under the GPLv3. + +## License + +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. This file is part of ActivityStreams. + +You should have received a copy of the GNU General Public License along with ActivityStreams. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). diff --git a/activitystreams-ext/build.sh b/activitystreams-ext/build.sh new file mode 100755 index 0000000..38b5b2a --- /dev/null +++ b/activitystreams-ext/build.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +BUILD_DATE=$(date) +VERSION=$1 +MIGRATIONS=$2 + +function require() { + if [ "$1" = "" ]; then + echo "input '$2' required" + print_help + exit 1 + fi +} + +function print_help() { + echo "build.sh" + echo "" + echo "Usage:" + echo " build.sh [version]" + echo "" + echo "Args:" + echo " version: The version of the current container" +} + +require "$VERSION" "version" + +if ! docker run --rm -it arm64v8/ubuntu:19.10 /bin/bash -c 'echo "docker is configured correctly"'; then + echo "docker is not configured to run on qemu-emulated architectures, fixing will require sudo" + sudo docker run --rm --privileged multiarch/qemu-user-static --reset -p yes +fi + +set -xe + +cargo clean +cargo doc --no-deps + +mkdir -p artifacts +rm -rf artifacts/html +cp -r ./target/doc artifacts/html + +docker build \ + --pull \ + --no-cache \ + --build-arg BUILD_DATE="${BUILD_DATE}" \ + --build-arg TAG="${TAG}" \ + -f Dockerfile.arm64v8 \ + -t "asonix/activitystreams-ext-docs:${VERSION}-arm64v8" \ + -t "asonix/activitystreams-ext-docs:latest-arm64v8" \ + -t "asonix/activitystreams-ext-docs:latest" \ + ./artifacts + +docker push "asonix/activitystreams-ext-docs:${VERSION}-arm64v8" +docker push "asonix/activitystreams-ext-docs:latest-arm64v8" +docker push "asonix/activitystreams-ext-docs:latest" diff --git a/activitystreams-ext/examples/public_key.rs b/activitystreams-ext/examples/public_key.rs new file mode 100644 index 0000000..8232aa1 --- /dev/null +++ b/activitystreams-ext/examples/public_key.rs @@ -0,0 +1,71 @@ +use activitystreams_ext::{Ext1, UnparsedExtension}; +use activitystreams::{ + actor::{ApActor, Person}, + context, + prelude::*, + primitives::XsdAnyUri, + security, + unparsed::UnparsedMutExt, +}; + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PublicKey { + public_key: PublicKeyInner, +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct PublicKeyInner { + id: XsdAnyUri, + owner: XsdAnyUri, + public_key_pem: String, +} + +impl UnparsedExtension for PublicKey +where + U: UnparsedMutExt, +{ + type Error = serde_json::Error; + + fn try_from_unparsed(unparsed_mut: &mut U) -> Result { + Ok(PublicKey { + public_key: unparsed_mut.remove("publicKey")?, + }) + } + + fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> { + unparsed_mut.insert("publicKey", self.public_key)?; + Ok(()) + } +} + +pub type ExtendedPerson = Ext1, PublicKey>; + +fn main() -> Result<(), anyhow::Error> { + let actor = ApActor::new( + "http://in.box".parse()?, + "http://out.box".parse()?, + Person::new(), + ); + + let mut person = Ext1::new( + actor, + PublicKey { + public_key: PublicKeyInner { + id: "http://key.id".parse()?, + owner: "http://owner.id".parse()?, + public_key_pem: "asdfasdfasdf".to_owned(), + }, + }, + ); + + person.set_context(context()).add_context(security()); + + let any_base = person.into_any_base()?; + println!("any_base: {:#?}", any_base); + let person = ExtendedPerson::from_any_base(any_base)?; + + println!("person: {:#?}", person); + Ok(()) +} diff --git a/activitystreams-ext/src/ext1.rs b/activitystreams-ext/src/ext1.rs new file mode 100644 index 0000000..c0d99df --- /dev/null +++ b/activitystreams-ext/src/ext1.rs @@ -0,0 +1,201 @@ +use crate::Ext1; +use activitystreams::{ + activity::{ + Activity, ActorAndObjectRef, AsActivity, AsQuestion, OptOriginRef, OptTargetRef, OriginRef, + Question, TargetRef, + }, + actor::{ApActor, AsApActor}, + base::{AnyBase, AsBase, Base}, + collection::{AsCollection, AsCollectionPage, Collection, CollectionPage}, + markers, + object::{ApObject, AsApObject, AsObject, Object}, + primitives::OneOrMany, +}; + +impl markers::Base for Ext1 where Inner: markers::Base {} +impl markers::Object for Ext1 where Inner: markers::Object {} +impl markers::Collection for Ext1 where Inner: markers::Collection {} +impl markers::CollectionPage for Ext1 where Inner: markers::CollectionPage {} +impl markers::Actor for Ext1 where Inner: markers::Actor {} +impl markers::Activity for Ext1 where Inner: markers::Activity {} +impl markers::IntransitiveActivity for Ext1 where + Inner: markers::IntransitiveActivity +{ +} + +impl AsBase for Ext1 +where + Inner: AsBase, +{ + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for Ext1 +where + Inner: AsObject, +{ + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsApObject for Ext1 +where + Inner: AsApObject, +{ + fn ap_object_ref(&self) -> &ApObject { + self.inner.ap_object_ref() + } + + fn ap_object_mut(&mut self) -> &mut ApObject { + self.inner.ap_object_mut() + } +} + +impl AsCollection for Ext1 +where + Inner: AsCollection, +{ + fn collection_ref(&self) -> &Collection { + self.inner.collection_ref() + } + + fn collection_mut(&mut self) -> &mut Collection { + self.inner.collection_mut() + } +} + +impl AsCollectionPage for Ext1 +where + Inner: AsCollectionPage, +{ + fn collection_page_ref(&self) -> &CollectionPage { + self.inner.collection_page_ref() + } + + fn collection_page_mut(&mut self) -> &mut CollectionPage { + self.inner.collection_page_mut() + } +} + +impl AsApActor for Ext1 +where + Inner: AsApActor, +{ + fn ap_actor_ref(&self) -> &ApActor { + self.inner.ap_actor_ref() + } + + fn ap_actor_mut(&mut self) -> &mut ApActor { + self.inner.ap_actor_mut() + } +} + +impl AsActivity for Ext1 +where + Inner: AsActivity, +{ + fn activity_ref(&self) -> &Activity { + self.inner.activity_ref() + } + + fn activity_mut(&mut self) -> &mut Activity { + self.inner.activity_mut() + } +} + +impl ActorAndObjectRef for Ext1 +where + Inner: ActorAndObjectRef, +{ + fn actor_field_ref(&self) -> &OneOrMany { + self.inner.actor_field_ref() + } + + fn actor_field_mut(&mut self) -> &mut OneOrMany { + self.inner.actor_field_mut() + } + + fn object_field_ref(&self) -> &OneOrMany { + self.inner.object_field_ref() + } + + fn object_field_mut(&mut self) -> &mut OneOrMany { + self.inner.object_field_mut() + } +} + +impl TargetRef for Ext1 +where + Inner: TargetRef, +{ + fn target_field_ref(&self) -> &OneOrMany { + self.inner.target_field_ref() + } + + fn target_field_mut(&mut self) -> &mut OneOrMany { + self.inner.target_field_mut() + } +} + +impl OriginRef for Ext1 +where + Inner: OriginRef, +{ + fn origin_field_ref(&self) -> &OneOrMany { + self.inner.origin_field_ref() + } + + fn origin_field_mut(&mut self) -> &mut OneOrMany { + self.inner.origin_field_mut() + } +} + +impl OptTargetRef for Ext1 +where + Inner: OptTargetRef, +{ + fn target_field_ref(&self) -> &Option> { + self.inner.target_field_ref() + } + + fn target_field_mut(&mut self) -> &mut Option> { + self.inner.target_field_mut() + } +} + +impl OptOriginRef for Ext1 +where + Inner: OptOriginRef, +{ + fn origin_field_ref(&self) -> &Option> { + self.inner.origin_field_ref() + } + + fn origin_field_mut(&mut self) -> &mut Option> { + self.inner.origin_field_mut() + } +} + +impl AsQuestion for Ext1 +where + Inner: AsQuestion, +{ + fn question_ref(&self) -> &Question { + self.inner.question_ref() + } + + fn question_mut(&mut self) -> &mut Question { + self.inner.question_mut() + } +} diff --git a/activitystreams-ext/src/ext2.rs b/activitystreams-ext/src/ext2.rs new file mode 100644 index 0000000..f76646c --- /dev/null +++ b/activitystreams-ext/src/ext2.rs @@ -0,0 +1,201 @@ +use crate::Ext2; +use activitystreams::{ + activity::{ + Activity, ActorAndObjectRef, AsActivity, AsQuestion, OptOriginRef, OptTargetRef, OriginRef, + Question, TargetRef, + }, + actor::{ApActor, AsApActor}, + base::{AnyBase, AsBase, Base}, + collection::{AsCollection, AsCollectionPage, Collection, CollectionPage}, + markers, + object::{ApObject, AsApObject, AsObject, Object}, + primitives::OneOrMany, +}; + +impl markers::Base for Ext2 where Inner: markers::Base {} +impl markers::Object for Ext2 where Inner: markers::Object {} +impl markers::Collection for Ext2 where Inner: markers::Collection {} +impl markers::CollectionPage for Ext2 where Inner: markers::CollectionPage {} +impl markers::Actor for Ext2 where Inner: markers::Actor {} +impl markers::Activity for Ext2 where Inner: markers::Activity {} +impl markers::IntransitiveActivity for Ext2 where + Inner: markers::IntransitiveActivity +{ +} + +impl AsBase for Ext2 +where + Inner: AsBase, +{ + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for Ext2 +where + Inner: AsObject, +{ + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsApObject for Ext2 +where + Inner: AsApObject, +{ + fn ap_object_ref(&self) -> &ApObject { + self.inner.ap_object_ref() + } + + fn ap_object_mut(&mut self) -> &mut ApObject { + self.inner.ap_object_mut() + } +} + +impl AsCollection for Ext2 +where + Inner: AsCollection, +{ + fn collection_ref(&self) -> &Collection { + self.inner.collection_ref() + } + + fn collection_mut(&mut self) -> &mut Collection { + self.inner.collection_mut() + } +} + +impl AsCollectionPage for Ext2 +where + Inner: AsCollectionPage, +{ + fn collection_page_ref(&self) -> &CollectionPage { + self.inner.collection_page_ref() + } + + fn collection_page_mut(&mut self) -> &mut CollectionPage { + self.inner.collection_page_mut() + } +} + +impl AsApActor for Ext2 +where + Inner: AsApActor, +{ + fn ap_actor_ref(&self) -> &ApActor { + self.inner.ap_actor_ref() + } + + fn ap_actor_mut(&mut self) -> &mut ApActor { + self.inner.ap_actor_mut() + } +} + +impl AsActivity for Ext2 +where + Inner: AsActivity, +{ + fn activity_ref(&self) -> &Activity { + self.inner.activity_ref() + } + + fn activity_mut(&mut self) -> &mut Activity { + self.inner.activity_mut() + } +} + +impl ActorAndObjectRef for Ext2 +where + Inner: ActorAndObjectRef, +{ + fn actor_field_ref(&self) -> &OneOrMany { + self.inner.actor_field_ref() + } + + fn actor_field_mut(&mut self) -> &mut OneOrMany { + self.inner.actor_field_mut() + } + + fn object_field_ref(&self) -> &OneOrMany { + self.inner.object_field_ref() + } + + fn object_field_mut(&mut self) -> &mut OneOrMany { + self.inner.object_field_mut() + } +} + +impl TargetRef for Ext2 +where + Inner: TargetRef, +{ + fn target_field_ref(&self) -> &OneOrMany { + self.inner.target_field_ref() + } + + fn target_field_mut(&mut self) -> &mut OneOrMany { + self.inner.target_field_mut() + } +} + +impl OriginRef for Ext2 +where + Inner: OriginRef, +{ + fn origin_field_ref(&self) -> &OneOrMany { + self.inner.origin_field_ref() + } + + fn origin_field_mut(&mut self) -> &mut OneOrMany { + self.inner.origin_field_mut() + } +} + +impl OptTargetRef for Ext2 +where + Inner: OptTargetRef, +{ + fn target_field_ref(&self) -> &Option> { + self.inner.target_field_ref() + } + + fn target_field_mut(&mut self) -> &mut Option> { + self.inner.target_field_mut() + } +} + +impl OptOriginRef for Ext2 +where + Inner: OptOriginRef, +{ + fn origin_field_ref(&self) -> &Option> { + self.inner.origin_field_ref() + } + + fn origin_field_mut(&mut self) -> &mut Option> { + self.inner.origin_field_mut() + } +} + +impl AsQuestion for Ext2 +where + Inner: AsQuestion, +{ + fn question_ref(&self) -> &Question { + self.inner.question_ref() + } + + fn question_mut(&mut self) -> &mut Question { + self.inner.question_mut() + } +} diff --git a/activitystreams-ext/src/ext3.rs b/activitystreams-ext/src/ext3.rs new file mode 100644 index 0000000..bad78eb --- /dev/null +++ b/activitystreams-ext/src/ext3.rs @@ -0,0 +1,204 @@ +use crate::Ext3; +use activitystreams::{ + activity::{ + Activity, ActorAndObjectRef, AsActivity, AsQuestion, OptOriginRef, OptTargetRef, OriginRef, + Question, TargetRef, + }, + actor::{ApActor, AsApActor}, + base::{AnyBase, AsBase, Base}, + collection::{AsCollection, AsCollectionPage, Collection, CollectionPage}, + markers, + object::{ApObject, AsApObject, AsObject, Object}, + primitives::OneOrMany, +}; + +impl markers::Base for Ext3 where Inner: markers::Base {} +impl markers::Object for Ext3 where Inner: markers::Object {} +impl markers::Collection for Ext3 where Inner: markers::Collection {} +impl markers::CollectionPage for Ext3 where + Inner: markers::CollectionPage +{ +} +impl markers::Actor for Ext3 where Inner: markers::Actor {} +impl markers::Activity for Ext3 where Inner: markers::Activity {} +impl markers::IntransitiveActivity for Ext3 where + Inner: markers::IntransitiveActivity +{ +} + +impl AsBase for Ext3 +where + Inner: AsBase, +{ + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for Ext3 +where + Inner: AsObject, +{ + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsApObject for Ext3 +where + Inner: AsApObject, +{ + fn ap_object_ref(&self) -> &ApObject { + self.inner.ap_object_ref() + } + + fn ap_object_mut(&mut self) -> &mut ApObject { + self.inner.ap_object_mut() + } +} + +impl AsCollection for Ext3 +where + Inner: AsCollection, +{ + fn collection_ref(&self) -> &Collection { + self.inner.collection_ref() + } + + fn collection_mut(&mut self) -> &mut Collection { + self.inner.collection_mut() + } +} + +impl AsCollectionPage for Ext3 +where + Inner: AsCollectionPage, +{ + fn collection_page_ref(&self) -> &CollectionPage { + self.inner.collection_page_ref() + } + + fn collection_page_mut(&mut self) -> &mut CollectionPage { + self.inner.collection_page_mut() + } +} + +impl AsApActor for Ext3 +where + Inner: AsApActor, +{ + fn ap_actor_ref(&self) -> &ApActor { + self.inner.ap_actor_ref() + } + + fn ap_actor_mut(&mut self) -> &mut ApActor { + self.inner.ap_actor_mut() + } +} + +impl AsActivity for Ext3 +where + Inner: AsActivity, +{ + fn activity_ref(&self) -> &Activity { + self.inner.activity_ref() + } + + fn activity_mut(&mut self) -> &mut Activity { + self.inner.activity_mut() + } +} + +impl ActorAndObjectRef for Ext3 +where + Inner: ActorAndObjectRef, +{ + fn actor_field_ref(&self) -> &OneOrMany { + self.inner.actor_field_ref() + } + + fn actor_field_mut(&mut self) -> &mut OneOrMany { + self.inner.actor_field_mut() + } + + fn object_field_ref(&self) -> &OneOrMany { + self.inner.object_field_ref() + } + + fn object_field_mut(&mut self) -> &mut OneOrMany { + self.inner.object_field_mut() + } +} + +impl TargetRef for Ext3 +where + Inner: TargetRef, +{ + fn target_field_ref(&self) -> &OneOrMany { + self.inner.target_field_ref() + } + + fn target_field_mut(&mut self) -> &mut OneOrMany { + self.inner.target_field_mut() + } +} + +impl OriginRef for Ext3 +where + Inner: OriginRef, +{ + fn origin_field_ref(&self) -> &OneOrMany { + self.inner.origin_field_ref() + } + + fn origin_field_mut(&mut self) -> &mut OneOrMany { + self.inner.origin_field_mut() + } +} + +impl OptTargetRef for Ext3 +where + Inner: OptTargetRef, +{ + fn target_field_ref(&self) -> &Option> { + self.inner.target_field_ref() + } + + fn target_field_mut(&mut self) -> &mut Option> { + self.inner.target_field_mut() + } +} + +impl OptOriginRef for Ext3 +where + Inner: OptOriginRef, +{ + fn origin_field_ref(&self) -> &Option> { + self.inner.origin_field_ref() + } + + fn origin_field_mut(&mut self) -> &mut Option> { + self.inner.origin_field_mut() + } +} + +impl AsQuestion for Ext3 +where + Inner: AsQuestion, +{ + fn question_ref(&self) -> &Question { + self.inner.question_ref() + } + + fn question_mut(&mut self) -> &mut Question { + self.inner.question_mut() + } +} diff --git a/activitystreams-ext/src/ext4.rs b/activitystreams-ext/src/ext4.rs new file mode 100644 index 0000000..a734471 --- /dev/null +++ b/activitystreams-ext/src/ext4.rs @@ -0,0 +1,207 @@ +use crate::Ext4; +use activitystreams::{ + activity::{ + Activity, ActorAndObjectRef, AsActivity, AsQuestion, OptOriginRef, OptTargetRef, OriginRef, + Question, TargetRef, + }, + actor::{ApActor, AsApActor}, + base::{AnyBase, AsBase, Base}, + collection::{AsCollection, AsCollectionPage, Collection, CollectionPage}, + markers, + object::{ApObject, AsApObject, AsObject, Object}, + primitives::OneOrMany, +}; + +impl markers::Base for Ext4 where Inner: markers::Base {} +impl markers::Object for Ext4 where Inner: markers::Object {} +impl markers::Collection for Ext4 where + Inner: markers::Collection +{ +} +impl markers::CollectionPage for Ext4 where + Inner: markers::CollectionPage +{ +} +impl markers::Actor for Ext4 where Inner: markers::Actor {} +impl markers::Activity for Ext4 where Inner: markers::Activity {} +impl markers::IntransitiveActivity for Ext4 where + Inner: markers::IntransitiveActivity +{ +} + +impl AsBase for Ext4 +where + Inner: AsBase, +{ + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for Ext4 +where + Inner: AsObject, +{ + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsApObject for Ext4 +where + Inner: AsApObject, +{ + fn ap_object_ref(&self) -> &ApObject { + self.inner.ap_object_ref() + } + + fn ap_object_mut(&mut self) -> &mut ApObject { + self.inner.ap_object_mut() + } +} + +impl AsCollection for Ext4 +where + Inner: AsCollection, +{ + fn collection_ref(&self) -> &Collection { + self.inner.collection_ref() + } + + fn collection_mut(&mut self) -> &mut Collection { + self.inner.collection_mut() + } +} + +impl AsCollectionPage for Ext4 +where + Inner: AsCollectionPage, +{ + fn collection_page_ref(&self) -> &CollectionPage { + self.inner.collection_page_ref() + } + + fn collection_page_mut(&mut self) -> &mut CollectionPage { + self.inner.collection_page_mut() + } +} + +impl AsApActor for Ext4 +where + Inner: AsApActor, +{ + fn ap_actor_ref(&self) -> &ApActor { + self.inner.ap_actor_ref() + } + + fn ap_actor_mut(&mut self) -> &mut ApActor { + self.inner.ap_actor_mut() + } +} + +impl AsActivity for Ext4 +where + Inner: AsActivity, +{ + fn activity_ref(&self) -> &Activity { + self.inner.activity_ref() + } + + fn activity_mut(&mut self) -> &mut Activity { + self.inner.activity_mut() + } +} + +impl ActorAndObjectRef for Ext4 +where + Inner: ActorAndObjectRef, +{ + fn actor_field_ref(&self) -> &OneOrMany { + self.inner.actor_field_ref() + } + + fn actor_field_mut(&mut self) -> &mut OneOrMany { + self.inner.actor_field_mut() + } + + fn object_field_ref(&self) -> &OneOrMany { + self.inner.object_field_ref() + } + + fn object_field_mut(&mut self) -> &mut OneOrMany { + self.inner.object_field_mut() + } +} + +impl TargetRef for Ext4 +where + Inner: TargetRef, +{ + fn target_field_ref(&self) -> &OneOrMany { + self.inner.target_field_ref() + } + + fn target_field_mut(&mut self) -> &mut OneOrMany { + self.inner.target_field_mut() + } +} + +impl OriginRef for Ext4 +where + Inner: OriginRef, +{ + fn origin_field_ref(&self) -> &OneOrMany { + self.inner.origin_field_ref() + } + + fn origin_field_mut(&mut self) -> &mut OneOrMany { + self.inner.origin_field_mut() + } +} + +impl OptTargetRef for Ext4 +where + Inner: OptTargetRef, +{ + fn target_field_ref(&self) -> &Option> { + self.inner.target_field_ref() + } + + fn target_field_mut(&mut self) -> &mut Option> { + self.inner.target_field_mut() + } +} + +impl OptOriginRef for Ext4 +where + Inner: OptOriginRef, +{ + fn origin_field_ref(&self) -> &Option> { + self.inner.origin_field_ref() + } + + fn origin_field_mut(&mut self) -> &mut Option> { + self.inner.origin_field_mut() + } +} + +impl AsQuestion for Ext4 +where + Inner: AsQuestion, +{ + fn question_ref(&self) -> &Question { + self.inner.question_ref() + } + + fn question_mut(&mut self) -> &mut Question { + self.inner.question_mut() + } +} diff --git a/activitystreams-ext/src/lib.rs b/activitystreams-ext/src/lib.rs new file mode 100644 index 0000000..126161e --- /dev/null +++ b/activitystreams-ext/src/lib.rs @@ -0,0 +1,375 @@ +//! # An extension API for activitystreams-new +//! _This crate provides Ext1, Ext2, Ext3, and Ext4 for adding extensions to ActivityStreams types_ +//! +//! - Find the code on [git.asonix.dog](https://git.asonix.dog/asonix/activitystreams-ext) +//! - Read the docs on [activitystreams-ext.asonix.dog](https://activitystreams-ext.asonix.dog) +//! - Join the matrix channel at [#activitypub:asonix.dog](https://matrix.to/#/!fAEcHyTUdAaKCzIKCt:asonix.dog?via=asonix.dog&via=matrix.org&via=t2bot.io) +//! - Hit me up on [mastodon](https://asonix.dog/@asonix) +//! +//! ## Usage +//! +//! First, add ActivityStreams to your dependencies +//! ```toml +//! [dependencies] +//! activitystreams-new = { git = "https://git.asonix.dog/asonix/activitystreams-sketch", branch = "main" } +//! activitystreams-ext = { git = "https://git.asonix.dog/asonix/activitystreams-ext", branch = "main" } +//! ``` +//! +//! For an example, we'll implement a PublicKey extension and demonstrate usage with Ext1 +//! ```rust +//! use activitystreams_ext::{Ext1, UnparsedExtension}; +//! use activitystreams::{ +//! actor::{ApActor, Person}, +//! context, +//! prelude::*, +//! primitives::XsdAnyUri, +//! security, +//! unparsed::UnparsedMutExt, +//! }; +//! +//! #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +//! #[serde(rename_all = "camelCase")] +//! pub struct PublicKey { +//! public_key: PublicKeyInner, +//! } +//! +//! #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +//! #[serde(rename_all = "camelCase")] +//! pub struct PublicKeyInner { +//! id: XsdAnyUri, +//! owner: XsdAnyUri, +//! public_key_pem: String, +//! } +//! +//! impl UnparsedExtension for PublicKey +//! where +//! U: UnparsedMutExt, +//! { +//! type Error = serde_json::Error; +//! +//! fn try_from_unparsed(unparsed_mut: &mut U) -> Result { +//! Ok(PublicKey { +//! public_key: unparsed_mut.remove("publicKey")?, +//! }) +//! } +//! +//! fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> { +//! unparsed_mut.insert("publicKey", self.public_key)?; +//! Ok(()) +//! } +//! } +//! +//! pub type ExtendedPerson = Ext1, PublicKey>; +//! +//! fn main() -> Result<(), anyhow::Error> { +//! let actor = ApActor::new( +//! "http://in.box".parse()?, +//! "http://out.box".parse()?, +//! Person::new(), +//! ); +//! +//! let mut person = Ext1::new( +//! actor, +//! PublicKey { +//! public_key: PublicKeyInner { +//! id: "http://key.id".parse()?, +//! owner: "http://owner.id".parse()?, +//! public_key_pem: "asdfasdfasdf".to_owned(), +//! }, +//! }, +//! ); +//! +//! person.set_context(context()).add_context(security()); +//! +//! let any_base = person.into_any_base()?; +//! println!("any_base: {:#?}", any_base); +//! let person = ExtendedPerson::from_any_base(any_base)?; +//! +//! println!("person: {:#?}", person); +//! Ok(()) +//! } +//! ``` + +#![doc(html_root_url = "https://activitystreams-ext.asonix.dog")] + +use activitystreams::{ + base::{Base, Extends}, + unparsed::{UnparsedMut, UnparsedMutExt}, +}; + +mod ext1; +mod ext2; +mod ext3; +mod ext4; + +/// Transform types from and into the Unparsed structure +pub trait UnparsedExtension +where + U: UnparsedMutExt, +{ + type Error: std::error::Error; + + /// Generate Self from Unparsed + fn try_from_unparsed(unparsed_mut: &mut U) -> Result + where + Self: Sized; + + /// Insert Self into Unparsed + fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error>; +} + +/// Extend a type with a single value +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct Ext1 { + #[serde(flatten)] + pub ext_one: A, + + /// The type being extended + #[serde(flatten)] + pub inner: Inner, +} + +/// Extend a type with two values +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct Ext2 { + #[serde(flatten)] + pub ext_one: A, + + #[serde(flatten)] + pub ext_two: B, + + /// The type being extended + #[serde(flatten)] + pub inner: Inner, +} + +/// Extend a type with three values +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct Ext3 { + #[serde(flatten)] + pub ext_one: A, + + #[serde(flatten)] + pub ext_two: B, + + #[serde(flatten)] + pub ext_three: C, + + /// The type being extended + #[serde(flatten)] + pub inner: Inner, +} + +/// Extend a type with four values +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +pub struct Ext4 { + #[serde(flatten)] + pub ext_one: A, + + #[serde(flatten)] + pub ext_two: B, + + #[serde(flatten)] + pub ext_three: C, + + #[serde(flatten)] + pub ext_four: D, + + /// The type being extended + #[serde(flatten)] + pub inner: Inner, +} + +impl Ext1 { + pub fn new(inner: Inner, ext_one: A) -> Self { + Ext1 { inner, ext_one } + } + + pub fn extend(self, ext_two: B) -> Ext2 { + Ext2 { + inner: self.inner, + ext_one: self.ext_one, + ext_two, + } + } +} + +impl Ext2 { + pub fn new(inner: Inner, ext_one: A, ext_two: B) -> Self { + Ext2 { + inner, + ext_one, + ext_two, + } + } + + pub fn extend(self, ext_three: C) -> Ext3 { + Ext3 { + inner: self.inner, + ext_one: self.ext_one, + ext_two: self.ext_two, + ext_three, + } + } +} + +impl Ext3 { + pub fn new(inner: Inner, ext_one: A, ext_two: B, ext_three: C) -> Self { + Ext3 { + inner, + ext_one, + ext_two, + ext_three, + } + } + + pub fn extend(self, ext_four: D) -> Ext4 { + Ext4 { + inner: self.inner, + ext_one: self.ext_one, + ext_two: self.ext_two, + ext_three: self.ext_three, + ext_four, + } + } +} + +impl Extends for Ext1 +where + Inner: Extends + UnparsedMut, + A: UnparsedExtension, + Error: From + std::error::Error, +{ + type Error = Error; + + fn extends(base: Base) -> Result { + let mut inner = Inner::extends(base)?; + let ext_one = A::try_from_unparsed(&mut inner)?; + + Ok(Ext1 { inner, ext_one }) + } + + fn retracts(self) -> Result, Self::Error> { + let Ext1 { mut inner, ext_one } = self; + + ext_one.try_into_unparsed(&mut inner)?; + inner.retracts() + } +} + +impl Extends for Ext2 +where + Inner: Extends + UnparsedMut, + A: UnparsedExtension, + B: UnparsedExtension, + Error: From + std::error::Error, +{ + type Error = Error; + + fn extends(base: Base) -> Result { + let mut inner = Inner::extends(base)?; + let ext_one = A::try_from_unparsed(&mut inner)?; + let ext_two = B::try_from_unparsed(&mut inner)?; + + Ok(Ext2 { + inner, + ext_one, + ext_two, + }) + } + + fn retracts(self) -> Result, Self::Error> { + let Ext2 { + mut inner, + ext_one, + ext_two, + } = self; + + ext_one.try_into_unparsed(&mut inner)?; + ext_two.try_into_unparsed(&mut inner)?; + inner.retracts() + } +} + +impl Extends for Ext3 +where + Inner: Extends + UnparsedMut, + A: UnparsedExtension, + B: UnparsedExtension, + C: UnparsedExtension, + Error: From + std::error::Error, +{ + type Error = Error; + + fn extends(base: Base) -> Result { + let mut inner = Inner::extends(base)?; + let ext_one = A::try_from_unparsed(&mut inner)?; + let ext_two = B::try_from_unparsed(&mut inner)?; + let ext_three = C::try_from_unparsed(&mut inner)?; + + Ok(Ext3 { + inner, + ext_one, + ext_two, + ext_three, + }) + } + + fn retracts(self) -> Result, Self::Error> { + let Ext3 { + mut inner, + ext_one, + ext_two, + ext_three, + } = self; + + ext_one.try_into_unparsed(&mut inner)?; + ext_two.try_into_unparsed(&mut inner)?; + ext_three.try_into_unparsed(&mut inner)?; + inner.retracts() + } +} + +impl Extends for Ext4 +where + Inner: Extends + UnparsedMut, + A: UnparsedExtension, + B: UnparsedExtension, + C: UnparsedExtension, + D: UnparsedExtension, + Error: From + std::error::Error, +{ + type Error = Error; + + fn extends(base: Base) -> Result { + let mut inner = Inner::extends(base)?; + let ext_one = A::try_from_unparsed(&mut inner)?; + let ext_two = B::try_from_unparsed(&mut inner)?; + let ext_three = C::try_from_unparsed(&mut inner)?; + let ext_four = D::try_from_unparsed(&mut inner)?; + + Ok(Ext4 { + inner, + ext_one, + ext_two, + ext_three, + ext_four, + }) + } + + fn retracts(self) -> Result, Self::Error> { + let Ext4 { + mut inner, + ext_one, + ext_two, + ext_three, + ext_four, + } = self; + + ext_one.try_into_unparsed(&mut inner)?; + ext_two.try_into_unparsed(&mut inner)?; + ext_three.try_into_unparsed(&mut inner)?; + ext_four.try_into_unparsed(&mut inner)?; + inner.retracts() + } +} diff --git a/src/activity.rs b/src/activity.rs new file mode 100644 index 0000000..f581624 --- /dev/null +++ b/src/activity.rs @@ -0,0 +1,3343 @@ +//! Types and traits for dealing with Activity attributes +//! +//! ```rust +//! # fn main() -> Result<(), anyhow::Error> { +//! use activitystreams::{ +//! activity::Create, +//! context, +//! prelude::*, +//! uri, +//! }; +//! +//! let mut create = Create::new( +//! uri!("https://example.com/actors/abcd"), +//! uri!("https://example.com/notes/1234"), +//! ); +//! +//! create +//! .set_result(uri!("https://example.com/")) +//! .set_instrument(uri!("https://example.com/")) +//! .set_id(uri!("https://example.com/activities/abcd")) +//! .set_context(context()); +//! # Ok(()) +//! # } +//! ``` +use crate::{ + base::{AnyBase, AsBase, Base, BaseExt, Extends}, + error::DomainError, + markers, + object::{ApObject, AsObject, Object}, + primitives::OneOrMany, + unparsed::{Unparsed, UnparsedMut, UnparsedMutExt}, +}; +use std::convert::TryFrom; +use url::Url; + +pub mod kind { + //! Kinds of activities defined by the spec + //! + //! These types exist only to be statically-typed versions of the associated string. e.g. + //! `CreateType` -> `"Create"` + + 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::*; + +/// Implementation trait for deriving Activity methods for a type +/// +/// Any type implementing AsObject will automatically gain methods provided by ActivityExt +pub trait AsActivity: markers::Activity { + /// Immutable borrow of `Activity` + fn activity_ref(&self) -> &Activity; + + /// Mutable borrow of `Activity` + fn activity_mut(&mut self) -> &mut Activity; +} + +/// Implementation trait for deriving Actor and Object methods for a type +/// +/// Any type implementing ActorAndObjectRef will automatically gain methods provided by +/// `ActorAndObjectRefExt` +pub trait ActorAndObjectRef: markers::Activity { + /// Immutable borrow of actor field + fn actor_field_ref(&self) -> &OneOrMany; + + /// Mutable borrow of actor field + fn object_field_ref(&self) -> &OneOrMany; + + /// Immutable borrow of object field + fn actor_field_mut(&mut self) -> &mut OneOrMany; + + /// Mutable borrow of object field + fn object_field_mut(&mut self) -> &mut OneOrMany; +} + +/// Implementation trait for deriving Target methods for a type +/// +/// Any type implementing TargetRef will automatically gain methods provided by `TargetRefExt` +pub trait TargetRef: markers::Activity { + /// Immutable borrow of target field + fn target_field_ref(&self) -> &OneOrMany; + + /// Mutable borrow of target field + fn target_field_mut(&mut self) -> &mut OneOrMany; +} + +/// Implementation trait for deriving Origin methods for a type +/// +/// Any type implementing OriginRef will automatically gain methods provided by +/// `OriginRefExt` +pub trait OriginRef: markers::Activity { + /// Immutable borrow of origin field + fn origin_field_ref(&self) -> &OneOrMany; + + /// Mutable borrow of origin field + fn origin_field_mut(&mut self) -> &mut OneOrMany; +} + +/// Implementation trait for deriving Target methods for a type +/// +/// Any type implementing OptTargetRef will automatically gain methods provided by +/// `OptTargetRefExt` +pub trait OptTargetRef: markers::Activity { + /// Immutable borrow of target field + fn target_field_ref(&self) -> &Option>; + + /// Mutable borrow of target field + fn target_field_mut(&mut self) -> &mut Option>; +} + +/// Implementation trait for deriving Origin methods for a type +/// +/// Any type implementing OptOriginRef will automatically gain methods provided by +/// `OptOriginRefExt` +pub trait OptOriginRef: markers::Activity { + /// Immutable borrow of origin field + fn origin_field_ref(&self) -> &Option>; + + /// Mutable borrow of origin field + fn origin_field_mut(&mut self) -> &mut Option>; +} + +/// Implementation trait for deriving Question methods for a type +/// +/// Any type implementing AsQuestion will automatically gain methods provided by +/// `QuestionExt` +pub trait AsQuestion: markers::Activity { + /// Immutable borrow of Question + fn question_ref(&self) -> &Question; + + /// Mutable borrow of Question + fn question_mut(&mut self) -> &mut Question; +} + +/// Helper methods for interacting with Activity types +/// +/// This trait represents methods valid for any ActivityStreams Activity +/// +/// Documentation for the fields related to these methods can be found on the `Activity` +/// struct +pub trait ActivityExt: AsActivity { + /// Fetch the result for the current activity + /// + /// ```rust + /// # use activitystreams::activity::Question; + /// # let mut question = Question::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(result) = question.result() { + /// println!("{:?}", result); + /// } + /// ``` + fn result<'a>(&'a self) -> Option<&'a OneOrMany> + where + Kind: 'a, + { + self.activity_ref().result.as_ref() + } + + /// Set the result for the current activity + /// + /// This overwrites the contents of result + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// + /// question.set_result(uri!("https://example.com")); + /// # Ok(()) + /// # } + /// ``` + fn set_result(&mut self, result: T) -> &mut Self + where + T: Into, + { + self.activity_mut().result = Some(result.into().into()); + self + } + + /// Set many results for the current activity + /// + /// This overwrites the contents of result + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// + /// question.set_many_results(vec![ + /// uri!("https://example.com/one"), + /// uri!("https://example.com/two"), + /// ]); + /// # Ok(()) + /// # } + /// ``` + fn set_many_results(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + self.activity_mut().result = Some(v.into()); + self + } + + /// Add a result to the current activity + /// + /// This does not overwrite the contents of result, only appends an item + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::{prelude::*, uri}; + /// # use activitystreams::{activity::Question}; + /// # let mut question = Question::new(); + /// + /// question + /// .add_result(uri!("https://example.com/one")) + /// .add_result(uri!("https://example.com/two")); + /// # Ok(()) + /// # } + /// ``` + fn add_result(&mut self, result: T) -> &mut Self + where + T: Into, + { + let c = match self.activity_mut().result.take() { + Some(mut c) => { + c.add(result.into()); + c + } + None => vec![result.into()].into(), + }; + self.activity_mut().result = Some(c); + self + } + + /// Take the result from the current activity, leaving nothing + /// + /// ```rust + /// # use activitystreams::activity::Question; + /// # let mut question = Question::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(result) = question.take_result() { + /// println!("{:?}", result); + /// } + /// ``` + fn take_result(&mut self) -> Option> { + self.activity_mut().result.take() + } + + /// Delete the result from the current activity + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// # question.set_result(uri!("https://example.com")); + /// # + /// use activitystreams::prelude::*; + /// + /// assert!(question.result().is_some()); + /// question.delete_result(); + /// assert!(question.result().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_result(&mut self) -> &mut Self { + self.activity_mut().result = None; + self + } + + /// Fetch the instrument for the current activity + /// + /// ```rust + /// # use activitystreams::activity::Question; + /// # let mut question = Question::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(instrument) = question.instrument() { + /// println!("{:?}", instrument); + /// } + /// ``` + fn instrument<'a>(&'a self) -> Option<&'a OneOrMany> + where + Kind: 'a, + { + self.activity_ref().instrument.as_ref() + } + + /// Set the instrument for the current activity + /// + /// This overwrites the contents of instrument + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// + /// question.set_instrument(uri!("https://example.com")); + /// # Ok(()) + /// # } + /// ``` + fn set_instrument(&mut self, instrument: T) -> &mut Self + where + T: Into, + { + self.activity_mut().instrument = Some(instrument.into().into()); + self + } + + /// Set many instruments for the current activity + /// + /// This overwrites the contents of instrument + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// + /// question.set_many_instruments(vec![ + /// uri!("https://example.com/one"), + /// uri!("https://example.com/two"), + /// ]); + /// # Ok(()) + /// # } + /// ``` + fn set_many_instruments(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + self.activity_mut().instrument = Some(v.into()); + self + } + + /// Add a instrument to the current activity + /// + /// This does not overwrite the contents of instrument, only appends an item + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// + /// question + /// .add_instrument(uri!("https://example.com/one")) + /// .add_instrument(uri!("https://example.com/two")); + /// # Ok(()) + /// # } + /// ``` + fn add_instrument(&mut self, instrument: T) -> &mut Self + where + T: Into, + { + let c = match self.activity_mut().instrument.take() { + Some(mut c) => { + c.add(instrument.into()); + c + } + None => vec![instrument.into()].into(), + }; + self.activity_mut().instrument = Some(c); + self + } + + /// Take the instrument from the current activity, leaving nothing + /// + /// ```rust + /// # use activitystreams::activity::Question; + /// # let mut question = Question::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(instrument) = question.take_instrument() { + /// println!("{:?}", instrument); + /// } + /// ``` + fn take_instrument(&mut self) -> Option> { + self.activity_mut().instrument.take() + } + + /// Delete the instrument from the current activity + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// # question.set_instrument(uri!("https://example.com")); + /// # + /// use activitystreams::prelude::*; + /// + /// assert!(question.instrument().is_some()); + /// question.delete_instrument(); + /// assert!(question.instrument().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_instrument(&mut self) -> &mut Self { + self.activity_mut().instrument = None; + self + } +} + +/// Helper methods for interacting with Activity types with actor and object fields +/// +/// Documentation for the fields related to these methods can be found on the +/// `ActorAndObject` struct +pub trait ActorAndObjectRefExt: ActorAndObjectRef { + /// Fetch the actor for the current activity, erroring if the actor's domain does not match the + /// ID's domain + /// + /// ```rust + /// # use activitystreams::{context, activity::Create}; + /// # let mut create = Create::new(context(), context()); + /// # + /// use activitystreams::prelude::*; + /// + /// let actor_ref = create.actor(); + /// println!("{:?}", actor_ref); + /// ``` + fn actor<'a, Kind>(&'a self) -> Result<&OneOrMany, DomainError> + where + Self: BaseExt, + Kind: 'a, + { + let unchecked = self.actor_unchecked(); + + if unchecked.as_single_id().and_then(|id| id.domain()) + != self.id_unchecked().and_then(|id| id.domain()) + { + return Err(DomainError); + } + + Ok(unchecked) + } + + /// Fetch the actor for the current activity + /// + /// ```rust + /// # use activitystreams::{context, activity::Create}; + /// # let mut create = Create::new(context(), context()); + /// # + /// use activitystreams::prelude::*; + /// + /// let actor_ref = create.actor_unchecked(); + /// println!("{:?}", actor_ref); + /// ``` + fn actor_unchecked(&self) -> &OneOrMany { + self.actor_field_ref() + } + + /// Check if the actor's ID is `id` + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{context, activity::Create, uri}; + /// # let mut create = Create::new(context(), context()); + /// use activitystreams::prelude::*; + /// + /// create.set_actor(uri!("https://example.com")); + /// + /// assert!(create.actor_is(&uri!("https://example.com"))); + /// # Ok(()) + /// # } + /// ``` + fn actor_is(&self, id: &Url) -> bool { + self.actor_unchecked().is_single_id(id) + } + + /// Set the actor for the current activity + /// + /// This overwrites the contents of actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Create, uri}; + /// # let mut create = Create::new(context(), context()); + /// + /// create.set_actor(uri!("https://example.com")); + /// # Ok(()) + /// # } + /// ``` + fn set_actor(&mut self, actor: T) -> &mut Self + where + T: Into, + { + *self.actor_field_mut() = actor.into().into(); + self + } + + /// Set many actors for the current activity + /// + /// This overwrites the contents of actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Create, uri}; + /// # let mut create = Create::new(context(), context()); + /// + /// create.set_many_actors(vec![ + /// uri!("https://example.com/one"), + /// uri!("https://example.com/two"), + /// ]); + /// # Ok(()) + /// # } + /// ``` + fn set_many_actors(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + *self.actor_field_mut() = v.into(); + self + } + + /// Add a actor to the current activity + /// + /// This does not overwrite the contents of actor, only appends an item + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Create, uri}; + /// # let mut create = Create::new(context(), context()); + /// + /// create + /// .add_actor(uri!("https://example.com/one")) + /// .add_actor(uri!("https://example.com/two")); + /// # Ok(()) + /// # } + /// ``` + fn add_actor(&mut self, actor: T) -> &mut Self + where + T: Into, + { + self.actor_field_mut().add(actor.into()); + self + } + + /// Fetch the object for the current activity + /// + /// ```rust + /// # use activitystreams::{context, activity::Create}; + /// # let mut create = Create::new(context(), context()); + /// # + /// use activitystreams::prelude::*; + /// + /// let object_ref = create.object(); + /// println!("{:?}", object_ref); + /// ``` + fn object(&self) -> &OneOrMany { + self.object_field_ref() + } + + /// Check if the object's ID is `id` + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{context, activity::Create, uri}; + /// # let mut create = Create::new(context(), context()); + /// use activitystreams::prelude::*; + /// + /// create.set_object(uri!("https://example.com")); + /// + /// assert!(create.object_is(&uri!("https://example.com"))); + /// # Ok(()) + /// # } + /// ``` + fn object_is(&self, id: &Url) -> bool { + self.object().is_single_id(id) + } + + /// Set the object for the current activity + /// + /// This overwrites the contents of object + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Create, uri}; + /// # let mut create = Create::new(context(), context()); + /// + /// create.set_object(uri!("https://example.com")); + /// # Ok(()) + /// # } + /// ``` + fn set_object(&mut self, object: T) -> &mut Self + where + T: Into, + { + *self.object_field_mut() = object.into().into(); + self + } + + /// Set many objects for the current activity + /// + /// This overwrites the contents of object + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Create, uri}; + /// # let mut create = Create::new(context(), context()); + /// + /// create.set_many_objects(vec![ + /// uri!("https://example.com/one"), + /// uri!("https://example.com/two"), + /// ]); + /// # Ok(()) + /// # } + /// ``` + fn set_many_objects(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + *self.object_field_mut() = v.into(); + self + } + + /// Add a object to the current activity + /// + /// This does not overwrite the contents of object, only appends an item + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Create, uri}; + /// # let mut create = Create::new(context(), context()); + /// + /// create + /// .add_object(uri!("https://example.com/one")) + /// .add_object(uri!("https://example.com/two")); + /// # Ok(()) + /// # } + /// ``` + fn add_object(&mut self, object: T) -> &mut Self + where + T: Into, + { + self.object_field_mut().add(object.into()); + self + } +} + +/// Helper methods for interacting with Activity types with a target field +/// +/// Documentation for the target field can be found on the `Invite` struct +pub trait TargetRefExt: TargetRef { + /// Fetch the target for the current activity + /// + /// ```rust + /// # use activitystreams::{context, activity::Invite}; + /// # let mut invite = Invite::new(context(), context(), context()); + /// # + /// use activitystreams::prelude::*; + /// + /// let target_ref = invite.target(); + /// println!("{:?}", target_ref); + /// ``` + fn target(&self) -> &OneOrMany { + self.target_field_ref() + } + + /// Set the target for the current activity + /// + /// This overwrites the contents of target + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Invite, uri}; + /// # let mut invite = Invite::new(context(), context(), context()); + /// + /// invite.set_target(uri!("https://example.com")); + /// # Ok(()) + /// # } + /// ``` + fn set_target(&mut self, target: T) -> &mut Self + where + T: Into, + { + *self.target_field_mut() = target.into().into(); + self + } + + /// Set many targets for the current activity + /// + /// This overwrites the contents of target + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Invite, uri}; + /// # let mut invite = Invite::new(context(), context(), context()); + /// + /// invite.set_many_targets(vec![ + /// uri!("https://example.com/one"), + /// uri!("https://example.com/two"), + /// ]); + /// # Ok(()) + /// # } + /// ``` + fn set_many_targets(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + *self.target_field_mut() = v.into(); + self + } + + /// Add a target to the current activity + /// + /// This does not overwrite the contents of target, only appends an item + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Invite, uri}; + /// # let mut invite = Invite::new(context(), context(), context()); + /// + /// invite + /// .add_target(uri!("https://example.com/one")) + /// .add_target(uri!("https://example.com/two")); + /// # Ok(()) + /// # } + /// ``` + fn add_target(&mut self, target: T) -> &mut Self + where + T: Into, + { + self.target_field_mut().add(target.into()); + self + } +} + +/// Helper methods for interacting with Activity types with an origin +/// +/// Documentation for the origin field can be found on the `Arrive` struct +pub trait OriginRefExt: OriginRef { + /// Fetch the origin for the current activity + /// + /// ```rust + /// # use activitystreams::{context, activity::Arrive}; + /// # let mut arrive = Arrive::new(context(), context()); + /// # + /// use activitystreams::prelude::*; + /// + /// let origin_ref = arrive.origin(); + /// println!("{:?}", origin_ref); + /// ``` + fn origin(&self) -> &OneOrMany { + self.origin_field_ref() + } + + /// Set the origin for the current activity + /// + /// This overwrites the contents of origin + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Arrive, uri}; + /// # let mut arrive = Arrive::new(context(), context()); + /// + /// arrive.set_origin(uri!("https://example.com")); + /// # Ok(()) + /// # } + /// ``` + fn set_origin(&mut self, origin: T) -> &mut Self + where + T: Into, + { + *self.origin_field_mut() = origin.into().into(); + self + } + + /// Set many origins for the current activity + /// + /// This overwrites the contents of origin + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Arrive, uri}; + /// # let mut arrive = Arrive::new(context(), context()); + /// + /// arrive.set_many_origins(vec![ + /// uri!("https://example.com/one"), + /// uri!("https://example.com/two"), + /// ]); + /// # Ok(()) + /// # } + /// ``` + fn set_many_origins(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + *self.origin_field_mut() = v.into(); + self + } + + /// Add a origin to the current activity + /// + /// This does not overwrite the contents of origin, only appends an item + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Arrive, uri}; + /// # let mut arrive = Arrive::new(context(), context()); + /// + /// arrive + /// .add_origin(uri!("https://example.com/one")) + /// .add_origin(uri!("https://example.com/two")); + /// # Ok(()) + /// # } + /// ``` + fn add_origin(&mut self, origin: T) -> &mut Self + where + T: Into, + { + self.origin_field_mut().add(origin.into()); + self + } +} + +/// Helper methods for interacting with Activity types with an optional target field +/// +/// Documentation for the target field can be found on the +/// `ActorAndObjectOptTarget` struct +pub trait OptTargetRefExt: OptTargetRef { + /// Fetch the target for the current activity + /// + /// ```rust + /// # use activitystreams::{context, activity::Announce}; + /// # let mut announce = Announce::new(context(), context()); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(target_ref) = announce.target() { + /// println!("{:?}", target_ref); + /// } + /// ``` + fn target(&self) -> Option<&OneOrMany> { + self.target_field_ref().as_ref() + } + + /// Set the target for the current activity + /// + /// This overwrites the contents of target + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Announce, uri}; + /// # let mut announce = Announce::new(context(), context()); + /// + /// announce.set_target(uri!("https://example.com")); + /// # Ok(()) + /// # } + /// ``` + fn set_target(&mut self, target: T) -> &mut Self + where + T: Into, + { + *self.target_field_mut() = Some(target.into().into()); + self + } + + /// Set many targets for the current activity + /// + /// This overwrites the contents of target + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Announce, uri}; + /// # let mut announce = Announce::new(context(), context()); + /// + /// announce.set_many_targets(vec![ + /// uri!("https://example.com/one"), + /// uri!("https://example.com/two"), + /// ]); + /// # Ok(()) + /// # } + /// ``` + fn set_many_targets(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + *self.target_field_mut() = Some(v.into()); + self + } + + /// Add a target to the current activity + /// + /// This does not overwrite the contents of target, only appends an item + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Announce, uri}; + /// # let mut announce = Announce::new(context(), context()); + /// + /// announce + /// .add_target(uri!("https://example.com/one")) + /// .add_target(uri!("https://example.com/two")); + /// # Ok(()) + /// # } + /// ``` + fn add_target(&mut self, target: T) -> &mut Self + where + T: Into, + { + let c = match self.target_field_mut().take() { + Some(mut c) => { + c.add(target.into()); + c + } + None => vec![target.into()].into(), + }; + *self.target_field_mut() = Some(c); + self + } + + /// Take a target from the current activity, leaving nothing + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{ + /// # context, + /// # activity::Announce, + /// # }; + /// # let mut announce = Announce::new(context(), context()); + /// + /// if let Some(target) = announce.take_target() { + /// println!("{:?}", target); + /// } + /// # Ok(()) + /// # } + /// ``` + fn take_target(&mut self) -> Option> { + self.target_field_mut().take() + } + + /// Delete a target from the current activity + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{ + /// # context, + /// # activity::Announce, + /// # }; + /// # let mut announce = Announce::new(context(), context()); + /// # announce.set_target(context()); + /// + /// assert!(announce.target().is_some()); + /// announce.delete_target(); + /// assert!(announce.target().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_target(&mut self) -> &mut Self { + *self.target_field_mut() = None; + self + } +} + +/// Helper methods for interacting with Activity types with an optional origin field +/// +/// Documentation for the origin field can be found on the +/// `Delete` struct +pub trait OptOriginRefExt: OptOriginRef { + /// Fetch the origin for the current activity + /// + /// ```rust + /// # use activitystreams::{context, activity::Delete}; + /// # let mut delete = Delete::new(context(), context()); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(origin_ref) = delete.origin() { + /// println!("{:?}", origin_ref); + /// } + /// ``` + fn origin(&self) -> Option<&OneOrMany> { + self.origin_field_ref().as_ref() + } + + /// Set the origin for the current activity + /// + /// This overwrites the contents of origin + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Delete, uri}; + /// # let mut delete = Delete::new(context(), context()); + /// + /// delete.set_origin(uri!("https://example.com")); + /// # Ok(()) + /// # } + /// ``` + fn set_origin(&mut self, origin: T) -> &mut Self + where + T: Into, + { + *self.origin_field_mut() = Some(origin.into().into()); + self + } + + /// Set many origins for the current activity + /// + /// This overwrites the contents of origin + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Delete, uri}; + /// # let mut delete = Delete::new(context(), context()); + /// + /// delete.set_many_origins(vec![ + /// uri!("https://example.com/one"), + /// uri!("https://example.com/two"), + /// ]); + /// # Ok(()) + /// # } + /// ``` + fn set_many_origins(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + *self.origin_field_mut() = Some(v.into()); + self + } + + /// Add a origin to the current activity + /// + /// This does not overwrite the contents of origin, only appends an item + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Delete, uri}; + /// # let mut delete = Delete::new(context(), context()); + /// + /// delete + /// .add_origin(uri!("https://example.com/one")) + /// .add_origin(uri!("https://example.com/two")); + /// # Ok(()) + /// # } + /// ``` + fn add_origin(&mut self, origin: T) -> &mut Self + where + T: Into, + { + let c = match self.origin_field_mut().take() { + Some(mut c) => { + c.add(origin.into()); + c + } + None => vec![origin.into()].into(), + }; + *self.origin_field_mut() = Some(c); + self + } + + /// Take a origin from the current activity, leaving nothing + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Delete}; + /// # let mut delete = Delete::new(context(), context()); + /// + /// if let Some(origin) = delete.take_origin() { + /// println!("{:?}", origin); + /// } + /// # Ok(()) + /// # } + /// ``` + fn take_origin(&mut self) -> Option> { + self.origin_field_mut().take() + } + + /// Delete a origin from the current activity + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{context, activity::Delete}; + /// # let mut delete = Delete::new(context(), context()); + /// # delete.set_origin(context()); + /// + /// assert!(delete.origin().is_some()); + /// delete.delete_origin(); + /// assert!(delete.origin().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_origin(&mut self) -> &mut Self { + *self.origin_field_mut() = None; + self + } +} + +/// Helper methods for interacting with Question types +/// +/// This trait represents methods valid for an ActivityStreams Question +/// +/// Documentation for the fields related to these methods can be found on the `Question` +/// struct +pub trait QuestionExt: AsQuestion { + /// Fetch the one_of field for the current activity + /// + /// ```rust + /// # use activitystreams::activity::Question; + /// # let mut question = Question::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(one_of) = question.one_of() { + /// println!("{:?}", one_of); + /// } + /// ``` + fn one_of(&self) -> Option<&OneOrMany> { + self.question_ref().one_of.as_ref() + } + + /// Set the one_of field for the current activity + /// + /// This overwrites the contents of one_of + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// + /// question.set_one_of(uri!("https://example.com")); + /// # Ok(()) + /// # } + /// ``` + fn set_one_of(&mut self, one_of: T) -> &mut Self + where + T: Into, + { + self.question_mut().one_of = Some(one_of.into().into()); + self + } + + /// Set many one_of items for the current activity + /// + /// This overwrites the contents of one_of + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// + /// question.set_many_one_ofs(vec![ + /// uri!("https://example.com/one"), + /// uri!("https://example.com/two"), + /// ]); + /// # Ok(()) + /// # } + /// ``` + fn set_many_one_ofs(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + self.question_mut().one_of = Some(v.into()); + self + } + + /// Add a one_of to the current activity + /// + /// This does not overwrite the contents of one_of, only appends an item + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// + /// question + /// .add_one_of(uri!("https://example.com/one")) + /// .add_one_of(uri!("https://example.com/two")); + /// # Ok(()) + /// # } + /// ``` + fn add_one_of(&mut self, one_of: T) -> &mut Self + where + T: Into, + { + let v = match self.question_mut().one_of.take() { + Some(mut v) => { + v.add(one_of.into()); + v + } + None => vec![one_of.into()].into(), + }; + self.question_mut().one_of = Some(v); + self + } + + /// Take the one_of field from the current activity, leaving nothing + /// + /// ```rust + /// # use activitystreams::activity::Question; + /// # let mut question = Question::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(one_of) = question.take_one_of() { + /// println!("{:?}", one_of); + /// } + /// ``` + fn take_one_of(&mut self) -> Option> { + self.question_mut().one_of.take() + } + + /// Delete the one_of field from the current activity + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// # question.set_one_of(uri!("https://example.com")); + /// # + /// use activitystreams::prelude::*; + /// + /// assert!(question.one_of().is_some()); + /// question.delete_one_of(); + /// assert!(question.one_of().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_one_of(&mut self) -> &mut Self { + self.question_mut().one_of = None; + self + } + + /// Fetch the any_of field for the current activity + /// + /// ```rust + /// # use activitystreams::activity::Question; + /// # let mut question = Question::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(any_of) = question.any_of() { + /// println!("{:?}", any_of); + /// } + /// ``` + fn any_of(&self) -> Option<&OneOrMany> { + self.question_ref().any_of.as_ref() + } + + /// Set the any_of field for the current activity + /// + /// This overwrites the contents of any_of + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// + /// question.set_any_of(uri!("https://example.com")); + /// # Ok(()) + /// # } + /// ``` + fn set_any_of(&mut self, any_of: T) -> &mut Self + where + T: Into, + { + self.question_mut().any_of = Some(any_of.into().into()); + self + } + + /// Set many any_of items for the current activity + /// + /// This overwrites the contents of any_of + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// + /// question.set_many_any_ofs(vec![ + /// uri!("https://example.com/one"), + /// uri!("https://example.com/two"), + /// ]); + /// # Ok(()) + /// # } + /// ``` + fn set_many_any_ofs(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + self.question_mut().any_of = Some(v.into()); + self + } + + /// Add an any_of to the current activity + /// + /// This does not overwrite the contents of any_of, only appends an item + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// + /// question + /// .add_any_of(uri!("https://example.com/one")) + /// .add_any_of(uri!("https://example.com/two")); + /// # Ok(()) + /// # } + /// ``` + fn add_any_of(&mut self, any_of: T) -> &mut Self + where + T: Into, + { + let v = match self.question_mut().any_of.take() { + Some(mut v) => { + v.add(any_of.into()); + v + } + None => vec![any_of.into()].into(), + }; + self.question_mut().any_of = Some(v); + self + } + + /// Take the any_of field from the current activity, leaving nothing + /// + /// ```rust + /// # use activitystreams::activity::Question; + /// # let mut question = Question::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(any_of) = question.take_any_of() { + /// println!("{:?}", any_of); + /// } + /// ``` + fn take_any_of(&mut self) -> Option> { + self.question_mut().any_of.take() + } + + /// Delete the any_of field from the current activity + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{activity::Question, uri}; + /// # let mut question = Question::new(); + /// # question.set_any_of(uri!("https://example.com")); + /// # + /// use activitystreams::prelude::*; + /// + /// assert!(question.any_of().is_some()); + /// question.delete_any_of(); + /// assert!(question.any_of().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_any_of(&mut self) -> &mut Self { + self.question_mut().any_of = None; + self + } +} + +/// Indicates that the actor accepts the object. +/// +/// The target property can be used in certain circumstances to indicate the context into which the +/// object has been accepted. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// Accept that aren't already present on an ActorAndObject. +pub type Accept = ActorAndObject; + +/// Indicates that the actor has added the object to the target. +/// +/// If the target property is not explicitly specified, the target would need to be determined +/// implicitly by context. The origin can be used to identify the context from which the object originated. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// Add that aren't already present on an ActorAndObject. +pub type Add = ActorAndObject; + +/// Indicates that the actor is blocking the object. +/// +/// Blocking is a stronger form of Ignore. The typical use is to support social systems that allow +/// one user to block activities or content of other users. The target and origin typically have no +/// defined meaning. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// Block that aren't already present on an ActorAndObject. +pub type Block = ActorAndObject; + +/// Indicates that the actor has created the object. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// Create that aren't already present on an ActorAndObject. +pub type Create = ActorAndObject; + +/// Indicates that the actor dislikes the object. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// Dislike that aren't already present on an ActorAndObject. +pub type Dislike = ActorAndObject; + +/// Indicates that the actor is "flagging" the object. +/// +/// Flagging is defined in the sense common to many social platforms as reporting content as being +/// inappropriate for any number of reasons. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// Flag that aren't already present on an ActorAndObject. +pub type Flag = ActorAndObject; + +/// Indicates that the actor is "following" the object. +/// +/// Following is defined in the sense typically used within Social systems in which the actor is +/// interested in any activity performed by or on the object. The target and origin typically have +/// no defined meaning. +/// +/// This is just an alias for `Object` because there's no fields inherent to Follow +/// that aren't already present on an ActorAndObject. +pub type Follow = ActorAndObject; + +/// Indicates that the actor is ignoring the object. +/// +/// The target and origin typically have no defined meaning. +/// +/// This is just an alias for `Object` because there's no fields inherent to Ignore +/// that aren't already present on an ActorAndObject. +pub type Ignore = ActorAndObject; + +/// Indicates that the actor has joined the object. +/// +/// The target and origin typically have no defined meaning +/// +/// This is just an alias for `Object` because there's no fields inherent to Join that +/// aren't already present on an ActorAndObject. +pub type Join = ActorAndObject; + +/// Indicates that the actor has left the object. +/// +/// The target and origin typically have no meaning. +/// +/// This is just an alias for `Object` because there's no fields inherent to Leave that +/// aren't already present on an ActorAndObject. +pub type Leave = ActorAndObject; + +/// Indicates that the actor likes, recommends or endorses the object. +/// +/// The target and origin typically have no defined meaning. +/// +/// This is just an alias for `Object` because there's no fields inherent to Like that +/// aren't already present on an ActorAndObject. +pub type Like = ActorAndObject; + +/// Indicates that the actor has listened to the object. +/// +/// This is just an alias for `Object` because there's no fields inherent to Listen +/// that aren't already present on an ActorAndObject. +pub type Listen = ActorAndObject; + +/// Indicates that the actor has read the object. +/// +/// This is just an alias for `Object` because there's no fields inherent to Read that +/// aren't already present on an ActorAndObject. +pub type Read = ActorAndObject; + +/// Indicates that the actor is rejecting the object. +/// +/// The target and origin typically have no defined meaning. +/// +/// This is just an alias for `Object` because there's no fields inherent to Reject +/// that aren't already present on an ActorAndObject. +pub type Reject = ActorAndObject; + +/// A specialization of Accept indicating that the acceptance is tentative. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// TentativeAccept that aren't already present on an ActorAndObject. +pub type TentativeAccept = ActorAndObject; + +/// A specialization of Reject in which the rejection is considered tentative. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// TentativeReject that aren't already present on an ActorAndObject. +pub type TentativeReject = ActorAndObject; + +/// Indicates that the actor is undoing the object. +/// +/// In most cases, the object will be an Activity describing some previously performed action (for +/// instance, a person may have previously "liked" an article but, for whatever reason, might +/// choose to undo that like at some later point in time). +/// +/// The target and origin typically have no defined meaning. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// Undo that aren't already present on an ActorAndObject. +pub type Undo = ActorAndObject; + +/// Indicates that the actor has updated the object. +/// +/// Note, however, that this vocabulary does not define a mechanism for describing the actual set +/// of modifications made to object. +/// +/// The target and origin typically have no defined meaning. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// Update that aren't already present on an ActorAndObject. +pub type Update = ActorAndObject; + +/// Indicates that the actor has viewed the object. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// View that aren't already present on an ActorAndObject. +pub type View = ActorAndObject; + +/// Indicates that the actor is calling the target's attention the object. +/// +/// The origin typically has no defined meaning. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// Announce that aren't already present on an ActorAndObjectOptTarget. +pub type Announce = ActorAndObjectOptTarget; + +/// Indicates that the actor is offering the object. +/// +/// If specified, the target indicates the entity to which the object is being offered. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// Offer that aren't already present on an ActorAndObjectOptTarget. +pub type Offer = ActorAndObjectOptTarget; + +/// Indicates that the actor has moved object from origin to target. +/// +/// If the origin or target are not specified, either can be determined by context. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// Move that aren't already present on an ActorAndObjectOptOriginAndTarget. +pub type Move = ActorAndObjectOptOriginAndTarget; + +/// Indicates that the actor is removing the object. +/// +/// If specified, the origin indicates the context from which the object is being removed. +/// +/// This is just an alias for `Object` because there's no fields inherent to +/// Remove that aren't already present on an ActorAndObjectOptOriginAndTarget. +pub type Remove = ActorAndObjectOptOriginAndTarget; + +/// 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)] +#[serde(rename_all = "camelCase")] +pub struct Activity { + /// Describes the result of the activity. + /// + /// For instance, if a particular action results in the creation of a new resource, the result + /// property can be used to describe that new resource. + /// + /// - Range: Object | Link + /// - Funcitonal: false + #[serde(skip_serializing_if = "Option::is_none")] + result: Option>, + + /// 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")] + instrument: Option>, + + /// base fields and unparsed json ends up here + #[serde(flatten)] + inner: Object, +} + +/// Activity with actor and object properties +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ActorAndObject { + /// Describes one or more entities that either performed or are expected to perform the + /// activity. + /// + /// Any single activity can have multiple actors. The actor MAY be specified using an indirect + /// Link. + /// + /// - Range: Object | Link + /// - Functional: false + actor: OneOrMany, + + /// When used within an Activity, describes the direct object of the activity. + /// + /// For instance, in the activity "John added a movie to his wishlist", the object of the + /// activity is the movie added. + /// + /// - Range: Object | Link + /// - Functional: false + object: OneOrMany, + + /// base fields and unparsed json ends up here + #[serde(flatten)] + inner: Activity, +} + +/// 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)] +#[serde(rename_all = "camelCase")] +pub struct Arrive { + /// Describes one or more entities that either performed or are expected to perform the + /// activity. + /// + /// Any single activity can have multiple actors. The actor MAY be specified using an indirect + /// Link. + /// + /// - Range: Object | Link + /// - Functional: false + actor: OneOrMany, + + /// Describes an indirect object of the activity from which the activity is directed. + /// + /// The precise meaning of the origin is the object of the English preposition "from". For + /// instance, in the activity "John moved an item to List B from List A", the origin of the + /// activity is "List A". + /// + /// - Range: Object | Link + /// - Functional: false + origin: OneOrMany, + + /// base fields and unparsed json ends up here + #[serde(flatten)] + inner: Activity, +} + +/// 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)] +#[serde(rename_all = "camelCase")] +pub struct Invite { + /// Describes one or more entities that either performed or are expected to perform the + /// activity. + /// + /// Any single activity can have multiple actors. The actor MAY be specified using an indirect + /// Link. + /// + /// - Range: Object | Link + /// - Functional: false + actor: OneOrMany, + + /// When used within an Activity, describes the direct object of the activity. + /// + /// For instance, in the activity "John added a movie to his wishlist", the object of the + /// activity is the movie added. + /// + /// - Range: Object | Link + /// - Functional: false + object: OneOrMany, + + /// Describes the indirect object, or target, of the activity. + /// + /// The precise meaning of the target is largely dependent on the type of action being + /// described but will often be the object of the English preposition "to". For instance, in + /// the activity "John added a movie to his wishlist", the target of the activity is John's + /// wishlist. An activity can have more than one target + /// + /// - Range: Object | Link + /// - Functional: false + target: OneOrMany, + + /// base fields and unparsed json ends up here + #[serde(flatten)] + inner: Activity, +} + +/// 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)] +#[serde(rename_all = "camelCase")] +pub struct Delete { + /// Describes one or more entities that either performed or are expected to perform the + /// activity. + /// + /// Any single activity can have multiple actors. The actor MAY be specified using an indirect + /// Link. + /// + /// - Range: Object | Link + /// - Functional: false + actor: OneOrMany, + + /// When used within an Activity, describes the direct object of the activity. + /// + /// For instance, in the activity "John added a movie to his wishlist", the object of the + /// activity is the movie added. + /// + /// - Range: Object | Link + /// - Functional: false + object: OneOrMany, + + /// Describes an indirect object of the activity from which the activity is directed. + /// + /// The precise meaning of the origin is the object of the English preposition "from". For + /// instance, in the activity "John moved an item to List B from List A", the origin of the + /// activity is "List A". + /// + /// - Range: Object | Link + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + origin: Option>, + + /// base fields and unparsed json ends up here + #[serde(flatten)] + inner: Activity, +} + +/// Activity with actor, object, and optional origin and target properties +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ActorAndObjectOptOriginAndTarget { + /// Describes one or more entities that either performed or are expected to perform the + /// activity. + /// + /// Any single activity can have multiple actors. The actor MAY be specified using an indirect + /// Link. + /// + /// - Range: Object | Link + /// - Functional: false + actor: OneOrMany, + + /// When used within an Activity, describes the direct object of the activity. + /// + /// For instance, in the activity "John added a movie to his wishlist", the object of the + /// activity is the movie added. + /// + /// - Range: Object | Link + /// - Functional: false + object: OneOrMany, + + /// Describes an indirect object of the activity from which the activity is directed. + /// + /// The precise meaning of the origin is the object of the English preposition "from". For + /// instance, in the activity "John moved an item to List B from List A", the origin of the + /// activity is "List A". + /// + /// - Range: Object | Link + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + origin: Option>, + + /// Describes the indirect object, or target, of the activity. + /// + /// The precise meaning of the target is largely dependent on the type of action being + /// described but will often be the object of the English preposition "to". For instance, in + /// the activity "John added a movie to his wishlist", the target of the activity is John's + /// wishlist. An activity can have more than one target + /// + /// - Range: Object | Link + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + target: Option>, + + /// base fields and unparsed json ends up here + #[serde(flatten)] + inner: Activity, +} + +/// Activity with actor, object, and optional target properties +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ActorAndObjectOptTarget { + /// Describes one or more entities that either performed or are expected to perform the + /// activity. + /// + /// Any single activity can have multiple actors. The actor MAY be specified using an indirect + /// Link. + /// + /// - Range: Object | Link + /// - Functional: false + actor: OneOrMany, + + /// When used within an Activity, describes the direct object of the activity. + /// + /// For instance, in the activity "John added a movie to his wishlist", the object of the + /// activity is the movie added. + /// + /// - Range: Object | Link + /// - Functional: false + object: OneOrMany, + + /// Describes the indirect object, or target, of the activity. + /// + /// The precise meaning of the target is largely dependent on the type of action being + /// described but will often be the object of the English preposition "to". For instance, in + /// the activity "John added a movie to his wishlist", the target of the activity is John's + /// wishlist. An activity can have more than one target + /// + /// - Range: Object | Link + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + target: Option>, + + /// base fields and unparsed json ends up here + #[serde(flatten)] + inner: Activity, +} + +/// 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)] +#[serde(rename_all = "camelCase")] +pub struct Travel { + /// Describes one or more entities that either performed or are expected to perform the + /// activity. + /// + /// Any single activity can have multiple actors. The actor MAY be specified using an indirect + /// Link. + /// + /// - Range: Object | Link + /// - Functional: false + actor: OneOrMany, + + /// Describes an indirect object of the activity from which the activity is directed. + /// + /// The precise meaning of the origin is the object of the English preposition "from". For + /// instance, in the activity "John moved an item to List B from List A", the origin of the + /// activity is "List A". + /// + /// - Range: Object | Link + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + origin: Option>, + + /// Describes the indirect object, or target, of the activity. + /// + /// The precise meaning of the target is largely dependent on the type of action being + /// described but will often be the object of the English preposition "to". For instance, in + /// the activity "John added a movie to his wishlist", the target of the activity is John's + /// wishlist. An activity can have more than one target + /// + /// - Range: Object | Link + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + target: Option>, + + /// base fields and unparsed json ends up here + #[serde(flatten)] + inner: Activity, +} + +/// Represents a question being asked. +/// +/// Question objects are an extension of IntransitiveActivity. That is, the Question object is an +/// Activity, but the direct object is the question itself and therefore it would not contain an +/// object property. +/// +/// 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)] +#[serde(rename_all = "camelCase")] +pub struct Question { + /// Identifies an exclusive option for a Question. + /// + /// Use of one_of implies that the Question can have only a single answer. To indicate that a + /// Question can have multiple answers, use any_of. + /// + /// - Range: Object | Link + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + one_of: Option>, + + /// Identifies an inclusive option for a Question. + /// + /// Use of any_of implies that the Question can have multiple answers. To indicate that a + /// Question can have only one answer, use one_of. + /// + /// - Range: Object | Link + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + any_of: Option>, + + /// base fields and unparsed json ends up here + #[serde(flatten)] + inner: Activity, +} + +impl Activity { + /// Create a new Activity + /// + /// ```rust + /// use activitystreams::activity::Activity; + /// + /// let activity = Activity::::new(); + /// ``` + pub fn new() -> Self + where + Kind: Default, + { + Activity { + result: None, + instrument: None, + inner: Object::new(), + } + } + + fn extending(mut inner: Object) -> Result { + let result = inner.remove("result")?; + let instrument = inner.remove("instrument")?; + + Ok(Activity { + result, + instrument, + inner, + }) + } + + fn retracting(self) -> Result, serde_json::Error> { + let Activity { + result, + instrument, + mut inner, + } = self; + + inner + .insert("result", result)? + .insert("instrument", instrument)?; + + Ok(inner) + } +} + +impl ActorAndObject { + /// Create a new ActorAndObject Activity + /// + /// ```rust + /// use activitystreams::activity::ActorAndObject; + /// + /// let activity = ActorAndObject::::new(vec![], vec![]); + /// ``` + pub fn new(actor: T, object: U) -> Self + where + T: Into>, + U: Into>, + Kind: Default, + { + ActorAndObject { + actor: actor.into(), + object: object.into(), + inner: Activity::new(), + } + } + + fn extending(object: Object) -> Result { + let mut inner = Activity::extending(object)?; + + let actor = inner.remove("actor")?; + let object = inner.remove("object")?; + + Ok(ActorAndObject { + actor, + object, + inner, + }) + } + + fn retracting(self) -> Result, serde_json::Error> { + let ActorAndObject { + actor, + object, + mut inner, + } = self; + + inner.insert("actor", actor)?.insert("object", object)?; + + inner.retracting() + } +} + +impl Arrive { + /// Create a new Arrive Activity + /// + /// ```rust + /// use activitystreams::activity::Arrive; + /// + /// let activity = Arrive::new(vec![], vec![]); + /// ``` + pub fn new(actor: T, origin: U) -> Self + where + T: Into>, + U: Into>, + { + Arrive { + actor: actor.into(), + origin: origin.into(), + inner: Activity::new(), + } + } + + fn extending(object: Object) -> Result { + let mut inner = Activity::extending(object)?; + + let actor = inner.remove("actor")?; + let origin = inner.remove("origin")?; + + Ok(Arrive { + actor, + origin, + inner, + }) + } + + fn retracting(self) -> Result, serde_json::Error> { + let Arrive { + actor, + origin, + mut inner, + } = self; + + inner.insert("actor", actor)?.insert("origin", origin)?; + + inner.retracting() + } +} + +impl Invite { + /// Create a new Invite Activity + /// + /// ```rust + /// use activitystreams::activity::Invite; + /// + /// let activity = Invite::new(vec![], vec![], vec![]); + /// ``` + pub fn new(actor: T, object: U, target: V) -> Self + where + T: Into>, + U: Into>, + V: Into>, + { + Invite { + actor: actor.into(), + object: object.into(), + target: target.into(), + inner: Activity::new(), + } + } + + fn extending(object: Object) -> Result { + let mut inner = Activity::extending(object)?; + + let actor = inner.remove("actor")?; + let object = inner.remove("object")?; + let target = inner.remove("target")?; + + Ok(Invite { + actor, + object, + target, + inner, + }) + } + + fn retracting(self) -> Result, serde_json::Error> { + let Invite { + actor, + object, + target, + mut inner, + } = self; + + inner + .insert("actor", actor)? + .insert("object", object)? + .insert("target", target)?; + + inner.retracting() + } +} + +impl Delete { + /// Create a new Delete Activity + /// + /// ```rust + /// use activitystreams::activity::Delete; + /// + /// let activity = Delete::new(vec![], vec![]); + /// ``` + pub fn new(actor: T, object: U) -> Self + where + T: Into>, + U: Into>, + { + Delete { + actor: actor.into(), + object: object.into(), + origin: None, + inner: Activity::new(), + } + } + + fn extending(object: Object) -> Result { + let mut inner = Activity::extending(object)?; + + let actor = inner.remove("actor")?; + let object = inner.remove("object")?; + let origin = inner.remove("origin")?; + + Ok(Delete { + actor, + object, + origin, + inner, + }) + } + + fn retracting(self) -> Result, serde_json::Error> { + let Delete { + actor, + object, + origin, + mut inner, + } = self; + + inner + .insert("actor", actor)? + .insert("object", object)? + .insert("origin", origin)?; + + inner.retracting() + } +} + +impl ActorAndObjectOptOriginAndTarget { + /// Create a new ActorAndObjectOptOriginAndTarget Activity + /// + /// ```rust + /// use activitystreams::activity::ActorAndObjectOptOriginAndTarget; + /// + /// let activity = ActorAndObjectOptOriginAndTarget::::new( + /// vec![], + /// vec![] + /// ); + /// ``` + pub fn new(actor: T, object: U) -> Self + where + T: Into>, + U: Into>, + Kind: Default, + { + ActorAndObjectOptOriginAndTarget { + actor: actor.into(), + object: object.into(), + origin: None, + target: None, + inner: Activity::new(), + } + } + + fn extending(object: Object) -> Result { + let mut inner = Activity::extending(object)?; + + let actor = inner.remove("actor")?; + let object = inner.remove("object")?; + let origin = inner.remove("origin")?; + let target = inner.remove("target")?; + + Ok(ActorAndObjectOptOriginAndTarget { + actor, + object, + origin, + target, + inner, + }) + } + + fn retracting(self) -> Result, serde_json::Error> { + let ActorAndObjectOptOriginAndTarget { + actor, + object, + origin, + target, + mut inner, + } = self; + + inner + .insert("actor", actor)? + .insert("object", object)? + .insert("origin", origin)? + .insert("target", target)?; + + inner.retracting() + } +} + +impl ActorAndObjectOptTarget { + /// Create a new ActorAndObjectOptTarget Activity + /// + /// ```rust + /// use activitystreams::activity::ActorAndObjectOptTarget; + /// + /// let activity = ActorAndObjectOptTarget::::new( + /// vec![], + /// vec![] + /// ); + /// ``` + pub fn new(actor: T, object: U) -> Self + where + T: Into>, + U: Into>, + Kind: Default, + { + ActorAndObjectOptTarget { + actor: actor.into(), + object: object.into(), + target: None, + inner: Activity::new(), + } + } + + fn extending(object: Object) -> Result { + let mut inner = Activity::extending(object)?; + + let actor = inner.remove("actor")?; + let object = inner.remove("object")?; + let target = inner.remove("target")?; + + Ok(ActorAndObjectOptTarget { + actor, + object, + target, + inner, + }) + } + + fn retracting(self) -> Result, serde_json::Error> { + let ActorAndObjectOptTarget { + actor, + object, + target, + mut inner, + } = self; + + inner + .insert("actor", actor)? + .insert("object", object)? + .insert("target", target)?; + + inner.retracting() + } +} + +impl Travel { + /// Create a new Travel Activity + /// + /// ```rust + /// use activitystreams::activity::Travel; + /// + /// let activity = Travel::new(vec![]); + /// ``` + pub fn new(actor: T) -> Self + where + T: Into>, + { + Travel { + actor: actor.into(), + origin: None, + target: None, + inner: Activity::new(), + } + } + + fn extending(object: Object) -> Result { + let mut inner = Activity::extending(object)?; + + let actor = inner.remove("actor")?; + let origin = inner.remove("origin")?; + let target = inner.remove("target")?; + + Ok(Travel { + actor, + origin, + target, + inner, + }) + } + + fn retracting(self) -> Result, serde_json::Error> { + let Travel { + actor, + origin, + target, + mut inner, + } = self; + + inner + .insert("actor", actor)? + .insert("origin", origin)? + .insert("target", target)?; + + inner.retracting() + } +} + +impl Question { + /// Create a new Question Activity + /// + /// ```rust + /// use activitystreams::activity::Question; + /// + /// let activity = Question::new(); + /// ``` + pub fn new() -> Self { + Question { + one_of: None, + any_of: None, + inner: Activity::new(), + } + } + + fn extending(object: Object) -> Result { + let mut inner = Activity::extending(object)?; + + let one_of = inner.remove("oneOf")?; + let any_of = inner.remove("anyOf")?; + + Ok(Question { + one_of, + any_of, + inner, + }) + } + + fn retracting(self) -> Result, serde_json::Error> { + let Question { + one_of, + any_of, + mut inner, + } = self; + + inner.insert("oneOf", one_of)?.insert("anyOf", any_of)?; + + inner.retracting() + } +} + +impl markers::Base for Activity {} +impl markers::Object for Activity {} +impl markers::Activity for Activity {} + +impl markers::Base for ActorAndObject {} +impl markers::Object for ActorAndObject {} +impl markers::Activity for ActorAndObject {} + +impl markers::Base for ActorAndObjectOptTarget {} +impl markers::Object for ActorAndObjectOptTarget {} +impl markers::Activity for ActorAndObjectOptTarget {} + +impl markers::Base for ActorAndObjectOptOriginAndTarget {} +impl markers::Object for ActorAndObjectOptOriginAndTarget {} +impl markers::Activity for ActorAndObjectOptOriginAndTarget {} + +impl markers::Base for Arrive {} +impl markers::Object for Arrive {} +impl markers::Activity for Arrive {} +impl markers::IntransitiveActivity for Arrive {} + +impl markers::Base for Invite {} +impl markers::Object for Invite {} +impl markers::Activity for Invite {} + +impl markers::Base for Delete {} +impl markers::Object for Delete {} +impl markers::Activity for Delete {} + +impl markers::Base for Travel {} +impl markers::Object for Travel {} +impl markers::Activity for Travel {} +impl markers::IntransitiveActivity for Travel {} + +impl markers::Base for Question {} +impl markers::Object for Question {} +impl markers::Activity for Question {} +impl markers::IntransitiveActivity for Question {} + +impl markers::Activity for ApObject where Inner: markers::Activity {} +impl markers::IntransitiveActivity for ApObject where + Inner: markers::IntransitiveActivity +{ +} + +impl Extends for Activity { + type Error = serde_json::Error; + + fn extends(base: Base) -> Result { + let inner = Object::extends(base)?; + Self::extending(inner) + } + + fn retracts(self) -> Result, Self::Error> { + let inner = self.retracting()?; + inner.retracts() + } +} + +impl TryFrom> for Activity { + type Error = serde_json::Error; + + fn try_from(object: Object) -> Result { + Self::extending(object) + } +} + +impl TryFrom> for Object { + type Error = serde_json::Error; + + fn try_from(activity: Activity) -> Result { + activity.retracting() + } +} + +impl Extends for ActorAndObject { + type Error = serde_json::Error; + + fn extends(base: Base) -> Result { + let inner = Object::extends(base)?; + Self::extending(inner) + } + + fn retracts(self) -> Result, Self::Error> { + let inner = self.retracting()?; + inner.retracts() + } +} + +impl TryFrom> for ActorAndObject { + type Error = serde_json::Error; + + fn try_from(object: Object) -> Result { + Self::extending(object) + } +} + +impl TryFrom> for Object { + type Error = serde_json::Error; + + fn try_from(activity: ActorAndObject) -> Result { + activity.retracting() + } +} + +impl Extends for Arrive { + type Error = serde_json::Error; + + fn extends(base: Base) -> Result { + let inner = Object::extends(base)?; + Self::extending(inner) + } + + fn retracts(self) -> Result, Self::Error> { + let inner = self.retracting()?; + inner.retracts() + } +} + +impl TryFrom> for Arrive { + type Error = serde_json::Error; + + fn try_from(object: Object) -> Result { + Self::extending(object) + } +} + +impl TryFrom for Object { + type Error = serde_json::Error; + + fn try_from(arrive: Arrive) -> Result { + arrive.retracting() + } +} + +impl Extends for Invite { + type Error = serde_json::Error; + + fn extends(base: Base) -> Result { + let inner = Object::extends(base)?; + Self::extending(inner) + } + + fn retracts(self) -> Result, Self::Error> { + let inner = self.retracting()?; + inner.retracts() + } +} + +impl TryFrom> for Invite { + type Error = serde_json::Error; + + fn try_from(object: Object) -> Result { + Self::extending(object) + } +} + +impl TryFrom for Object { + type Error = serde_json::Error; + + fn try_from(invite: Invite) -> Result { + invite.retracting() + } +} + +impl Extends for Delete { + type Error = serde_json::Error; + + fn extends(base: Base) -> Result { + let inner = Object::extends(base)?; + Self::extending(inner) + } + + fn retracts(self) -> Result, Self::Error> { + let inner = self.retracting()?; + inner.retracts() + } +} + +impl TryFrom> for Delete { + type Error = serde_json::Error; + + fn try_from(object: Object) -> Result { + Self::extending(object) + } +} + +impl TryFrom for Object { + type Error = serde_json::Error; + + fn try_from(delete: Delete) -> Result { + delete.retracting() + } +} + +impl Extends for ActorAndObjectOptOriginAndTarget { + type Error = serde_json::Error; + + fn extends(base: Base) -> Result { + let inner = Object::extends(base)?; + Self::extending(inner) + } + + fn retracts(self) -> Result, Self::Error> { + let inner = self.retracting()?; + inner.retracts() + } +} + +impl TryFrom> for ActorAndObjectOptOriginAndTarget { + type Error = serde_json::Error; + + fn try_from(object: Object) -> Result { + Self::extending(object) + } +} + +impl TryFrom> for Object { + type Error = serde_json::Error; + + fn try_from(activity: ActorAndObjectOptOriginAndTarget) -> Result { + activity.retracting() + } +} + +impl Extends for ActorAndObjectOptTarget { + type Error = serde_json::Error; + + fn extends(base: Base) -> Result { + let inner = Object::extends(base)?; + Self::extending(inner) + } + + fn retracts(self) -> Result, Self::Error> { + let inner = self.retracting()?; + inner.retracts() + } +} + +impl TryFrom> for ActorAndObjectOptTarget { + type Error = serde_json::Error; + + fn try_from(object: Object) -> Result { + Self::extending(object) + } +} + +impl TryFrom> for Object { + type Error = serde_json::Error; + + fn try_from(activity: ActorAndObjectOptTarget) -> Result { + activity.retracting() + } +} + +impl Extends for Travel { + type Error = serde_json::Error; + + fn extends(base: Base) -> Result { + let inner = Object::extends(base)?; + Self::extending(inner) + } + + fn retracts(self) -> Result, Self::Error> { + let inner = self.retracting()?; + inner.retracts() + } +} + +impl TryFrom> for Travel { + type Error = serde_json::Error; + + fn try_from(object: Object) -> Result { + Self::extending(object) + } +} + +impl TryFrom for Object { + type Error = serde_json::Error; + + fn try_from(travel: Travel) -> Result { + travel.retracting() + } +} + +impl Extends for Question { + type Error = serde_json::Error; + + fn extends(base: Base) -> Result { + let inner = Object::extends(base)?; + Self::extending(inner) + } + + fn retracts(self) -> Result, Self::Error> { + let inner = self.retracting()?; + inner.retracts() + } +} + +impl TryFrom> for Question { + type Error = serde_json::Error; + + fn try_from(object: Object) -> Result { + Self::extending(object) + } +} + +impl TryFrom for Object { + type Error = serde_json::Error; + + fn try_from(question: Question) -> Result { + question.retracting() + } +} + +impl UnparsedMut for Activity { + fn unparsed_mut(&mut self) -> &mut Unparsed { + self.inner.unparsed_mut() + } +} + +impl UnparsedMut for ActorAndObject { + fn unparsed_mut(&mut self) -> &mut Unparsed { + self.inner.unparsed_mut() + } +} + +impl UnparsedMut for Arrive { + fn unparsed_mut(&mut self) -> &mut Unparsed { + self.inner.unparsed_mut() + } +} + +impl UnparsedMut for Invite { + fn unparsed_mut(&mut self) -> &mut Unparsed { + self.inner.unparsed_mut() + } +} + +impl UnparsedMut for Delete { + fn unparsed_mut(&mut self) -> &mut Unparsed { + self.inner.unparsed_mut() + } +} + +impl UnparsedMut for ActorAndObjectOptOriginAndTarget { + fn unparsed_mut(&mut self) -> &mut Unparsed { + self.inner.unparsed_mut() + } +} + +impl UnparsedMut for ActorAndObjectOptTarget { + fn unparsed_mut(&mut self) -> &mut Unparsed { + self.inner.unparsed_mut() + } +} + +impl UnparsedMut for Travel { + fn unparsed_mut(&mut self) -> &mut Unparsed { + self.inner.unparsed_mut() + } +} + +impl UnparsedMut for Question { + fn unparsed_mut(&mut self) -> &mut Unparsed { + self.inner.unparsed_mut() + } +} + +impl AsBase for Activity { + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for Activity { + fn object_ref(&self) -> &Object { + &self.inner + } + + fn object_mut(&mut self) -> &mut Object { + &mut self.inner + } +} + +impl AsActivity for Activity { + fn activity_ref(&self) -> &Activity { + self + } + + fn activity_mut(&mut self) -> &mut Activity { + self + } +} + +impl AsBase for ActorAndObject { + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for ActorAndObject { + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsActivity for ActorAndObject { + fn activity_ref(&self) -> &Activity { + &self.inner + } + + fn activity_mut(&mut self) -> &mut Activity { + &mut self.inner + } +} + +impl ActorAndObjectRef for ActorAndObject { + fn actor_field_ref(&self) -> &OneOrMany { + &self.actor + } + + fn object_field_ref(&self) -> &OneOrMany { + &self.object + } + + fn actor_field_mut(&mut self) -> &mut OneOrMany { + &mut self.actor + } + + fn object_field_mut(&mut self) -> &mut OneOrMany { + &mut self.object + } +} + +impl AsBase for ActorAndObjectOptTarget { + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for ActorAndObjectOptTarget { + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsActivity for ActorAndObjectOptTarget { + fn activity_ref(&self) -> &Activity { + &self.inner + } + + fn activity_mut(&mut self) -> &mut Activity { + &mut self.inner + } +} + +impl ActorAndObjectRef for ActorAndObjectOptTarget { + fn actor_field_ref(&self) -> &OneOrMany { + &self.actor + } + + fn object_field_ref(&self) -> &OneOrMany { + &self.object + } + + fn actor_field_mut(&mut self) -> &mut OneOrMany { + &mut self.actor + } + + fn object_field_mut(&mut self) -> &mut OneOrMany { + &mut self.object + } +} + +impl OptTargetRef for ActorAndObjectOptTarget { + fn target_field_ref(&self) -> &Option> { + &self.target + } + + fn target_field_mut(&mut self) -> &mut Option> { + &mut self.target + } +} + +impl AsBase for ActorAndObjectOptOriginAndTarget { + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for ActorAndObjectOptOriginAndTarget { + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsActivity for ActorAndObjectOptOriginAndTarget { + fn activity_ref(&self) -> &Activity { + &self.inner + } + + fn activity_mut(&mut self) -> &mut Activity { + &mut self.inner + } +} + +impl ActorAndObjectRef for ActorAndObjectOptOriginAndTarget { + fn actor_field_ref(&self) -> &OneOrMany { + &self.actor + } + + fn object_field_ref(&self) -> &OneOrMany { + &self.object + } + + fn actor_field_mut(&mut self) -> &mut OneOrMany { + &mut self.actor + } + + fn object_field_mut(&mut self) -> &mut OneOrMany { + &mut self.object + } +} + +impl OptTargetRef for ActorAndObjectOptOriginAndTarget { + fn target_field_ref(&self) -> &Option> { + &self.target + } + + fn target_field_mut(&mut self) -> &mut Option> { + &mut self.target + } +} + +impl OptOriginRef for ActorAndObjectOptOriginAndTarget { + fn origin_field_ref(&self) -> &Option> { + &self.origin + } + + fn origin_field_mut(&mut self) -> &mut Option> { + &mut self.origin + } +} + +impl AsBase for Arrive { + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for Arrive { + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsActivity for Arrive { + fn activity_ref(&self) -> &Activity { + &self.inner + } + + fn activity_mut(&mut self) -> &mut Activity { + &mut self.inner + } +} + +impl OriginRef for Arrive { + fn origin_field_ref(&self) -> &OneOrMany { + &self.origin + } + + fn origin_field_mut(&mut self) -> &mut OneOrMany { + &mut self.origin + } +} + +impl AsBase for Invite { + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for Invite { + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsActivity for Invite { + fn activity_ref(&self) -> &Activity { + &self.inner + } + + fn activity_mut(&mut self) -> &mut Activity { + &mut self.inner + } +} + +impl ActorAndObjectRef for Invite { + fn actor_field_ref(&self) -> &OneOrMany { + &self.actor + } + + fn object_field_ref(&self) -> &OneOrMany { + &self.object + } + + fn actor_field_mut(&mut self) -> &mut OneOrMany { + &mut self.actor + } + + fn object_field_mut(&mut self) -> &mut OneOrMany { + &mut self.object + } +} + +impl TargetRef for Invite { + fn target_field_ref(&self) -> &OneOrMany { + &self.target + } + + fn target_field_mut(&mut self) -> &mut OneOrMany { + &mut self.target + } +} + +impl AsBase for Delete { + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for Delete { + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsActivity for Delete { + fn activity_ref(&self) -> &Activity { + &self.inner + } + + fn activity_mut(&mut self) -> &mut Activity { + &mut self.inner + } +} + +impl ActorAndObjectRef for Delete { + fn actor_field_ref(&self) -> &OneOrMany { + &self.actor + } + + fn object_field_ref(&self) -> &OneOrMany { + &self.object + } + + fn actor_field_mut(&mut self) -> &mut OneOrMany { + &mut self.actor + } + + fn object_field_mut(&mut self) -> &mut OneOrMany { + &mut self.object + } +} + +impl OptOriginRef for Delete { + fn origin_field_ref(&self) -> &Option> { + &self.origin + } + + fn origin_field_mut(&mut self) -> &mut Option> { + &mut self.origin + } +} + +impl AsBase for Travel { + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for Travel { + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsActivity for Travel { + fn activity_ref(&self) -> &Activity { + self.inner.activity_ref() + } + + fn activity_mut(&mut self) -> &mut Activity { + self.inner.activity_mut() + } +} + +impl OptTargetRef for Travel { + fn target_field_ref(&self) -> &Option> { + &self.target + } + + fn target_field_mut(&mut self) -> &mut Option> { + &mut self.target + } +} + +impl OptOriginRef for Travel { + fn origin_field_ref(&self) -> &Option> { + &self.origin + } + + fn origin_field_mut(&mut self) -> &mut Option> { + &mut self.origin + } +} + +impl AsBase for Question { + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for Question { + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsActivity for Question { + fn activity_ref(&self) -> &Activity { + &self.inner + } + + fn activity_mut(&mut self) -> &mut Activity { + &mut self.inner + } +} + +impl AsQuestion for Question { + fn question_ref(&self) -> &Question { + self + } + + fn question_mut(&mut self) -> &mut Question { + self + } +} + +impl AsActivity for ApObject +where + Inner: AsActivity, +{ + fn activity_ref(&self) -> &Activity { + self.inner().activity_ref() + } + + fn activity_mut(&mut self) -> &mut Activity { + self.inner_mut().activity_mut() + } +} + +impl ActorAndObjectRef for ApObject +where + Inner: ActorAndObjectRef, +{ + fn actor_field_ref(&self) -> &OneOrMany { + self.inner().actor_field_ref() + } + + fn object_field_ref(&self) -> &OneOrMany { + self.inner().object_field_ref() + } + + fn actor_field_mut(&mut self) -> &mut OneOrMany { + self.inner_mut().actor_field_mut() + } + + fn object_field_mut(&mut self) -> &mut OneOrMany { + self.inner_mut().object_field_mut() + } +} + +impl TargetRef for ApObject +where + Inner: TargetRef, +{ + fn target_field_ref(&self) -> &OneOrMany { + self.inner().target_field_ref() + } + + fn target_field_mut(&mut self) -> &mut OneOrMany { + self.inner_mut().target_field_mut() + } +} + +impl OriginRef for ApObject +where + Inner: OriginRef, +{ + fn origin_field_ref(&self) -> &OneOrMany { + self.inner().origin_field_ref() + } + + fn origin_field_mut(&mut self) -> &mut OneOrMany { + self.inner_mut().origin_field_mut() + } +} + +impl OptTargetRef for ApObject +where + Inner: OptTargetRef, +{ + fn target_field_ref(&self) -> &Option> { + self.inner().target_field_ref() + } + + fn target_field_mut(&mut self) -> &mut Option> { + self.inner_mut().target_field_mut() + } +} + +impl OptOriginRef for ApObject +where + Inner: OptOriginRef, +{ + fn origin_field_ref(&self) -> &Option> { + self.inner().origin_field_ref() + } + + fn origin_field_mut(&mut self) -> &mut Option> { + self.inner_mut().origin_field_mut() + } +} + +impl AsQuestion for ApObject +where + Inner: AsQuestion, +{ + fn question_ref(&self) -> &Question { + self.inner().question_ref() + } + + fn question_mut(&mut self) -> &mut Question { + self.inner_mut().question_mut() + } +} + +impl ActivityExt for T where T: AsActivity {} +impl ActorAndObjectRefExt for T where T: ActorAndObjectRef {} +impl TargetRefExt for T where T: TargetRef {} +impl OriginRefExt for T where T: OriginRef {} +impl OptTargetRefExt for T where T: OptTargetRef {} +impl OptOriginRefExt for T where T: OptOriginRef {} +impl QuestionExt for T where T: AsQuestion {} diff --git a/src/activity/kind.rs b/src/activity/kind.rs deleted file mode 100644 index 3763f3f..0000000 --- a/src/activity/kind.rs +++ /dev/null @@ -1,134 +0,0 @@ -/* - * 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 . - */ - -//! Namespace for Unit Structs that serialize to strings - -use crate::UnitString; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Accept)] -pub struct AcceptType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Add)] -pub struct AddType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Move)] -pub struct MoveType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Announce)] -pub struct AnnounceType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Arrive)] -pub struct ArriveType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Block)] -pub struct BlockType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Create)] -pub struct CreateType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Delete)] -pub struct DeleteType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Dislike)] -pub struct DislikeType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Flag)] -pub struct FlagType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Follow)] -pub struct FollowType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Ignore)] -pub struct IgnoreType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Invite)] -pub struct InviteType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Join)] -pub struct JoinType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Leave)] -pub struct LeaveType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Like)] -pub struct LikeType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Listen)] -pub struct ListenType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Offer)] -pub struct OfferType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Question)] -pub struct QuestionType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Real)] -pub struct ReadType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Reject)] -pub struct RejectType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Remove)] -pub struct RemoveType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(TentativeAccept)] -pub struct TentativeAcceptType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(TentativeReject)] -pub struct TentativeRejectType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Travel)] -pub struct TravelType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Undo)] -pub struct UndoType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Update)] -pub struct UpdateType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(View)] -pub struct ViewType; diff --git a/src/activity/mod.rs b/src/activity/mod.rs deleted file mode 100644 index 838a7e1..0000000 --- a/src/activity/mod.rs +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 . - */ - -//! Namespace for Activity types - -#[cfg(feature = "kinds")] -pub mod kind; -#[cfg(feature = "types")] -pub mod properties; -#[cfg(feature = "types")] -mod types; - -#[cfg(feature = "types")] -pub use self::types::{ - AMove, Accept, Add, Announce, Arrive, Block, Create, Delete, Dislike, Flag, Follow, Ignore, - Invite, Join, Leave, Like, Listen, Offer, Question, Read, Reject, Remove, TentativeAccept, - TentativeReject, Travel, Undo, Update, View, -}; - -use crate::object::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. -#[cfg_attr(feature = "derive", crate::wrapper_type)] -pub trait Activity: Object {} - -/// Instances of `IntransitiveActivity` are a subtype of `Activity` representing intransitive -/// actions. -/// -/// The `object` property is therefore inappropriate for these activities. -#[cfg_attr(feature = "derive", crate::wrapper_type)] -pub trait IntransitiveActivity: Activity {} diff --git a/src/activity/properties.rs b/src/activity/properties.rs deleted file mode 100644 index 5523b6c..0000000 --- a/src/activity/properties.rs +++ /dev/null @@ -1,624 +0,0 @@ -/* 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 . - */ - -//! Namespace for properties of standard Activity types -//! -//! To use these properties in your own types, you can flatten them into your struct with serde: -//! -//! ```rust -//! use activitystreams::{ -//! activity::{ -//! properties::ActivityProperties, -//! Activity, ActivityBox, -//! }, -//! ext::Ext, -//! object::{ -//! properties::ObjectProperties, -//! Object, ObjectBox, -//! }, -//! Base, BaseBox, PropRefs, -//! }; -//! use serde::{Deserialize, Serialize}; -//! -//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)] -//! #[serde(transparent)] -//! pub struct ObjProps(pub ObjectProperties); -//! -//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)] -//! #[serde(transparent)] -//! pub struct ActivityProps(pub ActivityProperties); -//! -//! #[derive(Clone, Debug, Default, Serialize, Deserialize, PropRefs)] -//! #[serde(rename_all = "camelCase")] -//! #[prop_refs(Object)] -//! #[prop_refs(Activity)] -//! pub struct MyActivity { -//! #[serde(rename = "type")] -//! #[serde(alias = "objectType")] -//! #[serde(alias = "verb")] -//! pub kind: String, -//! -//! /// Define a require property for the MyActivity type -//! pub my_property: String, -//! -//! #[serde(flatten)] -//! #[prop_refs] -//! pub object_properties: ObjProps, -//! -//! #[serde(flatten)] -//! #[prop_refs] -//! pub activity_properties: ActivityProps, -//! } -//! # -//! # fn main() {} -//! ``` - -use crate::{primitives::*, properties, BaseBox}; - -properties! { - Activity { - docs [ - "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.", - ], - - result { - docs [ - "Describes the result of the activity.", - "", - "For instance, if a particular action results in the creation of a new resource, the result", - "property can be used to describe that new resource.", - "", - "- Range: `Object` | `Link`", - "- Funcitonal: false", - ], - types [ - XsdAnyUri, - BaseBox - ], - }, - - instrument { - docs [ - "Identifies one or more objects used (or to be used) in the completion of an `Activity`.", - "", - "- Range: `Object` | `Link`", - "- Funcitonal: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - }, - } -} - -properties! { - ActorOptOriginAndTarget { - docs [ "Struct with `actor` and optional `origin` and `target` properties" ], - - actor { - docs [ - "Describes one or more entities that either performed or are expected to perform the", - "activity.", - "", - "Any single activity can have multiple actors. The actor MAY be specified using an indirect", - "Link.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required - }, - - origin { - docs [ - "Describes an indirect object of the activity from which the activity is directed.", - "", - "The precise meaning of the origin is the object of the English preposition \"from\". For", - "instance, in the activity \"John moved an item to List B from List A\", the origin of the", - "activity is \"List A\".", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - }, - - target { - docs [ - "Describes the indirect object, or target, of the activity.", - "", - "The precise meaning of the target is largely dependent on the type of action being", - "described but will often be the object of the English preposition \"to\". For instance, in", - "the activity \"John added a movie to his wishlist\", the target of the activity is John's", - "wishlist. An activity can have more than one target", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - }, - } -} - -properties! { - ActorAndObject { - docs [ "Struct with `actor` and `object` properties" ], - - actor { - docs [ - "Describes one or more entities that either performed or are expected to perform the", - "activity.", - "", - "Any single activity can have multiple actors. The actor MAY be specified using an indirect", - "Link.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required, - }, - - object { - docs [ - "When used within an Activity, describes the direct object of the activity.", - "", - "For instance, in the activity \"John added a movie to his wishlist\", the object of the", - "activity is the movie added.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required, - }, - } -} - -properties! { - ActorObjectAndTarget { - docs [ "Struct with `actor`, `object`, and `target` properties" ], - - actor { - docs [ - "Describes one or more entities that either performed or are expected to perform the", - "activity.", - "", - "Any single activity can have multiple actors. The actor MAY be specified using an indirect", - "Link.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required, - }, - - object { - docs [ - "When used within an Activity, describes the direct object of the activity.", - "", - "For instance, in the activity \"John added a movie to his wishlist\", the object of the", - "activity is the movie added.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required, - }, - - target { - docs [ - "Describes the indirect object, or target, of the activity.", - "", - "The precise meaning of the target is largely dependent on the type of action being", - "described but will often be the object of the English preposition \"to\". For instance, in", - "the activity \"John added a movie to his wishlist\", the target of the activity is John's", - "wishlist. An activity can have more than one target", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required, - }, - } -} - -properties! { - ActorAndObjectOptTarget { - docs [ "Struct with `actor`, `object`, and optional `target` properties" ], - - actor { - docs [ - "Describes one or more entities that either performed or are expected to perform the", - "activity.", - "", - "Any single activity can have multiple actors. The actor MAY be specified using an indirect", - "Link.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required, - }, - - object { - docs [ - "When used within an Activity, describes the direct object of the activity.", - "", - "For instance, in the activity \"John added a movie to his wishlist\", the object of the", - "activity is the movie added.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required, - }, - - target { - docs [ - "Describes the indirect object, or target, of the activity.", - "", - "The precise meaning of the target is largely dependent on the type of action being", - "described but will often be the object of the English preposition \"to\". For instance, in", - "the activity \"John added a movie to his wishlist\", the target of the activity is John's", - "wishlist. An activity can have more than one target", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - }, - } -} - -properties! { - ActorAndObjectOptOrigin { - docs [ "Struct with `actor`, `object`, and optional `origin` properties" ], - - actor { - docs [ - "Describes one or more entities that either performed or are expected to perform the", - "activity.", - "", - "Any single activity can have multiple actors. The actor MAY be specified using an indirect", - "Link.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required, - }, - - object { - docs [ - "When used within an Activity, describes the direct object of the activity.", - "", - "For instance, in the activity \"John added a movie to his wishlist\", the object of the", - "activity is the movie added.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required, - }, - - origin { - docs [ - "Describes an indirect object of the activity from which the activity is directed.", - "", - "The precise meaning of the origin is the object of the English preposition \"from\". For", - "instance, in the activity \"John moved an item to List B from List A\", the origin of the", - "activity is \"List A\".", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - }, - } -} - -properties! { - ActorAndObjectOptOthers { - docs [ "Struct with `actor`, `object`, and optional `origin` and `target` properties" ], - - actor { - docs [ - "Describes one or more entities that either performed or are expected to perform the", - "activity.", - "", - "Any single activity can have multiple actors. The actor MAY be specified using an indirect", - "Link.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required, - }, - - object { - docs [ - "When used within an Activity, describes the direct object of the activity.", - "", - "For instance, in the activity \"John added a movie to his wishlist\", the object of the", - "activity is the movie added.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required, - }, - - origin { - docs [ - "Describes an indirect object of the activity from which the activity is directed.", - "", - "The precise meaning of the origin is the object of the English preposition \"from\". For", - "instance, in the activity \"John moved an item to List B from List A\", the origin of the", - "activity is \"List A\".", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - }, - - target { - docs [ - "Describes the indirect object, or target, of the activity.", - "", - "The precise meaning of the target is largely dependent on the type of action being", - "described but will often be the object of the English preposition \"to\". For instance, in", - "the activity \"John added a movie to his wishlist\", the target of the activity is John's", - "wishlist. An activity can have more than one target", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - }, - } -} - -properties! { - ActorAndOrigin { - docs [ "Struct with `actor` and `origin` properties" ], - - actor { - docs [ - "Describes one or more entities that either performed or are expected to perform the", - "activity.", - "", - "Any single activity can have multiple actors. The actor MAY be specified using an indirect", - "Link.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdString, - BaseBox, - ], - required, - }, - - origin { - docs [ - "Describes an indirect object of the activity from which the activity is directed.", - "", - "The precise meaning of the origin is the object of the English preposition \"from\". For", - "instance, in the activity \"John moved an item to List B from List A\", the origin of the", - "activity is \"List A\".", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - required, - }, - } -} - -/// Properties for the Accept activity -pub type AcceptProperties = ActorAndObjectProperties; - -/// Properties for the Add activity -pub type AddProperties = ActorAndObjectProperties; - -/// Properties for the Move activity -pub type MoveProperties = ActorAndObjectOptOthersProperties; - -/// Properties for the Announce activity -pub type AnnounceProperties = ActorAndObjectOptTargetProperties; - -/// Properties for the Arrive activity -pub type ArriveProperties = ActorAndOriginProperties; - -/// Properties for the Block activity -pub type BlockProperties = ActorAndObjectProperties; - -/// Properties for the Create activity -pub type CreateProperties = ActorAndObjectProperties; - -/// Properties for the Delete activity -pub type DeleteProperties = ActorAndObjectOptOriginProperties; - -/// Properties for the Dislike activity -pub type DislikeProperties = ActorAndObjectProperties; - -/// Properties for the Flag activity -pub type FlagProperties = ActorAndObjectProperties; - -/// Properties for the Follow activity -pub type FollowProperties = ActorAndObjectProperties; - -/// Properties for the Ignore activity -pub type IgnoreProperties = ActorAndObjectProperties; - -/// Properties for the Invite activity -pub type InviteProperties = ActorObjectAndTargetProperties; - -/// Properties for the Join activity -pub type JoinProperties = ActorAndObjectProperties; - -/// Properties for the Leave activity -pub type LeaveProperties = ActorAndObjectProperties; - -/// Properties for the Like activity -pub type LikeProperties = ActorAndObjectProperties; - -/// Properties for the Listen activity -pub type ListenProperties = ActorAndObjectProperties; - -/// Properties for the Offer activity -pub type OfferProperties = ActorAndObjectOptTargetProperties; - -properties! { - Question { - docs [ "Properties for the Question activity" ], - - one_of { - docs [ - "Identifies an exclusive option for a Question.", - "", - "Use of `one_of` implies that the Question can have only a single answer. To indicate that a", - "`Question` can have multiple answers, use `any_of`.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - }, - - any_of { - docs [ - "Identifies an inclusive option for a Question.", - "", - "Use of `any_of` implies that the Question can have multiple answers. To indicate that a", - "`Question` can have only one answer, use `one_of`.", - "", - "- Range: `Object` | `Link`", - "- Functional: false", - ], - types [ - XsdAnyUri, - BaseBox, - ], - }, - } -} - -/// Properties for the Read activity -pub type ReadProperties = ActorAndObjectProperties; - -/// Properties for the Reject activity -pub type RejectProperties = ActorAndObjectProperties; - -/// Properties for the Remove activity -pub type RemoveProperties = ActorAndObjectOptOthersProperties; - -/// Properties for the TentativeAccept activity -pub type TentativeAcceptProperties = ActorAndObjectProperties; - -/// Properties for the TentativeReject activity -pub type TentativeRejectProperties = ActorAndObjectProperties; - -/// Properties for the Travel activity -pub type TravelProperties = ActorOptOriginAndTargetProperties; - -/// Properties for the Undo activity -pub type UndoProperties = ActorAndObjectProperties; - -/// Properties for the Update activity -pub type UpdateProperties = ActorAndObjectProperties; - -/// Properties for the View activity -pub type ViewProperties = ActorAndObjectProperties; diff --git a/src/activity/types.rs b/src/activity/types.rs deleted file mode 100644 index 88a9aa1..0000000 --- a/src/activity/types.rs +++ /dev/null @@ -1,881 +0,0 @@ -/* - * 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 . - */ - -use crate::{ - activity::{ - kind::*, properties::*, Activity, ActivityBox, IntransitiveActivity, - IntransitiveActivityBox, - }, - ext::Ext, - object::{ - properties::{ApObjectProperties, ObjectProperties}, - Object, ObjectBox, - }, - Base, BaseBox, Extensible, PropRefs, -}; - -/// Indicates that the actor accepts the object. -/// -/// The target property can be used in certain circumstances to indicate the context into which the -/// object has been accepted. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Accept { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: AcceptType, - - /// Adds all valid accept properties to this struct - #[serde(flatten)] - #[prop_refs] - pub accept_props: AcceptProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor has added the object to the target. -/// -/// If the target property is not explicitly specified, the target would need to be determined -/// implicitly by context. The origin can be used to identify the context from which the object -/// originated. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Add { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: AddType, - - /// Adds all valid add properties to this struct - #[serde(flatten)] - #[prop_refs] - pub add_props: AddProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor has moved object from origin to target. -/// -/// If the origin or target are not specified, either can be determined by context. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct AMove { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: MoveType, - - /// Adds all valid move properties to this struct - #[serde(flatten)] - #[prop_refs] - pub move_props: MoveProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor is calling the target's attention the object. -/// -/// The origin typically has no defined meaning. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Announce { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: AnnounceType, - - /// Adds all valid announce properties to this struct - #[serde(flatten)] - #[prop_refs] - pub announce_props: AnnounceProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// 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(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -#[prop_refs(IntransitiveActivity)] -pub struct Arrive { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: ArriveType, - - /// Adds all valid arrive properties to this struct - #[serde(flatten)] - #[prop_refs] - pub arrive_props: ArriveProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor is blocking the object. -/// -/// Blocking is a stronger form of Ignore. The typical use is to support social systems that allow -/// one user to block activities or content of other users. The target and origin typically have no -/// defined meaning. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Block { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: BlockType, - - /// Adds all valid block properties to this struct - #[serde(flatten)] - #[prop_refs] - pub block_props: BlockProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor has created the object. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Create { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: CreateType, - - /// Adds all valid create properties to this struct - #[serde(flatten)] - #[prop_refs] - pub create_props: CreateProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor has deleted the object. -/// -/// If specified, the origin indicates the context from which the object was deleted. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Delete { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: DeleteType, - - /// Adds all valid delete properties to this struct - #[serde(flatten)] - #[prop_refs] - pub delete_props: DeleteProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor dislikes the object. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Dislike { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: DislikeType, - - /// Adds all valid dislike properties to this struct - #[serde(flatten)] - #[prop_refs] - pub dislike_props: DislikeProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor is "flagging" the object. -/// -/// Flagging is defined in the sense common to many social platforms as reporting content as being -/// inappropriate for any number of reasons. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Flag { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: FlagType, - - /// Adds all valid flag properties to this struct - #[serde(flatten)] - #[prop_refs] - pub flag_props: FlagProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor is "following" the object. -/// -/// Following is defined in the sense typically used within Social systems in which the actor is -/// interested in any activity performed by or on the object. The target and origin typically have -/// no defined meaning. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Follow { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: FollowType, - - /// Adds all valid follow properties to this struct - #[serde(flatten)] - #[prop_refs] - pub follow_props: FollowProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor is ignoring the object. -/// -/// The target and origin typically have no defined meaning. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Ignore { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: IgnoreType, - - /// Adds all valid ignore properties to this struct - #[serde(flatten)] - #[prop_refs] - pub ignore_props: IgnoreProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// A specialization of Offer in which the actor is extending an invitation for the object to the -/// target. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Invite { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: InviteType, - - /// Adds all valid invite properties to this struct - #[serde(flatten)] - #[prop_refs] - pub invite_props: InviteProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor has joined the object. -/// -/// The target and origin typically have no defined meaning -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Join { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: JoinType, - - /// Adds all valid join properties to this struct - #[serde(flatten)] - #[prop_refs] - pub join_props: JoinProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor has left the object. -/// -/// The target and origin typically have no meaning. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Leave { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: LeaveType, - - /// Adds all valid leave properties to this struct - #[serde(flatten)] - #[prop_refs] - pub leave_props: LeaveProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor likes, recommends or endorses the object. -/// -/// The target and origin typically have no defined meaning. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Like { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: LikeType, - - /// Adds all valid like properties to this struct - #[serde(flatten)] - #[prop_refs] - pub like_props: LikeProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor has listened to the object. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Listen { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - kind: ListenType, - - /// Adds all valid listen properties to this struct - #[serde(flatten)] - #[prop_refs] - pub listen_props: ListenProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor is offering the object. -/// -/// If specified, the target indicates the entity to which the object is being offered. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Offer { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: OfferType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub offer_props: OfferProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Represents a question being asked. -/// -/// Question objects are an extension of IntransitiveActivity. That is, the Question object is an -/// Activity, but the direct object is the question itself and therefore it would not contain an -/// object property. -/// -/// 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, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -#[prop_refs(IntransitiveActivity)] -pub struct Question { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: QuestionType, - - /// Adds all valid question properties to this struct - #[serde(flatten)] - #[prop_refs] - pub question_props: QuestionProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor has read the object. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Read { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: ReadType, - - /// Adds all valid read properties to this struct - #[serde(flatten)] - #[prop_refs] - pub read_props: ReadProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor is rejecting the object. -/// -/// The target and origin typically have no defined meaning. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Reject { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: RejectType, - - /// Adds all valid reject properties to this struct - #[serde(flatten)] - #[prop_refs] - pub reject_props: RejectProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor is removing the object. -/// -/// If specified, the origin indicates the context from which the object is being removed. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Remove { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: RemoveType, - - /// Adds all valid remove properties to this struct - #[serde(flatten)] - #[prop_refs] - pub remove_props: RemoveProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// A specialization of Accept indicating that the acceptance is tentative. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct TentativeAccept { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: TentativeAcceptType, - - /// Adds all valid tentative_accept properties to this struct - #[serde(flatten)] - #[prop_refs] - pub tentative_accept_props: TentativeAcceptProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// A specialization of Reject in which the rejection is considered tentative. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct TentativeReject { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: TentativeRejectType, - - /// Adds all valid tentative_reject properties to this struct - #[serde(flatten)] - #[prop_refs] - pub tentative_reject_props: TentativeRejectProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// 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(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -#[prop_refs(IntransitiveActivity)] -pub struct Travel { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: TravelType, - - /// Adds all valid travel properties to this struct - #[serde(flatten)] - #[prop_refs] - pub travel_props: TravelProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor is undoing the object. -/// -/// In most cases, the object will be an Activity describing some previously performed action (for -/// instance, a person may have previously "liked" an article but, for whatever reason, might -/// choose to undo that like at some later point in time). -/// -/// The target and origin typically have no defined meaning. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Undo { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: UndoType, - - /// Adds all valid undo properties to this struct - #[serde(flatten)] - #[prop_refs] - pub undo_props: UndoProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor has updated the object. -/// -/// Note, however, that this vocabulary does not define a mechanism for describing the actual set -/// of modifications made to object. -/// -/// The target and origin typically have no defined meaning. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct Update { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: UpdateType, - - /// Adds all valid update properties to this struct - #[serde(flatten)] - #[prop_refs] - pub update_props: UpdateProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} - -/// Indicates that the actor has viewed the object. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Activity)] -#[extension(ApObjectProperties)] -pub struct View { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - pub kind: ViewType, - - /// Adds all valid view properties to this struct - #[serde(flatten)] - #[prop_refs] - pub view_props: ViewProperties, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, - - /// Adds all valid activity properties to this struct - #[serde(flatten)] - #[prop_refs] - pub activity_props: ActivityProperties, -} diff --git a/src/actor.rs b/src/actor.rs new file mode 100644 index 0000000..bb0eea6 --- /dev/null +++ b/src/actor.rs @@ -0,0 +1,1559 @@ +//! Types and traits for dealing with Actor attributes +//! +//! ```rust +//! # fn main() -> Result<(), anyhow::Error> { +//! use activitystreams::{ +//! actor::{ApActor, Person}, +//! prelude::*, +//! uri, +//! }; +//! +//! let mut person = ApActor::new( +//! uri!("https://example.com/actor/inbox"), +//! Person::new(), +//! ); +//! +//! person +//! .set_outbox(uri!("https://example.com/actor/outbox")) +//! .set_following(uri!("https://example.com/actor/following")) +//! .set_followers(uri!("https://example.com/actor/followers")); +//! # +//! # Ok(()) +//! # } +//! ``` +use crate::{ + base::{AsBase, Base, BaseExt, Extends}, + error::DomainError, + markers, + object::{ApObject, AsApObject, AsObject, Object}, + primitives::OneOrMany, + unparsed::{Unparsed, UnparsedMut, UnparsedMutExt}, +}; +use url::Url; + +pub mod kind { + //! Kinds of actors defined by the spec + //! + //! These types exist only to be statically-typed versions of the associated string. e.g. + //! `PersonType` -> `"Person"` + + use crate::kind; + + kind!(ApplicationType, Application); + kind!(GroupType, Group); + kind!(OrganizationType, Organization); + kind!(PersonType, Person); + kind!(ServiceType, Service); +} + +use self::kind::*; + +/// Implementation trait for deriving ActivityPub Actor methods for a type +/// +/// Any type implementing AsObject will automatically gain methods provided by ApActorExt +pub trait AsApActor: markers::Actor { + /// Immutable borrow of `ApActor` + fn ap_actor_ref(&self) -> &ApActor; + + /// Mutable borrow of `ApActor` + fn ap_actor_mut(&mut self) -> &mut ApActor; +} + +/// Helper methods for interacting with ActivityPub Actor types +/// +/// This trait represents methods valid for any ActivityPub Actor. +/// +/// Documentation for the fields related to these methods can be found on the `ApActor` struct +pub trait ApActorExt: AsApActor { + /// Fetch the inbox for the current actor, erroring if the inbox's domain does not match the + /// ID's domain + /// + /// ``` + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// # person.set_id(context()); + /// use activitystreams::prelude::*; + /// + /// let inbox = person.inbox()?; + /// println!("{:?}", inbox); + /// # Ok(()) + /// # } + /// ``` + fn inbox<'a, Kind>(&'a self) -> Result<&'a Url, DomainError> + where + Self: BaseExt + 'a, + Inner: 'a, + Kind: 'a, + { + let unchecked = self.inbox_unchecked(); + + if unchecked.domain() != self.id_unchecked().and_then(|id| id.domain()) { + return Err(DomainError); + } + + Ok(unchecked) + } + + /// Fetch the inbox for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// let inbox_ref = person.inbox_unchecked(); + /// ``` + fn inbox_unchecked<'a>(&'a self) -> &'a Url + where + Inner: 'a, + { + &self.ap_actor_ref().inbox + } + + /// Fetch a mutable referece to the current actor's inbox + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// let inbox_mut = person.inbox_mut(); + /// inbox_mut.set_path("/inbox"); + /// ``` + fn inbox_mut<'a>(&'a mut self) -> &'a mut Url + where + Inner: 'a, + { + &mut self.ap_actor_mut().inbox + } + + /// Set the inbox for the current actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// person.set_inbox(uri!("https://example.com/inbox")); + /// # Ok(()) + /// # } + /// ``` + fn set_inbox(&mut self, inbox: Url) -> &mut Self { + self.ap_actor_mut().inbox = inbox.into(); + self + } + + /// Fetch the outbox for the current user, erroring if the oubox's domain does not match the + /// ID's domain + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// let outbox_ref = person.outbox()?; + /// # Ok(()) + /// # } + /// ``` + fn outbox<'a, Kind>(&'a self) -> Result, DomainError> + where + Self: BaseExt, + Inner: 'a, + Kind: 'a, + { + if let Some(unchecked) = self.outbox_unchecked() { + if unchecked.domain() != self.id_unchecked().and_then(|id| id.domain()) { + return Err(DomainError); + } + + return Ok(Some(unchecked)); + } + + Ok(None) + } + + /// Fetch the outbox for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// let outbox_ref = person.outbox_unchecked(); + /// ``` + fn outbox_unchecked<'a>(&'a self) -> Option<&'a Url> + where + Inner: 'a, + { + self.ap_actor_ref().outbox.as_ref() + } + + /// Mutably fetch the outbox for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(outbox) = person.outbox_mut() { + /// outbox.set_path("/outbox"); + /// println!("{:?}", outbox); + /// } + /// ``` + fn outbox_mut<'a>(&'a mut self) -> Option<&'a mut Url> + where + Inner: 'a, + { + self.ap_actor_mut().outbox.as_mut() + } + + /// Set the outbox for the current actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// person.set_outbox(uri!("https://example.com/outbox")); + /// # Ok(()) + /// # } + /// ``` + fn set_outbox(&mut self, outbox: Url) -> &mut Self { + self.ap_actor_mut().outbox = Some(outbox.into()); + self + } + + /// Take the outbox from the current actor, leaving nothing + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(outbox) = person.take_outbox() { + /// println!("{:?}", outbox); + /// } + /// ``` + fn take_outbox(&mut self) -> Option { + self.ap_actor_mut().outbox.take() + } + + /// Delete the outbox from the current object + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// # person.set_outbox(uri!("https://example.com/outbox")); + /// use activitystreams::prelude::*; + /// + /// assert!(person.outbox_unchecked().is_some()); + /// person.delete_outbox(); + /// assert!(person.outbox_unchecked().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_outbox(&mut self) -> &mut Self { + self.ap_actor_mut().outbox = None; + self + } + + /// Fetch the following link for the current user, erroring if the following link's domain does + /// not match the ID's domain + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(following) = person.following()? { + /// println!("{:?}", following); + /// } + /// # Ok(()) + /// # } + /// ``` + fn following<'a, Kind>(&'a self) -> Result, DomainError> + where + Self: BaseExt, + Inner: 'a, + Kind: 'a, + { + if let Some(unchecked) = self.following_unchecked() { + if unchecked.domain() != self.id_unchecked().and_then(|id| id.domain()) { + return Err(DomainError); + } + + return Ok(Some(unchecked)); + } + + Ok(None) + } + + /// Fetch the following link for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(following) = person.following_unchecked() { + /// println!("{:?}", following); + /// } + /// ``` + fn following_unchecked<'a>(&'a self) -> Option<&'a Url> + where + Inner: 'a, + { + self.ap_actor_ref().following.as_ref() + } + + /// Mutably fetch the following link for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(following) = person.following_mut() { + /// following.set_path("/following"); + /// println!("{:?}", following); + /// } + /// ``` + fn following_mut<'a>(&'a mut self) -> Option<&'a mut Url> + where + Inner: 'a, + { + self.ap_actor_mut().following.as_mut() + } + + /// Set the following link for the current actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// person.set_following(uri!("https://example.com/following")); + /// # Ok(()) + /// # } + /// ``` + fn set_following(&mut self, following: Url) -> &mut Self { + self.ap_actor_mut().following = Some(following.into()); + self + } + + /// Take the following link from the current actor, leaving nothing + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(following) = person.take_following() { + /// println!("{:?}", following); + /// } + /// ``` + fn take_following(&mut self) -> Option { + self.ap_actor_mut().following.take() + } + + /// Delete the following link from the current object + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// # person.set_following(uri!("https://example.com/following")); + /// use activitystreams::prelude::*; + /// + /// assert!(person.following_unchecked().is_some()); + /// person.delete_following(); + /// assert!(person.following_unchecked().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_following(&mut self) -> &mut Self { + self.ap_actor_mut().following = None; + self + } + + /// Fetch the followers link for the current actor, erroring if the followers link's domain + /// does not match the ID's domain + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(followers) = person.followers()? { + /// println!("{:?}", followers); + /// } + /// # Ok(()) + /// # } + /// ``` + fn followers<'a, Kind>(&'a self) -> Result, DomainError> + where + Self: BaseExt, + Inner: 'a, + Kind: 'a, + { + if let Some(unchecked) = self.followers_unchecked() { + if unchecked.domain() != self.id_unchecked().and_then(|id| id.domain()) { + return Err(DomainError); + } + + return Ok(Some(unchecked)); + } + + Ok(None) + } + + /// Fetch the followers link for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(followers) = person.followers_unchecked() { + /// println!("{:?}", followers); + /// } + /// ``` + fn followers_unchecked<'a>(&'a self) -> Option<&'a Url> + where + Inner: 'a, + { + self.ap_actor_ref().followers.as_ref() + } + + /// Mutably fetch the followers link for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(followers) = person.followers_mut() { + /// followers.set_path("/followers"); + /// println!("{:?}", followers); + /// } + /// ``` + fn followers_mut<'a>(&'a mut self) -> Option<&'a mut Url> + where + Inner: 'a, + { + self.ap_actor_mut().followers.as_mut() + } + + /// Set the followers link for the current actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// person.set_followers(uri!("https://example.com/followers")); + /// # Ok(()) + /// # } + /// ``` + fn set_followers(&mut self, followers: Url) -> &mut Self { + self.ap_actor_mut().followers = Some(followers.into()); + self + } + + /// Take the followers link from the current actor, leaving nothing + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(followers) = person.take_followers() { + /// println!("{:?}", followers); + /// } + /// ``` + fn take_followers(&mut self) -> Option { + self.ap_actor_mut().followers.take() + } + + /// Delete the followers link from the current object + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// # person.set_followers(uri!("https://example.com/followers")); + /// use activitystreams::prelude::*; + /// + /// assert!(person.followers_unchecked().is_some()); + /// person.delete_followers(); + /// assert!(person.followers_unchecked().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_followers(&mut self) -> &mut Self { + self.ap_actor_mut().followers = None; + self + } + + /// Fetch the liked link for the current actor, erroring if the liked link's domain does not + /// match the ID's domain + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(liked) = person.liked()? { + /// println!("{:?}", liked); + /// } + /// # Ok(()) + /// # } + /// ``` + fn liked<'a, Kind>(&'a self) -> Result, DomainError> + where + Self: BaseExt, + Inner: 'a, + Kind: 'a, + { + if let Some(unchecked) = self.liked_unchecked() { + if unchecked.domain() != self.id_unchecked().and_then(|id| id.domain()) { + return Err(DomainError); + } + + return Ok(Some(unchecked)); + } + + Ok(None) + } + + /// Fetch the liked link for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(liked) = person.liked_unchecked() { + /// println!("{:?}", liked); + /// } + /// ``` + fn liked_unchecked<'a>(&'a self) -> Option<&'a Url> + where + Inner: 'a, + { + self.ap_actor_ref().liked.as_ref() + } + + /// Mutably fetch the liked link for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(liked) = person.liked_mut() { + /// liked.set_path("/liked"); + /// println!("{:?}", liked); + /// } + /// ``` + fn liked_mut<'a>(&'a mut self) -> Option<&'a mut Url> + where + Inner: 'a, + { + self.ap_actor_mut().liked.as_mut() + } + + /// Set the liked link for the current actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// person.set_streams(uri!("https://example.com/liked")); + /// # Ok(()) + /// # } + /// ``` + fn set_liked(&mut self, liked: Url) -> &mut Self { + self.ap_actor_mut().liked = Some(liked.into()); + self + } + + /// Take the liked link from the current actor, leaving nothing + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(liked) = person.take_liked() { + /// println!("{:?}", liked); + /// } + /// ``` + fn take_liked(&mut self) -> Option { + self.ap_actor_mut().liked.take() + } + + /// Delete the liked link from the current object + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// # person.set_liked(uri!("https://example.com/liked")); + /// use activitystreams::prelude::*; + /// + /// assert!(person.liked_unchecked().is_some()); + /// person.delete_liked(); + /// assert!(person.liked_unchecked().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_liked(&mut self) -> &mut Self { + self.ap_actor_mut().liked = None; + self + } + + /// Fetch the streams links for the current actor, erroring if the streams links's domains do + /// not match the ID's domains + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// # person.set_id(context()).add_streams(context()).add_streams(context()); + /// use activitystreams::prelude::*; + /// + /// if let Some(streams) = person.streams()? { + /// println!("{:?}", streams); + /// } + /// # Ok(()) + /// # } + /// ``` + fn streams<'a, Kind>(&'a self) -> Result>, DomainError> + where + Self: BaseExt, + Inner: 'a, + Kind: 'a, + { + if let Some(unchecked) = self.streams_unchecked() { + let domain_opt = self.id_unchecked().and_then(|id| id.domain()); + + let one = unchecked + .as_one() + .map(|url| url.domain() == domain_opt) + .unwrap_or(false); + let many = unchecked + .as_many() + .map(|urls| urls.iter().all(|url| url.domain() == domain_opt)) + .unwrap_or(false); + + if !one && !many { + return Err(DomainError); + } + + return Ok(Some(unchecked)); + } + + Ok(None) + } + + /// Fetch the streams links for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(streams) = person.streams_unchecked() { + /// println!("{:?}", streams); + /// } + /// ``` + fn streams_unchecked<'a>(&'a self) -> Option> + where + Inner: 'a, + { + self.ap_actor_ref().streams.as_ref().map(|o| o.as_ref()) + } + + /// Mutably fetch the streams links for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(mut streams) = person.streams_mut() { + /// streams.one_mut().map(|url| url.set_path("/streams")); + /// streams.many_mut().map(|urls| { + /// for url in urls.iter_mut() { + /// url.set_path("/streams"); + /// } + /// }); + /// println!("{:?}", streams); + /// } + /// ``` + fn streams_mut<'a>(&'a mut self) -> Option> + where + Inner: 'a, + { + self.ap_actor_mut().streams.as_mut().map(|o| o.as_mut()) + } + + /// Set the streams links for the current actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// person.set_streams(uri!("https://example.com/streams")); + /// # Ok(()) + /// # } + /// ``` + fn set_streams(&mut self, streams: Url) -> &mut Self { + self.ap_actor_mut().streams = Some(streams.into()); + self + } + + /// Set many streams links for the current actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// person.set_many_streams(vec![ + /// uri!("https://example.com/streams1"), + /// uri!("https://example.com/streams2"), + /// ]); + /// # Ok(()) + /// # } + /// ``` + fn set_many_streams(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + U: Into, + { + let v: Vec = items.into_iter().map(|u| u.into()).collect(); + self.ap_actor_mut().streams = Some(v.into()); + self + } + + /// Add a streams link for the current actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// person + /// .add_streams(uri!("https://example.com/streams1")) + /// .add_streams(uri!("https://example.com/streams2")); + /// # Ok(()) + /// # } + /// ``` + fn add_streams(&mut self, stream: Url) -> &mut Self { + let v = match self.ap_actor_mut().streams.take() { + Some(mut v) => { + v.add(stream); + v + } + None => vec![stream.into()].into(), + }; + self.ap_actor_mut().streams = Some(v); + self + } + + /// Take the streams links from the current actor, leaving nothing + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(streams) = person.take_streams() { + /// println!("{:?}", streams); + /// } + /// ``` + fn take_streams(&mut self) -> Option> { + self.ap_actor_mut().streams.take() + } + + /// Delete the streams links from the current object + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// # person.set_streams(uri!("https://example.com/streams")); + /// use activitystreams::prelude::*; + /// + /// assert!(person.streams_unchecked().is_some()); + /// person.delete_streams(); + /// assert!(person.streams_unchecked().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_streams(&mut self) -> &mut Self { + self.ap_actor_mut().streams = None; + self + } + + /// Fetch the preferred_username for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(preferred_username) = person.preferred_username() { + /// println!("{:?}", preferred_username); + /// } + /// ``` + fn preferred_username<'a>(&'a self) -> Option<&'a str> + where + Inner: 'a, + { + self.ap_actor_ref() + .preferred_username + .as_ref() + .map(|pu| pu.as_str()) + } + + /// Set the preferred_username for the current actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// person.set_preferred_username("user123"); + /// # Ok(()) + /// # } + /// ``` + fn set_preferred_username(&mut self, string: T) -> &mut Self + where + T: Into, + { + self.ap_actor_mut().preferred_username = Some(string.into()); + self + } + + /// Take the preferred_username from the current actor, leaving nothing + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(preferred_username) = person.take_preferred_username() { + /// println!("{:?}", preferred_username); + /// } + /// ``` + fn take_preferred_username(&mut self) -> Option { + self.ap_actor_mut().preferred_username.take() + } + + /// Delete the preferred_username from the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// # person.set_preferred_username("hey"); + /// use activitystreams::prelude::*; + /// + /// assert!(person.preferred_username().is_some()); + /// person.delete_preferred_username(); + /// assert!(person.preferred_username().is_none()); + /// ``` + fn delete_preferred_username(&mut self) -> &mut Self { + self.ap_actor_mut().preferred_username = None; + self + } + + /// Fetch the endpoints for the current actor, erroring if the Endpoints' domains do not + /// match the ID's domain + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Endpoints, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// # person.set_id(context()).set_endpoints(Endpoints { + /// # shared_inbox: Some(context()), + /// # ..Default::default() + /// # }); + /// use activitystreams::prelude::*; + /// + /// if let Some(endpoints) = person.endpoints()? { + /// println!("{:?}", endpoints); + /// } + /// # Ok(()) + /// # } + /// ``` + fn endpoints<'a, Kind>(&'a self) -> Result>, DomainError> + where + Self: BaseExt, + Inner: 'a, + Kind: 'a, + { + if let Some(endpoints) = self.endpoints_unchecked() { + let domain_opt = self.id_unchecked().and_then(|id| id.domain()); + + let mut any_failed = false; + + any_failed |= endpoints + .proxy_url + .map(|u| u.domain() != domain_opt) + .unwrap_or(false); + any_failed |= endpoints + .oauth_authorization_endpoint + .map(|u| u.domain() != domain_opt) + .unwrap_or(false); + any_failed |= endpoints + .oauth_token_endpoint + .map(|u| u.domain() != domain_opt) + .unwrap_or(false); + any_failed |= endpoints + .provide_client_key + .map(|u| u.domain() != domain_opt) + .unwrap_or(false); + any_failed |= endpoints + .sign_client_key + .map(|u| u.domain() != domain_opt) + .unwrap_or(false); + any_failed |= endpoints + .shared_inbox + .map(|u| u.domain() != domain_opt) + .unwrap_or(false); + + if any_failed { + return Err(DomainError); + } + + return Ok(Some(endpoints)); + } + + Ok(None) + } + + /// Fetch the endpoints for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(endpoints) = person.endpoints_unchecked() { + /// println!("{:?}", endpoints); + /// } + /// ``` + fn endpoints_unchecked<'a>(&'a self) -> Option> + where + Inner: 'a, + { + self.ap_actor_ref().endpoints.as_ref().map(|e| e.as_ref()) + } + + /// Mutably fetch the endpoints for the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(mut endpoints) = person.endpoints_mut() { + /// endpoints.shared_inbox.as_mut().map(|url| url.set_path("/inbox")); + /// println!("{:?}", endpoints); + /// } + /// ``` + fn endpoints_mut<'a>(&'a mut self) -> Option> + where + Inner: 'a, + { + self.ap_actor_mut().endpoints.as_mut().map(|e| e.as_mut()) + } + + /// Set the endpoints for the current actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{actor::{ApActor, Endpoints, Person}, context, uri}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// person.set_endpoints(Endpoints { + /// shared_inbox: Some(uri!("https://example.com/inbox")), + /// ..Default::default() + /// }); + /// # Ok(()) + /// # } + /// ``` + fn set_endpoints(&mut self, endpoints: Endpoints) -> &mut Self { + self.ap_actor_mut().endpoints = Some(endpoints.map(|u| u.into())); + self + } + + /// Take the endpoints from the current actor, leaving nothing + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// use activitystreams::prelude::*; + /// + /// if let Some(endpoints) = person.take_endpoints() { + /// println!("{:?}", endpoints); + /// } + /// ``` + fn take_endpoints(&mut self) -> Option> { + self.ap_actor_mut().endpoints.take() + } + + /// Delete the endpoints from the current actor + /// + /// ```rust + /// # use activitystreams::{actor::{ApActor, Person}, context}; + /// # let mut person = ApActor::new(context(), Person::new()); + /// # person.set_endpoints(Default::default()); + /// use activitystreams::prelude::*; + /// + /// assert!(person.endpoints_unchecked().is_some()); + /// person.delete_endpoints(); + /// assert!(person.endpoints_unchecked().is_none()); + /// ``` + fn delete_endpoints(&mut self) -> &mut Self { + self.ap_actor_mut().endpoints = None; + self + } +} + +/// Describes a software application. +/// +/// This is just an alias for `Actor` because there's no fields inherent to +/// Application that aren't already present on an Actor. +pub type Application = Actor; + +/// Represents a formal or informal collective of Actors. +/// +/// This is just an alias for `Actor` because there's no fields inherent to +/// Group that aren't already present on an Actor. +pub type Group = Actor; + +/// Represents an organization. +/// +/// This is just an alias for `Actor` because there's no fields inherent to +/// Organization that aren't already present on an Actor. +pub type Organization = Actor; + +/// Represents an individual person. +/// +/// This is just an alias for `Actor` because there's no fields inherent to +/// Person that aren't already present on an Actor. +pub type Person = Actor; + +/// Represents a service of any kind. +/// +/// This is just an alias for `Actor` because there's no fields inherent to +/// Service that aren't already present on an Actor. +pub type Service = Actor; + +/// 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 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. +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct ApActor { + /// A reference to an [ActivityStreams] OrderedCollection comprised of all the messages received by the actor. + /// + /// - Range: xsd:anyUri + /// - Functional: true + inbox: Url, + + /// An ActivityStreams] OrderedCollection comprised of all the messages produced by the actor. + /// + /// - Range: xsd:anyUri + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + outbox: Option, + + /// A link to an [ActivityStreams] collection of the actors that this actor is following. + /// + /// - Range: xsd:anyUri + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + following: Option, + + /// A link to an [ActivityStreams] collection of the actors that follow this actor. + /// + /// - Range: xsd:anyUri + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + followers: Option, + + /// A link to an [ActivityStreams] collection of objects this actor has liked. + /// + /// - Range: xsd:anyUri + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + liked: Option, + + /// A list of supplementary Collections which may be of interest. + /// + /// - Range: xsd:anyUri + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + streams: Option>, + + /// A short username which may be used to refer to the actor, with no uniqueness guarantees. + /// + /// - Range: xsd:string + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + preferred_username: Option, + + /// A json object which maps additional (typically server/domain-wide) endpoints which may be + /// useful either for this actor or someone referencing this actor. + /// + /// This mapping may be nested inside the actor document as the value or may be a link to a + /// JSON-LD document with these properties. + /// + /// - Range: Endpoint + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + endpoints: Option>, + + /// base fields and unparsed json ends up here + #[serde(flatten)] + inner: Inner, +} + +/// A json object which maps additional (typically server/domain-wide) endpoints which may be +/// useful either for this actor or someone referencing this actor. +/// +/// This mapping may be nested inside the actor document as the value or may be a link to a +/// JSON-LD document with these properties. +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Endpoints { + /// Endpoint URI so this actor's clients may access remote ActivityStreams objects which + /// require authentication to access. + /// + /// To use this endpoint, the client posts an x-www-form-urlencoded id parameter with the + /// value being the id of the requested ActivityStreams object. + /// + /// - Range: anyUri + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + pub proxy_url: Option, + + /// If OAuth 2.0 bearer tokens [RFC6749](https://tools.ietf.org/html/rfc6749) + /// [RFC6750](https://tools.ietf.org/html/rfc6750) are being used for authenticating client to + /// server interactions, this endpoint specifies a URI at which a browser-authenticated user + /// may obtain a new authorization grant. + /// + /// - Range: anyUri + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + pub oauth_authorization_endpoint: Option, + + /// If OAuth 2.0 bearer tokens [RFC6749](https://tools.ietf.org/html/rfc6749) + /// [RFC6750](https://tools.ietf.org/html/rfc6750) are being used for authenticating client to + /// server interactions, this endpoint specifies a URI at which a client may acquire an access + /// token. + /// + /// - Range: anyUri + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + pub oauth_token_endpoint: Option, + + /// If Linked Data Signatures and HTTP Signatures are being used for authentication and + /// authorization, this endpoint specifies a URI at which browser-authenticated users may + /// authorize a client's public key for client to server interactions. + /// + /// - Range: anyUri + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + pub provide_client_key: Option, + + /// If Linked Data Signatures and HTTP Signatures are being used for authentication and + /// authorization, this endpoint specifies a URI at which a client key may be signed by the + /// actor's key for a time window to act on behalf of the actor in interacting with foreign + /// servers. + /// + /// - Range: anyUri + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + pub sign_client_key: Option, + + /// An optional endpoint used for wide delivery of publicly addressed activities and + /// activities sent to followers. + /// + /// shared_inbox endpoints SHOULD also be publicly readable OrderedCollection objects + /// containing objects addressed to the Public special collection. Reading from the + /// shared_inbox endpoint MUST NOT present objects which are not addressed to the Public + /// endpoint. + /// + /// - Range: anyUri + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + pub shared_inbox: Option, +} + +/// A simple type to create an Actor out of any Object +/// +/// ```rust +/// # use activitystreams::{object::Object, actor::Actor, prelude::*}; +/// let object = Object::::new(); +/// let mut actor = Actor(object); +/// actor.set_kind("MyCustomActor".into()); +/// ``` +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(transparent)] +pub struct Actor(pub Object); + +impl Actor { + /// Create a new Actor + /// + /// ```rust + /// # use activitystreams::actor::Actor; + /// let actor = Actor::::new(); + /// ``` + pub fn new() -> Self + where + Kind: Default, + { + Actor(Object::new()) + } +} + +impl ApActor { + /// Create a new ActivityPub Actor + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::{actor::{ApActor, Person}, uri}; + /// + /// let actor = ApActor::new( + /// uri!("https://example.com/inbox"), + /// Person::new(), + /// ); + /// # Ok(()) + /// # } + /// ``` + pub fn new(inbox: Url, inner: Inner) -> Self + where + Inner: markers::Actor, + { + ApActor { + inbox: inbox.into(), + outbox: None, + following: None, + followers: None, + liked: None, + streams: None, + preferred_username: None, + endpoints: None, + inner, + } + } + + fn extending(mut inner: Inner) -> Result + where + Inner: UnparsedMut + markers::Actor, + { + let inbox = inner.remove("inbox")?; + let outbox = inner.remove("outbox")?; + let following = inner.remove("following")?; + let followers = inner.remove("followers")?; + let liked = inner.remove("liked")?; + let streams = inner.remove("streams")?; + let preferred_username = inner.remove("preferredUsername")?; + let endpoints = inner.remove("endpoints")?; + + Ok(ApActor { + inbox, + outbox, + following, + followers, + liked, + streams, + preferred_username, + endpoints, + inner, + }) + } + + fn retracting(self) -> Result + where + Inner: UnparsedMut + markers::Actor, + { + let ApActor { + inbox, + outbox, + following, + followers, + liked, + streams, + preferred_username, + endpoints, + mut inner, + } = self; + + inner + .insert("endpoints", endpoints)? + .insert("preferredUsername", preferred_username)? + .insert("streams", streams)? + .insert("liked", liked)? + .insert("followers", followers)? + .insert("following", following)? + .insert("outbox", outbox)? + .insert("inbox", inbox)?; + + Ok(inner) + } +} + +impl Endpoints { + /// Borrow the current Endpoints struct + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::{actor::Endpoints, uri}; + /// use url::Url; + /// + /// let uri = uri!("https://example.com"); + /// + /// let endpoints: Endpoints = Endpoints { + /// shared_inbox: Some(uri.clone()), + /// ..Default::default() + /// }; + /// + /// assert_eq!(endpoints.as_ref().shared_inbox, Some(&uri)); + /// # Ok(()) + /// # } + /// ``` + pub fn as_ref(&self) -> Endpoints<&T> { + Endpoints { + proxy_url: self.proxy_url.as_ref(), + oauth_authorization_endpoint: self.oauth_authorization_endpoint.as_ref(), + oauth_token_endpoint: self.oauth_token_endpoint.as_ref(), + provide_client_key: self.provide_client_key.as_ref(), + sign_client_key: self.sign_client_key.as_ref(), + shared_inbox: self.shared_inbox.as_ref(), + } + } + + /// Mutably borrow the endpoints struct + pub fn as_mut(&mut self) -> Endpoints<&mut T> { + Endpoints { + proxy_url: self.proxy_url.as_mut(), + oauth_authorization_endpoint: self.oauth_authorization_endpoint.as_mut(), + oauth_token_endpoint: self.oauth_token_endpoint.as_mut(), + provide_client_key: self.provide_client_key.as_mut(), + sign_client_key: self.sign_client_key.as_mut(), + shared_inbox: self.shared_inbox.as_mut(), + } + } + + /// Map the URLs in Endpoints from T to U + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::{actor::Endpoints, uri}; + /// use url::Url; + /// + /// let endpoints: Endpoints = Endpoints { + /// shared_inbox: Some(uri!("https://example.com")), + /// ..Default::default() + /// }; + /// + /// let endpoint_strings = endpoints.map(|u| u.to_string()); + /// + /// assert_eq!(endpoint_strings.shared_inbox, Some(String::from("https://example.com/"))); + /// # Ok(()) + /// # } + /// ``` + pub fn map(self, f: F) -> Endpoints + where + F: Fn(T) -> U + Copy, + { + Endpoints { + proxy_url: self.proxy_url.map(f), + oauth_authorization_endpoint: self.oauth_authorization_endpoint.map(f), + oauth_token_endpoint: self.oauth_token_endpoint.map(f), + provide_client_key: self.provide_client_key.map(f), + sign_client_key: self.sign_client_key.map(f), + shared_inbox: self.shared_inbox.map(f), + } + } +} + +impl Default for Endpoints { + fn default() -> Self { + Endpoints { + proxy_url: None, + oauth_authorization_endpoint: None, + oauth_token_endpoint: None, + provide_client_key: None, + sign_client_key: None, + shared_inbox: None, + } + } +} + +impl markers::Base for Actor {} +impl markers::Object for Actor {} +impl markers::Actor for Actor {} + +impl markers::Base for ApActor where Inner: markers::Base {} +impl markers::Object for ApActor where Inner: markers::Object {} +impl markers::Actor for ApActor where Inner: markers::Actor {} + +impl Extends for ApActor +where + Inner: Extends + UnparsedMut + markers::Actor, + Error: From + std::error::Error, +{ + type Error = Error; + + fn extends(base: Base) -> Result { + let inner = Inner::extends(base)?; + Ok(Self::extending(inner)?) + } + + fn retracts(self) -> Result, Self::Error> { + let inner = self.retracting()?; + inner.retracts() + } +} + +impl UnparsedMut for ApActor +where + Inner: UnparsedMut, +{ + fn unparsed_mut(&mut self) -> &mut Unparsed { + self.inner.unparsed_mut() + } +} + +impl AsBase for ApActor +where + Inner: AsBase, +{ + fn base_ref(&self) -> &Base { + self.inner.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.inner.base_mut() + } +} + +impl AsObject for ApActor +where + Inner: AsObject, +{ + fn object_ref(&self) -> &Object { + self.inner.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.inner.object_mut() + } +} + +impl AsApActor for ApActor +where + Inner: markers::Actor, +{ + fn ap_actor_ref(&self) -> &ApActor { + self + } + + fn ap_actor_mut(&mut self) -> &mut ApActor { + self + } +} + +impl AsApObject for ApActor +where + Inner1: AsApObject, +{ + fn ap_object_ref(&self) -> &ApObject { + self.inner.ap_object_ref() + } + + fn ap_object_mut(&mut self) -> &mut ApObject { + self.inner.ap_object_mut() + } +} + +impl Extends for Actor { + type Error = serde_json::Error; + + fn extends(base: Base) -> Result { + Object::extends(base).map(Actor) + } + + fn retracts(self) -> Result, Self::Error> { + self.0.retracts() + } +} + +impl UnparsedMut for Actor { + fn unparsed_mut(&mut self) -> &mut Unparsed { + self.0.unparsed_mut() + } +} + +impl AsBase for Actor { + fn base_ref(&self) -> &Base { + self.0.base_ref() + } + + fn base_mut(&mut self) -> &mut Base { + self.0.base_mut() + } +} + +impl AsObject for Actor { + fn object_ref(&self) -> &Object { + self.0.object_ref() + } + + fn object_mut(&mut self) -> &mut Object { + self.0.object_mut() + } +} + +impl ApActorExt for T where T: AsApActor {} diff --git a/src/actor/kind.rs b/src/actor/kind.rs deleted file mode 100644 index b92c62e..0000000 --- a/src/actor/kind.rs +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 . - */ - -//! Namespace for Unit Structs that serialize to strings -use crate::UnitString; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Application)] -pub struct ApplicationType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Group)] -pub struct GroupType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Organization)] -pub struct OrganizationType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Person)] -pub struct PersonType; - -#[derive(Clone, Debug, Default, UnitString)] -#[unit_string(Service)] -pub struct ServiceType; diff --git a/src/actor/mod.rs b/src/actor/mod.rs deleted file mode 100644 index da1df76..0000000 --- a/src/actor/mod.rs +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 . - */ - -//! Namespace for Actor types - -#[cfg(feature = "kinds")] -pub mod kind; -#[cfg(feature = "types")] -pub mod properties; -#[cfg(feature = "types")] -mod types; - -#[cfg(feature = "types")] -pub use self::types::{Application, Group, Organization, Person, Service}; - -use crate::object::Object; - -/// `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`. -#[cfg_attr(feature = "derive", crate::wrapper_type)] -pub trait Actor: Object {} diff --git a/src/actor/properties.rs b/src/actor/properties.rs deleted file mode 100644 index 2a4b329..0000000 --- a/src/actor/properties.rs +++ /dev/null @@ -1,192 +0,0 @@ -/* - * 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 . - */ - -//! Namespace for properties of standard Actor types -//! -//! To use these properties in your own types, you can flatten them into your struct with serde: -//! -//! ```rust -//! use activitystreams::{ -//! actor::{ -//! properties::ApActorProperties, -//! Actor, ActorBox, -//! }, -//! ext::Ext, -//! object::{ -//! properties::{ApObjectProperties, ObjectProperties}, -//! Object, ObjectBox, -//! }, -//! Base, BaseBox, PropRefs, -//! }; -//! use serde::{Deserialize, Serialize}; -//! -//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)] -//! #[serde(transparent)] -//! pub struct ObjProps(pub ObjectProperties); -//! -//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)] -//! #[serde(transparent)] -//! pub struct ApObjProps(pub ApObjectProperties); -//! -//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)] -//! #[serde(transparent)] -//! pub struct ApActorProps(pub ApActorProperties); -//! -//! #[derive(Clone, Debug, Default, Serialize, Deserialize, PropRefs)] -//! #[serde(rename_all = "camelCase")] -//! #[prop_refs(Object)] -//! #[prop_refs(Actor)] -//! pub struct MyActor { -//! #[serde(rename = "type")] -//! pub kind: String, -//! -//! /// Define a require property for the MyActor type -//! pub my_property: String, -//! -//! #[serde(flatten)] -//! #[prop_refs] -//! pub object_props: ObjProps, -//! -//! #[serde(flatten)] -//! #[prop_refs] -//! pub ap_object_props: ApObjProps, -//! -//! #[serde(flatten)] -//! #[prop_refs] -//! pub actor_props: ApActorProps, -//! } -//! # -//! # fn main() {} -//! ``` - -use crate::{ - actor::Actor, - endpoint::EndpointProperties, - ext::Extension, - primitives::{XsdAnyUri, XsdString}, - properties, -}; - -impl Extension for ApActorProperties where T: Actor {} - -properties! { - ApActor { - docs [ - "Define activitypub properties for the Actor type as described by the Activity Pub vocabulary." - ], - - inbox { - docs [ - "A reference to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)]", - "OrderedCollection comprised of all the messages received by the actor.", - "", - "- Range: `xsd:anyUri`", - "- Functional: true", - ], - types [ XsdAnyUri ], - functional, - required, - }, - - outbox { - docs [ - "An [ActivityStreams](https://www.w3.org/ns/activitystreams)] OrderedCollection comprised of", - "all the messages produced by the actor.", - "", - "- Range: `xsd:anyUri`", - "- Functional: true", - ], - types [ XsdAnyUri ], - functional, - required, - }, - - following { - docs [ - "A link to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] collection of the", - "actors that this actor is following.", - "", - "- Range: `xsd:anyUri`", - "- Functional: true", - ], - types [ XsdAnyUri ], - functional, - }, - - followers { - docs [ - "A link to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] collection of the", - "actors that follow this actor.", - "", - "- Range: `xsd:anyUri`", - "- Functional: true", - ], - types [ XsdAnyUri ], - functional, - }, - - liked { - docs [ - "A link to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] collection of", - "objects this actor has liked.", - "", - "- Range: `xsd:anyUri`", - "- Functional: true", - ], - types [ XsdAnyUri ], - functional, - }, - - streams { - docs [ - "A list of supplementary Collections which may be of interest.", - "", - "- Range: `xsd:anyUri`", - "- Functional: false", - ], - types [ XsdAnyUri ], - }, - - preferred_username { - docs [ - "A short username which may be used to refer to the actor, with no uniqueness guarantees.", - "", - "- Range: `xsd:string`", - "- Functional: true", - ], - types [ XsdString ], - functional, - }, - - endpoints { - docs [ - "A json object which maps additional (typically server/domain-wide) endpoints which may be", - "useful either for this actor or someone referencing this actor.", - "", - "This mapping may be nested inside the actor document as the value or may be a link to a", - "JSON-LD document with these properties.", - "", - "- Range: `Endpoint`", - "- Functional: true", - ], - types [ EndpointProperties ], - functional, - }, - } -} diff --git a/src/actor/types.rs b/src/actor/types.rs deleted file mode 100644 index 99e0f82..0000000 --- a/src/actor/types.rs +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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 . - */ - -use crate::{ - actor::{kind::*, properties::ApActorProperties, Actor, ActorBox}, - ext::Ext, - object::{ - properties::{ApObjectProperties, ObjectProperties}, - Object, ObjectBox, - }, - Base, BaseBox, Extensible, PropRefs, -}; - -/// Describes a software application. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Actor)] -#[extension(ApObjectProperties)] -#[extension(ApActorProperties)] -pub struct Application { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - kind: ApplicationType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, -} - -/// Represents a formal or informal collective of Actors. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Actor)] -#[extension(ApObjectProperties)] -#[extension(ApActorProperties)] -pub struct Group { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - kind: GroupType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, -} - -/// Represents an organization. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct Organization { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - kind: OrganizationType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, -} - -/// Represents an individual person. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Actor)] -#[extension(ApObjectProperties)] -#[extension(ApActorProperties)] -pub struct Person { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - kind: PersonType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, -} - -/// Represents a service of any kind. -#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -#[prop_refs(Object)] -#[prop_refs(Actor)] -#[extension(ApObjectProperties)] -#[extension(ApActorProperties)] -pub struct Service { - #[serde(rename = "type")] - #[serde(alias = "objectType")] - #[serde(alias = "verb")] - kind: ServiceType, - - /// Adds all valid object properties to this struct - #[serde(flatten)] - #[prop_refs] - pub object_props: ObjectProperties, -} diff --git a/src/base.rs b/src/base.rs new file mode 100644 index 0000000..46d7fa0 --- /dev/null +++ b/src/base.rs @@ -0,0 +1,1873 @@ +//! Types and traits for dealing with the base attributes common to all ActivityStreams Objects and +//! Links +//! +//! ```rust +//! # fn main() -> Result<(), anyhow::Error> { +//! use activitystreams::{ +//! context, +//! object::Video, +//! prelude::*, +//! security, +//! uri, +//! }; +//! let mut video = Video::new(); +//! +//! video +//! .set_id(uri!("https://example.com")) +//! .set_context(context()) +//! .add_context(security()) +//! .set_name("Hello"); +//! +//! let any_base = video.into_any_base()?; +//! +//! let mut new_video = Video::new(); +//! +//! new_video.set_preview(any_base); +//! # +//! # Ok(()) +//! # } +//! ``` +use crate::{ + either::Either, + error::DomainError, + markers, + primitives::{AnyString, MimeMediaType, OneOrMany}, + unparsed::{Unparsed, UnparsedMut}, +}; +use mime::Mime; +use url::Url; + +/// Implements conversion between `Base` and other ActivityStreams objects defined in this +/// crate +pub trait Extends: Sized { + /// The erro produced must be a StdError + type Error: std::error::Error; + + /// Produce an object from the Base + fn extends(base: Base) -> Result; + + /// Produce a base from the object + fn retracts(self) -> Result, Self::Error>; +} + +/// A helper function implemented for all Extends types to easily produce an AnyBase from a given +/// object. +/// +/// This is important because many APIs in this crate deal with AnyBases. +pub trait ExtendsExt: Extends { + /// Create an AnyBase from the given object + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::{object::Video, prelude::*}; + /// let video = Video::new(); + /// + /// let any_base = video.into_any_base()?; + /// # + /// # Ok(()) + /// # } + /// ``` + fn into_any_base(self) -> Result + where + Kind: serde::ser::Serialize, + Self::Error: From, + { + AnyBase::from_extended(self) + } + + /// Create an object from an AnyBase + /// + /// Before calling this, make sure the `AnyBase::is_base()` and `AnyBase::kind` match your + /// expectations + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::{object::Video, prelude::*}; + /// # let video = Video::new(); + /// # let any_base = video.into_any_base()?; + /// let video = Video::from_any_base(any_base)?; + /// # + /// # Ok(()) + /// # } + /// ``` + fn from_any_base(any_base: AnyBase) -> Result, Self::Error> + where + Kind: serde::de::DeserializeOwned, + Self::Error: From, + { + if let Some(base) = any_base.take_base() { + let my_base = base.solidify()?; + let extended = my_base.extend::()?; + + return Ok(Some(extended)); + } + + Ok(None) + } +} + +/// Implementation trait for deriving Base methods for a type +/// +/// Any type implementating AsBase will automatically gain methods provided by BaseExt +pub trait AsBase: markers::Base { + /// Immutable borrow of `Base` + fn base_ref(&self) -> &Base; + + /// Mutable borrow of Base + fn base_mut(&mut self) -> &mut Base; +} + +/// Helper methods for interacting with Base types +/// +/// This trait represents methods valid for Any ActivityStreams type, regardless of whether it's a +/// Link or an Object. +/// +/// Documentation for the fields related to these methods can be found on the `Base` struct +pub trait BaseExt: AsBase { + /// Fetch the context for the current object + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let video = Video::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// let video_context = video.context(); + /// ``` + fn context<'a>(&'a self) -> Option<&'a OneOrMany> + where + Kind: 'a, + { + self.base_ref().context.as_ref() + } + + /// Set the context for the current object + /// + /// This overwrites the contents of context + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::{context, prelude::*}; + /// + /// video.set_context(context()); + /// ``` + fn set_context(&mut self, context: T) -> &mut Self + where + T: Into, + { + self.base_mut().context = Some(context.into().into()); + self + } + + /// Set many contexts for the current object + /// + /// This overwrites the contents of context + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::{context, prelude::*, security}; + /// + /// video.set_many_contexts(vec![context(), security()]); + /// ``` + fn set_many_contexts(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + self.base_mut().context = Some(v.into()); + self + } + + /// Add a context to the current object + /// + /// This does not overwrite the contents of context, only appends a new item + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::{context, prelude::*, security}; + /// + /// video + /// .add_context(context()) + /// .add_context(security()); + /// ``` + fn add_context(&mut self, context: T) -> &mut Self + where + T: Into, + { + let c = match self.base_mut().context.take() { + Some(mut c) => { + c.add(context.into()); + c + } + None => vec![context.into()].into(), + }; + self.base_mut().context = Some(c); + self + } + + /// Take the context from the current object, leaving nothing + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(context) = video.take_context() { + /// println!("{:?}", context); + /// } + /// ``` + fn take_context(&mut self) -> Option> { + self.base_mut().context.take() + } + + /// Delete the context from the current object + /// + /// ```rust + /// # use activitystreams::{context, object::Video}; + /// # let mut video = Video::new(); + /// # video.set_context(context()); + /// # + /// use activitystreams::prelude::*; + /// + /// assert!(video.context().is_some()); + /// video.delete_context(); + /// assert!(video.context().is_none()); + /// ``` + fn delete_context(&mut self) -> &mut Self { + self.base_mut().context = None; + self + } + + /// Fetch the id for the current object, checking it against the provided domain + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{object::Video, uri}; + /// # let mut video = Video::new(); + /// # video.set_id(uri!("https://example.com")); + /// use activitystreams::prelude::*; + /// + /// assert_eq!(video.id("example.com")?, Some(&uri!("https://example.com"))); + /// # Ok(()) + /// # } + /// ``` + fn id<'a>(&'a self, domain: &str) -> Result, 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 + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(id) = video.id_unchecked() { + /// println!("{:?}", id); + /// } + /// ``` + fn id_unchecked<'a>(&'a self) -> Option<&'a Url> + where + Kind: 'a, + { + self.base_ref().id.as_ref() + } + + /// Mutably borrow the ID from the current object + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::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() + } + + /// Check if the provided id is equal to the object's id + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::{object::Video, prelude::*, uri}; + /// + /// let video: Video = serde_json::from_str(r#"{"type":"Video","id":"https://example.com"}"#)?; + /// + /// assert!(video.is_id(&uri!("https://example.com"))); + /// # Ok(()) + /// # } + /// ``` + fn is_id(&self, id: &Url) -> bool { + self.id_unchecked() == Some(id) + } + + /// Set the id for the current object + /// + /// This overwrites the contents of id + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::{prelude::*, uri}; + /// + /// video.set_id(uri!("https://example.com")); + /// # Ok(()) + /// # } + /// ``` + fn set_id(&mut self, id: Url) -> &mut Self { + self.base_mut().id = Some(id.into()); + self + } + + /// Take the id from the current object, leaving nothing + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(id) = video.take_id() { + /// println!("{:?}", id); + /// } + /// ``` + fn take_id(&mut self) -> Option { + self.base_mut().id.take() + } + + /// Delete the id from the current object + /// + /// ```rust + /// # use activitystreams::{context, object::Video}; + /// # let mut video = Video::new(); + /// # video.set_id(context()); + /// # + /// use activitystreams::prelude::*; + /// + /// assert!(video.id_unchecked().is_some()); + /// video.delete_id(); + /// assert!(video.id_unchecked().is_none()); + /// ``` + fn delete_id(&mut self) -> &mut Self { + self.base_mut().id = None; + self + } + + /// Fetch the kind for the current object + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(kind) = video.kind() { + /// println!("{:?}", kind); + /// } + /// ``` + fn kind<'a>(&'a self) -> Option<&'a Kind> + where + Kind: 'a, + { + self.base_ref().kind.as_ref() + } + + /// Check if the provided Kind is equal to the object's Kind + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::{base::Base, prelude::*}; + /// + /// #[derive(PartialEq, serde::Deserialize)] + /// pub enum ValidKinds { + /// Video, + /// Image, + /// } + /// + /// let video: Base = serde_json::from_str(r#"{"type":"Video"}"#)?; + /// + /// assert!(video.is_kind(&ValidKinds::Video)); + /// # Ok(()) + /// # } + /// ``` + fn is_kind(&self, kind: &Kind) -> bool + where + Kind: PartialEq, + { + self.kind() == Some(kind) + } + + /// Set the kind for the current object + /// + /// This overwrites the contents of kind + /// + /// ```rust + /// # use activitystreams::object::{Video, kind::VideoType}; + /// # let mut video = Video::new(); + /// use activitystreams::prelude::*; + /// + /// video.set_kind(VideoType::Video); + /// ``` + fn set_kind(&mut self, kind: Kind) -> &mut Self { + self.base_mut().kind = Some(kind); + self + } + + /// Take the kind from the current object, leaving nothing + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(kind) = video.take_kind() { + /// println!("{:?}", kind); + /// } + /// ``` + fn take_kind(&mut self) -> Option { + self.base_mut().kind.take() + } + + /// Delete the kind from the current object + /// + /// ```rust + /// # use activitystreams::{object::{Video, kind::VideoType}}; + /// # let mut video = Video::new(); + /// # video.set_kind(VideoType::Video); + /// # + /// use activitystreams::prelude::*; + /// + /// assert!(video.kind().is_some()); + /// video.delete_kind(); + /// assert!(video.kind().is_none()); + /// ``` + fn delete_kind(&mut self) -> &mut Self { + self.base_mut().kind = None; + self + } + + /// Fetch the name for the current object + /// + /// ``` + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(name) = video.name() { + /// println!("{:?}", name); + /// } + /// ``` + fn name<'a>(&'a self) -> Option> + where + Kind: 'a, + { + self.base_ref().name.as_ref().map(|o| o.as_ref()) + } + + /// Set the name for the current object + /// + /// This overwrites the contents of name + /// + /// ```rust + /// use activitystreams::prelude::*; + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// video.set_name("hi"); + /// ``` + fn set_name(&mut self, name: T) -> &mut Self + where + T: Into, + { + self.base_mut().name = Some(name.into().into()); + self + } + + /// Set many names for the current object + /// + /// This overwrites the contents of name + /// + /// ```rust + /// use activitystreams::prelude::*; + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// video.set_many_names(vec!["hi", "hey"]); + /// ``` + fn set_many_names(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + self.base_mut().name = Some(v.into()); + self + } + + /// Add a name to the current object + /// + /// This does not overwrite the contents of name, only appends a new item + /// + /// ```rust + /// use activitystreams::prelude::*; + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// video + /// .add_name("hi") + /// .add_name("hey"); + /// ``` + fn add_name(&mut self, name: T) -> &mut Self + where + T: Into, + { + let a = match self.base_mut().name.take() { + Some(mut a) => { + a.add(name.into()); + a + } + None => vec![name.into()].into(), + }; + self.base_mut().name = Some(a); + self + } + + /// Take the name from the current object, leaving nothing + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(name) = video.take_name() { + /// println!("{:?}", name); + /// } + /// ``` + fn take_name(&mut self) -> Option> { + self.base_mut().name.take() + } + + /// Delete the name from the current object + /// + /// ```rust + /// use activitystreams::prelude::*; + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # video.set_name("hi"); + /// # + /// + /// assert!(video.name().is_some()); + /// video.delete_name(); + /// assert!(video.name().is_none()); + /// ``` + fn delete_name(&mut self) -> &mut Self { + self.base_mut().name = None; + self + } + + /// Fetch the media type for the current object + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(media_type) = video.media_type() { + /// println!("{:?}", media_type); + /// } + /// ``` + fn media_type<'a>(&'a self) -> Option<&'a Mime> + where + Kind: 'a, + { + self.base_ref().media_type.as_ref().map(|m| m.as_ref()) + } + + /// Set the media type for the current object + /// + /// This overwrites the contents of media_type + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// + /// video.set_media_type("video/webm".parse()?); + /// # Ok(()) + /// # } + /// ``` + fn set_media_type(&mut self, media_type: Mime) -> &mut Self { + self.base_mut().media_type = Some(media_type.into()); + self + } + + /// Take the media type from the current object, leaving nothing + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(media_type) = video.take_media_type() { + /// println!("{:?}", media_type); + /// } + /// ``` + fn take_media_type(&mut self) -> Option { + self.base_mut().media_type.take().map(|m| m.into_inner()) + } + + /// Delete the media type from the current object + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{object::Video}; + /// # let mut video = Video::new(); + /// # video.set_media_type("video/webm".parse()?); + /// # + /// use activitystreams::prelude::*; + /// + /// assert!(video.media_type().is_some()); + /// video.delete_media_type(); + /// assert!(video.media_type().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_media_type(&mut self) -> &mut Self { + self.base_mut().media_type = None; + self + } + + /// Fetch the preview for the current object + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(preview) = video.preview() { + /// println!("{:?}", preview); + /// } + /// ``` + fn preview<'a>(&'a self) -> Option> + where + Kind: 'a, + { + self.base_ref().preview.as_ref().map(|o| o.as_ref()) + } + + /// Set the preview for the current object + /// + /// This overwrites the contents of preview + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{object::Video, uri}; + /// # let mut video = Video::new(); + /// + /// video.set_preview(uri!("https://example.com")); + /// # Ok(()) + /// # } + /// ``` + fn set_preview(&mut self, preview: T) -> &mut Self + where + T: Into, + { + self.base_mut().preview = Some(preview.into().into()); + self + } + + /// Set many previews for the current object + /// + /// This overwrites the contents of preview + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{object::Video, uri}; + /// # let mut video = Video::new(); + /// + /// video.set_many_previews(vec![ + /// uri!("https://example.com/one"), + /// uri!("https://example.com/two"), + /// ]); + /// # Ok(()) + /// # } + /// ``` + fn set_many_previews(&mut self, items: I) -> &mut Self + where + I: IntoIterator, + T: Into, + { + let v: Vec<_> = items.into_iter().map(Into::into).collect(); + self.base_mut().preview = Some(v.into()); + self + } + + /// Add a preview to the current object + /// + /// This does not overwrite the contents of preview, only appends an item + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// use activitystreams::prelude::*; + /// # use activitystreams::{object::Video, uri}; + /// # let mut video = Video::new(); + /// + /// video + /// .add_preview(uri!("https://example.com/one")) + /// .add_preview(uri!("https://example.com/two")); + /// # Ok(()) + /// # } + /// ``` + fn add_preview(&mut self, preview: T) -> &mut Self + where + T: Into, + { + let a = match self.base_mut().preview.take() { + Some(mut a) => { + a.add(preview.into()); + a + } + None => vec![preview.into()].into(), + }; + self.base_mut().preview = Some(a); + self + } + + /// Take the preview from the current object, leaving nothing + /// + /// ```rust + /// # use activitystreams::object::Video; + /// # let mut video = Video::new(); + /// # + /// use activitystreams::prelude::*; + /// + /// if let Some(preview) = video.take_preview() { + /// println!("{:?}", preview); + /// } + /// ``` + fn take_preview(&mut self) -> Option> { + self.base_mut().preview.take() + } + + /// Delete the preview from the current object + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{object::Video, uri}; + /// # let mut video = Video::new(); + /// # video.set_preview(uri!("https://example.com")); + /// # + /// use activitystreams::prelude::*; + /// + /// assert!(video.preview().is_some()); + /// video.delete_preview(); + /// assert!(video.preview().is_none()); + /// # Ok(()) + /// # } + /// ``` + fn delete_preview(&mut self) -> &mut Self { + self.base_mut().preview = None; + self + } +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(transparent)] +struct IdOrBase(Either>>); + +/// A type that can represent Any ActivityStreams type +/// +/// A type in activitystreams can be four things +/// - An Object +/// - A Link +/// - The ID of that Link or Object +/// - A string representing that Link or Object +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(transparent)] +pub struct AnyBase(Either); + +/// A representation of the common fields between Links and Objects in ActivityStreams +/// +/// 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)] +#[serde(rename_all = "camelCase")] +pub struct Base { + /// Identifies the context within which the object exists or an activity was performed. + /// + /// The notion of "context"used is intentionally vague. The intended function is to serve as a + /// means of grouping objects and activities that share a common originating context or + /// purpose. An example could be all activities relating to a common project or event. + /// + /// - Range: Object | Link + /// - Functional: false + #[serde(rename = "@context")] + #[serde(skip_serializing_if = "Option::is_none")] + context: Option>, + + /// Provides the globally unique identifier for an Object or Link. + /// + /// The id property is expressed as an absolute IRI in the spec, but for now is represented as + /// a string. + /// + /// - Range: xsd:anyUri + /// - Functional: true + /// + /// ### From ActivityStreams's deprecation notes + /// + /// 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")] + id: Option, + + /// The `type` field + /// + /// ### From JSON-LD's `type` documentation + /// + /// This section is non-normative. + /// + /// In Linked Data, it is common to specify the type of a graph node; in many cases, this can + /// be inferred based on the properties used within a given node object, or the property for + /// which a node is a value. For example, in the schema.org vocabulary, the givenName property + /// is associated with a Person. Therefore, one may reason that if a node object contains the + /// property givenName, that the type is a Person; making this explicit with @type helps to + /// clarify the association. + /// + /// The type of a particular node can be specified using the @type keyword. In Linked Data, + /// types are uniquely identified with an IRI. + /// + /// ### From ActivityStreams's deprecation notes + /// + /// When processing Activity Streams 1.0 documents and converting those to 2.0, implementations + /// ought to treat [...] the objectType and verb properties as aliases for the JSON-LD @type + /// keyword. + #[serde(rename = "type")] + #[serde(alias = "@type")] + #[serde(alias = "objectType")] + #[serde(alias = "verb")] + #[serde(skip_serializing_if = "Option::is_none")] + kind: Option, + + /// A simple, human-readable, plain-text name for the object. + /// + /// HTML markup MUST NOT be included. The name MAY be expressed using multiple language-tagged values. + /// + /// - Range: xsd:string | rdf:langString + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + name: Option>, + + /// When used on an Object, identifies the MIME media type of the value of the content property. + /// + /// If not specified, the content property is assumed to contain text/html content. + /// + /// - Range: Mime Media Type + /// - Functional: true + #[serde(skip_serializing_if = "Option::is_none")] + media_type: Option, + + /// Identifies an entity that provides a preview of this object. + /// + /// - Range: Object | Link + /// - Functional: false + #[serde(skip_serializing_if = "Option::is_none")] + preview: Option>, + + /// 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)] + unparsed: Unparsed, +} + +impl Base { + /// Convert this `Base` into a `Base` + /// + /// This is required before extending Base into the other types found in this crate + pub fn solidify(self) -> Result, serde_json::Error> + where + Kind: serde::de::DeserializeOwned, + { + self.try_map_kind(serde_json::from_value) + } +} + +impl Base { + /// Create a new Base + /// + /// ```rust + /// use activitystreams::base::Base; + /// + /// let base = Base::::new(); + /// ``` + pub fn new() -> Self + where + Kind: Default, + { + 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 + /// + /// ```rust + /// # fn main() -> Result<(), anyhow::Error> { + /// # use activitystreams::{base::Base, object::{Video, kind::VideoType}}; + /// # let base = Base::::new(); + /// # + /// let video = base.extend::