2018-05-12 20:14:43 +00:00
|
|
|
/*
|
|
|
|
* This file is part of ActivityStreams.
|
|
|
|
*
|
2020-03-11 16:17:40 +00:00
|
|
|
* Copyright © 2020 Riley Trautman
|
2018-05-12 20:14:43 +00:00
|
|
|
*
|
|
|
|
* ActivityStreams is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* ActivityStreams is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2018-05-14 06:18:12 +00:00
|
|
|
//! ActivityStreams
|
|
|
|
//!
|
2020-03-12 02:45:53 +00:00
|
|
|
//! A set of Traits and Types that make up the ActivityStreams and ActivityPub specifications
|
2018-05-14 06:18:12 +00:00
|
|
|
//!
|
2020-03-11 18:37:22 +00:00
|
|
|
//! ## Usage
|
|
|
|
//!
|
|
|
|
//! First, add ActivityStreams to your dependencies
|
|
|
|
//! ```toml
|
2020-03-12 02:45:53 +00:00
|
|
|
//! activitystreams = "0.4.0"
|
2020-03-11 18:37:22 +00:00
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! ### Types
|
|
|
|
//!
|
|
|
|
//! The project is laid out by Kind => vocabulary => Type
|
|
|
|
//!
|
|
|
|
//! So to use an ActivityStreams Video, you'd write
|
|
|
|
//! ```rust
|
|
|
|
//! use activitystreams::object::streams::Video;
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! And to use an ActivityPub profile, you'd write
|
|
|
|
//! ```rust
|
|
|
|
//! use activitystreams::object::apub::Profile;
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! Link is a little different, since there's only one defined link type, called Mention.
|
|
|
|
//! ```rust
|
|
|
|
//! use activitystreams::link::Mention;
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! ### Properties
|
|
|
|
//!
|
|
|
|
//! Each concrete type implements `AsRef<>` for each of their properties fields. A basic
|
|
|
|
//! ActivityStreams object will implement `AsRef<ObjectProperties>`, while an ActivityPub Actor
|
|
|
|
//! might implement `AsRef<ObjectProperties>`, `AsRef<ApObjectProperties>`, and
|
|
|
|
//! `AsRef<ApActorProperties>`.
|
|
|
|
//!
|
|
|
|
//! The Properties types can be found near the kind they're associated with. `ObjectProperties` and
|
|
|
|
//! `ApObjectProperties` are located in `activitystreams::object::properties`.
|
|
|
|
//!
|
|
|
|
//! The Properties types are generated by the `properties` macro, which attempts to create fields
|
|
|
|
//! that represent exactly the bounds of the ActivityStreams and ActivityPub specifications.
|
|
|
|
//!
|
|
|
|
//! For example, the Object type in ActivityStreams has a `summary` field, which can either be
|
|
|
|
//! represented as an `xsd:string` or an `rdf:langString`. It also states that the `summary` field
|
|
|
|
//! is not `functional`, meaning that any number of `xsd:string` or `rdf:langString`, or a
|
|
|
|
//! combination thereof, can be present. To represent this, the `properties` macro generates a
|
|
|
|
//! couple `enum` types.
|
|
|
|
//!
|
|
|
|
//! First, it generates `ObjectPropertiesSummaryTermEnum`, which is a "terminating" enum.
|
|
|
|
//! "terminating" in this context means it is the smallest unit of the type. This enum has two
|
|
|
|
//! variants, named after the types they contain, `XsdString(...)` and `RdfLangString(...)`.
|
|
|
|
//!
|
|
|
|
//! Next, it generates `ObjectPropertiesSummaryEnum`, which contains two variants, `Term(...)` and
|
|
|
|
//! `Array(...)`. The `Term` variant contains an `ObjectPropertiesSummaryTermEnum`, and the `Array`
|
|
|
|
//! variant contains a `Vec<ObjectPropertiesSummaryTermEnum>`.
|
|
|
|
//!
|
|
|
|
//! Finally, when declaring the field, it generates `summary: Option<ObjectPropertiesSummaryEnum>`,
|
|
|
|
//! since `summary` is not a required field.
|
|
|
|
//!
|
|
|
|
//! This resulting type is exactly specific enough to match the following valid ActivityStreams
|
|
|
|
//! json, without matching any invalid json.
|
|
|
|
//!
|
|
|
|
//! With no summary:
|
|
|
|
//! ```json
|
|
|
|
//! {}
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! With a sring summary:
|
|
|
|
//! ```json
|
|
|
|
//! {
|
|
|
|
//! "summary": "A string"
|
|
|
|
//! }
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! With an rdf langstring
|
|
|
|
//! ```json
|
|
|
|
//! {
|
|
|
|
//! "summary": {
|
|
|
|
//! "@value": "A string",
|
|
|
|
//! "@language": "en"
|
|
|
|
//! }
|
|
|
|
//! }
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! With multiple values
|
|
|
|
//! ```json
|
|
|
|
//! {
|
|
|
|
//! "summary": [
|
|
|
|
//! {
|
|
|
|
//! "@value": "A string",
|
|
|
|
//! "@language": "en"
|
|
|
|
//! },
|
2020-03-12 02:45:53 +00:00
|
|
|
//! "An xsd:string this time"
|
2020-03-11 18:37:22 +00:00
|
|
|
//! ]
|
|
|
|
//! }
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! It may seem like interacting with these types might get unweildy, so the `properties` macro
|
|
|
|
//! also generates methods for interacting with each field.
|
|
|
|
//!
|
|
|
|
//! ```ignore
|
|
|
|
//! fn set_summary_xsd_string<T>(&mut self, T) -> Result<...>;
|
|
|
|
//! fn set_summary_rdf_lang_string<T>(&mut self, T) -> Result<...>;
|
|
|
|
//! fn set_many_summary_xsd_strings<T>(&mut self, Vec<T>) -> Result<...>;
|
|
|
|
//! fn set_many_summary_rdf_lang_strings<T>(&mut self, Vec<T>) -> Result<...>;
|
|
|
|
//!
|
|
|
|
//! fn delete_summary(&mut self) -> &mut Self;
|
|
|
|
//!
|
|
|
|
//! fn get_summary_xsd_string(&self) -> Option<XsdString>;
|
|
|
|
//! fn get_summary_rdf_lang_string(&self) -> Option<RdfLangString>;
|
|
|
|
//! fn get_many_summary_xsd_strings(&self) -> Option<Vec<&XsdString>>;
|
|
|
|
//! fn get_many_summary_rdf_lang_strings(&self) -> Option<Vec<&RdfLangString>>;
|
|
|
|
//! ```
|
|
|
|
//! These methods provide access to setting and fetching uniformly typed data, as well as deleting
|
|
|
|
//! the data. In the setter methods, the type parameter T is bound by
|
|
|
|
//! `TryInto<XsdString>` or `TryInto<RdfLangString>`. This allows passing values to the method that
|
|
|
|
//! can be converted into the types, rather than requiring the caller to perform the conversion.
|
|
|
|
//!
|
|
|
|
//! Types like `XsdString` and `RdfLangString` can be found in the `primitives` module. Unless
|
|
|
|
//! you're building your own custom types, you shouldn't need to import them yourself. They each
|
|
|
|
//! implement `FromStr` for parsing and `Display` to convert back to strings, as well as `From` and
|
|
|
|
//! `Into` or `TryFrom` and `TryInto` for types you might expect them to (e.g.
|
|
|
|
//! `XsdNonNegativeInteger` implements `From<u64>` and `Into<u64>`).
|
|
|
|
//!
|
|
|
|
//! For some fields, like `id`, there is only one valid type. methods generated for fields like
|
|
|
|
//! these will leave out the type name from the function name.
|
|
|
|
//!
|
|
|
|
//! ```ignore
|
|
|
|
//! fn set_id<T>(&mut self, T) -> Result<...>;
|
|
|
|
//! fn delete_id(&mut self) -> &mut Self;
|
|
|
|
//! fn get_id(&self) -> Option<XsdAnyUri>;
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! ### Traits
|
|
|
|
//!
|
|
|
|
//! This library provides a number of traits, such as `Object`, `Link`, `Actor`, `Activity`,
|
|
|
|
//! `Collection`, and `CollectionPage`. The majority of these traits exist solely to "mark" types,
|
|
|
|
//! meaning they don't provide value, at runtime, but exist to add constraints to generics at
|
|
|
|
//! compiletime.
|
|
|
|
//!
|
|
|
|
//! If you want to make a function that manipulates an Activity, but not a normal object, you could
|
|
|
|
//! bound the function like so:
|
|
|
|
//! ```ignore
|
|
|
|
//! fn my_manipulator<T>(some_activity: T) -> Result<&mut ObjectProperties, SomeErrorType>
|
|
|
|
//! where
|
|
|
|
//! T: Activity + AsMut<ObjectProperties>,
|
|
|
|
//! {
|
|
|
|
//! some_activity.as_mut().set_whatever_tbh()
|
|
|
|
//! }
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! ### Kinds
|
|
|
|
//!
|
|
|
|
//! This library has a set of unit structs that serialize and deserialize to strings. This is to
|
|
|
|
//! enable different ActivityPub Object types to be deserialized into different Named structs.
|
|
|
|
//! These can be found in `activitystreams::objects::kind`, and similar paths.
|
|
|
|
//!
|
|
|
|
//! To build your own Person struct, for example, you could write
|
|
|
|
//! ```ignore
|
|
|
|
//! use activitystreams::actor::kind::PersonType;
|
|
|
|
//!
|
|
|
|
//! #[derive(serde::Deserialize, serde::Serialize)]
|
|
|
|
//! pub struct MyPerson {
|
|
|
|
//! // Do a rename since `type` is not a valid rust field name
|
|
|
|
//! #[serde(rename = "type")]
|
|
|
|
//! kind: PersonType,
|
|
|
|
//! }
|
|
|
|
//! ```
|
|
|
|
//! And this type would only deserialize for JSON where `"type":"Person"`
|
|
|
|
//!
|
|
|
|
//! ### Features
|
|
|
|
//! There are a number of features that can be disabled in this crate. By default, everything is
|
|
|
|
//! enabled.
|
|
|
|
//!
|
|
|
|
//! ```toml
|
|
|
|
//! activitystreams = { version = "0.4.0", default-features = "false", features = ["derive"] }
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! | feature | what you get |
|
|
|
|
//! | ---------- | --------------------------------------------------------- |
|
|
|
|
//! | none | Just the Marker Traits |
|
|
|
|
//! | derive | Marker Traits + derive macros from activitystreams-derive |
|
|
|
|
//! | kinds | Marker Traits + derive macros + Kind UnitStructs |
|
|
|
|
//! | primitives | Marker Traits + Primitive values |
|
|
|
|
//! | types | Everything, this is the default |
|
|
|
|
//!
|
2018-05-14 06:18:12 +00:00
|
|
|
//! ## Examples
|
|
|
|
//!
|
|
|
|
//! ### Basic
|
|
|
|
//!
|
|
|
|
//! ```rust
|
2020-03-11 18:37:22 +00:00
|
|
|
//! use activitystreams::object::{streams::Video, properties::ObjectProperties};
|
|
|
|
//! use anyhow::Error;
|
|
|
|
//!
|
|
|
|
//! // We perform configuration in a dedicated function to specify which Properties type we want to
|
|
|
|
//! // perform the operations on.
|
|
|
|
//! fn configure_video(mut v: impl AsMut<ObjectProperties>) -> Result<(), Error> {
|
|
|
|
//! v.as_mut()
|
|
|
|
//! .set_context_xsd_any_uri("https://www.w3.org/ns/activitystreams")?
|
|
|
|
//! .set_id("https://example.com/@example/lions")?
|
|
|
|
//! .set_url_xsd_any_uri("https://example.com/@example/lions/video.webm")?
|
|
|
|
//! .set_name_xsd_string("My Cool Video")?
|
|
|
|
//! .set_summary_xsd_string("A video about some cool lions")?
|
|
|
|
//! .set_media_type("video/webm")?
|
|
|
|
//! .set_duration("PT4M20S")?;
|
|
|
|
//!
|
|
|
|
//! Ok(())
|
|
|
|
//! }
|
|
|
|
//!
|
|
|
|
//! fn main() -> Result<(), Error> {
|
|
|
|
//! let mut v = Video::default();
|
|
|
|
//!
|
|
|
|
//! configure_video(&mut v)?;
|
|
|
|
//!
|
|
|
|
//! println!("Video, {:#?}", v);
|
|
|
|
//!
|
|
|
|
//! let s = serde_json::to_string(&v)?;
|
|
|
|
//!
|
|
|
|
//! println!("json, {}", s);
|
|
|
|
//!
|
|
|
|
//! let v: Video = serde_json::from_str(&s)?;
|
|
|
|
//!
|
|
|
|
//! println!("Video again, {:#?}", v);
|
|
|
|
//!
|
|
|
|
//! Ok(())
|
|
|
|
//! }
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! ### Intermediate
|
|
|
|
//!
|
|
|
|
//! ```rust
|
2020-03-10 19:55:15 +00:00
|
|
|
//! use activitystreams::{
|
|
|
|
//! context,
|
|
|
|
//! object::{
|
|
|
|
//! properties::{
|
|
|
|
//! ObjectProperties,
|
|
|
|
//! ProfileProperties
|
|
|
|
//! },
|
2020-03-11 18:37:22 +00:00
|
|
|
//! apub::Profile,
|
2020-03-10 19:55:15 +00:00
|
|
|
//! },
|
|
|
|
//! primitives::XsdAnyUri,
|
|
|
|
//! Actor,
|
|
|
|
//! Object,
|
|
|
|
//! };
|
|
|
|
//! use serde::{Deserialize, Serialize};
|
|
|
|
//! use std::any::Any;
|
2018-05-14 06:18:12 +00:00
|
|
|
//!
|
|
|
|
//! #[derive(Clone, Debug, Default, Deserialize, Serialize)]
|
|
|
|
//! #[serde(rename_all = "camelCase")]
|
|
|
|
//! pub struct Persona {
|
|
|
|
//! #[serde(rename = "@context")]
|
2020-03-10 19:55:15 +00:00
|
|
|
//! context: XsdAnyUri,
|
2018-05-14 06:18:12 +00:00
|
|
|
//!
|
|
|
|
//! #[serde(rename = "type")]
|
|
|
|
//! kind: String,
|
|
|
|
//! }
|
|
|
|
//!
|
2020-03-10 19:55:15 +00:00
|
|
|
//! #[typetag::serde]
|
|
|
|
//! impl Object for Persona {
|
|
|
|
//! fn as_any(&self) -> &(dyn Any + 'static) {
|
|
|
|
//! self
|
|
|
|
//! }
|
|
|
|
//!
|
|
|
|
//! fn as_any_mut(&mut self) -> &mut (dyn Any + 'static) {
|
|
|
|
//! self
|
|
|
|
//! }
|
|
|
|
//!
|
|
|
|
//! fn duplicate(&self) -> Box<dyn Object + 'static> {
|
|
|
|
//! Box::new(self.clone())
|
|
|
|
//! }
|
|
|
|
//! }
|
2018-05-14 06:18:12 +00:00
|
|
|
//! impl Actor for Persona {}
|
|
|
|
//!
|
2020-03-10 19:55:15 +00:00
|
|
|
//! fn main() -> Result<(), anyhow::Error> {
|
2018-05-14 06:18:12 +00:00
|
|
|
//! let mut profile = Profile::default();
|
|
|
|
//!
|
2020-03-10 19:55:15 +00:00
|
|
|
//! let pprops: &mut ProfileProperties = profile.as_mut();
|
2018-05-14 06:18:12 +00:00
|
|
|
//!
|
2020-03-10 19:55:15 +00:00
|
|
|
//! pprops.set_describes_object_box(Persona {
|
|
|
|
//! context: context(),
|
2018-05-14 06:18:12 +00:00
|
|
|
//! kind: "Persona".to_owned(),
|
|
|
|
//! })?;
|
|
|
|
//!
|
2020-03-10 19:55:15 +00:00
|
|
|
//! let oprops: &mut ObjectProperties = profile.as_mut();
|
|
|
|
//! oprops.set_context_xsd_any_uri(context())?;
|
2018-05-14 06:18:12 +00:00
|
|
|
//!
|
|
|
|
//! let profile_string = serde_json::to_string(&profile)?;
|
|
|
|
//!
|
|
|
|
//! let profile: Profile = serde_json::from_str(&profile_string)?;
|
|
|
|
//!
|
|
|
|
//! Ok(())
|
|
|
|
//! }
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! ### Advanced
|
|
|
|
//!
|
|
|
|
//! ```rust
|
2020-03-10 19:55:15 +00:00
|
|
|
//! use activitystreams::{
|
|
|
|
//! properties,
|
|
|
|
//! link::{
|
|
|
|
//! properties::LinkProperties,
|
|
|
|
//! Mention,
|
|
|
|
//! },
|
|
|
|
//! Link,
|
|
|
|
//! PropRefs,
|
|
|
|
//! UnitString,
|
|
|
|
//! };
|
|
|
|
//! use serde::{Deserialize, Serialize};
|
2018-05-14 06:18:12 +00:00
|
|
|
//!
|
|
|
|
//! /// Using the UnitString derive macro
|
|
|
|
//! ///
|
|
|
|
//! /// This macro implements Serialize and Deserialize for the given type, making this type
|
2020-03-10 19:55:15 +00:00
|
|
|
//! /// represent the string "MyLink" in JSON.
|
2018-05-14 06:18:12 +00:00
|
|
|
//! #[derive(Clone, Debug, Default, UnitString)]
|
2020-03-10 19:55:15 +00:00
|
|
|
//! #[activitystreams(MyLink)]
|
2018-05-14 06:18:12 +00:00
|
|
|
//! pub struct MyKind;
|
|
|
|
//!
|
2020-03-10 19:55:15 +00:00
|
|
|
//! properties! {
|
|
|
|
//! My {
|
|
|
|
//! docs [ "Defining our own properties struct called MyProperties" ],
|
|
|
|
//!
|
|
|
|
//! required_key {
|
|
|
|
//! docs [
|
|
|
|
//! "Our own required key field",
|
|
|
|
//! "",
|
|
|
|
//! "'types' defines the range of values that can be stored in required_key",
|
|
|
|
//! "",
|
|
|
|
//! "'functional' means there is at most one value for required_key",
|
|
|
|
//! "'required' means there is at least one value for required_key",
|
|
|
|
//! ],
|
|
|
|
//! types [ String ],
|
|
|
|
//! functional,
|
|
|
|
//! required,
|
|
|
|
//! },
|
|
|
|
//! }
|
|
|
|
//! }
|
|
|
|
//!
|
2018-05-14 06:18:12 +00:00
|
|
|
//! /// Using the Properties derive macro
|
|
|
|
//! ///
|
|
|
|
//! /// This macro generates getters and setters for the associated fields.
|
2020-03-10 19:55:15 +00:00
|
|
|
//! #[derive(Clone, Debug, Default, Deserialize, Serialize, PropRefs)]
|
2018-05-14 06:18:12 +00:00
|
|
|
//! #[serde(rename_all = "camelCase")]
|
2020-03-10 19:55:15 +00:00
|
|
|
//! pub struct My {
|
|
|
|
//! /// Use the UnitString MyKind to enforce the type of the object by "MyLink"
|
2018-05-14 06:18:12 +00:00
|
|
|
//! pub kind: MyKind,
|
|
|
|
//!
|
2020-03-10 19:55:15 +00:00
|
|
|
//! /// Derive AsRef/AsMut for My -> MyProperties
|
|
|
|
//! #[activitystreams(None)]
|
|
|
|
//! pub my_properties: MyProperties,
|
2018-05-14 06:18:12 +00:00
|
|
|
//!
|
2020-03-11 18:37:22 +00:00
|
|
|
//! /// Derive AsRef/AsMut/Link for My -> LinkProperties
|
2020-03-10 19:55:15 +00:00
|
|
|
//! #[activitystreams(Link)]
|
|
|
|
//! pub link_properties: LinkProperties,
|
|
|
|
//! }
|
2018-05-14 06:18:12 +00:00
|
|
|
//!
|
2020-03-10 19:55:15 +00:00
|
|
|
//! fn main() -> Result<(), anyhow::Error> {
|
|
|
|
//! let mut my_link = My::default();
|
2018-05-14 06:18:12 +00:00
|
|
|
//!
|
2020-03-10 19:55:15 +00:00
|
|
|
//! let lprops: &mut MyProperties = my_link.as_mut();
|
|
|
|
//! lprops.set_required_key("Hey")?;
|
2018-05-14 06:18:12 +00:00
|
|
|
//!
|
|
|
|
//! let my_link_string = serde_json::to_string(&my_link)?;
|
|
|
|
//!
|
2020-03-10 19:55:15 +00:00
|
|
|
//! let my_link: My = serde_json::from_str(&my_link_string)?;
|
2018-05-14 06:18:12 +00:00
|
|
|
//!
|
|
|
|
//! Ok(())
|
|
|
|
//! }
|
|
|
|
//! ```
|
|
|
|
|
2018-05-12 05:31:33 +00:00
|
|
|
pub mod activity;
|
|
|
|
pub mod actor;
|
|
|
|
pub mod collection;
|
2020-03-11 16:17:40 +00:00
|
|
|
#[cfg(feature = "types")]
|
|
|
|
pub mod endpoint;
|
2018-05-12 05:31:33 +00:00
|
|
|
pub mod link;
|
|
|
|
pub mod object;
|
2020-03-11 14:56:36 +00:00
|
|
|
#[cfg(feature = "primitives")]
|
|
|
|
pub mod primitives;
|
2018-05-13 20:47:27 +00:00
|
|
|
|
2020-03-10 03:54:41 +00:00
|
|
|
pub use self::{
|
2020-03-10 22:26:04 +00:00
|
|
|
activity::{Activity, IntransitiveActivity},
|
2020-03-10 03:54:41 +00:00
|
|
|
actor::Actor,
|
2020-03-10 22:26:04 +00:00
|
|
|
collection::{Collection, CollectionPage},
|
|
|
|
link::Link,
|
|
|
|
object::Object,
|
2020-03-10 03:54:41 +00:00
|
|
|
};
|
2020-03-10 19:55:15 +00:00
|
|
|
|
2020-03-11 14:56:36 +00:00
|
|
|
#[cfg(feature = "primitives")]
|
|
|
|
/// The context associated with all of the Activity Streams types defined in the crate.
|
|
|
|
pub fn context() -> crate::primitives::XsdAnyUri {
|
|
|
|
"https://www.w3.org/ns/activitystreams".parse().unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "derive")]
|
2020-03-10 19:55:15 +00:00
|
|
|
pub use activitystreams_derive::{properties, PropRefs, UnitString};
|