diff --git a/Cargo.toml b/Cargo.toml index 96c7349..8bc9648 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "activitystreams" description = "Activity Streams in Rust" -version = "0.4.0-alpha.3" +version = "0.4.0" license = "GPL-3.0" authors = ["asonix "] repository = "https://git.asonix.dog/Aardwolf/activitystreams" @@ -17,7 +17,7 @@ primitives = ["chrono", "mime", "serde", "thiserror", "url"] types = ["derive", "kinds", "primitives", "serde"] [dependencies] -activitystreams-derive = { version = "0.4.0-alpha.2", path = "activitystreams-derive", optional = true} +activitystreams-derive = { version = "0.4.0", path = "activitystreams-derive", optional = true} typetag = "0.1.4" chrono = { version = "0.4", optional = true } mime = { version = "0.3", optional = true } diff --git a/README.md b/README.md index f2a3d70..7862ce1 100644 --- a/README.md +++ b/README.md @@ -7,21 +7,198 @@ __A set of Traits and Types that make up the ActivityStreams and ActivityPub spe ## Usage -### Basic usage -For basic use, add the following to your Cargo.toml +First, add ActivityStreams to your dependencies ```toml -# Cargo.toml - -activitystreams = "0.4.0-alpha.3" +activitystreams = "0.4.0" ``` -And then use it in your project +### 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; +``` -fn main() -> Result<(), Box> { - let mut v = Video::default(); +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`, while an ActivityPub Actor +might implement `AsRef`, `AsRef`, and +`AsRef`. + +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`. + +Finally, when declaring the field, it generates `summary: Option`, +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(&mut self, T) -> Result<...>; +fn set_summary_rdf_lang_string(&mut self, T) -> Result<...>; +fn set_many_summary_xsd_strings(&mut self, Vec) -> Result<...>; +fn set_many_summary_rdf_lang_strings(&mut self, Vec) -> Result<...>; + +fn delete_summary(&mut self) -> &mut Self; + +fn get_summary_xsd_string(&self) -> Option; +fn get_summary_rdf_lang_string(&self) -> Option; +fn get_many_summary_xsd_strings(&self) -> Option>; +fn get_many_summary_rdf_lang_strings(&self) -> Option>; +``` +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` or `TryInto`. 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`). + +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(&mut self, T) -> Result<...>; +fn delete_id(&mut self) -> &mut Self; +fn get_id(&self) -> Option; +``` + +### 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(some_activity: T) -> Result<&mut ObjectProperties, SomeErrorType> +where + T: Activity + AsMut, +{ + 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 + +### Basic + +```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) -> Result<(), Error> { v.as_mut() .set_context_xsd_any_uri("https://www.w3.org/ns/activitystreams")? .set_id("https://example.com/@example/lions")? @@ -31,6 +208,14 @@ fn main() -> Result<(), Box> { .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)?; @@ -45,7 +230,7 @@ fn main() -> Result<(), Box> { } ``` -### Intermediate Usage +### Intermediate ```rust use activitystreams::{ @@ -55,7 +240,7 @@ use activitystreams::{ ObjectProperties, ProfileProperties }, - Profile, + apub::Profile, }, primitives::XsdAnyUri, Actor, @@ -111,79 +296,76 @@ fn main() -> Result<(), anyhow::Error> { } ``` -### Advanced Usage -Add the required crates to your `Cargo.toml` -```toml -# Cargo.toml +### Advanced -activitystreams = "0.4" -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -``` - -And then in your project ```rust use activitystreams::{ - context, - link::properties::LinkProperties, + properties, + link::{ + properties::LinkProperties, + Mention, + }, Link, - Object, PropRefs, - UnitString + UnitString, }; +use serde::{Deserialize, Serialize}; /// Using the UnitString derive macro /// /// This macro implements Serialize and Deserialize for the given type, making this type -/// represent the string "SomeKind" in JSON. +/// represent the string "MyLink" in JSON. #[derive(Clone, Debug, Default, UnitString)] #[activitystreams(MyLink)] pub struct MyKind; properties! { - MyLink { - docs [ "Document MyLinkProperties" ], + My { + docs [ "Defining our own properties struct called MyProperties" ], required_key { - docs [ "Document the required key" ], + docs [ + "Our own required key field", + "", + "'types' defines the range of values that can be stored in required_key", + "", + "'functional' means there is at most one value for required_key", + "'required' means there is at least one value for required_key", + ], types [ String ], functional, required, - } + }, } } /// Using the Properties derive macro /// /// This macro generates getters and setters for the associated fields. -#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize, PropRefs)] +#[derive(Clone, Debug, Default, Deserialize, Serialize, PropRefs)] #[serde(rename_all = "camelCase")] -pub struct MyLink { - /// Use the UnitString MyKind to enforce the type of the object by "SomeKind" +pub struct My { + /// Use the UnitString MyKind to enforce the type of the object by "MyLink" pub kind: MyKind, - #[activitystreams(Link)] - pub link_props: LinkProperties, - + /// Derive AsRef/AsMut for My -> MyProperties #[activitystreams(None)] - pub my_link_props: MyLinkProperties, + pub my_properties: MyProperties, + + /// Derive AsRef/AsMut/Link for My -> LinkProperties + #[activitystreams(Link)] + pub link_properties: LinkProperties, } -fn run() -> Result<(), anyhow::Error> { - let mut my_link = MyLink::default(); +fn main() -> Result<(), anyhow::Error> { + let mut my_link = My::default(); - let mprops: &mut MyLinkProperties = my_link.as_mut(); - mprops.set_required_key("hey")?; - - let lprops: &mut LinkProperties = my_link.as_mut(); - lprops.set_context_xsd_any_uri(context)?; + let lprops: &mut MyProperties = my_link.as_mut(); + lprops.set_required_key("Hey")?; let my_link_string = serde_json::to_string(&my_link)?; - let my_link: MyLink = serde_json::from_str(&my_link_string)?; - let mprops: &MyLinkProperties = my_link.as_ref(); - - println!("{}", mprops.get_required_key()); + let my_link: My = serde_json::from_str(&my_link_string)?; Ok(()) } diff --git a/activitystreams-derive/Cargo.toml b/activitystreams-derive/Cargo.toml index 03c27a4..907e710 100644 --- a/activitystreams-derive/Cargo.toml +++ b/activitystreams-derive/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "activitystreams-derive" description = "Derive macros for activitystreams" -version = "0.4.0-alpha.2" +version = "0.4.0" license = "GPL-3.0" authors = ["asonix "] repository = "https://git.asonix.dog/Aardwolf/activitystreams" diff --git a/activitystreams-derive/README.md b/activitystreams-derive/README.md index 813c64b..b619755 100644 --- a/activitystreams-derive/README.md +++ b/activitystreams-derive/README.md @@ -10,7 +10,7 @@ Add the required crates to your `Cargo.toml` ```toml # Cargo.toml -activitystreams = "0.4.0-alpha.3" +activitystreams = "0.4.0" serde = { version = "1.0", features = ["derive"] } ``` diff --git a/activitystreams-derive/src/lib.rs b/activitystreams-derive/src/lib.rs index 7d27263..1b4c31c 100644 --- a/activitystreams-derive/src/lib.rs +++ b/activitystreams-derive/src/lib.rs @@ -23,8 +23,8 @@ //! //! First, add `serde` and `activitystreams-derive` to your Cargo.toml //! ```toml -//! activitystreams-derive = "0.4.0-alpha.1" -//! # or activitystreams = "0.4.0-alpha.1" +//! activitystreams-derive = "0.4.0" +//! # or activitystreams = "0.4.0" //! serde = { version = "1.0", features = ["derive"] } //! ``` //! diff --git a/src/lib.rs b/src/lib.rs index c706c5b..da88a9a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,13 +19,13 @@ //! ActivityStreams //! -//! A set of Traits and Types that make up the Activity Streams specification +//! A set of Traits and Types that make up the ActivityStreams and ActivityPub specifications //! //! ## Usage //! //! First, add ActivityStreams to your dependencies //! ```toml -//! activitystreams = "0.4.0-alpha.3" +//! activitystreams = "0.4.0" //! ``` //! //! ### Types @@ -110,7 +110,7 @@ //! "@value": "A string", //! "@language": "en" //! }, -//! "An xsd:string this time", +//! "An xsd:string this time" //! ] //! } //! ```