ActivityStreams Vocabulary
This is a library for federating software with the ActivityPub protocol.
It implements the ActivityStreams 2.0 Vocabulary specification used to define common ActivityPub data structures.
Alternatives
- activitystreams-kinds
- provides a bare-bones approach to ActivityStreams Vocabulary types
- only provides "kind" types, and not the associated data structures
- go-ap/activitypub
- mature Go implementation of ActivityPub data structures
- influenced a number of design decisions in
activitystreams-vocabulary- ActivityVocabulary is based on Typer
- VocabularyType is based on ActivityVocabularyType
- VocabularyTypes is based on ActivityVocabularyTypes
Using the Base Vocabulary Types
activitystreams-vocabulary aims to implement as many base vocabulary types as specified in common ActivityPub documents.
This includes types from the base ActivityStreams 2.0 Vocabulary, and commonly implemented FEP extensions.
Constructing a Simple Object
use ;
#
Using a Tombstone in an OrderedCollection
use ;
#
Extending the Base Vocabulary
ActivityPub and ActivityStreams are both intended to be extendable protocols, and so this crate was designed to follow that lead.
A number of helper macros exist to help create types that build on the base vocabulary.
Creating a Derived Object Type
For the following examples, we're going to assume types are defined in a crate named is external_vocab.
hint: external-vocab is defined in tests/external-vocab.
Defining the crate in tests/external-vocab helps with automated testing of external usage, and working within the constraints of Rust doc-tests.
Defining a Custom Vocabulary Type
First, let's define the type field enum ExternalType:
use ;
use ;
/// Represents the extended vocabulary types for your new types.
///
/// These variants will encode to the `type` field of the new type.
// Convenience macro to implement the `Display` trait.
impl_display!;
// Implement the `ActivityVocabulary` trait for interoperability with the `activitystreams_vocabulary` base types.
impl_activity_vocabulary!;
// Implement the `From` trait to convert `ExternalType` into `VocabularyType` + `VocabularyTypes`.
impl_into_vocabulary!;
NOTE: The vocabulary type can be defined anywhere in the crate, but should be re-exported from the crate root. This is mostly needed because of limitations in current helper macro implementations.
Defining a Custom Object
Next, we can define an external Object-derived type:
use ;
// Create a custom `Object` type that inherits all of the base properties.
create_object!
field_access!
Defining a Custom Actor
Defining a custom Actor-derived type inherits all of the properties of an Object, along with all of the Actor properties.
To define a custom Actor-derived type:
use create_actor;
// Create a custom `Actor` type that inherits all of the base `Actor` + `Object` properties.
create_actor!
Additional fields can be defined for custom Actor types similar to Objects:
use ;
// Create a custom `Actor` type that inherits all of the base `Actor` + `Object` properties.
create_actor!
// Field access definitions need to be grouped based on the access type, e.g. `option`, `option_deref`, etc.
field_access!
// `option_deref` uses the `Option::as_deref` function to get a reference to the `Deref` type, e.g. `Option<&str>` for `Option<String>`.
field_access!
Design Tradeoffs
Even with heavy macro use, there is still a bit of boilerplate when creating new types (more helper macros to come :).
However, there is an enormous amount of boilerplate avoided by taking advantage of Rust's excellent macro infrastructure.
Future iterations may also use proc-macros which are even more powerful than the declarative-macros currently in use.
The choice to use declarative-macros is mostly due to not requiring an external macros-crate, and the reduced number of dependencies.
Fuck AI
This crate was made with 100% human engineering, entirely without the aid of LLMs.