From ca04dc05d7766c87334c0cec70f34e520e176c58 Mon Sep 17 00:00:00 2001 From: asonix Date: Sat, 16 May 2020 11:50:42 -0500 Subject: [PATCH] Readme, prelude, marker docs --- README.md | 21 ++- src/activity.rs | 2 +- src/lib.rs | 330 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 346 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3937d59..91d4586 100644 --- a/README.md +++ b/README.md @@ -15,22 +15,28 @@ The project is laid out by Kind => vocabulary => Type So to use an ActivityStreams Video, you'd write ```rust use activitystreams_new::object::Video; -let video = Video::builder().finish(); +let video = Video::builder() + .inner(Default::default()) + .build(); ``` And to use an ActivityPub profile, you'd write ```rust use activitystreams_new::object::{ApObject, Profile}; -let inner = Profile::builder().finish(); +let inner = Profile::builder() + .inner(Default::default()) + .build(); let profile = ApObject::builder() .inner(inner) - .finish(); + .build(); ``` There's only one kind of Link ```rust use activitystreams_new::link::Mention; -let mention = Mention::builder().finish(); +let mention = Mention::builder() + .inner(Default::default()) + .build(); ``` ### Fields @@ -158,10 +164,13 @@ 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 -fn manipulator(activity: T) -> Result<(), SomeErrorType> +use activitystreams_new::{base::BaseExt, context, markers::Activity}; + +fn manipulator(mut activity: T) -> Result<(), SomeErrorType> where - T: Activity + BaseExt, + T: Activity + BaseExt, { activity .set_id("https://example.com".parse()?) diff --git a/src/activity.rs b/src/activity.rs index ce46518..f50ca8c 100644 --- a/src/activity.rs +++ b/src/activity.rs @@ -493,7 +493,7 @@ pub type Offer = ActorAndObjectOptTarget; pub type Move = ActorAndObjectOptOriginAndTarget; pub type Remove = ActorAndObjectOptOriginAndTarget; -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize, TypedBuilder)] +#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize, TypedBuilder)] #[serde(rename_all = "camelCase")] #[builder(doc)] pub struct Activity { diff --git a/src/lib.rs b/src/lib.rs index 4411149..a0b3980 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,254 @@ +//! # ActivityStreams New +//! __A set of Traits and Types that make up the ActivityStreams and ActivityPub specifications__ +//! +//! ## Usage +//! +//! First, add ActivityStreams to your dependencies +//! ```toml +//! activitystreams-new = { version = "0.1.0", git = "https://git.asonix.dog/asonix/activitystreams-sketch" } +//! ``` +//! +//! ### Types +//! +//! The project is laid out by Kind => vocabulary => Type +//! +//! So to use an ActivityStreams Video, you'd write +//! ```rust +//! use activitystreams_new::object::Video; +//! let video = Video::builder() +//! .inner(Default::default()) +//! .build(); +//! ``` +//! +//! And to use an ActivityPub profile, you'd write +//! ```rust +//! use activitystreams_new::object::{ApObject, Profile}; +//! let inner = Profile::builder() +//! .inner(Default::default()) +//! .build(); +//! let profile = ApObject::builder() +//! .inner(inner) +//! .build(); +//! ``` +//! +//! There's only one kind of Link +//! ```rust +//! use activitystreams_new::link::Mention; +//! let mention = Mention::builder() +//! .inner(Default::default()) +//! .build(); +//! ``` +//! +//! ### 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 +//! +//! ### Basic +//! +//! ```rust +//! use activitystreams_new::{ +//! context, +//! object::{ApObject, Video}, +//! prelude::*, +//! primitives::{XsdAnyUri, XsdString}, +//! }; +//! +//! fn main() -> Result<(), anyhow::Error> { +//! let mut video: ApObject