//! # ActivityStreams New //! __A set of Traits and Types that make up the ActivityStreams and ActivityPub specifications__ //! //! ## Usage //! //! First, add ActivityStreams to your dependencies //! ```toml //! [dependencies] //! activitystreams-new = { git = "https://git.asonix.dog/asonix/activitystreams-sketch" } //! ``` //! //! ### Types //! //! The project is laid out by Kind => Type //! //! So to use an ActivityStreams Video, you'd write //! ```rust //! use activitystreams_new::object::Video; //! let video = Video::new(); //! ``` //! //! And to use an ActivityPub profile, you'd write //! ```rust //! use activitystreams_new::object::{ApObject, Profile}; //! let inner = Profile::new(); //! let profile = ApObject::new(inner); //! ``` //! //! There's only one kind of Link //! ```rust //! use activitystreams_new::link::Mention; //! let mention = Mention::new(); //! ``` //! //! ### Fields //! //! Many fields on the provided types are wrapped in `OneOrMany<>` or have a type of `AnyBase`. This //! is because the activitystreams spec is very open as to what is considered a valid structure. //! //! 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. This library represents this as `Option>`. //! //! 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" //! }, //! "An xsd:string this time" //! ] //! } //! ``` //! //! It may seem like interacting with these types might get unweildy, there are some custom methods //! implemented on the `OneOrMany` type depending on what's inside of it. //! //! ```rust,ignore //! fn from_xsd_string(&mut self, T) -> Self; //! fn from_rdf_lang_string(&mut self, T) -> Self; //! //! fn as_single_xsd_string(&self) -> Option<&XsdString>; //! fn as_single_rdf_langstring(&self) -> Option<&RdfLangString>; //! //! fn single_xsd_string(self) -> Option; //! fn single_rdf_lang_string(self) -> Option; //! //! fn add_xsd_string(&mut self, T) -> &mut Self; //! fn add_rdf_lang_string(&mut self, T) -> &mut Self; //! ``` //! 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 //! `Into` or `Into`. 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` and `Into`). //! //! ### Traits //! //! Since ActivityStreams is a heirarchical structure of data, it's represented as structs containing //! other structs. This means that the `context` field, which can be present on any ActivityStreams type, //! will be located in the innermost struct. In order to avoid writing code like //! `ap_object.collection.object.base.context = Some(context())`, this library provides traits that are //! automatically implmeneted for provided types. //! //! For example, the `BaseExt` trait provides the following methods for `context`, //! ```rust,ignore //! fn context(&self) -> Option<&OneOrMany>; //! //! fn set_context(&mut self, context: T) -> &mut Self //! where //! T: Into; //! //! fn set_many_contexts(&mut self, items: I) -> &mut Self //! where //! I: IntoIterator, //! T: Into; //! //! fn add_context(&mut self, context: T) -> &mut Self //! where //! T: Into; //! //! fn take_context(&mut self) -> Option>; //! fn delete_context(&mut self) -> &mut Self; //! ``` //! //! For fields with more specific bounds, like `id`, //! ```rust,ignore //! fn id(&self) -> Option<&XsdAnyUri>; //! fn set_id(&mut self, XsdAnyUri) -> &mut Self; //! fn take_id(&self) -> Option; //! fn delete_id(&mut self) -> &mut Self; //! ``` //! //! The full list of extension traits that implement methods like these on types can be found in the //! prelude module. By using `use activitystreams_new::prelude::*;` all of the methods will be //! implemented for types containing their fields. //! //! ### Markers //! //! 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: //! //! ```rust //! use activitystreams_new::{base::BaseExt, context, markers::Activity}; //! //! fn manipulator(mut activity: T) -> Result<(), anyhow::Error> //! where //! T: Activity + BaseExt, //! { //! activity //! .set_id("https://example.com".parse()?) //! .set_context(context()); //! Ok(()) //! } //! ``` //! //! ### 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_new::objects::kind`, and similar paths. //! //! To build your own Person struct, for example, you could write //! ```rust //! use activitystreams_new::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"` //! //! ## Examples //! //! ### Create //! //! ```rust //! use activitystreams_new::{ //! context, //! object::{ApObject, Video}, //! prelude::*, //! primitives::{XsdAnyUri, XsdString}, //! }; //! //! fn main() -> Result<(), anyhow::Error> { //! let mut video = ApObject::new(Video::new()); //! //! video //! .set_context(context()) //! .set_id("https://example.com/@example/lions".parse()?) //! .set_media_type("video/webm".parse()?) //! .set_url("https://example.com/@example/lions/video.webm".parse::()?) //! .set_summary(XsdString::from("A cool video")) //! .set_duration("PT4M20S".parse()?) //! .set_shares("https://example.com/@example/lions/video.webm#shares".parse()?); //! //! println!("Video, {:#?}", video); //! //! let s = serde_json::to_string(&video)?; //! //! println!("json, {}", s); //! //! let v: ApObject