Readme, prelude, marker docs
This commit is contained in:
parent
efc6b49a9b
commit
ca04dc05d7
3 changed files with 346 additions and 7 deletions
21
README.md
21
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<T>(activity: T) -> Result<(), SomeErrorType>
|
||||
use activitystreams_new::{base::BaseExt, context, markers::Activity};
|
||||
|
||||
fn manipulator<T>(mut activity: T) -> Result<(), SomeErrorType>
|
||||
where
|
||||
T: Activity + BaseExt,
|
||||
T: Activity + BaseExt<Kind>,
|
||||
{
|
||||
activity
|
||||
.set_id("https://example.com".parse()?)
|
||||
|
|
|
@ -493,7 +493,7 @@ pub type Offer = ActorAndObjectOptTarget<OfferType>;
|
|||
pub type Move = ActorAndObjectOptOriginAndTarget<MoveType>;
|
||||
pub type Remove = ActorAndObjectOptOriginAndTarget<RemoveType>;
|
||||
|
||||
#[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<Kind> {
|
||||
|
|
330
src/lib.rs
330
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<OneOrMany<AnyString>>`.
|
||||
//!
|
||||
//! 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<T>(&mut self, T) -> Self;
|
||||
//! fn from_rdf_lang_string<T>(&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<XsdString>;
|
||||
//! fn single_rdf_lang_string(self) -> Option<RdfLangString>;
|
||||
//!
|
||||
//! fn add_xsd_string<T>(&mut self, T) -> &mut Self;
|
||||
//! fn add_rdf_lang_string<T>(&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<XsdString>` or `Into<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>`).
|
||||
//!
|
||||
//! ### 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<AnyBase>>;
|
||||
//!
|
||||
//! fn set_context<T>(&mut self, context: T) -> &mut Self
|
||||
//! where
|
||||
//! T: Into<AnyBase>;
|
||||
//!
|
||||
//! fn set_many_contexts<I, T>(&mut self, items: I) -> &mut Self
|
||||
//! where
|
||||
//! I: IntoIterator<Item = T>,
|
||||
//! T: Into<AnyBase>;
|
||||
//!
|
||||
//! fn add_context<T>(&mut self, context: T) -> &mut Self
|
||||
//! where
|
||||
//! T: Into<AnyBase>;
|
||||
//!
|
||||
//! fn take_context(&mut self) -> Option<OneOrMany<AnyBase>>;
|
||||
//! 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<XsdAnyUri>;
|
||||
//! 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<T, Kind>(mut activity: T) -> Result<(), anyhow::Error>
|
||||
//! where
|
||||
//! T: Activity + BaseExt<Kind>,
|
||||
//! {
|
||||
//! 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<Video> = ApObject::default();
|
||||
//!
|
||||
//! 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::<XsdAnyUri>()?)
|
||||
//! .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<Video> = serde_json::from_str(&s)?;
|
||||
//!
|
||||
//! println!("Video again, {:#?}", v);
|
||||
//!
|
||||
//! 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/).
|
||||
|
||||
pub mod activity;
|
||||
pub mod actor;
|
||||
pub mod base;
|
||||
|
@ -11,12 +262,91 @@ pub mod unparsed;
|
|||
pub use activitystreams::{context, public, security};
|
||||
|
||||
pub mod markers {
|
||||
//! Marker traits for bounding methods
|
||||
//!
|
||||
//! ```rust
|
||||
//! use activitystreams_new::{base::BaseExt, markers::Activity, primitives::XsdString};
|
||||
//!
|
||||
//! /// Applies the name "hi" to any given activity
|
||||
//! fn manipulator<T, Kind>(mut some_type: T) -> T
|
||||
//! where
|
||||
//! T: Activity + BaseExt<Kind>,
|
||||
//! {
|
||||
//! some_type.set_name(XsdString::from("hi"));
|
||||
//!
|
||||
//! some_type
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
pub use activitystreams::{
|
||||
Activity, Actor, Base, Collection, CollectionPage, IntransitiveActivity, Link, Object,
|
||||
};
|
||||
}
|
||||
|
||||
pub mod prelude {
|
||||
//! Extension traits that provide the majority of the helper methods of the crate
|
||||
//!
|
||||
//! ```rust
|
||||
//! # fn main() -> Result<(), anyhow::Error> {
|
||||
//! use activitystreams_new::{
|
||||
//! activity::{kind::CreateType, Create},
|
||||
//! actor::{kind::PersonType, ApActor, Person},
|
||||
//! context,
|
||||
//! prelude::*,
|
||||
//! primitives::{XsdAnyUri, XsdString},
|
||||
//! public,
|
||||
//! object::{
|
||||
//! kind::{ImageType, VideoType},
|
||||
//! ApObject, Image, Video
|
||||
//! },
|
||||
//! security
|
||||
//! };
|
||||
//!
|
||||
//! let mut person = ApActor::<Person>::default();
|
||||
//! person
|
||||
//! .set_kind(PersonType)
|
||||
//! .set_inbox("https://localhost:8080/inbox".parse()?)
|
||||
//! .set_outbox("https://localhost:8080/outbox".parse()?)
|
||||
//! .set_name(XsdString::from("Demo Account"))
|
||||
//! .set_preferred_username(XsdString::from("demo"))
|
||||
//! .set_id("https://localhost:8080/actor".parse()?)
|
||||
//! .set_url("https://localhost:8080/actor".parse::<XsdAnyUri>()?);
|
||||
//!
|
||||
//! let mut preview = Image::default();
|
||||
//!
|
||||
//! preview
|
||||
//! .set_kind(ImageType)
|
||||
//! .set_url("https://localhost:8080/preview.png".parse::<XsdAnyUri>()?)
|
||||
//! .set_media_type("image/png".parse()?)
|
||||
//! .set_id("https://localhostst:8080/preview.png".parse()?);
|
||||
//!
|
||||
//!
|
||||
//! let mut video = ApObject::<Video>::default();
|
||||
//!
|
||||
//! video
|
||||
//! .set_kind(VideoType)
|
||||
//! .set_id("http://localhost:8080/video.webm".parse()?)
|
||||
//! .set_url("http://localhost:8080/video.webm".parse::<XsdAnyUri>()?)
|
||||
//! .set_media_type("video/webm".parse()?)
|
||||
//! .set_summary(XsdString::from("A cool video"))
|
||||
//! .set_preview(preview.into_any_base()?)
|
||||
//! .set_duration("PT4M20S".parse()?)
|
||||
//! .set_shares("http://localhost:8080/video.webm#shares".parse()?);
|
||||
//!
|
||||
//! let mut activity = Create::builder()
|
||||
//! .actor(person.into_any_base()?)
|
||||
//! .object(video.into_any_base()?)
|
||||
//! .inner(Default::default())
|
||||
//! .build();
|
||||
//!
|
||||
//! activity
|
||||
//! .set_kind(CreateType)
|
||||
//! .set_many_tos(vec![public()]);
|
||||
//! #
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
pub use crate::{
|
||||
activity::{
|
||||
ActivityExt, ActorAndObjectRefExt, OptOriginRefExt, OptTargetRefExt, OriginRefExt,
|
||||
|
|
Loading…
Reference in a new issue