mirror of
https://git.asonix.dog/asonix/activitystreams.git
synced 2024-11-25 13:21:00 +00:00
Add top-level documenation, fix get_many types
This commit is contained in:
parent
28694e786d
commit
c8e1249974
6 changed files with 259 additions and 23 deletions
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "activitystreams"
|
name = "activitystreams"
|
||||||
description = "Activity Streams in Rust"
|
description = "Activity Streams in Rust"
|
||||||
version = "0.4.0-alpha.2"
|
version = "0.4.0-alpha.3"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["asonix <asonix@asonix.dog>"]
|
authors = ["asonix <asonix@asonix.dog>"]
|
||||||
repository = "https://git.asonix.dog/Aardwolf/activitystreams"
|
repository = "https://git.asonix.dog/Aardwolf/activitystreams"
|
||||||
|
@ -17,7 +17,7 @@ primitives = ["chrono", "mime", "serde", "thiserror", "url"]
|
||||||
types = ["derive", "kinds", "primitives", "serde"]
|
types = ["derive", "kinds", "primitives", "serde"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
activitystreams-derive = { version = "0.4.0-alpha.1", path = "activitystreams-derive", optional = true}
|
activitystreams-derive = { version = "0.4.0-alpha.2", path = "activitystreams-derive", optional = true}
|
||||||
typetag = "0.1.4"
|
typetag = "0.1.4"
|
||||||
chrono = { version = "0.4", optional = true }
|
chrono = { version = "0.4", optional = true }
|
||||||
mime = { version = "0.3", optional = true }
|
mime = { version = "0.3", optional = true }
|
||||||
|
|
|
@ -12,7 +12,7 @@ For basic use, add the following to your Cargo.toml
|
||||||
```toml
|
```toml
|
||||||
# Cargo.toml
|
# Cargo.toml
|
||||||
|
|
||||||
activitystreams = "0.4.0-alpha.2"
|
activitystreams = "0.4.0-alpha.3"
|
||||||
```
|
```
|
||||||
|
|
||||||
And then use it in your project
|
And then use it in your project
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[package]
|
[package]
|
||||||
name = "activitystreams-derive"
|
name = "activitystreams-derive"
|
||||||
description = "Derive macros for activitystreams"
|
description = "Derive macros for activitystreams"
|
||||||
version = "0.4.0-alpha.1"
|
version = "0.4.0-alpha.2"
|
||||||
license = "GPL-3.0"
|
license = "GPL-3.0"
|
||||||
authors = ["asonix <asonix.dev@gmail.com>"]
|
authors = ["asonix <asonix.dev@gmail.com>"]
|
||||||
repository = "https://git.asonix.dog/Aardwolf/activitystreams"
|
repository = "https://git.asonix.dog/Aardwolf/activitystreams"
|
||||||
|
|
|
@ -10,20 +10,22 @@ Add the required crates to your `Cargo.toml`
|
||||||
```toml
|
```toml
|
||||||
# Cargo.toml
|
# Cargo.toml
|
||||||
|
|
||||||
activitystreams-derive = "0.4-alpha.0"
|
activitystreams = "0.4.0-alpha.3"
|
||||||
activitystreams-traits = "0.4-alpha.0"
|
|
||||||
activitystreams-types = "0.4-alpha.0"
|
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
```
|
```
|
||||||
|
|
||||||
And then in your project
|
And then in your project
|
||||||
```rust
|
```rust
|
||||||
use activitystreams_derive::{properties, PropRefs, UnitString};
|
// derive macros
|
||||||
use activitystreams_traits::Object;
|
use activitystreams::{
|
||||||
use activitystreams_types::object::{
|
properties,
|
||||||
properties::ObjectProperties,
|
PropRefs,
|
||||||
ObjectExt,
|
UnitString
|
||||||
};
|
};
|
||||||
|
// traits
|
||||||
|
use activitystreams::Object;
|
||||||
|
// properties
|
||||||
|
use activitystreams::object::properties::ObjectProperties;
|
||||||
|
|
||||||
/// Using the UnitString derive macro
|
/// Using the UnitString derive macro
|
||||||
///
|
///
|
||||||
|
@ -86,7 +88,7 @@ pub struct My {
|
||||||
|
|
||||||
/// Derive AsRef<ObjectProperties> and AsMut<ObjectProperties>
|
/// Derive AsRef<ObjectProperties> and AsMut<ObjectProperties>
|
||||||
///
|
///
|
||||||
/// as well as the Object and ObjectExt traits
|
/// as well as the Object trait
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
#[activitystreams(Object)]
|
#[activitystreams(Object)]
|
||||||
properties: ObjectProperties,
|
properties: ObjectProperties,
|
||||||
|
@ -95,9 +97,11 @@ pub struct My {
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let mut my = My::default();
|
let mut my = My::default();
|
||||||
|
|
||||||
my.as_mut().set_required_key("Hello")?;
|
let mprops: &mut MyProperties = my.as_mut();
|
||||||
|
mprops.set_required_key("Hello")?;
|
||||||
|
|
||||||
assert_eq!(my.as_ref().get_required_key(), "Hello");
|
let mprops: &MyProperties = my.as_ref();
|
||||||
|
assert_eq!(mprops.get_required_key(), "Hello");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
|
@ -551,7 +551,7 @@ pub fn properties(tokens: TokenStream) -> TokenStream {
|
||||||
String::new(),
|
String::new(),
|
||||||
format!("`{}` isn't functional, meaning it can only be represented as a single `{}`", fname, ty),
|
format!("`{}` isn't functional, meaning it can only be represented as a single `{}`", fname, ty),
|
||||||
String::new(),
|
String::new(),
|
||||||
format!("This enum's variants representa ll valid types to construct a `{}`", fname),
|
format!("This enum's variants represent all valid types to construct a `{}`", fname),
|
||||||
]);
|
]);
|
||||||
quote! {
|
quote! {
|
||||||
#doc_lines
|
#doc_lines
|
||||||
|
@ -944,15 +944,21 @@ pub fn properties(tokens: TokenStream) -> TokenStream {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let doc_line = to_doc(&format!("Get `{}` as a slice of `{}`s", fname, term_ty.to_token_stream()));
|
let doc_line = to_doc(&format!("Get `{}` as a vec of `&{}`s", fname, v_ty.to_token_stream()));
|
||||||
let get_many = quote! {
|
let get_many = quote! {
|
||||||
#doc_line
|
#doc_line
|
||||||
///
|
///
|
||||||
/// This returns `None` if
|
/// This returns `None` if
|
||||||
/// - There is only one value present
|
/// - There is only one value present
|
||||||
pub fn #get_many_ident(&self) -> Option<&[#term_ty]> {
|
///
|
||||||
|
/// The returned vec will be empty if no values match the requested
|
||||||
|
/// type, but values are present.
|
||||||
|
pub fn #get_many_ident(&self) -> Option<Vec<&#v_ty>> {
|
||||||
match self.#fname {
|
match self.#fname {
|
||||||
#ty::Array(ref array) => Some(array),
|
#ty::Array(ref array) => Some(array.iter().filter_map(|i| match i {
|
||||||
|
#term_ty::#v_ty(item) => Some(item),
|
||||||
|
_ => None,
|
||||||
|
}).collect()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1017,9 +1023,12 @@ pub fn properties(tokens: TokenStream) -> TokenStream {
|
||||||
/// This returns `None` if
|
/// This returns `None` if
|
||||||
/// - There is no value present
|
/// - There is no value present
|
||||||
/// - There is only one value present
|
/// - There is only one value present
|
||||||
pub fn #get_many_ident(&self) -> Option<&[#term_ty]> {
|
pub fn #get_many_ident(&self) -> Option<Vec<&#v_ty>> {
|
||||||
match self.#fname {
|
match self.#fname {
|
||||||
Some(#ty::Array(ref array)) => Some(array),
|
Some(#ty::Array(ref array)) => Some(array.iter().filter_map(|i| match i {
|
||||||
|
#term_ty::#v_ty(item) => Some(item),
|
||||||
|
_ => None,
|
||||||
|
}).collect()),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
227
src/lib.rs
227
src/lib.rs
|
@ -21,11 +21,234 @@
|
||||||
//!
|
//!
|
||||||
//! A set of Traits and Types that make up the Activity Streams specification
|
//! A set of Traits and Types that make up the Activity Streams specification
|
||||||
//!
|
//!
|
||||||
|
//! ## Usage
|
||||||
|
//!
|
||||||
|
//! First, add ActivityStreams to your dependencies
|
||||||
|
//! ```toml
|
||||||
|
//! activitystreams = "0.4.0-alpha.3"
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! ### 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"
|
||||||
|
//! },
|
||||||
|
//! "An xsd:string this time",
|
||||||
|
//! ]
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! 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 |
|
||||||
|
//!
|
||||||
//! ## Examples
|
//! ## Examples
|
||||||
//!
|
//!
|
||||||
//! ### Basic
|
//! ### Basic
|
||||||
//!
|
//!
|
||||||
//! ```rust
|
//! ```rust
|
||||||
|
//! 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
|
||||||
//! use activitystreams::{
|
//! use activitystreams::{
|
||||||
//! context,
|
//! context,
|
||||||
//! object::{
|
//! object::{
|
||||||
|
@ -33,7 +256,7 @@
|
||||||
//! ObjectProperties,
|
//! ObjectProperties,
|
||||||
//! ProfileProperties
|
//! ProfileProperties
|
||||||
//! },
|
//! },
|
||||||
//! streams::Profile,
|
//! apub::Profile,
|
||||||
//! },
|
//! },
|
||||||
//! primitives::XsdAnyUri,
|
//! primitives::XsdAnyUri,
|
||||||
//! Actor,
|
//! Actor,
|
||||||
|
@ -145,7 +368,7 @@
|
||||||
//! #[activitystreams(None)]
|
//! #[activitystreams(None)]
|
||||||
//! pub my_properties: MyProperties,
|
//! pub my_properties: MyProperties,
|
||||||
//!
|
//!
|
||||||
//! /// Derive AsRef/AsMut/Link for My -> MyProperties
|
//! /// Derive AsRef/AsMut/Link for My -> LinkProperties
|
||||||
//! #[activitystreams(Link)]
|
//! #[activitystreams(Link)]
|
||||||
//! pub link_properties: LinkProperties,
|
//! pub link_properties: LinkProperties,
|
||||||
//! }
|
//! }
|
||||||
|
|
Loading…
Reference in a new issue