Skip to main content

activitystreams/
lib.rs

1/*
2 * This file is part of ActivityStreams.
3 *
4 * Copyright © 2020 Riley Trautman
5 *
6 * ActivityStreams is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ActivityStreams is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with ActivityStreams.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20//! ActivityStreams
21//!
22//! A set of Traits and Types that make up the ActivityStreams  and ActivityPub specifications
23//!
24//! ## Usage
25//!
26//! First, add ActivityStreams to your dependencies
27//! ```toml
28//! activitystreams = "0.6.2"
29//! ```
30//!
31//! ### Types
32//!
33//! The project is laid out by Kind => Type
34//!
35//! So to use an ActivityStreams Video, you'd write
36//! ```rust
37//! use activitystreams::object::Video;
38//! ```
39//!
40//! And to use an ActivityStreams profile, you'd write
41//! ```rust
42//! use activitystreams::object::Profile;
43//! ```
44//!
45//! ### Properties
46//!
47//! Each concrete type implements `AsRef<>` for each of their properties fields. A basic
48//! ActivityStreams object will implement `AsRef<ObjectProperties>`.
49//!
50//! The Properties types can be found near the kind they're associated with. `ObjectProperties` and
51//! `ApObjectProperties` are located in `activitystreams::object::properties`.
52//!
53//! The Properties types are generated by the `properties` macro, which attempts to create fields
54//! that represent exactly the bounds of the ActivityStreams and ActivityPub specifications.
55//!
56//! For example, the Object type in ActivityStreams has a `summary` field, which can either be
57//! represented as an `xsd:string` or an `rdf:langString`. It also states that the `summary` field
58//! is not `functional`, meaning that any number of `xsd:string` or `rdf:langString`, or a
59//! combination thereof, can be present. To represent this, the `properties` macro generates a
60//! couple `enum` types.
61//!
62//! First, it generates `ObjectPropertiesSummaryTermEnum`, which is a "terminating" enum.
63//! "terminating" in this context means it is the smallest unit of the type. This enum has two
64//! variants, named after the types they contain, `XsdString(...)` and `RdfLangString(...)`.
65//!
66//! Next, it generates `ObjectPropertiesSummaryEnum`, which contains two variants, `Term(...)` and
67//! `Array(...)`. The `Term` variant contains an `ObjectPropertiesSummaryTermEnum`, and the `Array`
68//! variant contains a `Vec<ObjectPropertiesSummaryTermEnum>`.
69//!
70//! Finally, when declaring the field, it generates `summary: Option<ObjectPropertiesSummaryEnum>`,
71//! since `summary` is not a required field.
72//!
73//! This resulting type is exactly specific enough to match the following valid ActivityStreams
74//! json, without matching any invalid json.
75//!
76//! With no summary:
77//! ```json
78//! {}
79//! ```
80//!
81//! With a sring summary:
82//! ```json
83//! {
84//!     "summary": "A string"
85//! }
86//! ```
87//!
88//! With an rdf langstring
89//! ```json
90//! {
91//!     "summary": {
92//!         "@value": "A string",
93//!         "@language": "en"
94//!     }
95//! }
96//! ```
97//!
98//! With multiple values
99//! ```json
100//! {
101//!     "summary": [
102//!         {
103//!             "@value": "A string",
104//!             "@language": "en"
105//!         },
106//!         "An xsd:string this time"
107//!     ]
108//! }
109//! ```
110//!
111//! It may seem like interacting with these types might get unweildy, so the `properties` macro
112//! also generates methods for interacting with each field.
113//!
114//! ```ignore
115//! fn set_summary_xsd_string<T>(&mut self, T) -> Result<...>;
116//! fn set_summary_rdf_lang_string<T>(&mut self, T) -> Result<...>;
117//! fn set_many_summary_xsd_strings<T>(&mut self, Vec<T>) -> Result<...>;
118//! fn set_many_summary_rdf_lang_strings<T>(&mut self, Vec<T>) -> Result<...>;
119//!
120//! fn delete_summary(&mut self) -> &mut Self;
121//!
122//! fn get_summary_xsd_string(&self) -> Option<XsdString>;
123//! fn get_summary_rdf_lang_string(&self) -> Option<RdfLangString>;
124//! fn get_many_summary_xsd_strings(&self) -> Option<Vec<&XsdString>>;
125//! fn get_many_summary_rdf_lang_strings(&self) -> Option<Vec<&RdfLangString>>;
126//! ```
127//! These methods provide access to setting and fetching uniformly typed data, as well as deleting
128//! the data. In the setter methods, the type parameter T is bound by
129//! `TryInto<XsdString>` or `TryInto<RdfLangString>`. This allows passing values to the method that
130//! can be converted into the types, rather than requiring the caller to perform the conversion.
131//!
132//! Types like `XsdString` and `RdfLangString` can be found in the `primitives` module. Unless
133//! you're building your own custom types, you shouldn't need to import them yourself. They each
134//! implement `FromStr` for parsing and `Display` to convert back to strings, as well as `From` and
135//! `Into` or `TryFrom` and `TryInto` for types you might expect them to (e.g.
136//! `XsdNonNegativeInteger` implements `From<u64>` and `Into<u64>`).
137//!
138//! For some fields, like `id`, there is only one valid type. methods generated for fields like
139//! these will leave out the type name from the function name.
140//!
141//! ```ignore
142//! fn set_id<T>(&mut self, T) -> Result<...>;
143//! fn delete_id(&mut self) -> &mut Self;
144//! fn get_id(&self) -> Option<XsdAnyUri>;
145//! ```
146//!
147//! ### Traits
148//!
149//! This library provides a number of traits, such as `Object`, `Link`, `Actor`, `Activity`,
150//! `Collection`, and `CollectionPage`. The majority of these traits exist solely to "mark" types,
151//! meaning they don't provide value, at runtime, but exist to add constraints to generics at
152//! compiletime.
153//!
154//! If you want to make a function that manipulates an Activity, but not a normal object, you could
155//! bound the function like so:
156//! ```ignore
157//! fn my_manipulator<T>(some_activity: T) -> Result<&mut ObjectProperties, SomeErrorType>
158//! where
159//!     T: Activity + AsMut<ObjectProperties>,
160//! {
161//!     some_activity.as_mut().set_whatever_tbh()
162//! }
163//! ```
164//!
165//! ### Kinds
166//!
167//! This library has a set of unit structs that serialize and deserialize to strings. This is to
168//! enable different ActivityPub Object types to be deserialized into different Named structs.
169//! These can be found in `activitystreams::objects::kind`, and similar paths.
170//!
171//! To build your own Person struct, for example, you could write
172//! ```ignore
173//! use activitystreams::actor::kind::PersonType;
174//!
175//! #[derive(serde::Deserialize, serde::Serialize)]
176//! pub struct MyPerson {
177//!     // Do a rename since `type` is not a valid rust field name
178//!     #[serde(rename = "type")]
179//!     kind: PersonType,
180//! }
181//! ```
182//! And this type would only deserialize for JSON where `"type":"Person"`
183//!
184//! ### Extensions
185//!
186//! In some cases, like when dealing with ActivityPub, it is neccessary to extend the
187//! ActivityStreams specification. For this purpose, two traits and a type have been introduced.
188//!
189//! ```ignore
190//! use activitystreams::ext::{Ext, Extensible, Extension};
191//! ```
192//!
193//! The `Ext` type is a simple record containing first, the ActivityStreams type, and second, the
194//! extension to that type.
195//!
196//! There are two provided extensions in the ActivityStreams library.
197//! - ApObjectProperties, extra properties for all ActivityStreams objects in the ActivityPub spec
198//! - ApActorProperties, extra properties specifically for Actors in the ActivityPub spec
199//!
200//! To use an object with its default extensions, the object's `full()` associated function may be
201//! invoked.
202//! ```rust
203//! # use activitystreams::object::Video;
204//! let video_with_extensions = Video::full();
205//! ```
206//!
207//! ### Features
208//! There are a number of features that can be disabled in this crate. By default, everything is
209//! enabled.
210//!
211//! ```toml
212//! activitystreams = { version = "0.6.2", default-features = "false", features = ["derive"] }
213//! ```
214//!
215//! | feature    | what you get                                              |
216//! | ---------- | --------------------------------------------------------- |
217//! | none       | Just the Marker Traits                                    |
218//! | derive     | Marker Traits + derive macros from activitystreams-derive |
219//! | kinds      | Marker Traits + derive macros + Kind UnitStructs          |
220//! | primitives | Marker Traits + Primitive values                          |
221//! | types      | Everything, this is the default                           |
222//!
223//! ## Examples
224//!
225//! ### Basic
226//!
227//! ```rust
228//! use activitystreams::object::{Video, properties::ObjectProperties};
229//! use anyhow::Error;
230//!
231//! // We perform configuration in a dedicated function to specify which Properties type we want to
232//! // perform the operations on.
233//! fn configure_video(mut v: impl AsMut<ObjectProperties>) -> Result<(), Error> {
234//!     v.as_mut()
235//!         .set_context_xsd_any_uri("https://www.w3.org/ns/activitystreams")?
236//!         .set_id("https://example.com/@example/lions")?
237//!         .set_url_xsd_any_uri("https://example.com/@example/lions/video.webm")?
238//!         .set_name_xsd_string("My Cool Video")?
239//!         .set_summary_xsd_string("A video about some cool lions")?
240//!         .set_media_type("video/webm")?
241//!         .set_duration("PT4M20S")?;
242//!
243//!     Ok(())
244//! }
245//!
246//! fn main() -> Result<(), Error> {
247//!     let mut v = Video::default();
248//!
249//!     configure_video(&mut v)?;
250//!
251//!     println!("Video, {:#?}", v);
252//!
253//!     let s = serde_json::to_string(&v)?;
254//!
255//!     println!("json, {}", s);
256//!
257//!     let v: Video = serde_json::from_str(&s)?;
258//!
259//!     println!("Video again, {:#?}", v);
260//!
261//!     Ok(())
262//! }
263//! ```
264//!
265//! ### Intermediate
266//!
267//! ```rust
268//! use activitystreams::{
269//!     context,
270//!     actor::{Actor, ActorBox},
271//!     ext::Ext,
272//!     object::{
273//!         properties::{
274//!             ObjectProperties,
275//!             ProfileProperties
276//!         },
277//!         Profile,
278//!         Object,
279//!         ObjectBox,
280//!     },
281//!     primitives::XsdAnyUri,
282//!     Base, BaseBox, PropRefs,
283//! };
284//! use serde::{Deserialize, Serialize};
285//!
286//! #[derive(Clone, Debug, Default, Deserialize, Serialize, PropRefs)]
287//! #[serde(rename_all = "camelCase")]
288//! #[prop_refs(Object)]
289//! #[prop_refs(Actor)]
290//! pub struct Persona {
291//!     #[serde(rename = "@context")]
292//!     context: XsdAnyUri,
293//!
294//!     #[serde(rename = "type")]
295//!     kind: String,
296//! }
297//!
298//! fn main() -> Result<(), anyhow::Error> {
299//!     let mut profile = Profile::full();
300//!
301//!     let pprops: &mut ProfileProperties = profile.as_mut();
302//!
303//!     pprops.set_describes_object_box(Persona {
304//!         context: context(),
305//!         kind: "Persona".to_owned(),
306//!     })?;
307//!
308//!     let oprops: &mut ObjectProperties = profile.as_mut();
309//!     oprops.set_context_xsd_any_uri(context())?;
310//!
311//!     let profile_string = serde_json::to_string(&profile)?;
312//!
313//!     let profile: Profile = serde_json::from_str(&profile_string)?;
314//!
315//!     Ok(())
316//! }
317//! ```
318//!
319//! ### Advanced
320//!
321//! ```rust
322//! use activitystreams::{
323//!     properties,
324//!     ext::Ext,
325//!     link::{
326//!         properties::LinkProperties,
327//!         Link, LinkBox, Mention,
328//!     },
329//!     Base, BaseBox, PropRefs,
330//!     UnitString,
331//! };
332//! use serde::{Deserialize, Serialize};
333//!
334//! /// Using the UnitString derive macro
335//! ///
336//! /// This macro implements Serialize and Deserialize for the given type, making this type
337//! /// represent the string "MyLink" in JSON.
338//! #[derive(Clone, Debug, Default, UnitString)]
339//! #[unit_string(MyLink)]
340//! pub struct MyKind;
341//!
342//! properties! {
343//!     My {
344//!         docs [ "Defining our own properties struct called MyProperties" ],
345//!
346//!         required_key {
347//!             docs [
348//!                 "Our own required key field",
349//!                 "",
350//!                 "'types' defines the range of values that can be stored in required_key",
351//!                 "",
352//!                 "'functional' means there is at most one value for required_key",
353//!                 "'required' means there is at least one value for required_key",
354//!             ],
355//!             types [ String ],
356//!             functional,
357//!             required,
358//!         },
359//!     }
360//! }
361//!
362//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
363//! #[serde(transparent)]
364//! pub struct MyLinkProps(pub LinkProperties);
365//!
366//! /// Using the Properties derive macro
367//! ///
368//! /// This macro generates getters and setters for the associated fields.
369//! #[derive(Clone, Debug, Default, Deserialize, Serialize, PropRefs)]
370//! #[serde(rename_all = "camelCase")]
371//! #[prop_refs(Link)]
372//! pub struct My {
373//!     /// Use the UnitString MyKind to enforce the type of the object by "MyLink"
374//!     pub kind: MyKind,
375//!
376//!     /// Derive AsRef/AsMut for My -> MyProperties
377//!     #[prop_refs]
378//!     pub my_properties: MyProperties,
379//!
380//!     /// Derive AsRef/AsMut/Link for My -> MyLinkProperties
381//!     #[prop_refs]
382//!     pub link_properties: MyLinkProps,
383//! }
384//!
385//! fn main() -> Result<(), anyhow::Error> {
386//!     let mut my_link = My::default();
387//!
388//!     let lprops: &mut MyProperties = my_link.as_mut();
389//!     lprops.set_required_key("Hey")?;
390//!
391//!     let my_link_string = serde_json::to_string(&my_link)?;
392//!
393//!     let my_link: My = serde_json::from_str(&my_link_string)?;
394//!
395//!     Ok(())
396//! }
397//! ```
398
399pub mod activity;
400pub mod actor;
401pub mod collection;
402#[cfg(feature = "types")]
403pub mod endpoint;
404#[cfg(feature = "types")]
405pub mod ext;
406pub mod link;
407pub mod object;
408#[cfg(feature = "primitives")]
409pub mod primitives;
410
411pub use self::{
412    activity::{Activity, IntransitiveActivity},
413    actor::Actor,
414    collection::{Collection, CollectionPage},
415    link::Link,
416    object::Object,
417};
418
419#[cfg_attr(feature = "types", wrapper_type)]
420/// The lowermost trait of the trait structure
421///
422/// Base exists solely so Object and Link can have impls that don't potentially conflict
423pub trait Base {}
424
425#[cfg(feature = "primitives")]
426/// The context associated with all of the Activity Streams types defined in the crate.
427pub fn context() -> crate::primitives::XsdAnyUri {
428    "https://www.w3.org/ns/activitystreams".parse().unwrap()
429}
430
431#[cfg(feature = "primitives")]
432/// The 'security' extension used by some implementations
433pub fn security() -> crate::primitives::XsdAnyUri {
434    "https://w3id.org/security/v1".parse().unwrap()
435}
436
437#[cfg(feature = "primitives")]
438/// The 'public' actor, doesn't denote a real actor but describes a publicly available object.
439pub fn public() -> crate::primitives::XsdAnyUri {
440    "https://www.w3.org/ns/activitystreams#Public"
441        .parse()
442        .unwrap()
443}
444
445#[cfg(feature = "derive")]
446pub use activitystreams_derive::{properties, wrapper_type, Extensible, PropRefs, UnitString};