Copy in -new and -ext

This commit is contained in:
asonix 2020-07-25 16:59:15 -05:00
parent 310d29482b
commit afafe24d51
65 changed files with 17686 additions and 7563 deletions

View file

@ -1,39 +1,28 @@
[package] [package]
name = "activitystreams" name = "activitystreams"
description = "Activity Streams in Rust" version = "0.7.0-alpha.0"
version = "0.6.2"
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/asonix/activitystreams-sketch"
documentation = "https://activitystreams-new.asonix.dog"
readme = "README.md" readme = "README.md"
keywords = ["activitystreams", "activitypub"] keywords = ["activitystreams", "activitypub"]
edition = "2018" edition = "2018"
[features] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
default = ["types"]
derive = ["activitystreams-derive", "serde", "serde_json"]
kinds = ["derive", "serde"]
primitives = ["chrono", "mime", "serde", "thiserror", "url"]
types = ["derive", "kinds", "primitives"]
[dependencies]
activitystreams-derive = { version = "0.6.0", path = "activitystreams-derive", optional = true}
chrono = { version = "0.4", optional = true }
mime = { version = "0.3", optional = true }
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0", optional = true }
thiserror = { version = "1.0", optional = true }
url = { version = "2.1", optional = true }
[dev-dependencies]
anyhow = "1.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[profile.dev.package.activitystreams-derive]
opt-level = 3
[workspace] [workspace]
members = [ members = [
"activitystreams-derive", "activitystreams-ext"
] ]
[dependencies]
chrono = "0.4"
mime = "0.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
url = { version = "2.1", features = ["serde"] }
[dev-dependencies]
anyhow = "1.0"

View file

@ -1,22 +0,0 @@
[package]
name = "activitystreams-derive"
description = "Derive macros for activitystreams"
version = "0.6.1"
license = "GPL-3.0"
authors = ["asonix <asonix.dev@gmail.com>"]
repository = "https://git.asonix.dog/Aardwolf/activitystreams"
readme = "README.md"
keywords = ["activitystreams", "activitypub"]
edition = "2018"
[dependencies]
quote = "1.0"
syn = { version = "1.0", features = ["full"] }
proc-macro2 = "1.0"
[dev-dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[lib]
proc-macro = true

View file

@ -1,121 +0,0 @@
# ActivityStreams Derive
__derive macros for ActivityStreams__
- [Read the documentation on docs.rs](https://docs.rs/activitystreams-derive)
- [Find the crate on crates.io](https://crates.io/crates/activitystreams-derive)
- [Hit me up on Mastodon](https://asonix.dog/@asonix)
## Usage
Add the required crates to your `Cargo.toml`
```toml
# Cargo.toml
activitystreams = "0.6.0"
serde = { version = "1.0", features = ["derive"] }
```
And then in your project
```rust
// derive macros
use activitystreams::{
properties,
PropRefs,
UnitString
};
// traits
use activitystreams::Object;
// properties
use activitystreams::object::properties::ObjectProperties;
/// Using the UnitString derive macro
///
/// This macro implements Serialize and Deserialize for the given type, making this type
/// represent the string "SomeKind" in JSON.
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(SomeKind)]
pub struct MyKind;
properties! {
My {
docs [
"Using the properties macro",
"",
"This macro generates getters and setters for the associated fields.",
],
kind {
docs [
"Use the UnitString MyKind to enforce the type of the object by \"SomeKind\"",
"",
"Rename to/from 'type' when serializing/deserializing",
],
types [
MyKind,
],
functional,
required,
rename("type"),
},
required_key {
docs [
"Derive getters and setters for required_key with String type.",
"",
"In the Activity Streams spec, 'functional' means there can only be one item for",
"this key. This means all fields not labeled 'functional' can also be",
"serialized/deserialized as Vec<T>.",
"",
"'required' here means that the field must be present, otherwise, it's"
"represented as an Option<T>",
],
types [
String,
],
functional,
required,
},
}
}
#[derive(Clone, Default, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
pub struct My {
/// Derive AsRef<MyProperties> and AsMut<MyProperties>
#[serde(flatten)]
#[prop_refs]
my_properties: MyProperties,
/// Derive AsRef<ObjectProperties> and AsMut<ObjectProperties>
///
/// as well as the Object trait
#[serde(flatten)]
#[prop_refs]
properties: ObjectProperties,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut my = My::default();
let mprops: &mut MyProperties = my.as_mut();
mprops.set_required_key("Hello")?;
let mprops: &MyProperties = my.as_ref();
assert_eq!(mprops.get_required_key(), "Hello");
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 Derive 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 Derive 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 Derive.
You should have received a copy of the GNU General Public License along with ActivityStreams Derive. If not, see [http://www.gnu.org/licenses/](http://www.gnu.org/licenses/).

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,3 @@
/target /target
**/*.rs.bk /artifacts
Cargo.lock Cargo.lock

View file

@ -0,0 +1,20 @@
[package]
name = "activitystreams-ext"
version = "0.1.0"
license = "GPL-3.0"
authors = ["asonix <asonix@asonix.dog>"]
repository = "https://git.asonix.dog/asonix/activitystreams-ext"
documentation = "https://activitystreams-ext.asonix.dog"
readme = "README.md"
keywords = ["activitystreams", "activitypub"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
activitystreams = { path = ".." }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[dev-dependencies]
anyhow = "1.0"

View file

@ -0,0 +1,6 @@
FROM arm64v8/nginx:mainline-alpine
COPY html/ /usr/share/nginx/html/
RUN chown -R nginx:nginx /usr/share/nginx/html

View file

@ -0,0 +1,104 @@
# ActivityStreams Ext
_This crate provides Ext1, Ext2, Ext3, and Ext4 for adding extensions to ActivityStreams types_
- Find the code on [git.asonix.dog](https://git.asonix.dog/asonix/activitystreams-ext)
- Read the docs on [activitystreams-ext.asonix.dog](https://activitystreams-ext.asonix.dog)
- Join the matrix channel at [#activitypub:asonix.dog](https://matrix.to/#/!fAEcHyTUdAaKCzIKCt:asonix.dog?via=asonix.dog&via=matrix.org&via=t2bot.io)
- Hit me up on [mastodon](https://asonix.dog/@asonix)
## Usage
First, add ActivityStreams to your dependencies
```toml
[dependencies]
activitystreams-new = { git = "https://git.asonix.dog/asonix/activitystreams-sketch", branch = "main" }
activitystreams-ext = { git = "https://git.asonix.dog/asonix/activitystreams-ext", branch = "main" }
```
For an example, we'll implement a PublicKey extension and demonstrate usage with Ext1
```rust
use activitystreams_ext::{Ext1, UnparsedExtension};
use activitystreams_new::{
actor::{ApActor, Person},
context,
prelude::*,
primitives::XsdAnyUri,
security,
unparsed::UnparsedMutExt,
};
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicKey {
public_key: PublicKeyInner,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicKeyInner {
id: XsdAnyUri,
owner: XsdAnyUri,
public_key_pem: String,
}
impl<U> UnparsedExtension<U> for PublicKey
where
U: UnparsedMutExt,
{
type Error = serde_json::Error;
fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
Ok(PublicKey {
public_key: unparsed_mut.remove("publicKey")?,
})
}
fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
unparsed_mut.insert("publicKey", self.public_key)?;
Ok(())
}
}
pub type ExtendedPerson = Ext1<ApActor<Person>, PublicKey>;
fn main() -> Result<(), anyhow::Error> {
let actor = ApActor::new(
"http://in.box".parse()?,
"http://out.box".parse()?,
Person::new(),
);
let mut person = Ext1::new(
actor,
PublicKey {
public_key: PublicKeyInner {
id: "http://key.id".parse()?,
owner: "http://owner.id".parse()?,
public_key_pem: "asdfasdfasdf".to_owned(),
},
},
);
person.set_context(context()).add_context(security());
let any_base = person.into_any_base()?;
println!("any_base: {:#?}", any_base);
let person = ExtendedPerson::from_any_base(any_base)?;
println!("person: {:#?}", person);
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/).

54
activitystreams-ext/build.sh Executable file
View file

@ -0,0 +1,54 @@
#!/usr/bin/env bash
BUILD_DATE=$(date)
VERSION=$1
MIGRATIONS=$2
function require() {
if [ "$1" = "" ]; then
echo "input '$2' required"
print_help
exit 1
fi
}
function print_help() {
echo "build.sh"
echo ""
echo "Usage:"
echo " build.sh [version]"
echo ""
echo "Args:"
echo " version: The version of the current container"
}
require "$VERSION" "version"
if ! docker run --rm -it arm64v8/ubuntu:19.10 /bin/bash -c 'echo "docker is configured correctly"'; then
echo "docker is not configured to run on qemu-emulated architectures, fixing will require sudo"
sudo docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
fi
set -xe
cargo clean
cargo doc --no-deps
mkdir -p artifacts
rm -rf artifacts/html
cp -r ./target/doc artifacts/html
docker build \
--pull \
--no-cache \
--build-arg BUILD_DATE="${BUILD_DATE}" \
--build-arg TAG="${TAG}" \
-f Dockerfile.arm64v8 \
-t "asonix/activitystreams-ext-docs:${VERSION}-arm64v8" \
-t "asonix/activitystreams-ext-docs:latest-arm64v8" \
-t "asonix/activitystreams-ext-docs:latest" \
./artifacts
docker push "asonix/activitystreams-ext-docs:${VERSION}-arm64v8"
docker push "asonix/activitystreams-ext-docs:latest-arm64v8"
docker push "asonix/activitystreams-ext-docs:latest"

View file

@ -0,0 +1,71 @@
use activitystreams_ext::{Ext1, UnparsedExtension};
use activitystreams::{
actor::{ApActor, Person},
context,
prelude::*,
primitives::XsdAnyUri,
security,
unparsed::UnparsedMutExt,
};
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicKey {
public_key: PublicKeyInner,
}
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PublicKeyInner {
id: XsdAnyUri,
owner: XsdAnyUri,
public_key_pem: String,
}
impl<U> UnparsedExtension<U> for PublicKey
where
U: UnparsedMutExt,
{
type Error = serde_json::Error;
fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
Ok(PublicKey {
public_key: unparsed_mut.remove("publicKey")?,
})
}
fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
unparsed_mut.insert("publicKey", self.public_key)?;
Ok(())
}
}
pub type ExtendedPerson = Ext1<ApActor<Person>, PublicKey>;
fn main() -> Result<(), anyhow::Error> {
let actor = ApActor::new(
"http://in.box".parse()?,
"http://out.box".parse()?,
Person::new(),
);
let mut person = Ext1::new(
actor,
PublicKey {
public_key: PublicKeyInner {
id: "http://key.id".parse()?,
owner: "http://owner.id".parse()?,
public_key_pem: "asdfasdfasdf".to_owned(),
},
},
);
person.set_context(context()).add_context(security());
let any_base = person.into_any_base()?;
println!("any_base: {:#?}", any_base);
let person = ExtendedPerson::from_any_base(any_base)?;
println!("person: {:#?}", person);
Ok(())
}

View file

@ -0,0 +1,201 @@
use crate::Ext1;
use activitystreams::{
activity::{
Activity, ActorAndObjectRef, AsActivity, AsQuestion, OptOriginRef, OptTargetRef, OriginRef,
Question, TargetRef,
},
actor::{ApActor, AsApActor},
base::{AnyBase, AsBase, Base},
collection::{AsCollection, AsCollectionPage, Collection, CollectionPage},
markers,
object::{ApObject, AsApObject, AsObject, Object},
primitives::OneOrMany,
};
impl<Inner, A> markers::Base for Ext1<Inner, A> where Inner: markers::Base {}
impl<Inner, A> markers::Object for Ext1<Inner, A> where Inner: markers::Object {}
impl<Inner, A> markers::Collection for Ext1<Inner, A> where Inner: markers::Collection {}
impl<Inner, A> markers::CollectionPage for Ext1<Inner, A> where Inner: markers::CollectionPage {}
impl<Inner, A> markers::Actor for Ext1<Inner, A> where Inner: markers::Actor {}
impl<Inner, A> markers::Activity for Ext1<Inner, A> where Inner: markers::Activity {}
impl<Inner, A> markers::IntransitiveActivity for Ext1<Inner, A> where
Inner: markers::IntransitiveActivity
{
}
impl<Inner, A, Kind> AsBase<Kind> for Ext1<Inner, A>
where
Inner: AsBase<Kind>,
{
fn base_ref(&self) -> &Base<Kind> {
self.inner.base_ref()
}
fn base_mut(&mut self) -> &mut Base<Kind> {
self.inner.base_mut()
}
}
impl<Inner, A, Kind> AsObject<Kind> for Ext1<Inner, A>
where
Inner: AsObject<Kind>,
{
fn object_ref(&self) -> &Object<Kind> {
self.inner.object_ref()
}
fn object_mut(&mut self) -> &mut Object<Kind> {
self.inner.object_mut()
}
}
impl<Inner, A, ApInner> AsApObject<ApInner> for Ext1<Inner, A>
where
Inner: AsApObject<ApInner>,
{
fn ap_object_ref(&self) -> &ApObject<ApInner> {
self.inner.ap_object_ref()
}
fn ap_object_mut(&mut self) -> &mut ApObject<ApInner> {
self.inner.ap_object_mut()
}
}
impl<Inner, A, Kind> AsCollection<Kind> for Ext1<Inner, A>
where
Inner: AsCollection<Kind>,
{
fn collection_ref(&self) -> &Collection<Kind> {
self.inner.collection_ref()
}
fn collection_mut(&mut self) -> &mut Collection<Kind> {
self.inner.collection_mut()
}
}
impl<Inner, A, Kind> AsCollectionPage<Kind> for Ext1<Inner, A>
where
Inner: AsCollectionPage<Kind>,
{
fn collection_page_ref(&self) -> &CollectionPage<Kind> {
self.inner.collection_page_ref()
}
fn collection_page_mut(&mut self) -> &mut CollectionPage<Kind> {
self.inner.collection_page_mut()
}
}
impl<Inner, A, ApInner> AsApActor<ApInner> for Ext1<Inner, A>
where
Inner: AsApActor<ApInner>,
{
fn ap_actor_ref(&self) -> &ApActor<ApInner> {
self.inner.ap_actor_ref()
}
fn ap_actor_mut(&mut self) -> &mut ApActor<ApInner> {
self.inner.ap_actor_mut()
}
}
impl<Inner, A, Kind> AsActivity<Kind> for Ext1<Inner, A>
where
Inner: AsActivity<Kind>,
{
fn activity_ref(&self) -> &Activity<Kind> {
self.inner.activity_ref()
}
fn activity_mut(&mut self) -> &mut Activity<Kind> {
self.inner.activity_mut()
}
}
impl<Inner, A> ActorAndObjectRef for Ext1<Inner, A>
where
Inner: ActorAndObjectRef,
{
fn actor_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.actor_field_ref()
}
fn actor_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.actor_field_mut()
}
fn object_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.object_field_ref()
}
fn object_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.object_field_mut()
}
}
impl<Inner, A> TargetRef for Ext1<Inner, A>
where
Inner: TargetRef,
{
fn target_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.target_field_ref()
}
fn target_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.target_field_mut()
}
}
impl<Inner, A> OriginRef for Ext1<Inner, A>
where
Inner: OriginRef,
{
fn origin_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.origin_field_ref()
}
fn origin_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.origin_field_mut()
}
}
impl<Inner, A> OptTargetRef for Ext1<Inner, A>
where
Inner: OptTargetRef,
{
fn target_field_ref(&self) -> &Option<OneOrMany<AnyBase>> {
self.inner.target_field_ref()
}
fn target_field_mut(&mut self) -> &mut Option<OneOrMany<AnyBase>> {
self.inner.target_field_mut()
}
}
impl<Inner, A> OptOriginRef for Ext1<Inner, A>
where
Inner: OptOriginRef,
{
fn origin_field_ref(&self) -> &Option<OneOrMany<AnyBase>> {
self.inner.origin_field_ref()
}
fn origin_field_mut(&mut self) -> &mut Option<OneOrMany<AnyBase>> {
self.inner.origin_field_mut()
}
}
impl<Inner, A> AsQuestion for Ext1<Inner, A>
where
Inner: AsQuestion,
{
fn question_ref(&self) -> &Question {
self.inner.question_ref()
}
fn question_mut(&mut self) -> &mut Question {
self.inner.question_mut()
}
}

View file

@ -0,0 +1,201 @@
use crate::Ext2;
use activitystreams::{
activity::{
Activity, ActorAndObjectRef, AsActivity, AsQuestion, OptOriginRef, OptTargetRef, OriginRef,
Question, TargetRef,
},
actor::{ApActor, AsApActor},
base::{AnyBase, AsBase, Base},
collection::{AsCollection, AsCollectionPage, Collection, CollectionPage},
markers,
object::{ApObject, AsApObject, AsObject, Object},
primitives::OneOrMany,
};
impl<Inner, A, B> markers::Base for Ext2<Inner, A, B> where Inner: markers::Base {}
impl<Inner, A, B> markers::Object for Ext2<Inner, A, B> where Inner: markers::Object {}
impl<Inner, A, B> markers::Collection for Ext2<Inner, A, B> where Inner: markers::Collection {}
impl<Inner, A, B> markers::CollectionPage for Ext2<Inner, A, B> where Inner: markers::CollectionPage {}
impl<Inner, A, B> markers::Actor for Ext2<Inner, A, B> where Inner: markers::Actor {}
impl<Inner, A, B> markers::Activity for Ext2<Inner, A, B> where Inner: markers::Activity {}
impl<Inner, A, B> markers::IntransitiveActivity for Ext2<Inner, A, B> where
Inner: markers::IntransitiveActivity
{
}
impl<Inner, A, B, Kind> AsBase<Kind> for Ext2<Inner, A, B>
where
Inner: AsBase<Kind>,
{
fn base_ref(&self) -> &Base<Kind> {
self.inner.base_ref()
}
fn base_mut(&mut self) -> &mut Base<Kind> {
self.inner.base_mut()
}
}
impl<Inner, A, B, Kind> AsObject<Kind> for Ext2<Inner, A, B>
where
Inner: AsObject<Kind>,
{
fn object_ref(&self) -> &Object<Kind> {
self.inner.object_ref()
}
fn object_mut(&mut self) -> &mut Object<Kind> {
self.inner.object_mut()
}
}
impl<Inner, A, B, ApInner> AsApObject<ApInner> for Ext2<Inner, A, B>
where
Inner: AsApObject<ApInner>,
{
fn ap_object_ref(&self) -> &ApObject<ApInner> {
self.inner.ap_object_ref()
}
fn ap_object_mut(&mut self) -> &mut ApObject<ApInner> {
self.inner.ap_object_mut()
}
}
impl<Inner, A, B, Kind> AsCollection<Kind> for Ext2<Inner, A, B>
where
Inner: AsCollection<Kind>,
{
fn collection_ref(&self) -> &Collection<Kind> {
self.inner.collection_ref()
}
fn collection_mut(&mut self) -> &mut Collection<Kind> {
self.inner.collection_mut()
}
}
impl<Inner, A, B, Kind> AsCollectionPage<Kind> for Ext2<Inner, A, B>
where
Inner: AsCollectionPage<Kind>,
{
fn collection_page_ref(&self) -> &CollectionPage<Kind> {
self.inner.collection_page_ref()
}
fn collection_page_mut(&mut self) -> &mut CollectionPage<Kind> {
self.inner.collection_page_mut()
}
}
impl<Inner, A, B, ApInner> AsApActor<ApInner> for Ext2<Inner, A, B>
where
Inner: AsApActor<ApInner>,
{
fn ap_actor_ref(&self) -> &ApActor<ApInner> {
self.inner.ap_actor_ref()
}
fn ap_actor_mut(&mut self) -> &mut ApActor<ApInner> {
self.inner.ap_actor_mut()
}
}
impl<Inner, A, B, Kind> AsActivity<Kind> for Ext2<Inner, A, B>
where
Inner: AsActivity<Kind>,
{
fn activity_ref(&self) -> &Activity<Kind> {
self.inner.activity_ref()
}
fn activity_mut(&mut self) -> &mut Activity<Kind> {
self.inner.activity_mut()
}
}
impl<Inner, A, B> ActorAndObjectRef for Ext2<Inner, A, B>
where
Inner: ActorAndObjectRef,
{
fn actor_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.actor_field_ref()
}
fn actor_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.actor_field_mut()
}
fn object_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.object_field_ref()
}
fn object_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.object_field_mut()
}
}
impl<Inner, A, B> TargetRef for Ext2<Inner, A, B>
where
Inner: TargetRef,
{
fn target_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.target_field_ref()
}
fn target_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.target_field_mut()
}
}
impl<Inner, A, B> OriginRef for Ext2<Inner, A, B>
where
Inner: OriginRef,
{
fn origin_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.origin_field_ref()
}
fn origin_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.origin_field_mut()
}
}
impl<Inner, A, B> OptTargetRef for Ext2<Inner, A, B>
where
Inner: OptTargetRef,
{
fn target_field_ref(&self) -> &Option<OneOrMany<AnyBase>> {
self.inner.target_field_ref()
}
fn target_field_mut(&mut self) -> &mut Option<OneOrMany<AnyBase>> {
self.inner.target_field_mut()
}
}
impl<Inner, A, B> OptOriginRef for Ext2<Inner, A, B>
where
Inner: OptOriginRef,
{
fn origin_field_ref(&self) -> &Option<OneOrMany<AnyBase>> {
self.inner.origin_field_ref()
}
fn origin_field_mut(&mut self) -> &mut Option<OneOrMany<AnyBase>> {
self.inner.origin_field_mut()
}
}
impl<Inner, A, B> AsQuestion for Ext2<Inner, A, B>
where
Inner: AsQuestion,
{
fn question_ref(&self) -> &Question {
self.inner.question_ref()
}
fn question_mut(&mut self) -> &mut Question {
self.inner.question_mut()
}
}

View file

@ -0,0 +1,204 @@
use crate::Ext3;
use activitystreams::{
activity::{
Activity, ActorAndObjectRef, AsActivity, AsQuestion, OptOriginRef, OptTargetRef, OriginRef,
Question, TargetRef,
},
actor::{ApActor, AsApActor},
base::{AnyBase, AsBase, Base},
collection::{AsCollection, AsCollectionPage, Collection, CollectionPage},
markers,
object::{ApObject, AsApObject, AsObject, Object},
primitives::OneOrMany,
};
impl<Inner, A, B, C> markers::Base for Ext3<Inner, A, B, C> where Inner: markers::Base {}
impl<Inner, A, B, C> markers::Object for Ext3<Inner, A, B, C> where Inner: markers::Object {}
impl<Inner, A, B, C> markers::Collection for Ext3<Inner, A, B, C> where Inner: markers::Collection {}
impl<Inner, A, B, C> markers::CollectionPage for Ext3<Inner, A, B, C> where
Inner: markers::CollectionPage
{
}
impl<Inner, A, B, C> markers::Actor for Ext3<Inner, A, B, C> where Inner: markers::Actor {}
impl<Inner, A, B, C> markers::Activity for Ext3<Inner, A, B, C> where Inner: markers::Activity {}
impl<Inner, A, B, C> markers::IntransitiveActivity for Ext3<Inner, A, B, C> where
Inner: markers::IntransitiveActivity
{
}
impl<Inner, A, B, C, Kind> AsBase<Kind> for Ext3<Inner, A, B, C>
where
Inner: AsBase<Kind>,
{
fn base_ref(&self) -> &Base<Kind> {
self.inner.base_ref()
}
fn base_mut(&mut self) -> &mut Base<Kind> {
self.inner.base_mut()
}
}
impl<Inner, A, B, C, Kind> AsObject<Kind> for Ext3<Inner, A, B, C>
where
Inner: AsObject<Kind>,
{
fn object_ref(&self) -> &Object<Kind> {
self.inner.object_ref()
}
fn object_mut(&mut self) -> &mut Object<Kind> {
self.inner.object_mut()
}
}
impl<Inner, A, B, C, ApInner> AsApObject<ApInner> for Ext3<Inner, A, B, C>
where
Inner: AsApObject<ApInner>,
{
fn ap_object_ref(&self) -> &ApObject<ApInner> {
self.inner.ap_object_ref()
}
fn ap_object_mut(&mut self) -> &mut ApObject<ApInner> {
self.inner.ap_object_mut()
}
}
impl<Inner, A, B, C, Kind> AsCollection<Kind> for Ext3<Inner, A, B, C>
where
Inner: AsCollection<Kind>,
{
fn collection_ref(&self) -> &Collection<Kind> {
self.inner.collection_ref()
}
fn collection_mut(&mut self) -> &mut Collection<Kind> {
self.inner.collection_mut()
}
}
impl<Inner, A, B, C, Kind> AsCollectionPage<Kind> for Ext3<Inner, A, B, C>
where
Inner: AsCollectionPage<Kind>,
{
fn collection_page_ref(&self) -> &CollectionPage<Kind> {
self.inner.collection_page_ref()
}
fn collection_page_mut(&mut self) -> &mut CollectionPage<Kind> {
self.inner.collection_page_mut()
}
}
impl<Inner, A, B, C, ApInner> AsApActor<ApInner> for Ext3<Inner, A, B, C>
where
Inner: AsApActor<ApInner>,
{
fn ap_actor_ref(&self) -> &ApActor<ApInner> {
self.inner.ap_actor_ref()
}
fn ap_actor_mut(&mut self) -> &mut ApActor<ApInner> {
self.inner.ap_actor_mut()
}
}
impl<Inner, A, B, C, Kind> AsActivity<Kind> for Ext3<Inner, A, B, C>
where
Inner: AsActivity<Kind>,
{
fn activity_ref(&self) -> &Activity<Kind> {
self.inner.activity_ref()
}
fn activity_mut(&mut self) -> &mut Activity<Kind> {
self.inner.activity_mut()
}
}
impl<Inner, A, B, C> ActorAndObjectRef for Ext3<Inner, A, B, C>
where
Inner: ActorAndObjectRef,
{
fn actor_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.actor_field_ref()
}
fn actor_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.actor_field_mut()
}
fn object_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.object_field_ref()
}
fn object_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.object_field_mut()
}
}
impl<Inner, A, B, C> TargetRef for Ext3<Inner, A, B, C>
where
Inner: TargetRef,
{
fn target_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.target_field_ref()
}
fn target_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.target_field_mut()
}
}
impl<Inner, A, B, C> OriginRef for Ext3<Inner, A, B, C>
where
Inner: OriginRef,
{
fn origin_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.origin_field_ref()
}
fn origin_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.origin_field_mut()
}
}
impl<Inner, A, B, C> OptTargetRef for Ext3<Inner, A, B, C>
where
Inner: OptTargetRef,
{
fn target_field_ref(&self) -> &Option<OneOrMany<AnyBase>> {
self.inner.target_field_ref()
}
fn target_field_mut(&mut self) -> &mut Option<OneOrMany<AnyBase>> {
self.inner.target_field_mut()
}
}
impl<Inner, A, B, C> OptOriginRef for Ext3<Inner, A, B, C>
where
Inner: OptOriginRef,
{
fn origin_field_ref(&self) -> &Option<OneOrMany<AnyBase>> {
self.inner.origin_field_ref()
}
fn origin_field_mut(&mut self) -> &mut Option<OneOrMany<AnyBase>> {
self.inner.origin_field_mut()
}
}
impl<Inner, A, B, C> AsQuestion for Ext3<Inner, A, B, C>
where
Inner: AsQuestion,
{
fn question_ref(&self) -> &Question {
self.inner.question_ref()
}
fn question_mut(&mut self) -> &mut Question {
self.inner.question_mut()
}
}

View file

@ -0,0 +1,207 @@
use crate::Ext4;
use activitystreams::{
activity::{
Activity, ActorAndObjectRef, AsActivity, AsQuestion, OptOriginRef, OptTargetRef, OriginRef,
Question, TargetRef,
},
actor::{ApActor, AsApActor},
base::{AnyBase, AsBase, Base},
collection::{AsCollection, AsCollectionPage, Collection, CollectionPage},
markers,
object::{ApObject, AsApObject, AsObject, Object},
primitives::OneOrMany,
};
impl<Inner, A, B, C, D> markers::Base for Ext4<Inner, A, B, C, D> where Inner: markers::Base {}
impl<Inner, A, B, C, D> markers::Object for Ext4<Inner, A, B, C, D> where Inner: markers::Object {}
impl<Inner, A, B, C, D> markers::Collection for Ext4<Inner, A, B, C, D> where
Inner: markers::Collection
{
}
impl<Inner, A, B, C, D> markers::CollectionPage for Ext4<Inner, A, B, C, D> where
Inner: markers::CollectionPage
{
}
impl<Inner, A, B, C, D> markers::Actor for Ext4<Inner, A, B, C, D> where Inner: markers::Actor {}
impl<Inner, A, B, C, D> markers::Activity for Ext4<Inner, A, B, C, D> where Inner: markers::Activity {}
impl<Inner, A, B, C, D> markers::IntransitiveActivity for Ext4<Inner, A, B, C, D> where
Inner: markers::IntransitiveActivity
{
}
impl<Inner, A, B, C, D, Kind> AsBase<Kind> for Ext4<Inner, A, B, C, D>
where
Inner: AsBase<Kind>,
{
fn base_ref(&self) -> &Base<Kind> {
self.inner.base_ref()
}
fn base_mut(&mut self) -> &mut Base<Kind> {
self.inner.base_mut()
}
}
impl<Inner, A, B, C, D, Kind> AsObject<Kind> for Ext4<Inner, A, B, C, D>
where
Inner: AsObject<Kind>,
{
fn object_ref(&self) -> &Object<Kind> {
self.inner.object_ref()
}
fn object_mut(&mut self) -> &mut Object<Kind> {
self.inner.object_mut()
}
}
impl<Inner, A, B, C, D, ApInner> AsApObject<ApInner> for Ext4<Inner, A, B, C, D>
where
Inner: AsApObject<ApInner>,
{
fn ap_object_ref(&self) -> &ApObject<ApInner> {
self.inner.ap_object_ref()
}
fn ap_object_mut(&mut self) -> &mut ApObject<ApInner> {
self.inner.ap_object_mut()
}
}
impl<Inner, A, B, C, D, Kind> AsCollection<Kind> for Ext4<Inner, A, B, C, D>
where
Inner: AsCollection<Kind>,
{
fn collection_ref(&self) -> &Collection<Kind> {
self.inner.collection_ref()
}
fn collection_mut(&mut self) -> &mut Collection<Kind> {
self.inner.collection_mut()
}
}
impl<Inner, A, B, C, D, Kind> AsCollectionPage<Kind> for Ext4<Inner, A, B, C, D>
where
Inner: AsCollectionPage<Kind>,
{
fn collection_page_ref(&self) -> &CollectionPage<Kind> {
self.inner.collection_page_ref()
}
fn collection_page_mut(&mut self) -> &mut CollectionPage<Kind> {
self.inner.collection_page_mut()
}
}
impl<Inner, A, B, C, D, ApInner> AsApActor<ApInner> for Ext4<Inner, A, B, C, D>
where
Inner: AsApActor<ApInner>,
{
fn ap_actor_ref(&self) -> &ApActor<ApInner> {
self.inner.ap_actor_ref()
}
fn ap_actor_mut(&mut self) -> &mut ApActor<ApInner> {
self.inner.ap_actor_mut()
}
}
impl<Inner, A, B, C, D, Kind> AsActivity<Kind> for Ext4<Inner, A, B, C, D>
where
Inner: AsActivity<Kind>,
{
fn activity_ref(&self) -> &Activity<Kind> {
self.inner.activity_ref()
}
fn activity_mut(&mut self) -> &mut Activity<Kind> {
self.inner.activity_mut()
}
}
impl<Inner, A, B, C, D> ActorAndObjectRef for Ext4<Inner, A, B, C, D>
where
Inner: ActorAndObjectRef,
{
fn actor_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.actor_field_ref()
}
fn actor_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.actor_field_mut()
}
fn object_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.object_field_ref()
}
fn object_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.object_field_mut()
}
}
impl<Inner, A, B, C, D> TargetRef for Ext4<Inner, A, B, C, D>
where
Inner: TargetRef,
{
fn target_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.target_field_ref()
}
fn target_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.target_field_mut()
}
}
impl<Inner, A, B, C, D> OriginRef for Ext4<Inner, A, B, C, D>
where
Inner: OriginRef,
{
fn origin_field_ref(&self) -> &OneOrMany<AnyBase> {
self.inner.origin_field_ref()
}
fn origin_field_mut(&mut self) -> &mut OneOrMany<AnyBase> {
self.inner.origin_field_mut()
}
}
impl<Inner, A, B, C, D> OptTargetRef for Ext4<Inner, A, B, C, D>
where
Inner: OptTargetRef,
{
fn target_field_ref(&self) -> &Option<OneOrMany<AnyBase>> {
self.inner.target_field_ref()
}
fn target_field_mut(&mut self) -> &mut Option<OneOrMany<AnyBase>> {
self.inner.target_field_mut()
}
}
impl<Inner, A, B, C, D> OptOriginRef for Ext4<Inner, A, B, C, D>
where
Inner: OptOriginRef,
{
fn origin_field_ref(&self) -> &Option<OneOrMany<AnyBase>> {
self.inner.origin_field_ref()
}
fn origin_field_mut(&mut self) -> &mut Option<OneOrMany<AnyBase>> {
self.inner.origin_field_mut()
}
}
impl<Inner, A, B, C, D> AsQuestion for Ext4<Inner, A, B, C, D>
where
Inner: AsQuestion,
{
fn question_ref(&self) -> &Question {
self.inner.question_ref()
}
fn question_mut(&mut self) -> &mut Question {
self.inner.question_mut()
}
}

View file

@ -0,0 +1,375 @@
//! # An extension API for activitystreams-new
//! _This crate provides Ext1, Ext2, Ext3, and Ext4 for adding extensions to ActivityStreams types_
//!
//! - Find the code on [git.asonix.dog](https://git.asonix.dog/asonix/activitystreams-ext)
//! - Read the docs on [activitystreams-ext.asonix.dog](https://activitystreams-ext.asonix.dog)
//! - Join the matrix channel at [#activitypub:asonix.dog](https://matrix.to/#/!fAEcHyTUdAaKCzIKCt:asonix.dog?via=asonix.dog&via=matrix.org&via=t2bot.io)
//! - Hit me up on [mastodon](https://asonix.dog/@asonix)
//!
//! ## Usage
//!
//! First, add ActivityStreams to your dependencies
//! ```toml
//! [dependencies]
//! activitystreams-new = { git = "https://git.asonix.dog/asonix/activitystreams-sketch", branch = "main" }
//! activitystreams-ext = { git = "https://git.asonix.dog/asonix/activitystreams-ext", branch = "main" }
//! ```
//!
//! For an example, we'll implement a PublicKey extension and demonstrate usage with Ext1
//! ```rust
//! use activitystreams_ext::{Ext1, UnparsedExtension};
//! use activitystreams::{
//! actor::{ApActor, Person},
//! context,
//! prelude::*,
//! primitives::XsdAnyUri,
//! security,
//! unparsed::UnparsedMutExt,
//! };
//!
//! #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
//! #[serde(rename_all = "camelCase")]
//! pub struct PublicKey {
//! public_key: PublicKeyInner,
//! }
//!
//! #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
//! #[serde(rename_all = "camelCase")]
//! pub struct PublicKeyInner {
//! id: XsdAnyUri,
//! owner: XsdAnyUri,
//! public_key_pem: String,
//! }
//!
//! impl<U> UnparsedExtension<U> for PublicKey
//! where
//! U: UnparsedMutExt,
//! {
//! type Error = serde_json::Error;
//!
//! fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
//! Ok(PublicKey {
//! public_key: unparsed_mut.remove("publicKey")?,
//! })
//! }
//!
//! fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
//! unparsed_mut.insert("publicKey", self.public_key)?;
//! Ok(())
//! }
//! }
//!
//! pub type ExtendedPerson = Ext1<ApActor<Person>, PublicKey>;
//!
//! fn main() -> Result<(), anyhow::Error> {
//! let actor = ApActor::new(
//! "http://in.box".parse()?,
//! "http://out.box".parse()?,
//! Person::new(),
//! );
//!
//! let mut person = Ext1::new(
//! actor,
//! PublicKey {
//! public_key: PublicKeyInner {
//! id: "http://key.id".parse()?,
//! owner: "http://owner.id".parse()?,
//! public_key_pem: "asdfasdfasdf".to_owned(),
//! },
//! },
//! );
//!
//! person.set_context(context()).add_context(security());
//!
//! let any_base = person.into_any_base()?;
//! println!("any_base: {:#?}", any_base);
//! let person = ExtendedPerson::from_any_base(any_base)?;
//!
//! println!("person: {:#?}", person);
//! Ok(())
//! }
//! ```
#![doc(html_root_url = "https://activitystreams-ext.asonix.dog")]
use activitystreams::{
base::{Base, Extends},
unparsed::{UnparsedMut, UnparsedMutExt},
};
mod ext1;
mod ext2;
mod ext3;
mod ext4;
/// Transform types from and into the Unparsed structure
pub trait UnparsedExtension<U>
where
U: UnparsedMutExt,
{
type Error: std::error::Error;
/// Generate Self from Unparsed
fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error>
where
Self: Sized;
/// Insert Self into Unparsed
fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error>;
}
/// Extend a type with a single value
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Ext1<Inner, A> {
#[serde(flatten)]
pub ext_one: A,
/// The type being extended
#[serde(flatten)]
pub inner: Inner,
}
/// Extend a type with two values
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Ext2<Inner, A, B> {
#[serde(flatten)]
pub ext_one: A,
#[serde(flatten)]
pub ext_two: B,
/// The type being extended
#[serde(flatten)]
pub inner: Inner,
}
/// Extend a type with three values
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Ext3<Inner, A, B, C> {
#[serde(flatten)]
pub ext_one: A,
#[serde(flatten)]
pub ext_two: B,
#[serde(flatten)]
pub ext_three: C,
/// The type being extended
#[serde(flatten)]
pub inner: Inner,
}
/// Extend a type with four values
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct Ext4<Inner, A, B, C, D> {
#[serde(flatten)]
pub ext_one: A,
#[serde(flatten)]
pub ext_two: B,
#[serde(flatten)]
pub ext_three: C,
#[serde(flatten)]
pub ext_four: D,
/// The type being extended
#[serde(flatten)]
pub inner: Inner,
}
impl<Inner, A> Ext1<Inner, A> {
pub fn new(inner: Inner, ext_one: A) -> Self {
Ext1 { inner, ext_one }
}
pub fn extend<B>(self, ext_two: B) -> Ext2<Inner, A, B> {
Ext2 {
inner: self.inner,
ext_one: self.ext_one,
ext_two,
}
}
}
impl<Inner, A, B> Ext2<Inner, A, B> {
pub fn new(inner: Inner, ext_one: A, ext_two: B) -> Self {
Ext2 {
inner,
ext_one,
ext_two,
}
}
pub fn extend<C>(self, ext_three: C) -> Ext3<Inner, A, B, C> {
Ext3 {
inner: self.inner,
ext_one: self.ext_one,
ext_two: self.ext_two,
ext_three,
}
}
}
impl<Inner, A, B, C> Ext3<Inner, A, B, C> {
pub fn new(inner: Inner, ext_one: A, ext_two: B, ext_three: C) -> Self {
Ext3 {
inner,
ext_one,
ext_two,
ext_three,
}
}
pub fn extend<D>(self, ext_four: D) -> Ext4<Inner, A, B, C, D> {
Ext4 {
inner: self.inner,
ext_one: self.ext_one,
ext_two: self.ext_two,
ext_three: self.ext_three,
ext_four,
}
}
}
impl<Inner, A, Kind, Error> Extends<Kind> for Ext1<Inner, A>
where
Inner: Extends<Kind, Error = Error> + UnparsedMut,
A: UnparsedExtension<Inner, Error = Error>,
Error: From<serde_json::Error> + std::error::Error,
{
type Error = Error;
fn extends(base: Base<Kind>) -> Result<Self, Self::Error> {
let mut inner = Inner::extends(base)?;
let ext_one = A::try_from_unparsed(&mut inner)?;
Ok(Ext1 { inner, ext_one })
}
fn retracts(self) -> Result<Base<Kind>, Self::Error> {
let Ext1 { mut inner, ext_one } = self;
ext_one.try_into_unparsed(&mut inner)?;
inner.retracts()
}
}
impl<Inner, A, B, Kind, Error> Extends<Kind> for Ext2<Inner, A, B>
where
Inner: Extends<Kind, Error = Error> + UnparsedMut,
A: UnparsedExtension<Inner, Error = Error>,
B: UnparsedExtension<Inner, Error = Error>,
Error: From<serde_json::Error> + std::error::Error,
{
type Error = Error;
fn extends(base: Base<Kind>) -> Result<Self, Self::Error> {
let mut inner = Inner::extends(base)?;
let ext_one = A::try_from_unparsed(&mut inner)?;
let ext_two = B::try_from_unparsed(&mut inner)?;
Ok(Ext2 {
inner,
ext_one,
ext_two,
})
}
fn retracts(self) -> Result<Base<Kind>, Self::Error> {
let Ext2 {
mut inner,
ext_one,
ext_two,
} = self;
ext_one.try_into_unparsed(&mut inner)?;
ext_two.try_into_unparsed(&mut inner)?;
inner.retracts()
}
}
impl<Inner, A, B, C, Kind, Error> Extends<Kind> for Ext3<Inner, A, B, C>
where
Inner: Extends<Kind, Error = Error> + UnparsedMut,
A: UnparsedExtension<Inner, Error = Error>,
B: UnparsedExtension<Inner, Error = Error>,
C: UnparsedExtension<Inner, Error = Error>,
Error: From<serde_json::Error> + std::error::Error,
{
type Error = Error;
fn extends(base: Base<Kind>) -> Result<Self, Self::Error> {
let mut inner = Inner::extends(base)?;
let ext_one = A::try_from_unparsed(&mut inner)?;
let ext_two = B::try_from_unparsed(&mut inner)?;
let ext_three = C::try_from_unparsed(&mut inner)?;
Ok(Ext3 {
inner,
ext_one,
ext_two,
ext_three,
})
}
fn retracts(self) -> Result<Base<Kind>, Self::Error> {
let Ext3 {
mut inner,
ext_one,
ext_two,
ext_three,
} = self;
ext_one.try_into_unparsed(&mut inner)?;
ext_two.try_into_unparsed(&mut inner)?;
ext_three.try_into_unparsed(&mut inner)?;
inner.retracts()
}
}
impl<Inner, A, B, C, D, Kind, Error> Extends<Kind> for Ext4<Inner, A, B, C, D>
where
Inner: Extends<Kind, Error = Error> + UnparsedMut,
A: UnparsedExtension<Inner, Error = Error>,
B: UnparsedExtension<Inner, Error = Error>,
C: UnparsedExtension<Inner, Error = Error>,
D: UnparsedExtension<Inner, Error = Error>,
Error: From<serde_json::Error> + std::error::Error,
{
type Error = Error;
fn extends(base: Base<Kind>) -> Result<Self, Self::Error> {
let mut inner = Inner::extends(base)?;
let ext_one = A::try_from_unparsed(&mut inner)?;
let ext_two = B::try_from_unparsed(&mut inner)?;
let ext_three = C::try_from_unparsed(&mut inner)?;
let ext_four = D::try_from_unparsed(&mut inner)?;
Ok(Ext4 {
inner,
ext_one,
ext_two,
ext_three,
ext_four,
})
}
fn retracts(self) -> Result<Base<Kind>, Self::Error> {
let Ext4 {
mut inner,
ext_one,
ext_two,
ext_three,
ext_four,
} = self;
ext_one.try_into_unparsed(&mut inner)?;
ext_two.try_into_unparsed(&mut inner)?;
ext_three.try_into_unparsed(&mut inner)?;
ext_four.try_into_unparsed(&mut inner)?;
inner.retracts()
}
}

3343
src/activity.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,134 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for Unit Structs that serialize to strings
use crate::UnitString;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Accept)]
pub struct AcceptType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Add)]
pub struct AddType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Move)]
pub struct MoveType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Announce)]
pub struct AnnounceType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Arrive)]
pub struct ArriveType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Block)]
pub struct BlockType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Create)]
pub struct CreateType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Delete)]
pub struct DeleteType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Dislike)]
pub struct DislikeType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Flag)]
pub struct FlagType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Follow)]
pub struct FollowType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Ignore)]
pub struct IgnoreType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Invite)]
pub struct InviteType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Join)]
pub struct JoinType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Leave)]
pub struct LeaveType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Like)]
pub struct LikeType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Listen)]
pub struct ListenType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Offer)]
pub struct OfferType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Question)]
pub struct QuestionType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Real)]
pub struct ReadType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Reject)]
pub struct RejectType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Remove)]
pub struct RemoveType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(TentativeAccept)]
pub struct TentativeAcceptType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(TentativeReject)]
pub struct TentativeRejectType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Travel)]
pub struct TravelType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Undo)]
pub struct UndoType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Update)]
pub struct UpdateType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(View)]
pub struct ViewType;

View file

@ -1,52 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for Activity types
#[cfg(feature = "kinds")]
pub mod kind;
#[cfg(feature = "types")]
pub mod properties;
#[cfg(feature = "types")]
mod types;
#[cfg(feature = "types")]
pub use self::types::{
AMove, Accept, Add, Announce, Arrive, Block, Create, Delete, Dislike, Flag, Follow, Ignore,
Invite, Join, Leave, Like, Listen, Offer, Question, Read, Reject, Remove, TentativeAccept,
TentativeReject, Travel, Undo, Update, View,
};
use crate::object::Object;
/// An Activity is a subtype of `Object` that describes some form of action that may happen, is
/// currently happening, or has already happened.
///
/// The `Activity` type itself serves as an abstract base type for all types of activities. It is
/// important to note that the `Activity` type itself does not carry any specific semantics about
/// the kind of action being taken.
#[cfg_attr(feature = "derive", crate::wrapper_type)]
pub trait Activity: Object {}
/// Instances of `IntransitiveActivity` are a subtype of `Activity` representing intransitive
/// actions.
///
/// The `object` property is therefore inappropriate for these activities.
#[cfg_attr(feature = "derive", crate::wrapper_type)]
pub trait IntransitiveActivity: Activity {}

View file

@ -1,624 +0,0 @@
/* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for properties of standard Activity types
//!
//! To use these properties in your own types, you can flatten them into your struct with serde:
//!
//! ```rust
//! use activitystreams::{
//! activity::{
//! properties::ActivityProperties,
//! Activity, ActivityBox,
//! },
//! ext::Ext,
//! object::{
//! properties::ObjectProperties,
//! Object, ObjectBox,
//! },
//! Base, BaseBox, PropRefs,
//! };
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
//! #[serde(transparent)]
//! pub struct ObjProps(pub ObjectProperties);
//!
//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
//! #[serde(transparent)]
//! pub struct ActivityProps(pub ActivityProperties);
//!
//! #[derive(Clone, Debug, Default, Serialize, Deserialize, PropRefs)]
//! #[serde(rename_all = "camelCase")]
//! #[prop_refs(Object)]
//! #[prop_refs(Activity)]
//! pub struct MyActivity {
//! #[serde(rename = "type")]
//! #[serde(alias = "objectType")]
//! #[serde(alias = "verb")]
//! pub kind: String,
//!
//! /// Define a require property for the MyActivity type
//! pub my_property: String,
//!
//! #[serde(flatten)]
//! #[prop_refs]
//! pub object_properties: ObjProps,
//!
//! #[serde(flatten)]
//! #[prop_refs]
//! pub activity_properties: ActivityProps,
//! }
//! #
//! # fn main() {}
//! ```
use crate::{primitives::*, properties, BaseBox};
properties! {
Activity {
docs [
"Activity objects are specializations of the base Object type that provide information about",
"actions that have either already occurred, are in the process of occurring, or may occur in the",
"future.",
],
result {
docs [
"Describes the result of the activity.",
"",
"For instance, if a particular action results in the creation of a new resource, the result",
"property can be used to describe that new resource.",
"",
"- Range: `Object` | `Link`",
"- Funcitonal: false",
],
types [
XsdAnyUri,
BaseBox
],
},
instrument {
docs [
"Identifies one or more objects used (or to be used) in the completion of an `Activity`.",
"",
"- Range: `Object` | `Link`",
"- Funcitonal: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
}
}
properties! {
ActorOptOriginAndTarget {
docs [ "Struct with `actor` and optional `origin` and `target` properties" ],
actor {
docs [
"Describes one or more entities that either performed or are expected to perform the",
"activity.",
"",
"Any single activity can have multiple actors. The actor MAY be specified using an indirect",
"Link.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required
},
origin {
docs [
"Describes an indirect object of the activity from which the activity is directed.",
"",
"The precise meaning of the origin is the object of the English preposition \"from\". For",
"instance, in the activity \"John moved an item to List B from List A\", the origin of the",
"activity is \"List A\".",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
target {
docs [
"Describes the indirect object, or target, of the activity.",
"",
"The precise meaning of the target is largely dependent on the type of action being",
"described but will often be the object of the English preposition \"to\". For instance, in",
"the activity \"John added a movie to his wishlist\", the target of the activity is John's",
"wishlist. An activity can have more than one target",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
}
}
properties! {
ActorAndObject {
docs [ "Struct with `actor` and `object` properties" ],
actor {
docs [
"Describes one or more entities that either performed or are expected to perform the",
"activity.",
"",
"Any single activity can have multiple actors. The actor MAY be specified using an indirect",
"Link.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required,
},
object {
docs [
"When used within an Activity, describes the direct object of the activity.",
"",
"For instance, in the activity \"John added a movie to his wishlist\", the object of the",
"activity is the movie added.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required,
},
}
}
properties! {
ActorObjectAndTarget {
docs [ "Struct with `actor`, `object`, and `target` properties" ],
actor {
docs [
"Describes one or more entities that either performed or are expected to perform the",
"activity.",
"",
"Any single activity can have multiple actors. The actor MAY be specified using an indirect",
"Link.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required,
},
object {
docs [
"When used within an Activity, describes the direct object of the activity.",
"",
"For instance, in the activity \"John added a movie to his wishlist\", the object of the",
"activity is the movie added.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required,
},
target {
docs [
"Describes the indirect object, or target, of the activity.",
"",
"The precise meaning of the target is largely dependent on the type of action being",
"described but will often be the object of the English preposition \"to\". For instance, in",
"the activity \"John added a movie to his wishlist\", the target of the activity is John's",
"wishlist. An activity can have more than one target",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required,
},
}
}
properties! {
ActorAndObjectOptTarget {
docs [ "Struct with `actor`, `object`, and optional `target` properties" ],
actor {
docs [
"Describes one or more entities that either performed or are expected to perform the",
"activity.",
"",
"Any single activity can have multiple actors. The actor MAY be specified using an indirect",
"Link.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required,
},
object {
docs [
"When used within an Activity, describes the direct object of the activity.",
"",
"For instance, in the activity \"John added a movie to his wishlist\", the object of the",
"activity is the movie added.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required,
},
target {
docs [
"Describes the indirect object, or target, of the activity.",
"",
"The precise meaning of the target is largely dependent on the type of action being",
"described but will often be the object of the English preposition \"to\". For instance, in",
"the activity \"John added a movie to his wishlist\", the target of the activity is John's",
"wishlist. An activity can have more than one target",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
}
}
properties! {
ActorAndObjectOptOrigin {
docs [ "Struct with `actor`, `object`, and optional `origin` properties" ],
actor {
docs [
"Describes one or more entities that either performed or are expected to perform the",
"activity.",
"",
"Any single activity can have multiple actors. The actor MAY be specified using an indirect",
"Link.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required,
},
object {
docs [
"When used within an Activity, describes the direct object of the activity.",
"",
"For instance, in the activity \"John added a movie to his wishlist\", the object of the",
"activity is the movie added.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required,
},
origin {
docs [
"Describes an indirect object of the activity from which the activity is directed.",
"",
"The precise meaning of the origin is the object of the English preposition \"from\". For",
"instance, in the activity \"John moved an item to List B from List A\", the origin of the",
"activity is \"List A\".",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
}
}
properties! {
ActorAndObjectOptOthers {
docs [ "Struct with `actor`, `object`, and optional `origin` and `target` properties" ],
actor {
docs [
"Describes one or more entities that either performed or are expected to perform the",
"activity.",
"",
"Any single activity can have multiple actors. The actor MAY be specified using an indirect",
"Link.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required,
},
object {
docs [
"When used within an Activity, describes the direct object of the activity.",
"",
"For instance, in the activity \"John added a movie to his wishlist\", the object of the",
"activity is the movie added.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required,
},
origin {
docs [
"Describes an indirect object of the activity from which the activity is directed.",
"",
"The precise meaning of the origin is the object of the English preposition \"from\". For",
"instance, in the activity \"John moved an item to List B from List A\", the origin of the",
"activity is \"List A\".",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
target {
docs [
"Describes the indirect object, or target, of the activity.",
"",
"The precise meaning of the target is largely dependent on the type of action being",
"described but will often be the object of the English preposition \"to\". For instance, in",
"the activity \"John added a movie to his wishlist\", the target of the activity is John's",
"wishlist. An activity can have more than one target",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
}
}
properties! {
ActorAndOrigin {
docs [ "Struct with `actor` and `origin` properties" ],
actor {
docs [
"Describes one or more entities that either performed or are expected to perform the",
"activity.",
"",
"Any single activity can have multiple actors. The actor MAY be specified using an indirect",
"Link.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdString,
BaseBox,
],
required,
},
origin {
docs [
"Describes an indirect object of the activity from which the activity is directed.",
"",
"The precise meaning of the origin is the object of the English preposition \"from\". For",
"instance, in the activity \"John moved an item to List B from List A\", the origin of the",
"activity is \"List A\".",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
required,
},
}
}
/// Properties for the Accept activity
pub type AcceptProperties = ActorAndObjectProperties;
/// Properties for the Add activity
pub type AddProperties = ActorAndObjectProperties;
/// Properties for the Move activity
pub type MoveProperties = ActorAndObjectOptOthersProperties;
/// Properties for the Announce activity
pub type AnnounceProperties = ActorAndObjectOptTargetProperties;
/// Properties for the Arrive activity
pub type ArriveProperties = ActorAndOriginProperties;
/// Properties for the Block activity
pub type BlockProperties = ActorAndObjectProperties;
/// Properties for the Create activity
pub type CreateProperties = ActorAndObjectProperties;
/// Properties for the Delete activity
pub type DeleteProperties = ActorAndObjectOptOriginProperties;
/// Properties for the Dislike activity
pub type DislikeProperties = ActorAndObjectProperties;
/// Properties for the Flag activity
pub type FlagProperties = ActorAndObjectProperties;
/// Properties for the Follow activity
pub type FollowProperties = ActorAndObjectProperties;
/// Properties for the Ignore activity
pub type IgnoreProperties = ActorAndObjectProperties;
/// Properties for the Invite activity
pub type InviteProperties = ActorObjectAndTargetProperties;
/// Properties for the Join activity
pub type JoinProperties = ActorAndObjectProperties;
/// Properties for the Leave activity
pub type LeaveProperties = ActorAndObjectProperties;
/// Properties for the Like activity
pub type LikeProperties = ActorAndObjectProperties;
/// Properties for the Listen activity
pub type ListenProperties = ActorAndObjectProperties;
/// Properties for the Offer activity
pub type OfferProperties = ActorAndObjectOptTargetProperties;
properties! {
Question {
docs [ "Properties for the Question activity" ],
one_of {
docs [
"Identifies an exclusive option for a Question.",
"",
"Use of `one_of` implies that the Question can have only a single answer. To indicate that a",
"`Question` can have multiple answers, use `any_of`.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
any_of {
docs [
"Identifies an inclusive option for a Question.",
"",
"Use of `any_of` implies that the Question can have multiple answers. To indicate that a",
"`Question` can have only one answer, use `one_of`.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
}
}
/// Properties for the Read activity
pub type ReadProperties = ActorAndObjectProperties;
/// Properties for the Reject activity
pub type RejectProperties = ActorAndObjectProperties;
/// Properties for the Remove activity
pub type RemoveProperties = ActorAndObjectOptOthersProperties;
/// Properties for the TentativeAccept activity
pub type TentativeAcceptProperties = ActorAndObjectProperties;
/// Properties for the TentativeReject activity
pub type TentativeRejectProperties = ActorAndObjectProperties;
/// Properties for the Travel activity
pub type TravelProperties = ActorOptOriginAndTargetProperties;
/// Properties for the Undo activity
pub type UndoProperties = ActorAndObjectProperties;
/// Properties for the Update activity
pub type UpdateProperties = ActorAndObjectProperties;
/// Properties for the View activity
pub type ViewProperties = ActorAndObjectProperties;

View file

@ -1,881 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::{
activity::{
kind::*, properties::*, Activity, ActivityBox, IntransitiveActivity,
IntransitiveActivityBox,
},
ext::Ext,
object::{
properties::{ApObjectProperties, ObjectProperties},
Object, ObjectBox,
},
Base, BaseBox, Extensible, PropRefs,
};
/// Indicates that the actor accepts the object.
///
/// The target property can be used in certain circumstances to indicate the context into which the
/// object has been accepted.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Accept {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: AcceptType,
/// Adds all valid accept properties to this struct
#[serde(flatten)]
#[prop_refs]
pub accept_props: AcceptProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor has added the object to the target.
///
/// If the target property is not explicitly specified, the target would need to be determined
/// implicitly by context. The origin can be used to identify the context from which the object
/// originated.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Add {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: AddType,
/// Adds all valid add properties to this struct
#[serde(flatten)]
#[prop_refs]
pub add_props: AddProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor has moved object from origin to target.
///
/// If the origin or target are not specified, either can be determined by context.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct AMove {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: MoveType,
/// Adds all valid move properties to this struct
#[serde(flatten)]
#[prop_refs]
pub move_props: MoveProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor is calling the target's attention the object.
///
/// The origin typically has no defined meaning.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Announce {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: AnnounceType,
/// Adds all valid announce properties to this struct
#[serde(flatten)]
#[prop_refs]
pub announce_props: AnnounceProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// An IntransitiveActivity that indicates that the actor has arrived at the location.
///
/// The origin can be used to identify the context from which the actor originated. The target
/// typically has no defined meaning.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
#[prop_refs(IntransitiveActivity)]
pub struct Arrive {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: ArriveType,
/// Adds all valid arrive properties to this struct
#[serde(flatten)]
#[prop_refs]
pub arrive_props: ArriveProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor is blocking the object.
///
/// Blocking is a stronger form of Ignore. The typical use is to support social systems that allow
/// one user to block activities or content of other users. The target and origin typically have no
/// defined meaning.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Block {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: BlockType,
/// Adds all valid block properties to this struct
#[serde(flatten)]
#[prop_refs]
pub block_props: BlockProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor has created the object.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Create {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: CreateType,
/// Adds all valid create properties to this struct
#[serde(flatten)]
#[prop_refs]
pub create_props: CreateProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor has deleted the object.
///
/// If specified, the origin indicates the context from which the object was deleted.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Delete {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: DeleteType,
/// Adds all valid delete properties to this struct
#[serde(flatten)]
#[prop_refs]
pub delete_props: DeleteProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor dislikes the object.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Dislike {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: DislikeType,
/// Adds all valid dislike properties to this struct
#[serde(flatten)]
#[prop_refs]
pub dislike_props: DislikeProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor is "flagging" the object.
///
/// Flagging is defined in the sense common to many social platforms as reporting content as being
/// inappropriate for any number of reasons.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Flag {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: FlagType,
/// Adds all valid flag properties to this struct
#[serde(flatten)]
#[prop_refs]
pub flag_props: FlagProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor is "following" the object.
///
/// Following is defined in the sense typically used within Social systems in which the actor is
/// interested in any activity performed by or on the object. The target and origin typically have
/// no defined meaning.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Follow {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: FollowType,
/// Adds all valid follow properties to this struct
#[serde(flatten)]
#[prop_refs]
pub follow_props: FollowProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor is ignoring the object.
///
/// The target and origin typically have no defined meaning.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Ignore {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: IgnoreType,
/// Adds all valid ignore properties to this struct
#[serde(flatten)]
#[prop_refs]
pub ignore_props: IgnoreProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// A specialization of Offer in which the actor is extending an invitation for the object to the
/// target.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Invite {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: InviteType,
/// Adds all valid invite properties to this struct
#[serde(flatten)]
#[prop_refs]
pub invite_props: InviteProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor has joined the object.
///
/// The target and origin typically have no defined meaning
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Join {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: JoinType,
/// Adds all valid join properties to this struct
#[serde(flatten)]
#[prop_refs]
pub join_props: JoinProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor has left the object.
///
/// The target and origin typically have no meaning.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Leave {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: LeaveType,
/// Adds all valid leave properties to this struct
#[serde(flatten)]
#[prop_refs]
pub leave_props: LeaveProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor likes, recommends or endorses the object.
///
/// The target and origin typically have no defined meaning.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Like {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: LikeType,
/// Adds all valid like properties to this struct
#[serde(flatten)]
#[prop_refs]
pub like_props: LikeProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor has listened to the object.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Listen {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: ListenType,
/// Adds all valid listen properties to this struct
#[serde(flatten)]
#[prop_refs]
pub listen_props: ListenProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor is offering the object.
///
/// If specified, the target indicates the entity to which the object is being offered.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Offer {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: OfferType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub offer_props: OfferProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Represents a question being asked.
///
/// Question objects are an extension of IntransitiveActivity. That is, the Question object is an
/// Activity, but the direct object is the question itself and therefore it would not contain an
/// object property.
///
/// Either of the anyOf and oneOf properties MAY be used to express possible answers, but a
/// Question object MUST NOT have both properties.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
#[prop_refs(IntransitiveActivity)]
pub struct Question {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: QuestionType,
/// Adds all valid question properties to this struct
#[serde(flatten)]
#[prop_refs]
pub question_props: QuestionProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor has read the object.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Read {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: ReadType,
/// Adds all valid read properties to this struct
#[serde(flatten)]
#[prop_refs]
pub read_props: ReadProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor is rejecting the object.
///
/// The target and origin typically have no defined meaning.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Reject {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: RejectType,
/// Adds all valid reject properties to this struct
#[serde(flatten)]
#[prop_refs]
pub reject_props: RejectProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor is removing the object.
///
/// If specified, the origin indicates the context from which the object is being removed.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Remove {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: RemoveType,
/// Adds all valid remove properties to this struct
#[serde(flatten)]
#[prop_refs]
pub remove_props: RemoveProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// A specialization of Accept indicating that the acceptance is tentative.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct TentativeAccept {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: TentativeAcceptType,
/// Adds all valid tentative_accept properties to this struct
#[serde(flatten)]
#[prop_refs]
pub tentative_accept_props: TentativeAcceptProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// A specialization of Reject in which the rejection is considered tentative.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct TentativeReject {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: TentativeRejectType,
/// Adds all valid tentative_reject properties to this struct
#[serde(flatten)]
#[prop_refs]
pub tentative_reject_props: TentativeRejectProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor is traveling to target from origin.
///
/// Travel is an IntransitiveObject whose actor specifies the direct object. If the target or
/// origin are not specified, either can be determined by context.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
#[prop_refs(IntransitiveActivity)]
pub struct Travel {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: TravelType,
/// Adds all valid travel properties to this struct
#[serde(flatten)]
#[prop_refs]
pub travel_props: TravelProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor is undoing the object.
///
/// In most cases, the object will be an Activity describing some previously performed action (for
/// instance, a person may have previously "liked" an article but, for whatever reason, might
/// choose to undo that like at some later point in time).
///
/// The target and origin typically have no defined meaning.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Undo {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: UndoType,
/// Adds all valid undo properties to this struct
#[serde(flatten)]
#[prop_refs]
pub undo_props: UndoProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor has updated the object.
///
/// Note, however, that this vocabulary does not define a mechanism for describing the actual set
/// of modifications made to object.
///
/// The target and origin typically have no defined meaning.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct Update {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: UpdateType,
/// Adds all valid update properties to this struct
#[serde(flatten)]
#[prop_refs]
pub update_props: UpdateProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}
/// Indicates that the actor has viewed the object.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Activity)]
#[extension(ApObjectProperties)]
pub struct View {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
pub kind: ViewType,
/// Adds all valid view properties to this struct
#[serde(flatten)]
#[prop_refs]
pub view_props: ViewProperties,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid activity properties to this struct
#[serde(flatten)]
#[prop_refs]
pub activity_props: ActivityProperties,
}

1559
src/actor.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,41 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for Unit Structs that serialize to strings
use crate::UnitString;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Application)]
pub struct ApplicationType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Group)]
pub struct GroupType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Organization)]
pub struct OrganizationType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Person)]
pub struct PersonType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Service)]
pub struct ServiceType;

View file

@ -1,54 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for Actor types
#[cfg(feature = "kinds")]
pub mod kind;
#[cfg(feature = "types")]
pub mod properties;
#[cfg(feature = "types")]
mod types;
#[cfg(feature = "types")]
pub use self::types::{Application, Group, Organization, Person, Service};
use crate::object::Object;
/// `Actor` types are `Object` types that are capable of performing activities.
///
/// This specification intentionally defines `Actors` in only the most generalized way, stopping
/// short of defining semantically specific properties for each. All Actor objects are
/// specializations of `Object` and inherit all of the core properties common to all Objects.
/// External vocabularies can be used to express additional detail not covered by the Activity
/// Vocabulary. VCard [[vcard-rdf](https://www.w3.org/TR/vcard-rdf/) SHOULD be used to provide
/// additional metadata for `Person`, `Group`, and `Organization` instances.
///
/// While implementations are free to introduce new types of Actors beyond those defined by the
/// Activity Vocabulary, interoperability issues can arise when applications rely too much on
/// extension types that are not recognized by other implementations. Care should be taken to not
/// unduly overlap with or duplicate the existing `Actor` types.
///
/// When an implementation uses an extension type that overlaps with a core vocabulary type, the
/// implementation MUST also specify the core vocabulary type. For instance, some vocabularies
/// (e.g. VCard) define their own types for describing people. An implementation that wishes, for
/// example, to use a `vcard:Individual` as an `Actor` MUST also identify that `Actor` as a
/// `Person`.
#[cfg_attr(feature = "derive", crate::wrapper_type)]
pub trait Actor: Object {}

View file

@ -1,192 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for properties of standard Actor types
//!
//! To use these properties in your own types, you can flatten them into your struct with serde:
//!
//! ```rust
//! use activitystreams::{
//! actor::{
//! properties::ApActorProperties,
//! Actor, ActorBox,
//! },
//! ext::Ext,
//! object::{
//! properties::{ApObjectProperties, ObjectProperties},
//! Object, ObjectBox,
//! },
//! Base, BaseBox, PropRefs,
//! };
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
//! #[serde(transparent)]
//! pub struct ObjProps(pub ObjectProperties);
//!
//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
//! #[serde(transparent)]
//! pub struct ApObjProps(pub ApObjectProperties);
//!
//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
//! #[serde(transparent)]
//! pub struct ApActorProps(pub ApActorProperties);
//!
//! #[derive(Clone, Debug, Default, Serialize, Deserialize, PropRefs)]
//! #[serde(rename_all = "camelCase")]
//! #[prop_refs(Object)]
//! #[prop_refs(Actor)]
//! pub struct MyActor {
//! #[serde(rename = "type")]
//! pub kind: String,
//!
//! /// Define a require property for the MyActor type
//! pub my_property: String,
//!
//! #[serde(flatten)]
//! #[prop_refs]
//! pub object_props: ObjProps,
//!
//! #[serde(flatten)]
//! #[prop_refs]
//! pub ap_object_props: ApObjProps,
//!
//! #[serde(flatten)]
//! #[prop_refs]
//! pub actor_props: ApActorProps,
//! }
//! #
//! # fn main() {}
//! ```
use crate::{
actor::Actor,
endpoint::EndpointProperties,
ext::Extension,
primitives::{XsdAnyUri, XsdString},
properties,
};
impl<T> Extension<T> for ApActorProperties where T: Actor {}
properties! {
ApActor {
docs [
"Define activitypub properties for the Actor type as described by the Activity Pub vocabulary."
],
inbox {
docs [
"A reference to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)]",
"OrderedCollection comprised of all the messages received by the actor.",
"",
"- Range: `xsd:anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
required,
},
outbox {
docs [
"An [ActivityStreams](https://www.w3.org/ns/activitystreams)] OrderedCollection comprised of",
"all the messages produced by the actor.",
"",
"- Range: `xsd:anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
required,
},
following {
docs [
"A link to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] collection of the",
"actors that this actor is following.",
"",
"- Range: `xsd:anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
},
followers {
docs [
"A link to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] collection of the",
"actors that follow this actor.",
"",
"- Range: `xsd:anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
},
liked {
docs [
"A link to an [[ActivityStreams](https://www.w3.org/ns/activitystreams)] collection of",
"objects this actor has liked.",
"",
"- Range: `xsd:anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
},
streams {
docs [
"A list of supplementary Collections which may be of interest.",
"",
"- Range: `xsd:anyUri`",
"- Functional: false",
],
types [ XsdAnyUri ],
},
preferred_username {
docs [
"A short username which may be used to refer to the actor, with no uniqueness guarantees.",
"",
"- Range: `xsd:string`",
"- Functional: true",
],
types [ XsdString ],
functional,
},
endpoints {
docs [
"A json object which maps additional (typically server/domain-wide) endpoints which may be",
"useful either for this actor or someone referencing this actor.",
"",
"This mapping may be nested inside the actor document as the value or may be a link to a",
"JSON-LD document with these properties.",
"",
"- Range: `Endpoint`",
"- Functional: true",
],
types [ EndpointProperties ],
functional,
},
}
}

View file

@ -1,119 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::{
actor::{kind::*, properties::ApActorProperties, Actor, ActorBox},
ext::Ext,
object::{
properties::{ApObjectProperties, ObjectProperties},
Object, ObjectBox,
},
Base, BaseBox, Extensible, PropRefs,
};
/// Describes a software application.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Actor)]
#[extension(ApObjectProperties)]
#[extension(ApActorProperties)]
pub struct Application {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: ApplicationType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}
/// Represents a formal or informal collective of Actors.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Actor)]
#[extension(ApObjectProperties)]
#[extension(ApActorProperties)]
pub struct Group {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: GroupType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}
/// Represents an organization.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Organization {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: OrganizationType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}
/// Represents an individual person.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Actor)]
#[extension(ApObjectProperties)]
#[extension(ApActorProperties)]
pub struct Person {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: PersonType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}
/// Represents a service of any kind.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Actor)]
#[extension(ApObjectProperties)]
#[extension(ApActorProperties)]
pub struct Service {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: ServiceType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}

1873
src/base.rs Normal file

File diff suppressed because it is too large Load diff

1338
src/collection.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,37 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for Unit Structs that serialize to strings
use crate::UnitString;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Collection)]
pub struct CollectionType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(CollectionPage)]
pub struct CollectionPageType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(OrderedCollection)]
pub struct OrderedCollectionType;
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(OrderedCollectionPage)]
pub struct OrderedCollectionPageType;

View file

@ -1,59 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for Collection types
#[cfg(feature = "kinds")]
pub mod kind;
#[cfg(feature = "types")]
pub mod properties;
#[cfg(feature = "types")]
mod types;
#[cfg(feature = "types")]
pub use self::types::{
OrderedCollection, OrderedCollectionPage, UnorderedCollection, UnorderedCollectionPage,
};
use crate::object::Object;
/// A Collection is a subtype of `Object` that represents ordered or unordered sets of `Object` or
/// `Link` instances.
///
/// The items within a Collection can be ordered or unordered. The OrderedCollection type MAY be
/// used to identify a Collection whose items are always ordered. In the JSON serialization, the
/// unordered items of a Collection are represented using the items property while ordered items
/// are represented using the orderedItems property.
///
/// `UnorderedCollection` and `OrderedCollection` types are provided by the `activitystreams-types`
/// crate.
#[cfg_attr(feature = "derive", crate::wrapper_type)]
pub trait Collection: Object {}
/// Used to represent distinct subsets of items from a Collection.
///
/// A `Collection` can contain a large number of items. Often, it becomes impractical for an
/// implementation to serialize every item contained by a `Collection` using the items (or
/// `ordered_items`) property alone. In such cases, the items within a `Collection` can be divided
/// into distinct subsets or "pages". A page is identified using the `CollectionPage` type.
///
/// `UnorderedCollectionPage` and `OrderedCollectionPage` types are provied by the
/// `activitystreams-types` crate.
#[cfg_attr(feature = "derive", crate::wrapper_type)]
pub trait CollectionPage: Collection {}

View file

@ -1,226 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for properties of standard collection types
//!
//! To use these properties in your own types, you can flatten them into your struct with serde:
//!
//! ```rust
//! use activitystreams::{
//! collection::{
//! properties::CollectionProperties,
//! Collection, CollectionBox,
//! },
//! ext::Ext,
//! object::{
//! properties::ObjectProperties,
//! Object, ObjectBox,
//! },
//! Base, BaseBox, PropRefs,
//! };
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
//! #[serde(transparent)]
//! pub struct ObjProps(pub ObjectProperties);
//!
//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
//! #[serde(transparent)]
//! pub struct CollectionProps(pub CollectionProperties);
//!
//! #[derive(Clone, Debug, Default, Serialize, Deserialize, PropRefs)]
//! #[serde(rename_all = "camelCase")]
//! #[prop_refs(Object)]
//! #[prop_refs(Collection)]
//! pub struct MyCollection {
//! #[serde(rename = "type")]
//! pub kind: String,
//!
//! /// Define a require property for the MyCollection type
//! pub my_property: String,
//!
//! #[serde(flatten)]
//! #[prop_refs]
//! pub object_properties: ObjProps,
//!
//! #[serde(flatten)]
//! #[prop_refs]
//! pub collection_properties: CollectionProps,
//! }
//! #
//! # fn main() {}
//! ```
use crate::{primitives::*, properties, BaseBox};
properties! {
Collection {
docs [
"`Collection` objects are a specialization of the base `Object` that serve as a container for",
"other `Objects` or `Links`.",
"",
"The items within a `Collection` can be ordered or unordered. The `OrderedCollection` type MAY be",
"used to identify a `Collection` whose items are always ordered. In the JSON serialization, the",
"unordered items of a `Collection` are represented using the `items` property while ordered items",
"are represented using the `ordered_items` property.",
],
items {
docs [
"Identifies the items contained in a collection. The items might be ordered or unordered.",
"",
"- Range: `Object` | `Link` | Ordered List of [ `Object` | `Link` ]",
"- Functional: false",
],
types [
XsdString,
BaseBox,
],
required,
},
total_items {
docs [
"A non-negative integer specifying the total number of objects contained by the logical view",
"of the collection.",
"",
"This number might not reflect the actual number of items serialized within the `Collection`",
"object instance.",
"",
"- Range: `xsd:nonNegativeInteger`",
"- Functional: true",
],
types [
XsdNonNegativeInteger,
],
functional,
},
current {
docs [
"In a paged `Collection`, indicates the page that contains the most recently updated member",
"items.",
"",
"- Range: `CollectionPage` | `Link`",
"- Functional: true",
],
types [
XsdAnyUri,
BaseBox,
],
functional,
},
first {
docs [
"In a paged `Collection`, indicates the furthest preceeding page of items in the collection.",
"",
"- Range: `CollectionPage` | `Link`",
"- Functional: true",
],
types [
XsdAnyUri,
BaseBox,
],
functional,
},
last {
docs [
"In a paged `Collection`, indicates the furthest proceeding page of the collection.",
"",
"- Range: `CollectionPage` | `Link`",
"- Functional: true",
],
types [
XsdAnyUri,
BaseBox,
],
},
}
}
properties! {
CollectionPage {
docs [
"The `CollectionPage` type extends from the base `Collection` type and inherits all of it's",
"properties.",
],
part_of {
docs [
"Identifies the `Collection` to which a `CollectionPage` objects items belong.",
"",
"Range: `Collection` | `Link`",
"Functional: true",
],
types [
XsdAnyUri,
BaseBox,
],
functional,
},
next {
docs [
"In a paged `Collection`, indicates the next page of items.",
"",
"- Range: `CollectionPage` | `Link`",
"- Functional: true",
],
types [
XsdAnyUri,
BaseBox,
],
functional,
},
prev {
docs [
"In a paged `Collection`, identifies the previous page of items.",
"",
"- Range: `CollectionPage` | `Link`",
"- Functional: true",
],
types [
XsdAnyUri,
BaseBox,
],
functional,
},
}
}
properties! {
OrderedCollectionPage {
docs ["The OrderedCollectionPage type MAY be used to identify a page whose items are strictly ordered." ],
start_index {
docs ["A non-negative integer value identifying the relative position within the logical view of a",
"strictly ordered collection.",
"",
"- Range: `xsd:nonNegativeInteger`",
"- Functional: true",
],
types [
XsdNonNegativeInteger,
],
functional,
},
}
}

View file

@ -1,142 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for Collection types
use crate::{
collection::{
kind::*, properties::*, Collection, CollectionBox, CollectionPage, CollectionPageBox,
},
ext::Ext,
object::{
properties::{ApObjectProperties, ObjectProperties},
Object, ObjectBox,
},
Base, BaseBox, Extensible, PropRefs,
};
/// The default `Collection` type.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Collection)]
#[extension(ApObjectProperties)]
pub struct UnorderedCollection {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: CollectionType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid collection properties to this struct
#[serde(flatten)]
#[prop_refs]
pub collection_props: CollectionProperties,
}
/// A subtype of `Collection` in which members of the logical collection are assumed to always be
/// strictly ordered.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Collection)]
#[extension(ApObjectProperties)]
pub struct OrderedCollection {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: OrderedCollectionType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid collection properties to this struct
#[serde(flatten)]
#[prop_refs]
pub collection_props: CollectionProperties,
}
/// Used to represent distinct subsets of items from a `Collection`.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Collection)]
#[prop_refs(CollectionPage)]
#[extension(ApObjectProperties)]
pub struct UnorderedCollectionPage {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: CollectionPageType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid collection properties to this struct
#[serde(flatten)]
#[prop_refs]
pub collection_props: CollectionProperties,
/// Adds all valid collection page properties to this struct
#[serde(flatten)]
#[prop_refs]
pub collection_page_props: CollectionPageProperties,
}
/// Used to represent ordered subsets of items from an `OrderedCollection`.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[prop_refs(Collection)]
#[prop_refs(CollectionPage)]
#[extension(ApObjectProperties)]
pub struct OrderedCollectionPage {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: OrderedCollectionPageType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid collection properties to this struct
#[serde(flatten)]
#[prop_refs]
pub collection_props: CollectionProperties,
/// Adds all valid collection page properties to this struct
#[serde(flatten)]
#[prop_refs]
pub collection_page_props: CollectionPageProperties,
/// Adds all valid ordered collection page properties to this struct
#[serde(flatten)]
#[prop_refs]
pub ordered_collection_page_props: OrderedCollectionPageProperties,
}

51
src/either.rs Normal file
View file

@ -0,0 +1,51 @@
#[derive(
Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
)]
#[serde(untagged)]
pub enum Either<L, R> {
Left(L),
Right(R),
}
impl<L, R> Either<L, R> {
pub fn left(self) -> Option<L> {
if let Either::Left(l) = self {
Some(l)
} else {
None
}
}
pub fn right(self) -> Option<R> {
if let Either::Right(r) = self {
Some(r)
} else {
None
}
}
pub fn as_ref(&self) -> Either<&L, &R> {
match self {
Either::Left(ref l) => Either::Left(l),
Either::Right(ref r) => Either::Right(r),
}
}
pub fn as_mut(&mut self) -> Either<&mut L, &mut R> {
match self {
Either::Left(ref mut l) => Either::Left(l),
Either::Right(ref mut r) => Either::Right(r),
}
}
pub fn map<F1, F2, L2, R2>(self, f1: F1, f2: F2) -> Either<L2, R2>
where
F1: Fn(L) -> L2,
F2: Fn(R) -> R2,
{
match self {
Either::Left(l) => Either::Left((f1)(l)),
Either::Right(r) => Either::Right((f2)(r)),
}
}
}

View file

@ -1,121 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Endpoint traits and types
use crate::{primitives::XsdAnyUri, properties};
properties! {
Endpoint {
docs [
"A json object which maps additional (typically server/domain-wide) endpoints which may be",
"useful either for this actor or someone referencing this actor.",
"",
"This mapping may be nested inside the actor document as the value or may be a link to a JSON-LD",
"document with these properties.",
],
proxy_url {
docs [
"Endpoint URI so this actor's clients may access remote ActivityStreams objects which",
"require authentication to access.",
"",
"To use this endpoint, the client posts an x-www-form-urlencoded id parameter with the value",
"being the id of the requested ActivityStreams object.",
"",
"- Range: `anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
},
oauth_authorization_endpoint {
docs [
"If OAuth 2.0 bearer tokens [[RFC6749](https://tools.ietf.org/html/rfc6749)]",
"[[RFC6750](https://tools.ietf.org/html/rfc6750)] are being used for authenticating client",
"to server interactions, this endpoint specifies a URI at which a browser-authenticated user",
"may obtain a new authorization grant.",
"",
"- Range: `anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
},
oauth_token_endpoint {
docs [
"If OAuth 2.0 bearer tokens [[RFC6749](https://tools.ietf.org/html/rfc6749)]",
"[[RFC6750](https://tools.ietf.org/html/rfc6750)] are being used for authenticating client",
"to server interactions, this endpoint specifies a URI at which a client may acquire an",
"access token.",
"",
"- Range: `anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
},
provide_client_key {
docs [
"If Linked Data Signatures and HTTP Signatures are being used for authentication and",
"authorization, this endpoint specifies a URI at which browser-authenticated users may",
"authorize a client's public key for client to server interactions.",
"",
"- Range: `anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
},
sign_client_key {
docs [
"If Linked Data Signatures and HTTP Signatures are being used for authentication and",
"authorization, this endpoint specifies a URI at which a client key may be signed by the",
"actor's key for a time window to act on behalf of the actor in interacting with foreign",
"servers.",
"",
"- Range: `anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
},
shared_inbox {
docs [
"An optional endpoint used for wide delivery of publicly addressed activities and activities",
"sent to followers.",
"",
"`shared_inbox`endpoints SHOULD also be publicly readable `OrderedCollection` objects",
"containing objects addressed to the Public special collection. Reading from the",
"`shared_inbox` endpoint MUST NOT present objects which are not addressed to the Public",
"endpoint.",
"",
"- Range: `anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
},
}
}

3
src/error.rs Normal file
View file

@ -0,0 +1,3 @@
#[derive(Clone, Debug, thiserror::Error)]
#[error("URL did not match expected domain")]
pub struct DomainError;

View file

@ -1,384 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Defining extensibility in the ActivityStreams spec
//!
//! In ActivityStreams, there are many times you may want to use an extension. For example, to
//! interact with Mastodon, you need to at least understand the `publicKey` field on their actor
//! type. If not, you won't be able to use HTTP Signatures, and will have your messages rejected.
//!
//! But this library doesn't provide any of the security extensions to ActivityStreams. In order to
//! support it, you could implment your own extensions to this library. Let's cover a basic
//! example.
//!
//! ```rust
//! // For this example, we'll use the Extensible trait, the Extension trait, the Actor trait, and
//! // the Person type
//! use activitystreams::{
//! actor::{Actor, Person},
//! ext::{Extensible, Extension},
//! };
//!
//! /// Let's define the PublicKey type. The three fields in this PublicKey struct are how Mastodon
//! /// represents Public Keys on actors. We'll need to derive Serialize and Deserialize for these
//! /// in order for them to be useful.
//! #[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
//! #[serde(rename_all = "camelCase")]
//! pub struct PublicKey {
//! /// The ID of the key.
//! ///
//! /// In mastodon, this is the same as the actor's URL with a #main-key on
//! /// the end.
//! pub id: String,
//!
//! /// The ID of the actor who owns this key.
//! pub owner: String,
//!
//! /// This is a PEM file with PKCS#8 encoded data.
//! pub public_key_pem: String,
//! }
//!
//! /// Now, we'll need more than just a PublicKey struct to make this work. We'll need to define a
//! /// second struct that declares the correct key to house this information inside of
//! ///
//! /// The information is represented as the following json:
//! /// ```json
//! /// {
//! /// "publicKey": {
//! /// "id": "key id",
//! /// "owner": "actor id",
//! /// "publicKeyPem": "pem string"
//! /// }
//! /// }
//! /// ```
//! ///
//! /// This means we'll need to define the 'publicKey' key
//! #[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
//! #[serde(rename_all = "camelCase")]
//! pub struct PublicKeyExtension {
//! /// The public key's information
//! pub public_key: PublicKey
//! }
//!
//! impl PublicKey {
//! /// Let's add a convenience method to turn a PublicKey into a PublicKeyExtension
//! pub fn to_ext(self) -> PublicKeyExtension {
//! PublicKeyExtension { public_key: self }
//! }
//! }
//!
//! // And finally, we'll implement the Extension trait for PublicKeyExtension
//! //
//! // We'll bound this extension by the Actor trait, since we don't care about non-actors having
//! // keys. This means that you can put a key on a `Person`, but not on a `Video`.
//! impl<T> Extension<T> for PublicKeyExtension where T: Actor {}
//!
//! // Now that these types are defined, we can put them to use!
//! let person = Person::new();
//!
//! // let's just create a dummy key for this example
//! let public_key = PublicKey {
//! id: "My ID".to_owned(),
//! owner: "Owner ID".to_owned(),
//! public_key_pem: "My Public Key".to_owned(),
//! };
//!
//! // We're doing it! The person is being extended with a public key
//! //
//! // Note that calling `extend` on person here is possible because the Extensible trait is in
//! // scope
//! let person_with_key = person.extend(public_key.to_ext());
//! ```
use crate::{
activity::{Activity, ActivityBox, IntransitiveActivity, IntransitiveActivityBox},
actor::{Actor, ActorBox},
collection::{Collection, CollectionBox, CollectionPage, CollectionPageBox},
link::{Link, LinkBox},
object::{Object, ObjectBox},
Base, BaseBox,
};
use std::{convert::TryFrom, fmt::Debug};
/// Defines an extension to an activitystreams object or link
///
/// This type provides two fields, the first field, `base`, should always the be base
/// ActivityStreams type. As long as `base` implements an ActivityStreams trait, Ext will also.
///
/// For example, the type `Ext<Video, HashMap<String, String>>` will implement the `Object` trait,
/// because `Video` implements that trait.
///
/// Additionally, AsRef and AsMut have been implemented for Ext. If type type
/// `Ext<Ext<Follow, SomeTime>, AnotherType>` exists, that will implement
/// `AsRef<ActivityProperties>` just like the innermost `Follow`.
///
/// Usage:
/// ```rust
/// use activitystreams::object::Video;
///
/// fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let mut video = Video::full();
///
/// // AsMut works even though this is an Ext<Video, ApObjectProperties>
/// video
/// .as_mut()
/// .set_id("https://example.com")?;
///
/// // set information on the extension
/// video
/// .extension
/// .set_source_xsd_any_uri("https://example.com")?;
///
/// Ok(())
/// }
/// ```
#[derive(Clone, Debug, Default)]
#[cfg_attr(feature = "derive", derive(serde::Deserialize, serde::Serialize))]
pub struct Ext<T, U> {
/// The ActivityStreams base type, or another extension containing one
#[cfg_attr(feature = "derive", serde(flatten))]
pub base: T,
/// The extension being applied to the type
#[cfg_attr(feature = "derive", serde(flatten))]
pub extension: U,
}
/// A trait implemented by extensions to the ActivityStreams specification
///
/// This is implemented for a couple types by default, such as
/// ApObjectProperties, and ApActorProperties.
///
/// Extensions are not intended to be used as trait objects
pub trait Extension<T>
where
T: Base,
{
/// A default implementation that simply returns the Ext type with Self and the base type
/// inside of it.
fn extends(self, base: T) -> Ext<T, Self>
where
Self: Sized,
{
Ext {
base,
extension: self,
}
}
}
/// A trait implemented (automatically) by objects and links in the ActivityStreams specification
///
/// This is used to easily extend objects.
///
/// ```rust
/// # use activitystreams::object::{Video, properties::ApObjectProperties};
/// use activitystreams::ext::Extensible;
/// let vid = Video::new();
/// let ap_props = ApObjectProperties::default();
///
/// let extended_vid = vid.extend(ap_props);
/// ```
pub trait Extensible<U> {
fn extend(self, extension: U) -> Ext<Self, U>
where
Self: Sized;
}
impl<T, U, V> AsRef<V> for Ext<T, U>
where
T: Base + AsRef<V>,
U: Extension<T> + Debug,
{
fn as_ref(&self) -> &V {
self.base.as_ref()
}
}
impl<T, U, V> AsMut<V> for Ext<T, U>
where
T: Base + AsMut<V>,
U: Extension<T> + Debug,
{
fn as_mut(&mut self) -> &mut V {
self.base.as_mut()
}
}
impl<T, U> TryFrom<Ext<T, U>> for BaseBox
where
T: Base + serde::ser::Serialize,
U: Extension<T> + serde::ser::Serialize + Debug,
{
type Error = std::io::Error;
fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
BaseBox::from_concrete(e)
}
}
impl<T, U> TryFrom<Ext<T, U>> for ObjectBox
where
T: Object + serde::ser::Serialize,
U: Extension<T> + serde::ser::Serialize + Debug,
{
type Error = std::io::Error;
fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
ObjectBox::from_concrete(e)
}
}
impl<T, U> TryFrom<Ext<T, U>> for LinkBox
where
T: Link + serde::ser::Serialize,
U: Extension<T> + serde::ser::Serialize + Debug,
{
type Error = std::io::Error;
fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
LinkBox::from_concrete(e)
}
}
impl<T, U> TryFrom<Ext<T, U>> for CollectionBox
where
T: Collection + serde::ser::Serialize,
U: Extension<T> + serde::ser::Serialize + Debug,
{
type Error = std::io::Error;
fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
CollectionBox::from_concrete(e)
}
}
impl<T, U> TryFrom<Ext<T, U>> for CollectionPageBox
where
T: CollectionPage + serde::ser::Serialize,
U: Extension<T> + serde::ser::Serialize + Debug,
{
type Error = std::io::Error;
fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
CollectionPageBox::from_concrete(e)
}
}
impl<T, U> TryFrom<Ext<T, U>> for ActivityBox
where
T: Activity + serde::ser::Serialize,
U: Extension<T> + serde::ser::Serialize + Debug,
{
type Error = std::io::Error;
fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
ActivityBox::from_concrete(e)
}
}
impl<T, U> TryFrom<Ext<T, U>> for IntransitiveActivityBox
where
T: IntransitiveActivity + serde::ser::Serialize,
U: Extension<T> + serde::ser::Serialize + Debug,
{
type Error = std::io::Error;
fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
IntransitiveActivityBox::from_concrete(e)
}
}
impl<T, U> TryFrom<Ext<T, U>> for ActorBox
where
T: Actor + serde::ser::Serialize,
U: Extension<T> + serde::ser::Serialize + Debug,
{
type Error = std::io::Error;
fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
ActorBox::from_concrete(e)
}
}
impl<T, U> Extensible<U> for T
where
T: crate::Base,
U: Extension<T>,
{
fn extend(self, item: U) -> Ext<Self, U> {
item.extends(self)
}
}
impl<T, U> Base for Ext<T, U>
where
T: Base,
U: Debug,
{
}
impl<T, U> Object for Ext<T, U>
where
T: Object,
U: Debug,
{
}
impl<T, U> Link for Ext<T, U>
where
T: Link,
U: Debug,
{
}
impl<T, U> Actor for Ext<T, U>
where
T: Actor,
U: Debug,
{
}
impl<T, U> Collection for Ext<T, U>
where
T: Collection,
U: Debug,
{
}
impl<T, U> CollectionPage for Ext<T, U>
where
T: CollectionPage,
U: Debug,
{
}
impl<T, U> Activity for Ext<T, U>
where
T: Activity,
U: Debug,
{
}
impl<T, U> IntransitiveActivity for Ext<T, U>
where
T: IntransitiveActivity,
U: Debug,
{
}

View file

@ -1,31 +1,17 @@
/* //! # ActivityStreams New
* This file is part of ActivityStreams. //! _A set of Traits and Types that make up the ActivityStreams and ActivityPub specifications_
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! ActivityStreams
//! //!
//! A set of Traits and Types that make up the ActivityStreams and ActivityPub specifications //! - Find the code on [git.asonix.dog](https://git.asonix.dog/asonix/activitystreams-sketch)
//! - Read the docs on [activitystreams-new.asonix.dog](https://activitystreams-new.asonix.dog)
//! - Join the matrix channel at [#activitypub:asonix.dog](https://matrix.to/#/!fAEcHyTUdAaKCzIKCt:asonix.dog?via=asonix.dog&via=matrix.org&via=t2bot.io)
//! - Hit me up on [mastodon](https://asonix.dog/@asonix)
//! //!
//! ## Usage //! ## Usage
//! //!
//! First, add ActivityStreams to your dependencies //! First, add ActivityStreams to your dependencies
//! ```toml //! ```toml
//! activitystreams = "0.6.2" //! [dependencies]
//! activitystreams-new = { git = "https://git.asonix.dog/asonix/activitystreams-sketch", branch = "main" }
//! ``` //! ```
//! //!
//! ### Types //! ### Types
@ -35,40 +21,31 @@
//! So to use an ActivityStreams Video, you'd write //! So to use an ActivityStreams Video, you'd write
//! ```rust //! ```rust
//! use activitystreams::object::Video; //! use activitystreams::object::Video;
//! let video = Video::new();
//! ``` //! ```
//! //!
//! And to use an ActivityStreams profile, you'd write //! And to use an ActivityPub profile, you'd write
//! ```rust //! ```rust
//! use activitystreams::object::Profile; //! use activitystreams::object::{ApObject, Profile};
//! let inner = Profile::new();
//! let profile = ApObject::new(inner);
//! ``` //! ```
//! //!
//! ### Properties //! There's only one kind of Link
//! ```rust
//! use activitystreams::link::Mention;
//! let mention = Mention::new();
//! ```
//! //!
//! Each concrete type implements `AsRef<>` for each of their properties fields. A basic //! ### Fields
//! ActivityStreams object will implement `AsRef<ObjectProperties>`.
//! //!
//! The Properties types can be found near the kind they're associated with. `ObjectProperties` and //! Many fields on the provided types are wrapped in `OneOrMany<>` or have a type of `AnyBase`. This
//! `ApObjectProperties` are located in `activitystreams::object::properties`. //! is because the activitystreams spec is very open as to what is considered a valid structure.
//!
//! 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 //! 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 //! 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 //! 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 //! combination thereof, can be present. This library represents this as `Option<OneOrMany<AnyString>>`.
//! 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 //! This resulting type is exactly specific enough to match the following valid ActivityStreams
//! json, without matching any invalid json. //! json, without matching any invalid json.
@ -78,7 +55,7 @@
//! {} //! {}
//! ``` //! ```
//! //!
//! With a sring summary: //! With a string summary:
//! ```json //! ```json
//! { //! {
//! "summary": "A string" //! "summary": "A string"
@ -108,43 +85,75 @@
//! } //! }
//! ``` //! ```
//! //!
//! It may seem like interacting with these types might get unweildy, so the `properties` macro //! It may seem like interacting with these types might get unweildy, there are some custom methods
//! also generates methods for interacting with each field. //! implemented on the `OneOrMany` type depending on what's inside of it.
//! //!
//! ```ignore //! ```rust,ignore
//! fn set_summary_xsd_string<T>(&mut self, T) -> Result<...>; //! fn from_xsd_string<T>(&mut self, T) -> Self;
//! fn set_summary_rdf_lang_string<T>(&mut self, T) -> Result<...>; //! fn from_rdf_lang_string<T>(&mut self, T) -> Self;
//! 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 as_single_xsd_string(&self) -> Option<&str>;
//! fn as_single_rdf_langstring(&self) -> Option<&RdfLangString>;
//! //!
//! fn get_summary_xsd_string(&self) -> Option<XsdString>; //! fn single_xsd_string(self) -> Option<String>;
//! fn get_summary_rdf_lang_string(&self) -> Option<RdfLangString>; //! fn single_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>>; //! 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 //! 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 //! 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 //! `Into<String>` 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. //! 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 //! Types like `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 //! 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 //! 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. //! `Into` or `TryFrom` and `TryInto` for types you might expect them to (e.g.
//! `XsdNonNegativeInteger` implements `From<u64>` and `Into<u64>`). //! `XsdNonNegativeInteger` implements `From<u64>` and `Into<u64>`).
//! //!
//! For some fields, like `id`, there is only one valid type. methods generated for fields like //! ### Traits
//! these will leave out the type name from the function name.
//! //!
//! ```ignore //! Since ActivityStreams is a heirarchical structure of data, it's represented as structs containing
//! fn set_id<T>(&mut self, T) -> Result<...>; //! other structs. This means that the `context` field, which can be present on any ActivityStreams type,
//! fn delete_id(&mut self) -> &mut Self; //! will be located in the innermost struct. In order to avoid writing code like
//! fn get_id(&self) -> Option<XsdAnyUri>; //! `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;
//! ``` //! ```
//! //!
//! ### Traits //! For fields with more specific bounds, like `id`,
//! ```rust,ignore
//! fn id(&self) -> Option<&Url>;
//! fn set_id(&mut self, Url) -> &mut Self;
//! fn take_id(&self) -> Option<Url>;
//! 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::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`, //! 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, //! `Collection`, and `CollectionPage`. The majority of these traits exist solely to "mark" types,
@ -153,12 +162,18 @@
//! //!
//! If you want to make a function that manipulates an Activity, but not a normal object, you could //! If you want to make a function that manipulates an Activity, but not a normal object, you could
//! bound the function like so: //! bound the function like so:
//! ```ignore //!
//! fn my_manipulator<T>(some_activity: T) -> Result<&mut ObjectProperties, SomeErrorType> //! ```rust
//! use activitystreams::{base::BaseExt, context, markers::Activity, uri};
//!
//! fn manipulator<T, Kind>(mut activity: T) -> Result<(), anyhow::Error>
//! where //! where
//! T: Activity + AsMut<ObjectProperties>, //! T: Activity + BaseExt<Kind>,
//! { //! {
//! some_activity.as_mut().set_whatever_tbh() //! activity
//! .set_id(uri!("https://example.com"))
//! .set_context(context());
//! Ok(())
//! } //! }
//! ``` //! ```
//! //!
@ -169,7 +184,7 @@
//! These can be found in `activitystreams::objects::kind`, and similar paths. //! These can be found in `activitystreams::objects::kind`, and similar paths.
//! //!
//! To build your own Person struct, for example, you could write //! To build your own Person struct, for example, you could write
//! ```ignore //! ```rust
//! use activitystreams::actor::kind::PersonType; //! use activitystreams::actor::kind::PersonType;
//! //!
//! #[derive(serde::Deserialize, serde::Serialize)] //! #[derive(serde::Deserialize, serde::Serialize)]
@ -181,80 +196,38 @@
//! ``` //! ```
//! And this type would only deserialize for JSON where `"type":"Person"` //! And this type would only deserialize for JSON where `"type":"Person"`
//! //!
//! ### Extensions
//!
//! In some cases, like when dealing with ActivityPub, it is neccessary to extend the
//! ActivityStreams specification. For this purpose, two traits and a type have been introduced.
//!
//! ```ignore
//! use activitystreams::ext::{Ext, Extensible, Extension};
//! ```
//!
//! The `Ext` type is a simple record containing first, the ActivityStreams type, and second, the
//! extension to that type.
//!
//! There are two provided extensions in the ActivityStreams library.
//! - ApObjectProperties, extra properties for all ActivityStreams objects in the ActivityPub spec
//! - ApActorProperties, extra properties specifically for Actors in the ActivityPub spec
//!
//! To use an object with its default extensions, the object's `full()` associated function may be
//! invoked.
//! ```rust
//! # use activitystreams::object::Video;
//! let video_with_extensions = Video::full();
//! ```
//!
//! ### Features
//! There are a number of features that can be disabled in this crate. By default, everything is
//! enabled.
//!
//! ```toml
//! activitystreams = { version = "0.6.2", 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 //! ### Create
//! //!
//! ```rust //! ```rust
//! use activitystreams::object::{Video, properties::ObjectProperties}; //! use activitystreams::{
//! use anyhow::Error; //! context,
//! object::{ApObject, Video},
//! prelude::*,
//! uri,
//! };
//! use chrono::Duration;
//! //!
//! // We perform configuration in a dedicated function to specify which Properties type we want to //! fn main() -> Result<(), anyhow::Error> {
//! // perform the operations on. //! let mut video = ApObject::new(Video::new());
//! 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(()) //! video
//! } //! .set_context(context())
//! .set_id(uri!("https://example.com/@example/lions"))
//! .set_media_type("video/webm".parse()?)
//! .set_url(uri!("https://example.com/@example/lions/video.webm"))
//! .set_summary("A cool video")
//! .set_duration(Duration::minutes(4) + Duration::seconds(20))
//! .set_shares(uri!("https://example.com/@example/lions/video.webm#shares"));
//! //!
//! fn main() -> Result<(), Error> { //! println!("Video, {:#?}", video);
//! let mut v = Video::default();
//! //!
//! configure_video(&mut v)?; //! let s = serde_json::to_string(&video)?;
//!
//! println!("Video, {:#?}", v);
//!
//! let s = serde_json::to_string(&v)?;
//! //!
//! println!("json, {}", s); //! println!("json, {}", s);
//! //!
//! let v: Video = serde_json::from_str(&s)?; //! let v: ApObject<Video> = serde_json::from_str(&s)?;
//! //!
//! println!("Video again, {:#?}", v); //! println!("Video again, {:#?}", v);
//! //!
@ -262,185 +235,164 @@
//! } //! }
//! ``` //! ```
//! //!
//! ### Intermediate //! ### Parse
//! //!
//! ```rust //! ```rust
//! use activitystreams::{ //! use activitystreams::{activity::ActorAndObject, prelude::*};
//! context,
//! actor::{Actor, ActorBox},
//! ext::Ext,
//! object::{
//! properties::{
//! ObjectProperties,
//! ProfileProperties
//! },
//! Profile,
//! Object,
//! ObjectBox,
//! },
//! primitives::XsdAnyUri,
//! Base, BaseBox, PropRefs,
//! };
//! use serde::{Deserialize, Serialize};
//! //!
//! #[derive(Clone, Debug, Default, Deserialize, Serialize, PropRefs)] //! #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
//! #[serde(rename_all = "camelCase")] //! pub enum AcceptedTypes {
//! #[prop_refs(Object)] //! Accept,
//! #[prop_refs(Actor)] //! Announce,
//! pub struct Persona { //! Create,
//! #[serde(rename = "@context")] //! Delete,
//! context: XsdAnyUri, //! Follow,
//! //! Reject,
//! #[serde(rename = "type")] //! Update,
//! kind: String, //! Undo,
//! } //! }
//! //!
//! fn main() -> Result<(), anyhow::Error> { //! pub type AcceptedActivity = ActorAndObject<AcceptedTypes>;
//! let mut profile = Profile::full();
//! //!
//! let pprops: &mut ProfileProperties = profile.as_mut(); //! pub fn handle_activity(activity: AcceptedActivity) -> Result<(), anyhow::Error> {
//! println!("Actor: {:?}", activity.actor());
//! println!("Object: {:?}", activity.object());
//! //!
//! pprops.set_describes_object_box(Persona { //! match activity.kind() {
//! context: context(), //! Some(AcceptedTypes::Accept) => println!("Accept"),
//! kind: "Persona".to_owned(), //! Some(AcceptedTypes::Announce) => println!("Announce"),
//! })?; //! Some(AcceptedTypes::Create) => println!("Create"),
//! //! Some(AcceptedTypes::Delete) => println!("Delete"),
//! let oprops: &mut ObjectProperties = profile.as_mut(); //! Some(AcceptedTypes::Follow) => println!("Follow"),
//! oprops.set_context_xsd_any_uri(context())?; //! Some(AcceptedTypes::Reject) => println!("Reject"),
//! //! Some(AcceptedTypes::Update) => println!("Update"),
//! let profile_string = serde_json::to_string(&profile)?; //! Some(AcceptedTypes::Undo) => println!("Undo"),
//! //! None => return Err(anyhow::Error::msg("No activity type provided")),
//! let profile: Profile = serde_json::from_str(&profile_string)?;
//!
//! Ok(())
//! }
//! ```
//!
//! ### Advanced
//!
//! ```rust
//! use activitystreams::{
//! properties,
//! ext::Ext,
//! link::{
//! properties::LinkProperties,
//! Link, LinkBox, Mention,
//! },
//! Base, BaseBox, PropRefs,
//! 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 "MyLink" in JSON.
//! #[derive(Clone, Debug, Default, UnitString)]
//! #[unit_string(MyLink)]
//! pub struct MyKind;
//!
//! properties! {
//! My {
//! docs [ "Defining our own properties struct called MyProperties" ],
//!
//! 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,
//! },
//! } //! }
//! }
//!
//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
//! #[serde(transparent)]
//! pub struct MyLinkProps(pub LinkProperties);
//!
//! /// Using the Properties derive macro
//! ///
//! /// This macro generates getters and setters for the associated fields.
//! #[derive(Clone, Debug, Default, Deserialize, Serialize, PropRefs)]
//! #[serde(rename_all = "camelCase")]
//! #[prop_refs(Link)]
//! pub struct My {
//! /// Use the UnitString MyKind to enforce the type of the object by "MyLink"
//! pub kind: MyKind,
//!
//! /// Derive AsRef/AsMut for My -> MyProperties
//! #[prop_refs]
//! pub my_properties: MyProperties,
//!
//! /// Derive AsRef/AsMut/Link for My -> MyLinkProperties
//! #[prop_refs]
//! pub link_properties: MyLinkProps,
//! }
//!
//! fn main() -> Result<(), anyhow::Error> {
//! let mut my_link = My::default();
//!
//! 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: My = serde_json::from_str(&my_link_string)?;
//! //!
//! Ok(()) //! Ok(())
//! } //! }
//!
//! static EXAMPLE_JSON: &str = r#"{"actor":"https://asonix.dog/users/asonix","object":"https://asonix.dog/users/asonix/posts/1","type":"Announce"}"#;
//!
//! fn main() -> Result<(), anyhow::Error> {
//! handle_activity(serde_json::from_str(EXAMPLE_JSON)?)
//! }
//! ``` //! ```
//!
//! ## 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/).
#![doc(html_root_url = "https://activitystreams-new.asonix.dog")]
pub mod activity; pub mod activity;
pub mod actor; pub mod actor;
pub mod base;
pub mod collection; pub mod collection;
#[cfg(feature = "types")] mod either;
pub mod endpoint; pub mod error;
#[cfg(feature = "types")]
pub mod ext;
pub mod link; pub mod link;
mod macros;
pub mod markers;
pub mod object; pub mod object;
#[cfg(feature = "primitives")]
pub mod primitives; pub mod primitives;
pub mod unparsed;
pub use self::{ pub extern crate chrono;
activity::{Activity, IntransitiveActivity}, pub extern crate mime;
actor::Actor, pub extern crate url;
collection::{Collection, CollectionPage},
link::Link,
object::Object,
};
#[cfg_attr(feature = "types", wrapper_type)] pub fn context() -> url::Url {
/// The lowermost trait of the trait structure
///
/// Base exists solely so Object and Link can have impls that don't potentially conflict
pub trait Base {}
#[cfg(feature = "primitives")]
/// The context associated with all of the Activity Streams types defined in the crate.
pub fn context() -> crate::primitives::XsdAnyUri {
"https://www.w3.org/ns/activitystreams".parse().unwrap() "https://www.w3.org/ns/activitystreams".parse().unwrap()
} }
#[cfg(feature = "primitives")] pub fn public() -> url::Url {
/// The 'security' extension used by some implementations
pub fn security() -> crate::primitives::XsdAnyUri {
"https://w3id.org/security/v1".parse().unwrap()
}
#[cfg(feature = "primitives")]
/// The 'public' actor, doesn't denote a real actor but describes a publicly available object.
pub fn public() -> crate::primitives::XsdAnyUri {
"https://www.w3.org/ns/activitystreams#Public" "https://www.w3.org/ns/activitystreams#Public"
.parse() .parse()
.unwrap() .unwrap()
} }
#[cfg(feature = "derive")] pub fn security() -> url::Url {
pub use activitystreams_derive::{properties, wrapper_type, Extensible, PropRefs, UnitString}; "https://w3id.org/security/v1".parse().unwrap()
}
pub mod prelude {
//! Extension traits that provide the majority of the helper methods of the crate
//!
//! ```rust
//! # fn main() -> Result<(), anyhow::Error> {
//! use activitystreams::{
//! activity::Create,
//! actor::{ApActor, Person},
//! context,
//! prelude::*,
//! public,
//! object::{ApObject, Image, Video},
//! security,
//! uri,
//! };
//! use chrono::Duration;
//!
//! let mut person = ApActor::new(
//! uri!("http://localhost:8080/inbox"),
//! Person::new(),
//! );
//! person
//! .set_outbox(uri!("http:/localhost:8080/outbox"))
//! .set_name("Demo Account")
//! .set_preferred_username("demo")
//! .set_id(uri!("https://localhost:8080/actor"))
//! .set_url(uri!("https://localhost:8080/actor"));
//!
//! let mut preview = Image::new();
//!
//! preview
//! .set_url(uri!("https://localhost:8080/preview.png"))
//! .set_media_type("image/png".parse()?)
//! .set_id(uri!("https://localhostst:8080/preview.png"));
//!
//! let mut video = ApObject::new(Video::new());
//!
//! video
//! .set_id(uri!("http://localhost:8080/video.webm"))
//! .set_url(uri!("http://localhost:8080/video.webm"))
//! .set_media_type("video/webm".parse()?)
//! .set_summary("A cool video")
//! .set_preview(preview.into_any_base()?)
//! .set_duration(Duration::minutes(4) + Duration::seconds(20))
//! .set_shares(uri!("http://localhost:8080/video.webm#shares"));
//!
//! let mut activity = Create::new(
//! person.into_any_base()?,
//! video.into_any_base()?
//! );
//!
//! activity
//! .set_many_tos(vec![public()]);
//! #
//! # Ok(())
//! # }
//! ```
pub use crate::{
activity::{
ActivityExt, ActorAndObjectRefExt, OptOriginRefExt, OptTargetRefExt, OriginRefExt,
QuestionExt, TargetRefExt,
},
actor::ApActorExt,
base::{BaseExt, ExtendsExt},
collection::{CollectionExt, CollectionPageExt, OrderedCollectionPageExt},
link::LinkExt,
object::{ApObjectExt, ObjectExt, PlaceExt, ProfileExt, RelationshipExt, TombstoneExt},
};
}

663
src/link.rs Normal file
View file

@ -0,0 +1,663 @@
//! Types and traits for dealing with Link attributes
//!
//! ```rust
//! # fn main() -> Result<(), anyhow::Error> {
//! use activitystreams::{
//! link::Mention,
//! object::Image,
//! prelude::*,
//! uri,
//! };
//!
//! let mut mention = Mention::new();
//!
//! mention
//! .set_href(uri!("https://example.com"))
//! .set_hreflang("en")
//! .set_rel("link")
//! .set_preview(Image::new().into_any_base()?);
//! #
//! # Ok(())
//! # }
//! ```
use crate::{
base::{AsBase, Base, Extends},
markers,
primitives::OneOrMany,
unparsed::{Unparsed, UnparsedMut, UnparsedMutExt},
};
use std::convert::TryFrom;
use url::Url;
pub mod kind {
//! Kinds of links defined by the spec
//!
//! These types exist only to be statically-typed versions of the associated string. e.g.
//! `MentionType` -> `"Mention"`
use crate::kind;
kind!(MentionType, Mention);
}
use self::kind::MentionType;
/// Implementation trait for deriving Link methods for a type
///
/// Any type implementing AsLink will automatically gain methods provided by LinkExt
pub trait AsLink<Kind>: markers::Link {
/// Immutable borrow of `Link<Kind>`
fn link_ref(&self) -> &Link<Kind>;
/// Mutable borrow of `Link<Kind>`
fn link_mut(&mut self) -> &mut Link<Kind>;
}
/// Helper methods for interacting with Link types
///
/// This trait represents methods valid for any ActivityStreams Link.
///
/// Documentation for the fields related to these methods can be found on the `Link` struct
pub trait LinkExt<Kind>: AsLink<Kind> {
/// Fetch the href for the current object
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// let mention_href = mention.href();
/// ```
fn href<'a>(&'a self) -> Option<&'a Url>
where
Kind: 'a,
{
self.link_ref().href.as_ref()
}
/// Set the href for the current object
///
/// This overwrites the contents of href
///
/// ```rust
/// # fn main() -> Result<(), anyhow::Error> {
/// # use activitystreams::{link::Mention, uri};
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// mention.set_href(uri!("https://example.com"));
/// # Ok(())
/// # }
/// ```
fn set_href(&mut self, href: Url) -> &mut Self {
self.link_mut().href = Some(href);
self
}
/// Take the href from the current object, leaving nothing
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// if let Some(href) = mention.take_href() {
/// println!("{:?}", href);
/// }
/// ```
fn take_href(&mut self) -> Option<Url> {
self.link_mut().href.take()
}
/// Delete the href from the current object
///
/// ```rust
/// # fn main() -> Result<(), anyhow::Error> {
/// # use activitystreams::{link::Mention, uri};
/// # let mut mention = Mention::new();
/// # mention.set_href(uri!("https://example.com"));
/// #
/// use activitystreams::prelude::*;
///
/// assert!(mention.href().is_some());
/// mention.delete_href();
/// assert!(mention.href().is_none());
/// # Ok(())
/// # }
/// ```
fn delete_href(&mut self) -> &mut Self {
self.link_mut().href = None;
self
}
/// Fetch the hreflang for the current object
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// let mention_hreflang = mention.hreflang();
/// ```
fn hreflang<'a>(&'a self) -> Option<&'a str>
where
Kind: 'a,
{
self.link_ref().hreflang.as_ref().map(|lr| lr.as_str())
}
/// Set the hreflang for the current object
///
/// This overwrites the contents of hreflang
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// mention.set_hreflang("en");
/// ```
fn set_hreflang<T>(&mut self, hreflang: T) -> &mut Self
where
T: Into<String>,
{
self.link_mut().hreflang = Some(hreflang.into());
self
}
/// Take the hreflang from the current object, leaving nothing
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// if let Some(hreflang) = mention.take_hreflang() {
/// println!("{:?}", hreflang);
/// }
/// ```
fn take_hreflang(&mut self) -> Option<String> {
self.link_mut().hreflang.take()
}
/// Delete the hreflang from the current object
///
/// ```rust
/// # fn main() -> Result<(), anyhow::Error> {
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// # mention.set_hreflang("en");
/// #
/// use activitystreams::prelude::*;
///
/// assert!(mention.hreflang().is_some());
/// mention.delete_hreflang();
/// assert!(mention.hreflang().is_none());
/// # Ok(())
/// # }
/// ```
fn delete_hreflang(&mut self) -> &mut Self {
self.link_mut().hreflang = None;
self
}
/// Fetch the rel for the current object
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// let mention_rel = mention.rel();
/// ```
fn rel<'a>(&'a self) -> Option<&'a OneOrMany<String>>
where
Kind: 'a,
{
self.link_ref().rel.as_ref()
}
/// Set the rel for the current object
///
/// This overwrites the contents of rel
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// mention.set_rel("link");
/// ```
fn set_rel<T>(&mut self, rel: T) -> &mut Self
where
T: Into<String>,
{
self.link_mut().rel = Some(rel.into().into());
self
}
/// Set many rels for the current object
///
/// This overwrites the contents of rel
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// mention.set_many_rels(vec!["link".into(), "stylesheet".into()]);
/// ```
fn set_many_rels<I>(&mut self, items: I) -> &mut Self
where
I: IntoIterator<Item = String>,
{
let v: Vec<_> = items.into_iter().collect();
self.link_mut().rel = Some(v.into());
self
}
/// Add a rel to the current object
///
/// This does not overwrite the contents of rel, only appends an item
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// mention
/// .add_rel("link".into())
/// .add_rel("stylesheet".into());
/// ```
fn add_rel(&mut self, rel: String) -> &mut Self {
let v = match self.link_mut().rel.take() {
Some(mut v) => {
v.add(rel);
v
}
None => vec![rel].into(),
};
self.link_mut().rel = Some(v);
self
}
/// Take the rel from the current object, leaving nothing
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// if let Some(rel) = mention.take_rel() {
/// println!("{:?}", rel);
/// }
/// ```
fn take_rel(&mut self) -> Option<OneOrMany<String>> {
self.link_mut().rel.take()
}
/// Delete the rel from the current object
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// # mention.set_rel("link");
/// #
/// use activitystreams::prelude::*;
///
/// assert!(mention.rel().is_some());
/// mention.delete_rel();
/// assert!(mention.rel().is_none());
/// ```
fn delete_rel(&mut self) -> &mut Self {
self.link_mut().rel = None;
self
}
/// Fetch the height of the current object
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// if let Some(height) = mention.height() {
/// println!("{:?}", height);
/// }
/// ```
fn height<'a>(&'a self) -> Option<u64>
where
Kind: 'a,
{
self.link_ref().height
}
/// Set the height for the current object
///
/// This overwrites the contents of height
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// mention.set_height(5u64);
/// ```
fn set_height<T>(&mut self, height: T) -> &mut Self
where
T: Into<u64>,
{
self.link_mut().height = Some(height.into());
self
}
/// Take the height of the current object, leaving nothing
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// if let Some(height) = mention.height() {
/// println!("{:?}", height);
/// }
/// ```
fn take_height(&mut self) -> Option<u64> {
self.link_mut().height.take()
}
/// Delete the height from the current object
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// # mention.set_height(5u64);
/// #
/// use activitystreams::prelude::*;
///
/// assert!(mention.height().is_some());
/// mention.delete_height();
/// assert!(mention.height().is_none());
/// ```
fn delete_height(&mut self) -> &mut Self {
self.link_mut().height = None;
self
}
/// Fetch the width of the current object
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// if let Some(width) = mention.width() {
/// println!("{:?}", width);
/// }
/// ```
fn width<'a>(&'a self) -> Option<u64>
where
Kind: 'a,
{
self.link_ref().width
}
/// Set the width for the current object
///
/// This overwrites the contents of width
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// mention.set_width(5u64);
/// ```
fn set_width<T>(&mut self, width: T) -> &mut Self
where
T: Into<u64>,
{
self.link_mut().width = Some(width.into());
self
}
/// Take the width of the current object, leaving nothing
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// #
/// use activitystreams::prelude::*;
///
/// if let Some(width) = mention.take_width() {
/// println!("{:?}", width);
/// }
/// ```
fn take_width(&mut self) -> Option<u64> {
self.link_mut().width.take()
}
/// Delete the width from the current object
///
/// ```rust
/// # use activitystreams::link::Mention;
/// # let mut mention = Mention::new();
/// # mention.set_width(5u64);
/// #
/// use activitystreams::prelude::*;
///
/// assert!(mention.width().is_some());
/// mention.delete_width();
/// assert!(mention.width().is_none());
/// ```
fn delete_width(&mut self) -> &mut Self {
self.link_mut().width = None;
self
}
}
/// A specialized Link that represents an @mention.
///
/// This is just an alias for `Link<MentionType>` because there's no fields inherent to Mention
/// that aren't already present on a Link.
pub type Mention = Link<MentionType>;
/// Define all the properties of the Object base type as described by the Activity Streams
/// vocabulary.
///
/// The properties of the Link object are not the properties of the referenced resource, but are
/// provided as hints for rendering agents to understand how to make use of the resource. For
/// example, height and width might represent the desired rendered size of a referenced image,
/// rather than the actual pixel dimensions of the referenced image.
///
/// The target URI of the Link is expressed using the required href property.
///
/// For example, all Objects can contain an image property whose value describes a graphical
/// representation of the containing object. This property will typically be used to provide the
/// URL to an image (e.g. JPEG, GIF or PNG) resource that can be displayed to the user. Any given
/// object might have multiple such visual representations -- multiple screenshots, for instance,
/// or the same image at different resolutions. In Activity Streams 2.0, there are essentially
/// three ways of describing such references.
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Link<Kind> {
/// The target resource pointed to by a Link.
///
/// - Range: xsd:anyUri
/// - Functional: true
#[serde(skip_serializing_if = "Option::is_none")]
href: Option<Url>,
/// Hints as to the language used by the target resource.
///
/// Value MUST be a [BCP47] Language-Tag.
///
/// - Range: [BCP47] Language Tag
/// - Functional: true
#[serde(skip_serializing_if = "Option::is_none")]
hreflang: Option<String>,
/// A link relation associated with a Link.
///
/// The value MUST conform to both the [HTML5] and [RFC5988] "link relation" definitions.
///
/// In the [HTML5], any string not containing the "space" U+0020, "tab" (U+0009), "LF" (U+000A), "FF" (U+000C), "CR" (U+000D) or "," (U+002C) characters can be used as a valid link relation.
///
/// - Range: [RFC5988] or [HTML5] Link Relation
/// - Functional: false
#[serde(skip_serializing_if = "Option::is_none")]
rel: Option<OneOrMany<String>>,
/// On a Link, specifies a hint as to the rendering height in device-independent pixels of the linked resource.
///
/// - Range: xsd:nonNegativeInteger
/// - Functional: true
#[serde(skip_serializing_if = "Option::is_none")]
height: Option<u64>,
/// On a Link, specifies a hint as to the rendering width in device-independent pixels of the linked resource.
///
/// Range: xsd:nonNegativeInteger
/// Functional: true
#[serde(skip_serializing_if = "Option::is_none")]
width: Option<u64>,
/// Base fields and unparsed json ends up here
#[serde(flatten)]
inner: Base<Kind>,
}
impl<Kind> Link<Kind> {
/// Create a new Link
///
/// ```rust
/// use activitystreams::link::Link;
///
/// let link = Link::<String>::new();
/// ```
pub fn new() -> Self
where
Kind: Default,
{
Link {
href: None,
hreflang: None,
rel: None,
height: None,
width: None,
inner: Base::new(),
}
}
fn extending(mut inner: Base<Kind>) -> Result<Self, serde_json::Error> {
Ok(Link {
href: inner.remove("href")?,
hreflang: inner.remove("hreflang")?,
rel: inner.remove("rel")?,
height: inner.remove("height")?,
width: inner.remove("width")?,
inner,
})
}
fn retracting(self) -> Result<Base<Kind>, serde_json::Error> {
let Link {
href,
hreflang,
rel,
height,
width,
mut inner,
} = self;
inner
.insert("href", href)?
.insert("hreflang", hreflang)?
.insert("rel", rel)?
.insert("height", height)?
.insert("width", width)?;
Ok(inner)
}
}
impl<Kind> markers::Base for Link<Kind> {}
impl<Kind> markers::Link for Link<Kind> {}
impl<Kind> Extends<Kind> for Link<Kind> {
type Error = serde_json::Error;
fn extends(base: Base<Kind>) -> Result<Self, Self::Error> {
Self::extending(base)
}
fn retracts(self) -> Result<Base<Kind>, Self::Error> {
self.retracting()
}
}
impl<Kind> TryFrom<Base<Kind>> for Link<Kind>
where
Kind: serde::de::DeserializeOwned,
{
type Error = serde_json::Error;
fn try_from(base: Base<Kind>) -> Result<Self, Self::Error> {
Self::extending(base)
}
}
impl<Kind> TryFrom<Link<Kind>> for Base<Kind>
where
Kind: serde::ser::Serialize,
{
type Error = serde_json::Error;
fn try_from(link: Link<Kind>) -> Result<Self, Self::Error> {
link.retracting()
}
}
impl<Kind> UnparsedMut for Link<Kind> {
fn unparsed_mut(&mut self) -> &mut Unparsed {
self.inner.unparsed_mut()
}
}
impl<Kind> AsBase<Kind> for Link<Kind> {
fn base_ref(&self) -> &Base<Kind> {
&self.inner
}
fn base_mut(&mut self) -> &mut Base<Kind> {
&mut self.inner
}
}
impl<Kind> AsLink<Kind> for Link<Kind> {
fn link_ref(&self) -> &Link<Kind> {
self
}
fn link_mut(&mut self) -> &mut Link<Kind> {
self
}
}
impl<T, Kind> LinkExt<Kind> for T where T: AsLink<Kind> {}

View file

@ -1,26 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for Unit Structs that serialize to strings
use crate::UnitString;
/// A Unit Struct that represents the string "Mention"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Mention)]
pub struct MentionType;

View file

@ -1,41 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for Link types
#[cfg(feature = "kinds")]
pub mod kind;
#[cfg(feature = "types")]
pub mod properties;
#[cfg(feature = "types")]
mod types;
#[cfg(feature = "types")]
pub use self::types::Mention;
/// A Link is an indirect, qualified reference to a resource identified by a URL.
///
/// The fundamental model for links is established by
/// [[RFC5988](https://tools.ietf.org/html/rfc5988)]. Many of the properties defined by the
/// Activity Vocabulary allow values that are either instances of Object or Link. When a Link is
/// used, it establishes a qualified relation connecting the subject (the containing object) to the
/// resource identified by the href. Properties of the Link are properties of the reference as
/// opposed to properties of the resource.
#[cfg_attr(feature = "derive", crate::wrapper_type)]
pub trait Link: crate::Base {}

View file

@ -1,236 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for properties of standard link types
//!
//! To use these properties in your own types, you can flatten them into your struct with serde:
//!
//! ```rust
//! use activitystreams::{
//! ext::Ext,
//! link::{
//! properties::LinkProperties,
//! Link, LinkBox,
//! },
//! Base, BaseBox, PropRefs,
//! };
//! use serde::{Deserialize, Serialize};
//!
//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
//! #[serde(transparent)]
//! pub struct MyProps(pub LinkProperties);
//!
//! #[derive(Clone, Debug, Default, Serialize, Deserialize, PropRefs)]
//! #[serde(rename_all = "camelCase")]
//! #[prop_refs(Link)]
//! pub struct MyLink {
//! #[serde(rename = "type")]
//! pub kind: String,
//!
//! /// Define a require property for the MyLink type
//! pub my_property: String,
//!
//! #[serde(flatten)]
//! #[prop_refs]
//! pub link_properties: MyProps,
//! }
//! #
//! # fn main() {}
//! ```
use crate::{link::LinkBox, object::ObjectBox, primitives::*, properties};
properties! {
Link {
docs [
"Define all the properties of the Object base type as described by the Activity Streams vocabulary.",
"",
"The properties of the `Link` object are not the properties of the referenced resource, but are",
"provided as hints for rendering agents to understand how to make use of the resource. For",
"example, height and width might represent the desired rendered size of a referenced image,",
"rather than the actual pixel dimensions of the referenced image.",
"",
"The target URI of the Link is expressed using the required href property.",
"",
"For example, all Objects can contain an image property whose value describes a graphical",
"representation of the containing object. This property will typically be used to provide the URL",
"to an image (e.g. JPEG, GIF or PNG) resource that can be displayed to the user. Any given object",
"might have multiple such visual representations -- multiple screenshots, for instance, or the",
"same image at different resolutions. In Activity Streams 2.0, there are essentially three ways",
"of describing such references.",
],
id {
docs [
"Provides the globally unique identifier for an Object or Link.",
"",
"The `id` property is expressed as an absolute IRI in the spec, but for now is represented",
"as a string.",
"",
"- Range: `anyUri`",
"- Functional: true",
],
types [
XsdAnyUri,
],
functional,
},
context {
docs [
"Identifies the context within which the object exists or an activity was performed.",
"",
"The notion of \"context\" used is intentionally vague. The intended function is to serve as a",
"means of grouping objects and activities that share a common originating context or purpose.",
"An example could be all activities relating to a common project or event.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
ObjectBox,
LinkBox,
],
rename("@context"),
},
name {
docs [
"A simple, human-readable, plain-text name for the object.",
"",
"HTML markup MUST NOT be included. The name MAY be expressed using multiple language-tagged",
"values.",
"",
"- Range: `xsd:string` | `rdf:langString`",
"- Functional: false",
],
types [
XsdString,
RdfLangString,
],
},
href {
docs [
"The target resource pointed to by a Link.",
"",
"- Range: `xsd:anyUri`",
"- Functional: true",
],
types [
XsdAnyUri,
],
functional,
},
hreflang {
docs [
"Hints as to the language used by the target resource.",
"",
"Value MUST be a [[BCP47](https://tools.ietf.org/html/bcp47)]",
"Language-Tag.",
"",
"- Range: [[BCP47](https://tools.ietf.org/html/bcp47)] Language",
"Tag",
"- Functional: true",
],
types [
XsdString,
],
functional,
},
media_type {
docs [
"When used on a `Link`, identifies the MIME media type of the referenced resource.",
"",
"If not specified, the content property is assumed to contain text/html content.",
"",
"- Range: `Mime Media Type`",
"- Functional: true",
],
types [
MimeMediaType,
],
functional,
},
rel {
docs [
"A link relation associated with a Link.",
"",
"The value MUST conform to both the",
"[[HTML5](https://www.w3.org/TR/html5/)] and [[RFC5988](https://tools.ietf.org/html/rfc5988)]",
"\"link relation\" definitions.",
"",
"In the [[HTML5](https://www.w3.org/TR/html5/)], any string",
"not containing the \"space\" U+0020, \"tab\" (U+0009), \"LF\" (U+000A), \"FF\" (U+000C), \"CR\"",
"(U+000D) or \",\" (U+002C) characters can be used as a valid link relation.",
"",
"- Range:",
" [[RFC5988](https://tools.ietf.org/html/rfc5988)] or",
" [[HTML5](https://www.w3.org/TR/html5/)] Link Relation",
"- Functional: false",
],
types [
XsdString,
],
},
height {
docs ["On a `Link`, specifies a hint as to the rendering height in device-independent pixels of the",
"linked resource.",
"",
"- Range: `xsd:nonNegativeInteger`",
"- Functional: true",
],
types [
XsdNonNegativeInteger,
],
functional,
},
width {
docs [
"On a `Link`, specifies a hint as to the rendering width in device-independent pixels of the",
"linked resource.",
"",
"- Range: `xsd:nonNegativeInteger`",
"- Functional: true",
],
types [
XsdNonNegativeInteger,
],
},
preview {
docs [
"Identifies an entity that provides a preview of this object.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
ObjectBox,
LinkBox,
],
},
}
}

View file

@ -1,40 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::{
link::{kind::*, properties::*, Link, LinkBox},
Base, BaseBox, PropRefs,
};
#[cfg(feature = "types")]
/// A specialized Link that represents an @mention.
#[derive(Clone, Debug, Default, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Link)]
pub struct Mention {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: MentionType,
/// Adds all valid link properties to this struct
#[serde(flatten)]
#[prop_refs]
pub link_props: LinkProperties,
}

85
src/macros.rs Normal file
View file

@ -0,0 +1,85 @@
/// Generate an enum implementing serde's Serialize and Deserialize with a single variant
///
/// This is useful for describing constants
///
/// ```rust
/// # fn main() -> Result<(), anyhow::Error> {
/// use activitystreams::kind;
///
/// kind!(CustomType, Custom);
///
/// #[derive(serde::Deserialize)]
/// struct MyStruct {
/// #[serde(rename = "type")]
/// kind: CustomType,
/// }
///
/// let s: MyStruct = serde_json::from_str(r#"{"type":"Custom"}"#)?;
///
/// assert_eq!(s.kind, CustomType::Custom);
/// # Ok(())
/// # }
/// ```
#[macro_export]
macro_rules! kind {
($x:ident, $y:ident) => {
#[derive(
Clone,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
serde::Deserialize,
serde::Serialize,
)]
/// A type stand-in for the constant $y, deriving serde traits
pub enum $x {
$y,
}
impl std::fmt::Display for $x {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, stringify!($y))
}
}
impl Default for $x {
fn default() -> Self {
$x::$y
}
}
};
}
/// A macro to shorten the `string.parse::<Url>()?` calls inevitably made in downstream code
///
/// ```rust
/// use activitystreams::uri;
///
/// fn fallible() -> Result<(), anyhow::Error> {
/// let my_uri = uri!("https://example.com");
/// Ok(())
/// }
///
/// # fn main() -> Result<(), anyhow::Error> { fallible() }
/// ```
#[macro_export]
macro_rules! uri {
( $x:expr ) => {{
use activitystreams::url::Url;
$x.parse::<Url>()?
}};
}
#[cfg(test)]
mod tests {
#[test]
fn to_string_works() {
kind!(MyType, My);
assert_eq!(MyType::My.to_string(), "My")
}
}

89
src/markers.rs Normal file
View file

@ -0,0 +1,89 @@
//! Marker traits for bounding methods
//!
//! ```rust
//! use activitystreams::{base::BaseExt, markers::Activity};
//!
//! /// 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("hi");
//!
//! some_type
//! }
//! ```
/// The lowermost trait of the trait structure
///
/// Base exists solely so Object and Link can have impls that don't potentially conflict
pub trait Base {}
/// Describes an object of any kind.
///
/// The Object type serves as the base type for most of the other kinds of objects defined in the
/// Activity Vocabulary, including other Core types such as `Activity`, `IntransitiveActivity`,
/// `Collection` and `OrderedCollection`.
pub trait Object: Base {}
/// A Link is an indirect, qualified reference to a resource identified by a URL.
///
/// The fundamental model for links is established by
/// [[RFC5988](https://tools.ietf.org/html/rfc5988)]. Many of the properties defined by the
/// Activity Vocabulary allow values that are either instances of Object or Link. When a Link is
/// used, it establishes a qualified relation connecting the subject (the containing object) to the
/// resource identified by the href. Properties of the Link are properties of the reference as
/// opposed to properties of the resource.
pub trait Link: Base {}
/// A Collection is a subtype of `Object` that represents ordered or unordered sets of `Object` or
/// `Link` instances.
///
/// The items within a Collection can be ordered or unordered. The OrderedCollection type MAY be
/// used to identify a Collection whose items are always ordered. In the JSON serialization, the
/// unordered items of a Collection are represented using the items property while ordered items
/// are represented using the orderedItems property.
pub trait Collection: Object {}
/// Used to represent distinct subsets of items from a Collection.
///
/// A `Collection` can contain a large number of items. Often, it becomes impractical for an
/// implementation to serialize every item contained by a `Collection` using the items (or
/// `ordered_items`) property alone. In such cases, the items within a `Collection` can be divided
/// into distinct subsets or "pages". A page is identified using the `CollectionPage` type.
pub trait CollectionPage: Collection {}
/// `Actor` types are `Object` types that are capable of performing activities.
///
/// This specification intentionally defines `Actors` in only the most generalized way, stopping
/// short of defining semantically specific properties for each. All Actor objects are
/// specializations of `Object` and inherit all of the core properties common to all Objects.
/// External vocabularies can be used to express additional detail not covered by the Activity
/// Vocabulary. VCard [[vcard-rdf](https://www.w3.org/TR/vcard-rdf/) SHOULD be used to provide
/// additional metadata for `Person`, `Group`, and `Organization` instances.
///
/// While implementations are free to introduce new types of Actors beyond those defined by the
/// Activity Vocabulary, interoperability issues can arise when applications rely too much on
/// extension types that are not recognized by other implementations. Care should be taken to not
/// unduly overlap with or duplicate the existing `Actor` types.
///
/// When an implementation uses an extension type that overlaps with a core vocabulary type, the
/// implementation MUST also specify the core vocabulary type. For instance, some vocabularies
/// (e.g. VCard) define their own types for describing people. An implementation that wishes, for
/// example, to use a `vcard:Individual` as an `Actor` MUST also identify that `Actor` as a
/// `Person`.
pub trait Actor: Object {}
/// An Activity is a subtype of `Object` that describes some form of action that may happen, is
/// currently happening, or has already happened.
///
/// The `Activity` type itself serves as an abstract base type for all types of activities. It is
/// important to note that the `Activity` type itself does not carry any specific semantics about
/// the kind of action being taken.
pub trait Activity: Object {}
/// Instances of `IntransitiveActivity` are a subtype of `Activity` representing intransitive
/// actions.
///
/// The `object` property is therefore inappropriate for these activities.
pub trait IntransitiveActivity: Activity {}

5540
src/object.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,81 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for Unit Structs that serialize to strings
use crate::UnitString;
/// A Unit Struct that represents the string "Article"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Article)]
pub struct ArticleType;
/// A Unit Struct that represents the string "Audio"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Audio)]
pub struct AudioType;
/// A Unit Struct that represents the string "Document"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Document)]
pub struct DocumentType;
/// A Unit Struct that represents the string "Event"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Event)]
pub struct EventType;
/// A Unit Struct that represents the string "Image"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Image)]
pub struct ImageType;
/// A Unit Struct that represents the string "Note"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Note)]
pub struct NoteType;
/// A Unit Struct that represents the string "Page"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Page)]
pub struct PageType;
/// A Unit Struct that represents the string "Place"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Place)]
pub struct PlaceType;
/// A Unit Struct that represents the string "Profile"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Profile)]
pub struct ProfileType;
/// A Unit Struct that represents the string "Relationship"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Relationship)]
pub struct RelationshipType;
/// A Unit Struct that represents the string "Tombstone"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Tombstone)]
pub struct TombstoneType;
/// A Unit Struct that represents the string "Video"
#[derive(Clone, Debug, Default, UnitString)]
#[unit_string(Video)]
pub struct VideoType;

View file

@ -1,73 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for Object types
#[cfg(feature = "kinds")]
pub mod kind;
#[cfg(feature = "types")]
pub mod properties;
#[cfg(feature = "types")]
mod types;
#[cfg(feature = "types")]
pub use self::types::{
Article, Audio, Document, Event, Image, Note, Page, Place, Profile, Relationship, Tombstone,
Video,
};
/// Describes an object of any kind.
///
/// The Object type serves as the base type for most of the other kinds of objects defined in the
/// Activity Vocabulary, including other Core types such as `Activity`, `IntransitiveActivity`,
/// `Collection` and `OrderedCollection`.
#[cfg_attr(feature = "derive", crate::wrapper_type)]
pub trait Object: crate::Base {}
#[cfg(feature = "types")]
/// Describes any kind of Image
///
/// Since Image is "concrete" in the ActivityStreams spec, but multiple fields in ObjectProperties
/// require an "Image", this type acts as a filter to ensure only Images can be serialized or
/// deserialized, but allows any adjacent fields through
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub struct AnyImage {
#[serde(rename = "type")]
kind: self::kind::ImageType,
#[serde(flatten)]
rest: std::collections::HashMap<String, serde_json::Value>,
}
#[cfg(feature = "types")]
impl AnyImage {
pub fn from_concrete<T>(t: T) -> Result<Self, serde_json::Error>
where
T: Object + serde::ser::Serialize,
{
serde_json::from_value(serde_json::to_value(t)?)
}
pub fn into_concrete<T>(self) -> Result<T, serde_json::Error>
where
T: Object + serde::de::DeserializeOwned,
{
serde_json::from_value(serde_json::to_value(self)?)
}
}

View file

@ -1,764 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Namespace for properties of standard object types
//!
//! To use these properties in your own types, you can flatten them into your struct with serde:
//!
//! ```rust
//! use activitystreams::{
//! ext::Ext,
//! object::{
//! properties::ObjectProperties,
//! Object, ObjectBox,
//! },
//! Base, BaseBox, PropRefs,
//! };
//! use std::collections::HashMap;
//!
//! #[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
//! #[serde(transparent)]
//! pub struct MyProps(pub ObjectProperties);
//!
//! #[derive(Clone, Debug, Default, PropRefs, serde::Serialize, serde::Deserialize)]
//! #[serde(rename_all = "camelCase")]
//! #[prop_refs(Object)]
//! pub struct MyObject {
//! #[serde(rename = "type")]
//! pub kind: String,
//!
//! /// Define a required property for the MyObject type
//! pub my_property: String,
//!
//! #[serde(flatten)]
//! #[prop_refs]
//! pub extra: MyProps,
//! }
//! #
//! # fn main() {}
//! ```
use crate::{
ext::Extension,
link::LinkBox,
object::{AnyImage, Object, ObjectBox},
primitives::*,
properties, BaseBox,
};
impl<T> Extension<T> for ApObjectProperties where T: Object {}
properties! {
Object {
docs [
"Define all the properties of the Object base type as described by the Activity Streams",
"vocabulary.",
"",
"In addition to having a global identifier (expressed as an absolute IRI using the id property)",
"and an \"object type\"(expressed using the type property), all instances of the Object type share",
"a common set of properties normatively defined by the Activity Vocabulary.",
"",
"This struct does not provide an optional `type` property, if you are implementing your own",
"object type, you must supply your own type. This crate's provided object types all supply their",
"own `type` properties as Unit Structs with custom serde behaviour.",
"",
"All properties are optional (including the id and type).",
],
id {
docs [
"Provides the globally unique identifier for an Object or Link.",
"",
"The `id` property is expressed as an absolute IRI in the spec, but for now is represented",
"as a string.",
"",
"- Range: `xsd:anyUri`",
"- Functional: true",
],
types [
XsdAnyUri,
],
functional,
alias [ "@id" ],
},
attachment {
docs [
"Identifies a resource attached or related to an object that potentially requires special",
"handling.",
"",
"The intent is to provide a model that is at least semantically similar to attachments in",
"email.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
attributed_to {
docs [
"Identifies one or more entities to which this object is attributed.",
"",
"The attributed entities might not be Actors. For instance, an object might be attributed to",
"the completion of another activity.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
audience {
docs [
"Identifies one or more entities that represent the total population of entities for which",
"the object can considered to be relevant.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
content {
docs [
"The content or textual representation of the Object encoded as a JSON string.",
"",
"By default, the value of content is HTML. The mediaType property can be used in the object",
"to indicate a different content type.",
"",
"The content MAY be expressed using multiple language-tagged values.",
"",
"- Range: `xsd:string` | `rdf:langString`",
"- Functional: false",
],
types [
XsdString,
RdfLangString,
],
},
context {
docs [
"Identifies the context within which the object exists or an activity was performed.",
"",
"The notion of \"context\"used is intentionally vague. The intended function is to serve as a",
"means of grouping objects and activities that share a common originating context or purpose.",
"An example could be all activities relating to a common project or event.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
rename("@context"),
},
name {
docs [
"A simple, human-readable, plain-text name for the object.",
"",
"HTML markup MUST NOT be included. The name MAY be expressed using multiple language-tagged",
"values.",
"",
"- Range: `xsd:string` | `rdf:langString`",
"- Functional: false",
],
types [
XsdString,
RdfLangString,
],
alias [ "displayName" ],
},
end_time {
docs [
"The date and time describing the actual or expected ending time of the object.",
"",
"When used with an Activity object, for instance, the endTime property specifies the moment",
"the activity concluded or is expected to conclude.",
"",
"- Range: `xsd:dateTime`",
"- Functional: true",
],
types [
XsdDateTime,
],
functional,
},
generator {
docs [
"Identifies the entity (e.g. an application) that generated the object.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
icon {
docs [
"Indicates an entity that describes an icon for this object.",
"",
"The image should have an aspect ratio of one (horizontal) to one (vertical) and should be",
"suitable for presentation at a small size.",
"",
"- Range: `Image` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
AnyImage,
LinkBox,
],
},
image {
docs [
"Indicates an entity that describes an image for this object.",
"",
"Unlike the icon property, there are no aspect ratio or display size limitations assumed.",
"",
"- Range: `Image` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
AnyImage,
LinkBox,
],
},
in_reply_to {
docs [
"Indicates one or more entities for which this object is considered a response.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
location {
docs [
"Indicates one or more physical or logical locations associated with the object.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
preview {
docs [
"Identifies an entity that provides a preview of this object.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
published {
docs [
"The date and time at which the object was published.",
"",
"- Range: `xsd:dateTime`",
"- Functional: true",
],
types [
XsdDateTime,
],
functional,
},
replies {
docs [
"Identifies a `Collection` containing objects considered to be responses to this object.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
start_time {
docs [
"The date and time describing the actual or expected starting time of the object.",
"",
"When used with an `Activity` object, for instance, the `start_time` property specifies the",
"moment the activity began or is scheduled to begin.",
"",
"- Range: `xsd:DateTime`",
"- Functional: true",
],
types [
XsdDateTime,
],
functional,
},
summary {
docs [
"A natural language summarization of the object encoded as HTML.",
"",
"Multiple language tagged summaries MAY be provided.",
"",
"- Range: `xsd:string` | `rdf:langString`",
"- Functional: false",
],
types [
XsdString,
RdfLangString,
],
},
tag {
docs [
"One or more \"tags\" that have been associated with an objects. A tag can be any kind of",
"`Object`.",
"",
"The key difference between attachment and tag is that the former implies association by",
"inclusion, while the latter implies associated by reference.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
updated {
docs [
"The date and time at which the object was updated,",
"",
"- Range: `xsd:dateTime`",
"- Functional: true",
],
types [
XsdDateTime,
],
functional,
},
url {
docs [
"Identifies one or more links to representations of the object.",
"",
"- Range: `xsd:anyUri` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
LinkBox,
],
},
to {
docs [
"Identifies an entity considered to be part of the public primary audience of an `Object`.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
bto {
docs [
"Identifies an `Object` that is part of the private primary audience of this `Object`.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
cc {
docs [
"Identifies an `Object` that is part of the public secondary audience of this `Object`.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
bcc {
docs [
"Identifies one or more `Objects` that are part of the private secondary audience of this",
"`Object`.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
media_type {
docs [
"When used on an `Object`, identifies the MIME media type of the value of the content",
"property.",
"",
"If not specified, the content property is assumed to contain text/html content.",
"",
"- Range: `Mime Media Type`",
"- Functional: true",
],
types [
MimeMediaType,
],
functional,
},
duration {
docs [
"When the object describes a time-bound resource, such as an audio or video, a meeting, etc,",
"the duration property indicates the object's approximate duration.",
"",
"The value MUST be expressed as an xsd:duration as defined by",
"[[xmlschema11-2](https://www.w3.org/TR/xmlschema11-2/)], section",
"3.3.6 (e.g. a period of 5 seconds is represented as \"PT5S\").",
"",
"- Range: `xsd:duration`",
"- Functional: true",
],
types [
XsdDuration,
],
functional,
},
}
}
properties! {
Place {
docs [ "Define all the properties of the Location type as described by the Activity Streams vocabulary." ],
accuracy {
docs [
"Indicates the accuracy of position coordinates on a `Place` objects.",
"",
"Expressed in properties of percentage. e.g. \"94.0\"means \"94.0% accurate\".",
"",
"- Range: `xsd:float` [>= 0.0f, <= 100.0f]",
"- Functional: true",
],
types [
XsdFloat,
],
functional,
},
altitude {
docs [
"Indicates the altitude of a place. The measurement units is indicated using the units",
"property.",
"",
"If units is not specified, the default is assumed to be \"m\" indicating meters.",
"",
"- Range: `xsd:float`",
"- Functional: true",
],
types [
XsdFloat,
],
functional,
},
latitude {
docs [
"The latitude of a place.",
"",
"- Range: `xsd:float`",
"- Functional: true",
],
types [
XsdFloat,
],
functional,
},
longitude {
docs [
"The longitude of a place.",
"",
"- Range: `xsd:float`",
"- Functional: true",
],
types [
XsdFloat,
],
functional,
},
radius {
docs [
"The radius from the given latitude and longitude for a Place.",
"",
"The units is expressed by the units property. If units is not specified, the default is",
"assumed to be \"m\" indicating meters.",
"",
"- Range: `xsd:float`",
"- Functional: true",
],
types [
XsdFloat,
],
functional,
},
units {
docs [
"Specifies the measurement units for the radius and altitude properties on a `Place` object.",
"",
"If not specified, the default is assumed to be \"m\" for meters.",
"",
"- Range: `\"cm\"` | `\"feet\"` | `\"inches\"` | `\"km\"` | `\"m\"` | `xsd:anyUri` | `xsd:anyUri`",
"- Functional: true",
],
types [
Length,
XsdAnyUri,
],
functional,
},
}
}
properties! {
Profile {
docs [ "Define all the properties of the Profile type as described by the Activity Streams vocabulary." ],
describes {
docs [
"On a `Profile` object, the describes property identifies the object described by the",
"`Profile`.",
"",
"- Range: `Object`",
"- Functional: true",
],
types [
XsdAnyUri,
ObjectBox,
],
functional,
},
}
}
properties! {
Relationship {
docs [
"Define all the properties of the Relationship type as described by the Activity Streams",
"vocabulary.",
],
subject {
docs [
"On a `Relationship` object, the subject property identifies one of the connected",
"individuals.",
"",
"For instance, for a `Relationship` object describing \"John is related to Sally\", subject",
"would refer to John.",
"",
"- Range: `Object` | `Link`",
"- Functional: true",
],
types [
XsdAnyUri,
BaseBox,
],
functional,
},
object {
docs [
"When used within a `Relationship` describes the entity to which the subject is related.",
"",
"- Range: `Object` | `Link`",
"- Functional: false",
],
types [
XsdAnyUri,
BaseBox,
],
},
relationship {
docs [
"On a `Relationship` object, the relationship property identifies the kind of relationship",
"that exists between subject and object.",
"",
"- Range: `Object`",
"- Functional: false",
],
types [
XsdAnyUri,
ObjectBox,
],
},
}
}
properties! {
Tombstone {
docs [ "Define all the properties of the Tombstone type as described by the Activity Streams vocabulary." ],
former_type {
docs [
"On a `Tombstone` object, the formerType property identifies the type of the object that was",
"deleted.",
"",
"- Range: `Object`",
"- Functional: false",
],
types [
XsdAnyUri,
XsdString,
ObjectBox,
],
},
deleted {
docs [
"On a `Tombstone` object, the deleted property is a timestamp for when the object was",
"deleted.",
"",
"- Range: `xsd:dateTime`",
"- Functional: true",
],
types [
XsdDateTime,
],
functional,
},
}
}
properties! {
ApObject {
docs [
"Define activitypub properties for the Object type as described by the Activity Pub vocabulary.",
],
shares {
docs [
"This is a list of all Announce activities with this object as the object property, added as",
"a side effect.",
"",
"The shares collection MUST be either an OrderedCollection or a Collection and MAY be",
"filtered on privileges of an authenticated user or as appropriate when no authentication is",
"given.",
"",
"- Range: `anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
},
likes {
docs [
"This is a list of all Like activities with this object as the object property, added as a",
"side effect.",
"",
"The likes collection MUST be either an OrderedCollection or a Collection and MAY be",
"filtered on privileges of an authenticated user or as appropriate when no authentication is",
"given.",
"",
"- Range: `anyUri`",
"- Functional: true",
],
types [ XsdAnyUri ],
functional,
},
source {
docs [
"The source property is intended to convey some sort of source from which the content markup",
"was derived, as a form of provenance, or to support future editing by clients.",
"",
"In general, clients do the conversion from source to content, not the other way around.",
"",
"The value of source is itself an object which uses its own content and mediaType fields to",
"supply source information.",
"",
"- Range: `Object`",
"- Functional: true",
],
types [
XsdAnyUri,
ObjectBox,
],
functional,
},
upload_media {
docs [
"Servers MAY support uploading document types to be referenced in activites, such as images,",
"video or other binary data, but the precise mechanism is out of scope for this version of",
"`ActivityPub`.",
"",
"The Social Web Community Group is refining the protocol in the",
"[`ActivityPub` Media Upload report](https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload).",
"",
"- Range: `anyUri`",
"- Functional: false",
],
types [ XsdAnyUri ],
},
}
}

View file

@ -1,284 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::{
ext::Ext,
object::{kind::*, properties::*, Object, ObjectBox},
Base, BaseBox, Extensible, PropRefs,
};
/// Represents any kind of multi-paragraph written work.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[extension(ApObjectProperties)]
pub struct Article {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: ArticleType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}
/// Represents an audio document of any kind.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[extension(ApObjectProperties)]
pub struct Audio {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: AudioType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}
/// Represents a document of any kind.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[extension(ApObjectProperties)]
pub struct Document {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: DocumentType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}
/// Represents any kind of event.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[extension(ApObjectProperties)]
pub struct Event {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: EventType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}
/// An image document of any kind
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[extension(ApObjectProperties)]
pub struct Image {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: ImageType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}
/// Represents a short written work typically less than a single paragraph in length.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[extension(ApObjectProperties)]
pub struct Note {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: NoteType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}
/// Represents a Web Page.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[extension(ApObjectProperties)]
pub struct Page {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: PageType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}
/// Represents a logical or physical location.
///
/// The Place object is used to represent both physical and logical locations. While numerous
/// existing vocabularies exist for describing locations in a variety of ways, inconsistencies and
/// incompatibilities between those vocabularies make it difficult to achieve appropriate
/// interoperability between implementations. The Place object is included within the Activity
/// vocabulary to provide a minimal, interoperable starting point for describing locations
/// consistently across Activity Streams 2.0 implementations.
///
/// The Place object is intentionally flexible. It can, for instance, be used to identify a location
/// simply by name, or by longitude and latitude.
///
/// The Place object can also describe an area around a given point using the radius property, the
/// altitude of the location, and a degree of accuracy.
///
/// While publishers are not required to use these specific properties and MAY make use of other
/// mechanisms for describing locations, consuming implementations that support the Place object
/// MUST support the use of these properties.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[extension(ApObjectProperties)]
pub struct Place {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: PlaceType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid place properties to this struct
#[serde(flatten)]
#[prop_refs]
pub place: PlaceProperties,
}
/// A Profile is a content object that describes another `Object`, typically used to describe
/// `Actor` Type objects.
///
/// The `describes` property is used to reference the object being described by the profile.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[extension(ApObjectProperties)]
pub struct Profile {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: ProfileType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid profile properties to this struct
#[serde(flatten)]
#[prop_refs]
pub profile: ProfileProperties,
}
/// Describes a relationship between two individuals.
///
/// The subject and object properties are used to identify the connected individuals.
///
/// The `Relationship` object is used to represent relationships between individuals. It can be
/// used, for instance, to describe that one person is a friend of another, or that one person is a
/// member of a particular organization. The intent of modeling Relationship in this way is to allow
/// descriptions of activities that operate on the relationships in general, and to allow
/// representation of Collections of relationships.
///
/// For instance, many social systems have a notion of a "friends list". These are the collection of
/// individuals that are directly connected within a person's social graph. Suppose we have a user,
/// Sally, with direct relationships to users Joe and Jane. Sally follows Joe's updates while Sally
/// and Jane have a mutual relationship.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[extension(ApObjectProperties)]
pub struct Relationship {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: RelationshipType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid relationship properties to this struct
#[serde(flatten)]
#[prop_refs]
pub relationship: RelationshipProperties,
}
/// A Tombstone represents a content object that has been deleted.
///
/// It can be used in Collections to signify that there used to be an object at this position, but
/// it has been deleted.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[extension(ApObjectProperties)]
pub struct Tombstone {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: TombstoneType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
/// Adds all valid tombstone properties to this struct
#[serde(flatten)]
#[prop_refs]
pub tombstone_props: TombstoneProperties,
}
/// Represents a video document of any kind.
#[derive(Clone, Debug, Default, Extensible, PropRefs, serde::Deserialize, serde::Serialize)]
#[serde(rename_all = "camelCase")]
#[prop_refs(Object)]
#[extension(ApObjectProperties)]
pub struct Video {
#[serde(rename = "type")]
#[serde(alias = "objectType")]
#[serde(alias = "verb")]
kind: VideoType,
/// Adds all valid object properties to this struct
#[serde(flatten)]
#[prop_refs]
pub object_props: ObjectProperties,
}

View file

@ -0,0 +1,345 @@
use crate::{
either::Either,
primitives::{OneOrMany, RdfLangString},
};
/// A type representing any kind of string
///
/// In the ActivityStreams specification, string types are often defined as either an xsd:String or
/// and rdf:langString. The AnyString type represents this union.
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(transparent)]
pub struct AnyString(Either<String, RdfLangString>);
impl AnyString {
/// Borrow the AnyString as an &str
///
/// ```rust
/// # fn main() -> Result<(), anyhow::Error> {
/// # use activitystreams::primitives::AnyString;
/// # let any_string = AnyString::from_xsd_string("hi");
/// #
/// let s_borrow = any_string
/// .as_xsd_string()
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
/// # Ok(())
/// # }
/// ```
pub fn as_xsd_string(&self) -> Option<&str> {
self.0.as_ref().left().map(|l| l.as_str())
}
/// Borrow the AnyString as an RdfLangString
///
/// ```rust
/// # fn main() -> Result<(), anyhow::Error> {
/// # use activitystreams::primitives::{AnyString, RdfLangString};
/// # let any_string = AnyString::from_rdf_lang_string(RdfLangString {
/// # value: "hi".into(),
/// # language: "en".into(),
/// # });
/// #
/// let s_borrow = any_string
/// .as_rdf_lang_string()
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
/// # Ok(())
/// # }
/// ```
pub fn as_rdf_lang_string(&self) -> Option<&RdfLangString> {
self.0.as_ref().right()
}
/// Take the AnyString as a String
///
/// ```rust
/// # fn main() -> Result<(), anyhow::Error> {
/// # use activitystreams::primitives::AnyString;
/// # let any_string = AnyString::from_xsd_string("hi");
/// #
/// let xsd_string = any_string
/// .xsd_string()
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
/// # Ok(())
/// # }
/// ```
pub fn xsd_string(self) -> Option<String> {
self.0.left()
}
/// Take the AnyString as an RdfLangString
///
/// ```rust
/// # fn main() -> Result<(), anyhow::Error> {
/// # use activitystreams::primitives::{AnyString, RdfLangString};
/// # let any_string = AnyString::from_rdf_lang_string(RdfLangString {
/// # value: "hi".into(),
/// # language: "en".into(),
/// # });
/// #
/// let rdf_lang_string = any_string
/// .rdf_lang_string()
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
/// # Ok(())
/// # }
/// ```
pub fn rdf_lang_string(self) -> Option<RdfLangString> {
self.0.right()
}
/// Create a new AnyString from an `Into<String>`
///
/// ```rust
/// use activitystreams::primitives::AnyString;
///
/// let any_string = AnyString::from_xsd_string("hi");
/// ```
pub fn from_xsd_string<T>(string: T) -> Self
where
T: Into<String>,
{
AnyString(Either::Left(string.into()))
}
/// Create a new AnyString from an RdfLangString
///
/// ```rust
/// use activitystreams::primitives::{AnyString, RdfLangString};
///
/// let any_string = AnyString::from_rdf_lang_string(RdfLangString {
/// value: "hi".into(),
/// language: "en".into(),
/// });
/// ```
pub fn from_rdf_lang_string<T>(string: T) -> Self
where
T: Into<RdfLangString>,
{
AnyString(Either::Right(string.into()))
}
/// Replace the contents of self with a String
///
/// ```rust
/// use activitystreams::primitives::{AnyString, RdfLangString};
///
/// let mut any_string = AnyString::from_rdf_lang_string(RdfLangString {
/// value: "hi".into(),
/// language: "en".into(),
/// });
///
/// any_string.set_xsd_string("hi");
///
/// assert!(any_string.as_xsd_string().is_some());
/// ```
pub fn set_xsd_string<T>(&mut self, string: T)
where
T: Into<String>,
{
self.0 = Either::Left(string.into());
}
/// Replace the contents of self with an RdfLangString
///
/// ```rust
/// use activitystreams::primitives::{AnyString, RdfLangString};
///
/// let mut any_string = AnyString::from_xsd_string("hi");
///
/// any_string.set_rdf_lang_string(RdfLangString {
/// value: "hi".into(),
/// language: "en".into(),
/// });
///
/// assert!(any_string.as_rdf_lang_string().is_some());
/// ```
pub fn set_rdf_lang_string<T>(&mut self, string: T)
where
T: Into<RdfLangString>,
{
self.0 = Either::Right(string.into());
}
}
impl OneOrMany<AnyString> {
/// Try to borrow a single String from the current object
///
/// ```rust
/// # fn main() -> Result<(), anyhow::Error> {
/// # use activitystreams::primitives::{OneOrMany, AnyString};
/// # let string = OneOrMany::<AnyString>::from_xsd_string("Hey");
/// string
/// .as_single_xsd_string()
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
/// # Ok(())
/// # }
/// ```
pub fn as_single_xsd_string(&self) -> Option<&str> {
self.as_one()
.and_then(|any_string| any_string.as_xsd_string())
}
/// Try to borrow a single RdfLangString from the current object
///
/// ```rust
/// # fn main() -> Result<(), anyhow::Error> {
/// # use activitystreams::primitives::{OneOrMany, RdfLangString};
/// # let string = OneOrMany::from_rdf_lang_string(RdfLangString {
/// # value: "hi".into(),
/// # language: "en".into(),
/// # });
/// string
/// .as_single_rdf_lang_string()
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
/// # Ok(())
/// # }
/// ```
pub fn as_single_rdf_lang_string(&self) -> Option<&RdfLangString> {
self.as_one()
.and_then(|any_string| any_string.as_rdf_lang_string())
}
/// Try to take a single String from the current object
///
/// ```rust
/// # fn main() -> Result<(), anyhow::Error> {
/// # use activitystreams::primitives::{OneOrMany, AnyString};
/// # let string = OneOrMany::<AnyString>::from_xsd_string("Hey");
/// string
/// .single_xsd_string()
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
/// # Ok(())
/// # }
/// ```
pub fn single_xsd_string(self) -> Option<String> {
self.one().and_then(|any_string| any_string.xsd_string())
}
/// Try to take a single RdfLangString from the current object
///
/// ```rust
/// # fn main() -> Result<(), anyhow::Error> {
/// # use activitystreams::primitives::{OneOrMany, RdfLangString};
/// # let string = OneOrMany::from_rdf_lang_string(RdfLangString {
/// # value: "hi".into(),
/// # language: "en".into(),
/// # });
/// string
/// .single_rdf_lang_string()
/// .ok_or(anyhow::Error::msg("Wrong string type"))?;
/// # Ok(())
/// # }
/// ```
pub fn single_rdf_lang_string(self) -> Option<RdfLangString> {
self.one()
.and_then(|any_string| any_string.rdf_lang_string())
}
/// Create the object from a single String
///
/// ```rust
/// use activitystreams::primitives::{OneOrMany, AnyString};
///
/// let string = OneOrMany::<AnyString>::from_xsd_string("hi");
/// ```
pub fn from_xsd_string<T>(string: T) -> Self
where
T: Into<String>,
{
Self::from_one(AnyString::from_xsd_string(string))
}
/// Create the object from a single RdfLangString
///
/// ```rust
/// use activitystreams::primitives::{OneOrMany, RdfLangString};
///
/// let string = OneOrMany::from_rdf_lang_string(RdfLangString {
/// value: "hi".into(),
/// language: "en".into(),
/// });
/// ```
pub fn from_rdf_lang_string<T>(string: T) -> Self
where
T: Into<RdfLangString>,
{
Self::from_one(AnyString::from_rdf_lang_string(string))
}
/// Add a String to the object, appending to whatever is currently included
///
/// ```rust
/// use activitystreams::primitives::{OneOrMany, AnyString};
///
/// let mut string = OneOrMany::<AnyString>::from_xsd_string("Hello");
///
/// string
/// .add_xsd_string("Hey")
/// .add_xsd_string("hi");
/// ```
pub fn add_xsd_string<T>(&mut self, string: T) -> &mut Self
where
T: Into<String>,
{
self.add(string.into())
}
/// Add an RdfLangString to the object, appending to whatever is currently included
///
/// ```rust
/// use activitystreams::primitives::{AnyString, OneOrMany, RdfLangString};
///
/// let mut string = OneOrMany::<AnyString>::from_xsd_string("Hello");
///
/// string
/// .add_rdf_lang_string(RdfLangString {
/// value: "Hey".into(),
/// language: "en".into(),
/// })
/// .add_rdf_lang_string(RdfLangString {
/// value: "hi".into(),
/// language: "en".into(),
/// });
/// ```
pub fn add_rdf_lang_string<T>(&mut self, string: T) -> &mut Self
where
T: Into<RdfLangString>,
{
self.add(string.into())
}
}
impl From<&str> for AnyString {
fn from(s: &str) -> Self {
AnyString::from_xsd_string(s.to_owned())
}
}
impl From<String> for AnyString {
fn from(s: String) -> Self {
AnyString::from_xsd_string(s)
}
}
impl From<RdfLangString> for AnyString {
fn from(s: RdfLangString) -> Self {
AnyString::from_rdf_lang_string(s)
}
}
impl From<&str> for OneOrMany<AnyString> {
fn from(s: &str) -> Self {
OneOrMany::<AnyString>::from_xsd_string(s.to_owned())
}
}
impl From<String> for OneOrMany<AnyString> {
fn from(s: String) -> Self {
OneOrMany::<AnyString>::from_xsd_string(s)
}
}
impl From<RdfLangString> for OneOrMany<AnyString> {
fn from(s: RdfLangString) -> Self {
OneOrMany::<AnyString>::from_rdf_lang_string(s)
}
}

View file

@ -1,139 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
/// A list of units of length that represent valid units for certain ActivityStreams objects
#[derive(
Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
)]
#[serde(untagged)]
pub enum Length {
#[serde(rename = "cm")]
Centimeters,
#[serde(rename = "feet")]
Feet,
#[serde(rename = "inches")]
Inches,
#[serde(rename = "km")]
Kilometers,
#[serde(rename = "m")]
Meters,
}
#[derive(Clone, Debug, thiserror::Error)]
#[error("Could not parse units")]
/// The error type produced when a Length cannot be parsed
pub struct LengthError;
impl Length {
pub fn is_centimeters(&self) -> bool {
match self {
Length::Centimeters => true,
_ => false,
}
}
pub fn is_feet(&self) -> bool {
match self {
Length::Feet => true,
_ => false,
}
}
pub fn is_inches(&self) -> bool {
match self {
Length::Inches => true,
_ => false,
}
}
pub fn is_kilometers(&self) -> bool {
match self {
Length::Kilometers => true,
_ => false,
}
}
pub fn is_meters(&self) -> bool {
match self {
Length::Meters => true,
_ => false,
}
}
}
impl Default for Length {
fn default() -> Self {
Length::Meters
}
}
impl std::str::FromStr for Length {
type Err = LengthError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"cm" => Ok(Length::Centimeters),
"feet" => Ok(Length::Feet),
"inches" => Ok(Length::Inches),
"km" => Ok(Length::Kilometers),
"m" => Ok(Length::Meters),
_ => Err(LengthError),
}
}
}
impl std::convert::TryFrom<&str> for Length {
type Error = LengthError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<&mut str> for Length {
type Error = LengthError;
fn try_from(s: &mut str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<String> for Length {
type Error = LengthError;
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::fmt::Display for Length {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Length::Centimeters => write!(f, "cm"),
Length::Feet => write!(f, "feet"),
Length::Inches => write!(f, "inches"),
Length::Kilometers => write!(f, "km"),
Length::Meters => write!(f, "meters"),
}
}
}

View file

@ -1,114 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
/// A MIME Media Type consists of a top-level type and a subtype, which is further structured into
/// trees.
///
/// Optionally, media types can define companion data, known as parameters.
///
/// See [`RFC 2045`](https://tools.ietf.org/html/rfc2045) and
/// [`RFC 2046`](https://tools.ietf.org/html/rfc2046) for more information.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct MimeMediaType(mime::Mime);
#[derive(Clone, Debug, thiserror::Error)]
#[error("Error parsing MIME")]
/// The error type produced when a MimeMediaType cannot be parsed
pub struct MimeMediaTypeError;
impl<'a> PartialEq<&'a str> for MimeMediaType {
fn eq(&self, rhs: &&'a str) -> bool {
self.0.eq(rhs)
}
}
impl From<mime::Mime> for MimeMediaType {
fn from(m: mime::Mime) -> Self {
MimeMediaType(m)
}
}
impl From<MimeMediaType> for mime::Mime {
fn from(m: MimeMediaType) -> Self {
m.0
}
}
impl AsRef<mime::Mime> for MimeMediaType {
fn as_ref(&self) -> &mime::Mime {
&self.0
}
}
impl AsMut<mime::Mime> for MimeMediaType {
fn as_mut(&mut self) -> &mut mime::Mime {
&mut self.0
}
}
impl std::convert::TryFrom<String> for MimeMediaType {
type Error = MimeMediaTypeError;
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<&str> for MimeMediaType {
type Error = MimeMediaTypeError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<&mut str> for MimeMediaType {
type Error = MimeMediaTypeError;
fn try_from(s: &mut str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::str::FromStr for MimeMediaType {
type Err = MimeMediaTypeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(MimeMediaType(s.parse().map_err(|_| MimeMediaTypeError)?))
}
}
impl serde::ser::Serialize for MimeMediaType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str(&self.0.to_string())
}
}
impl<'de> serde::de::Deserialize<'de> for MimeMediaType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}

View file

@ -1,78 +1,36 @@
/* //! Types creating the base for most ActivityStreams fields.
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
//! Types defined as 'primitives' are used as building-blocks for ActivityStreams objects.
//! //!
//! For example, an `Object` may have a `summary` field, which is defined as a range of //! These types are not themselves defined by ActivityStreams, but are referenced by the
//! `xsd:string` and `rdf:langString`. As code, this is represented as an enum that either //! specification.
//! contains an `XsdString` or an `RdfLangString`.
//! //!
//! ```rust //! ```rust
//! use activitystreams::primitives::{RdfLangString, XsdString}; //! use activitystreams::primitives::{AnyString, OneOrMany, Unit};
//! //!
//! /// Define a terminating enum for the Summary field in Object Properties //! let any_string = AnyString::from_xsd_string("hey");
//! ///
//! /// In this case, terminating means it does not contain child elements.
//! pub enum ObjectPropertiesSummaryTermEnum {
//! XsdString(XsdString),
//! RdfLangString(RdfLangString),
//! }
//! //!
//! /// Since summary isn't functional, we can either have a single string, or multiple strings. //! let one_or_many = OneOrMany::<i32>::from_one(1234);
//! pub enum ObjectPropertiesSummaryEnum {
//! Term(ObjectPropertiesSummaryTermEnum),
//! Array(Vec<ObjectPropertiesSummaryTermEnum>),
//! }
//! //!
//! /// Define an excerpt from the ObjectProperties struct //! let cm = Unit::centimeters();
//! pub struct ObjectProperties {
//! // ...
//!
//! /// Since summary isn't a required field, it's stored as an option
//! summary: Option<ObjectPropertiesSummaryEnum>,
//!
//! // ...
//! }
//! #
//! # fn main() {}
//! ``` //! ```
mod length; mod any_string;
mod mime_media_type; mod one_or_many;
mod rdf_lang_string; mod rdf_lang_string;
mod xsd_any_uri; mod serde_parse;
mod unit;
mod xsd_datetime; mod xsd_datetime;
mod xsd_duration; mod xsd_duration;
mod xsd_float;
mod xsd_non_negative_float;
mod xsd_non_negative_integer;
mod xsd_string;
pub use self::{ pub use self::{
length::Length, any_string::AnyString,
mime_media_type::{MimeMediaType, MimeMediaTypeError}, one_or_many::OneOrMany,
rdf_lang_string::RdfLangString, rdf_lang_string::RdfLangString,
xsd_any_uri::{XsdAnyUri, XsdAnyUriError}, unit::Unit,
xsd_datetime::{XsdDateTime, XsdDateTimeError}, xsd_datetime::XsdDateTime,
xsd_duration::{XsdDuration, XsdDurationError}, xsd_duration::{XsdDuration, XsdDurationError},
xsd_float::{XsdFloat, XsdFloatError},
xsd_non_negative_float::{XsdNonNegativeFloat, XsdNonNegativeFloatError},
xsd_non_negative_integer::{XsdNonNegativeInteger, XsdNonNegativeIntegerError},
xsd_string::XsdString,
}; };
use self::serde_parse::SerdeParse;
/// An alias for the mime::Mime struct with serde compatibility
pub(crate) type MimeMediaType = SerdeParse<mime::Mime>;

View file

@ -0,0 +1,292 @@
use crate::either::Either;
/// A type representing at least one value
///
/// When translated to JSON, it can represent the following structures:
/// ```json
/// {
/// "key": value
/// }
/// ```
/// ```json
/// {
/// "key": [],
/// }
/// ```
/// ```json
/// {
/// "key": [value, ...]
/// }
/// ```
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
#[serde(transparent)]
pub struct OneOrMany<T>(pub(crate) Either<T, Vec<T>>);
impl<T> OneOrMany<T> {
/// Create a OneOrMany referencing the existing one
///
/// ```rust
/// use activitystreams::primitives::OneOrMany;
///
/// let value = OneOrMany::from_one(String::from("hi"));
/// let value_ref = value.as_ref();
///
/// assert_eq!(value_ref.one(), Some(&String::from("hi")));
/// ```
pub fn as_ref(&self) -> OneOrMany<&T> {
OneOrMany(self.0.as_ref().map(|l| l, |r| r.iter().collect()))
}
/// Map the value inside the OneOrMany from T to U
///
/// ```rust
/// use activitystreams::primitives::OneOrMany;
///
/// let value = OneOrMany::from_one("Jake from StateFarm");
/// let new_value = value.map(|s| format!("Hi, {}", s));
///
/// assert_eq!(new_value.one(), Some(String::from("Hi, Jake from StateFarm")));
/// ```
pub fn map<F, U>(self, f: F) -> OneOrMany<U>
where
F: Fn(T) -> U + Copy,
{
OneOrMany(self.0.map(f, |v| v.into_iter().map(f).collect()))
}
/// Create a OneOrMany mutably referencing the existing one
///
/// ```rust
/// use activitystreams::primitives::OneOrMany;
///
/// let mut value = OneOrMany::from_one(5);
/// let value_mut = value.as_mut();
/// ```
pub fn as_mut(&mut self) -> OneOrMany<&mut T> {
OneOrMany(self.0.as_mut().map(|l| l, |r| r.iter_mut().collect()))
}
/// Get a reference to a single value
///
/// ```rust
/// # use activitystreams::primitives::OneOrMany;
/// # let value = OneOrMany::from_one(1);
/// if let Some(v) = value.as_one() {
/// println!("{:?}", v);
/// }
/// ```
pub fn as_one(&self) -> Option<&T> {
self.0.as_ref().left()
}
/// Borrow one as mutable
///
/// ```rust
/// use activitystreams::primitives::OneOrMany;
///
/// let mut value = OneOrMany::from_one(1);
///
/// if let Some(i) = value.one_mut() {
/// *i += 1;
/// }
///
/// assert_eq!(value.one(), Some(2));
/// ```
pub fn one_mut(&mut self) -> Option<&mut T> {
self.0.as_mut().left()
}
/// Take a single value
///
/// ```rust
/// # use activitystreams::primitives::OneOrMany;
/// # let value = OneOrMany::from_one(1);
/// if let Some(v) = value.one() {
/// println!("{:?}", v);
/// }
/// ```
pub fn one(self) -> Option<T> {
self.0.left()
}
/// Get a slice of values
///
/// ```rust
/// # use activitystreams::primitives::OneOrMany;
/// # let value = OneOrMany::from_many(vec![1, 2, 3]);
/// if let Some(v) = value.as_many() {
/// for item in v.iter() {
/// println!("{:?}", item);
/// }
/// }
/// ```
pub fn as_many(&self) -> Option<&[T]> {
self.0.as_ref().right().map(|v| v.as_ref())
}
/// Borrow many as mutable
///
/// ```rust
/// use activitystreams::primitives::OneOrMany;
///
/// let mut value = OneOrMany::from_many(vec![1, 2, 3]);
///
/// if let Some(v) = value.many_mut() {
/// for i in v.iter_mut() {
/// *i += 3;
/// }
/// }
///
/// assert_eq!(value.many(), Some(vec![4, 5, 6]));
/// ```
pub fn many_mut(&mut self) -> Option<&mut [T]> {
self.0.as_mut().right().map(|v| v.as_mut())
}
/// Take a Vec of values
///
/// ```rust
/// # use activitystreams::primitives::OneOrMany;
/// # let value = OneOrMany::from_many(vec![1, 2, 3]);
/// if let Some(v) = value.many() {
/// for item in v.into_iter() {
/// println!("{:?}", item);
/// }
/// }
/// ```
pub fn many(self) -> Option<Vec<T>> {
self.0.right()
}
/// Consume the type, returning a vec
///
/// ```rust
/// # use activitystreams::primitives::OneOrMany;
/// # let value = OneOrMany::from_many(vec![1, 2, 3]);
/// for item in value.unwrap_to_vec() {
/// println!("{:?}", item);
/// }
/// ```
pub fn unwrap_to_vec(self) -> Vec<T> {
match self.0 {
Either::Left(t) => vec![t],
Either::Right(v) => v,
}
}
/// Produce a new object from one value
///
/// ```
/// use activitystreams::primitives::OneOrMany;
/// let v = OneOrMany::from_one(1234);
/// ```
pub fn from_one(t: T) -> Self {
OneOrMany(Either::Left(t))
}
/// Produce a new object from a vec of values
///
/// ```
/// use activitystreams::primitives::OneOrMany;
/// let v = OneOrMany::from_many(vec![1, 2, 3, 4]);
/// ```
pub fn from_many(items: Vec<T>) -> Self {
OneOrMany(Either::Right(items))
}
/// Overwrite the contents with a single value
///
/// ```
/// # use activitystreams::primitives::OneOrMany;
/// # let mut value = OneOrMany::from_many(vec![1, 2, 3]);
/// value.set_one(3);
///
/// assert!(value.as_one().is_some());
/// ```
pub fn set_one<U>(&mut self, u: U) -> &mut Self
where
U: Into<T>,
{
self.0 = Either::Left(u.into());
self
}
/// Overwrite the contents with vec of values
///
/// ```
/// # use activitystreams::primitives::OneOrMany;
/// # let mut value = OneOrMany::from_one(1234);
/// value.set_many(vec![1, 2, 3, 4]);
///
/// assert!(value.as_many().is_some());
/// ```
pub fn set_many<U>(&mut self, items: impl IntoIterator<Item = U>) -> &mut Self
where
U: Into<T>,
{
self.0 = Either::Right(items.into_iter().map(Into::into).collect());
self
}
/// Add a value to the object
///
/// This appends the value to the existing vec, or converts the single value into a vec, and
/// then appends the new value
///
/// ```
/// use activitystreams::primitives::OneOrMany;
/// let mut value = OneOrMany::from_one(1234);
/// value.add(4321);
///
/// assert!(value.as_many().is_some());
/// ```
pub fn add<U>(&mut self, u: U) -> &mut Self
where
U: Into<T>,
{
let mut v = match std::mem::replace(&mut self.0, Either::Right(vec![])) {
Either::Left(one) => vec![one],
Either::Right(v) => v,
};
v.push(u.into());
self.0 = Either::Right(v);
self
}
/// Add many values to the object
///
/// This appends the values to the existing vec, or converts the single value into a vec, and
/// then appends the new values
///
/// ```
/// use activitystreams::primitives::OneOrMany;
/// let mut value = OneOrMany::from_one(1234);
/// value.add_many(vec![4321, 2345]);
///
/// assert!(value.as_many().is_some());
/// ```
pub fn add_many<U>(&mut self, items: impl IntoIterator<Item = U>) -> &mut Self
where
U: Into<T>,
{
let mut v = match std::mem::replace(&mut self.0, Either::Right(vec![])) {
Either::Left(one) => vec![one],
Either::Right(v) => v,
};
v.extend(items.into_iter().map(Into::into));
self.0 = Either::Right(v);
self
}
}
impl<T> From<T> for OneOrMany<T> {
fn from(t: T) -> Self {
OneOrMany::from_one(t)
}
}
impl<T> From<Vec<T>> for OneOrMany<T> {
fn from(t: Vec<T>) -> Self {
OneOrMany::from_many(t)
}
}

View file

@ -17,8 +17,6 @@
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>. * along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/ */
use crate::primitives::XsdString;
/// The rdf.langString type extends xs.string, and represents a language tagged string in RDF. /// The rdf.langString type extends xs.string, and represents a language tagged string in RDF.
#[derive( #[derive(
Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize, Clone, Debug, Default, Eq, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
@ -28,13 +26,13 @@ pub struct RdfLangString {
/// ///
/// Represented in json as "@value" /// Represented in json as "@value"
#[serde(rename = "@value")] #[serde(rename = "@value")]
pub value: XsdString, pub value: String,
/// The language identifier /// The language identifier
/// ///
/// Represented in json as "@language" /// Represented in json as "@language"
#[serde(rename = "@language")] #[serde(rename = "@language")]
pub language: XsdString, pub language: String,
} }
impl std::fmt::Display for RdfLangString { impl std::fmt::Display for RdfLangString {

View file

@ -0,0 +1,89 @@
/// A struct that wraps a type implementing FromStr and Display implements Serde's Deserialize and
/// Serialize
#[derive(Clone, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
pub struct SerdeParse<T>(T);
impl<T> SerdeParse<T> {
/// Extract the inner item from SerdeParse
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> std::ops::Deref for SerdeParse<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> std::ops::DerefMut for SerdeParse<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> AsRef<T> for SerdeParse<T> {
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T> AsMut<T> for SerdeParse<T> {
fn as_mut(&mut self) -> &mut T {
&mut self.0
}
}
impl<T> std::str::FromStr for SerdeParse<T>
where
T: std::str::FromStr,
{
type Err = <T as std::str::FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
T::from_str(s).map(SerdeParse)
}
}
impl<T> std::fmt::Display for SerdeParse<T>
where
T: std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl<T> serde::ser::Serialize for SerdeParse<T>
where
T: std::fmt::Display,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
self.0.to_string().serialize(serializer)
}
}
impl<'de, T> serde::de::Deserialize<'de> for SerdeParse<T>
where
T: std::str::FromStr,
T::Err: std::fmt::Display,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}
impl<T> From<T> for SerdeParse<T> {
fn from(t: T) -> Self {
SerdeParse(t)
}
}

342
src/primitives/unit.rs Normal file
View file

@ -0,0 +1,342 @@
use crate::either::Either;
/// A type representing units of length
///
/// It can be any of the following
/// - Centimeters
/// - Meters
/// - Kilometers
/// - Inches
/// - Feet
/// - A custom value
#[derive(
Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
)]
#[serde(transparent)]
pub struct Unit(Either<Length, String>);
impl Unit {
/// Create a new unit measuring Centimeters
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// Unit::centimeters();
/// ```
pub fn centimeters() -> Self {
Unit(Either::Left(Length::Centimeters))
}
/// Check if the unit is Centimeters
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// assert!(Unit::centimeters().is_centimeters());
/// ```
pub fn is_centimeters(&self) -> bool {
self.0
.as_ref()
.left()
.map(|l| l.is_centimeters())
.unwrap_or(false)
}
/// Create a new unit measuring Meters
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// Unit::meters();
/// ```
pub fn meters() -> Self {
Unit(Either::Left(Length::Meters))
}
/// Check if the unit is Meters
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// assert!(Unit::meters().is_meters());
/// ```
pub fn is_meters(&self) -> bool {
self.0
.as_ref()
.left()
.map(|l| l.is_meters())
.unwrap_or(false)
}
/// Create a new unit measuring Kilometers
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// Unit::kilometers();
/// ```
pub fn kilometers() -> Self {
Unit(Either::Left(Length::Kilometers))
}
/// Check if the unit is Kilometers
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// assert!(Unit::kilometers().is_kilometers());
/// ```
pub fn is_kilometers(&self) -> bool {
self.0
.as_ref()
.left()
.map(|l| l.is_kilometers())
.unwrap_or(false)
}
/// Create a new unit measuring Feet
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// Unit::feet();
/// ```
pub fn feet() -> Self {
Unit(Either::Left(Length::Feet))
}
/// Check if the unit is Feet
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// assert!(Unit::feet().is_feet());
/// ```
pub fn is_feet(&self) -> bool {
self.0.as_ref().left().map(|l| l.is_feet()).unwrap_or(false)
}
/// Create a new unit measuring Inches
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// Unit::inches();
/// ```
pub fn inches() -> Self {
Unit(Either::Left(Length::Inches))
}
/// Check if the unit is Inches
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// assert!(Unit::inches().is_inches());
/// ```
pub fn is_inches(&self) -> bool {
self.0
.as_ref()
.left()
.map(|l| l.is_inches())
.unwrap_or(false)
}
/// Create a new custom unit
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// Unit::custom("Yards");
/// ```
pub fn custom<T>(string: T) -> Self
where
T: Into<String>,
{
Unit(Either::Right(string.into()))
}
/// Check if a unit is custom
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// assert!(Unit::custom("Yards").is_custom());
/// ```
pub fn is_custom(&self) -> bool {
self.as_custom().is_some()
}
/// Fetch a custom unit
///
/// ```rust
/// use activitystreams::primitives::Unit;
///
/// assert!(Unit::custom("Yards").as_custom() == Some("Yards"));
/// ```
pub fn as_custom(&self) -> Option<&str> {
self.0.as_ref().right().map(|r| r.as_str())
}
}
impl Default for Unit {
fn default() -> Self {
Self::meters()
}
}
impl std::str::FromStr for Unit {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let unit = match s {
"cm" => Self::centimeters(),
"m" => Self::meters(),
"km" => Self::kilometers(),
"inches" => Self::inches(),
"feet" => Self::feet(),
other => Self::custom(other),
};
Ok(unit)
}
}
impl From<String> for Unit {
fn from(s: String) -> Self {
match s.parse() {
Ok(u) => u,
Err(e) => match e {},
}
}
}
impl From<&str> for Unit {
fn from(s: &str) -> Self {
match s.parse() {
Ok(u) => u,
Err(e) => match e {},
}
}
}
/// A list of units of length that represent valid units for certain ActivityStreams objects
#[derive(
Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize,
)]
#[serde(untagged)]
enum Length {
#[serde(rename = "cm")]
Centimeters,
#[serde(rename = "feet")]
Feet,
#[serde(rename = "inches")]
Inches,
#[serde(rename = "km")]
Kilometers,
#[serde(rename = "m")]
Meters,
}
#[derive(Clone, Debug, thiserror::Error)]
#[error("Could not parse units")]
/// The error type produced when a Length cannot be parsed
struct LengthError;
impl Length {
fn is_centimeters(&self) -> bool {
match self {
Length::Centimeters => true,
_ => false,
}
}
fn is_feet(&self) -> bool {
match self {
Length::Feet => true,
_ => false,
}
}
fn is_inches(&self) -> bool {
match self {
Length::Inches => true,
_ => false,
}
}
fn is_kilometers(&self) -> bool {
match self {
Length::Kilometers => true,
_ => false,
}
}
fn is_meters(&self) -> bool {
match self {
Length::Meters => true,
_ => false,
}
}
}
impl Default for Length {
fn default() -> Self {
Length::Meters
}
}
impl std::str::FromStr for Length {
type Err = LengthError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"cm" => Ok(Length::Centimeters),
"feet" => Ok(Length::Feet),
"inches" => Ok(Length::Inches),
"km" => Ok(Length::Kilometers),
"m" => Ok(Length::Meters),
_ => Err(LengthError),
}
}
}
impl std::convert::TryFrom<&str> for Length {
type Error = LengthError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<&mut str> for Length {
type Error = LengthError;
fn try_from(s: &mut str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<String> for Length {
type Error = LengthError;
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::fmt::Display for Length {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Length::Centimeters => write!(f, "cm"),
Length::Feet => write!(f, "feet"),
Length::Inches => write!(f, "inches"),
Length::Kilometers => write!(f, "km"),
Length::Meters => write!(f, "meters"),
}
}
}

View file

@ -1,169 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
/// The type xsd:anyURI represents a Uniform Resource Identifier (URI) reference.
///
/// URIs are used to identify resources, and they may be absolute or relative. Absolute URIs
/// provide the entire context for locating the resources, such as http://datypic.com/prod.html.
/// Relative URIs are specified as the difference from a base URI, such as ../prod.html. It is also
/// possible to specify a fragment identifier, using the # character, such as ../prod.html#shirt.
///
/// The three previous examples happen to be HTTP URLs (Uniform Resource Locators), but URIs also
/// encompass URLs of other schemes (e.g., FTP, gopher, telnet), as well as URNs (Uniform Resource
/// Names). URIs are not required to be dereferencable; that is, it is not necessary for there to
/// be a web page at http://datypic.com/prod.html in order for this to be a valid URI.
///
/// URIs require that some characters be escaped with their hexadecimal Unicode code point preceded
/// by the % character. This includes non-ASCII characters and some ASCII characters, namely
/// control characters, spaces, and the following characters (unless they are used as deliimiters
/// in the URI): <>#%{}|\^`. For example, ../édition.html must be represented instead as
/// ../%C3%A9dition.html, with the é escaped as %C3%A9. However, the anyURI type will accept these
/// characters either escaped or unescaped. With the exception of the characters % and #, it will
/// assume that unescaped characters are intended to be escaped when used in an actual URI,
/// although the schema processor will do nothing to alter them. It is valid for an anyURI value to
/// contain a space, but this practice is strongly discouraged. Spaces should instead be escaped
/// using %20.
///
/// The schema processor is not required to parse the contents of an xsd:anyURI value to determine
/// whether it is valid according to any particular URI scheme. Since the bare minimum rules for
/// valid URI references are fairly generic, the schema processor will accept most character
/// strings, including an empty value. The only values that are not accepted are ones that make
/// inappropriate use of reserved characters, such as ones that contain multiple # characters or
/// have % characters that are not followed by two hexadecimal digits.
///
/// Note that when relative URI references such as "../prod" are used as values of xsd:anyURI, no
/// attempt is made to determine or keep track of the base URI to which they may be applied. For
/// more information on URIs, see RFC 2396, Uniform Resource Identifiers (URI): Generic Syntax.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct XsdAnyUri(url::Url);
/// The error type produced when an XsdAnyUri cannot be parsed
#[derive(Clone, Debug, thiserror::Error)]
#[error("Could not parse XsdAnyUri")]
pub struct XsdAnyUriError;
impl XsdAnyUri {
/// Borrow the underlying string from the XsdAnyUri
pub fn as_str(&self) -> &str {
self.as_ref()
}
/// Borrow a `url::Url` from the XsdAnyUri
pub fn as_url(&self) -> &url::Url {
self.as_ref()
}
/// Mutably borrow a `url::Url` from the XsdAnyUri
pub fn as_url_mut(&mut self) -> &mut url::Url {
self.as_mut()
}
}
impl From<url::Url> for XsdAnyUri {
fn from(u: url::Url) -> Self {
XsdAnyUri(u)
}
}
impl From<XsdAnyUri> for url::Url {
fn from(u: XsdAnyUri) -> Self {
u.0
}
}
impl Default for XsdAnyUri {
fn default() -> Self {
"data:text/plain,uwu".parse().unwrap()
}
}
impl AsRef<str> for XsdAnyUri {
fn as_ref(&self) -> &str {
self.0.as_ref()
}
}
impl AsRef<url::Url> for XsdAnyUri {
fn as_ref(&self) -> &url::Url {
&self.0
}
}
impl AsMut<url::Url> for XsdAnyUri {
fn as_mut(&mut self) -> &mut url::Url {
&mut self.0
}
}
impl std::convert::TryFrom<String> for XsdAnyUri {
type Error = XsdAnyUriError;
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<&str> for XsdAnyUri {
type Error = XsdAnyUriError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<&mut str> for XsdAnyUri {
type Error = XsdAnyUriError;
fn try_from(s: &mut str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::str::FromStr for XsdAnyUri {
type Err = XsdAnyUriError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(XsdAnyUri(s.parse().map_err(|_| XsdAnyUriError)?))
}
}
impl std::fmt::Display for XsdAnyUri {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl serde::ser::Serialize for XsdAnyUri {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str(&self.0.to_string())
}
}
impl<'de> serde::de::Deserialize<'de> for XsdAnyUri {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}

View file

@ -31,14 +31,19 @@
/// UTC, is represented as -05:00. If no time zone value is present, it is considered unknown; it /// UTC, is represented as -05:00. If no time zone value is present, it is considered unknown; it
/// is not assumed to be UTC. /// is not assumed to be UTC.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct XsdDateTime(chrono::DateTime<chrono::FixedOffset>); pub struct XsdDateTime(pub chrono::DateTime<chrono::FixedOffset>);
/// The error type produced when an XsdDateTime cannot be parsed
#[derive(Clone, Debug, thiserror::Error)]
#[error("Error parsing DateTime")]
pub struct XsdDateTimeError;
impl XsdDateTime { impl XsdDateTime {
/// Create a XsdDateTime from a chrono::DateTime
pub fn new(d: chrono::DateTime<chrono::FixedOffset>) -> Self {
XsdDateTime(d)
}
/// Extract the chrono::DateTime from XsdDateTime
pub fn into_inner(self) -> chrono::DateTime<chrono::FixedOffset> {
self.0
}
/// Borrow the underlying `chrono::DateTime<chrono::FixedOffset>` /// Borrow the underlying `chrono::DateTime<chrono::FixedOffset>`
pub fn as_datetime(&self) -> &chrono::DateTime<chrono::FixedOffset> { pub fn as_datetime(&self) -> &chrono::DateTime<chrono::FixedOffset> {
self.as_ref() self.as_ref()
@ -75,7 +80,7 @@ impl AsMut<chrono::DateTime<chrono::FixedOffset>> for XsdDateTime {
} }
impl std::convert::TryFrom<String> for XsdDateTime { impl std::convert::TryFrom<String> for XsdDateTime {
type Error = XsdDateTimeError; type Error = chrono::format::ParseError;
fn try_from(s: String) -> Result<Self, Self::Error> { fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse() s.parse()
@ -83,7 +88,7 @@ impl std::convert::TryFrom<String> for XsdDateTime {
} }
impl std::convert::TryFrom<&str> for XsdDateTime { impl std::convert::TryFrom<&str> for XsdDateTime {
type Error = XsdDateTimeError; type Error = chrono::format::ParseError;
fn try_from(s: &str) -> Result<Self, Self::Error> { fn try_from(s: &str) -> Result<Self, Self::Error> {
s.parse() s.parse()
@ -91,7 +96,7 @@ impl std::convert::TryFrom<&str> for XsdDateTime {
} }
impl std::convert::TryFrom<&mut str> for XsdDateTime { impl std::convert::TryFrom<&mut str> for XsdDateTime {
type Error = XsdDateTimeError; type Error = chrono::format::ParseError;
fn try_from(s: &mut str) -> Result<Self, Self::Error> { fn try_from(s: &mut str) -> Result<Self, Self::Error> {
s.parse() s.parse()
@ -99,12 +104,10 @@ impl std::convert::TryFrom<&mut str> for XsdDateTime {
} }
impl std::str::FromStr for XsdDateTime { impl std::str::FromStr for XsdDateTime {
type Err = XsdDateTimeError; type Err = chrono::format::ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(XsdDateTime( Ok(XsdDateTime(chrono::DateTime::parse_from_rfc3339(s)?))
chrono::DateTime::parse_from_rfc3339(s).map_err(|_| XsdDateTimeError)?,
))
} }
} }

View file

@ -42,7 +42,7 @@
/// multiplying by 365. If this is an issue for your application, look into specifying days /// multiplying by 365. If this is an issue for your application, look into specifying days
/// directly. /// directly.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct XsdDuration(chrono::Duration); pub struct XsdDuration(pub chrono::Duration);
/// The error type produced when an XsdDuration cannot be parsed /// The error type produced when an XsdDuration cannot be parsed
#[derive(Clone, Debug, thiserror::Error)] #[derive(Clone, Debug, thiserror::Error)]
@ -50,6 +50,16 @@ pub struct XsdDuration(chrono::Duration);
pub struct XsdDurationError; pub struct XsdDurationError;
impl XsdDuration { impl XsdDuration {
/// Create a new XsdDuration from a chrono::Duration
pub fn new(duration: chrono::Duration) -> Self {
XsdDuration(duration)
}
/// Extract the chrono::Duration from an XsdDuration
pub fn into_inner(self) -> chrono::Duration {
self.0
}
/// Borrow the underlying `chrono::Duration` /// Borrow the underlying `chrono::Duration`
pub fn as_duration(&self) -> &chrono::Duration { pub fn as_duration(&self) -> &chrono::Duration {
self.as_ref() self.as_ref()

View file

@ -1,114 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
/// The type xsd:float represents an IEEE single-precision 32-bit floating-point number.
///
/// TODO: handle exponents, infinity, not-a-number
///
/// The format of xsd:float values is a mantissa (a number which conforms to the type decimal)
/// followed, optionally, by the character "E" or "e" followed by an exponent. The exponent must be
/// an integer. For example, 3E2 represents 3 times 10 to the 2nd power, or 300. The exponent must
/// be an integer.
///
/// In addition, the following values are valid: INF (infinity), -INF (negative infinity), and NaN
/// (Not a Number). INF is considered to be greater than all other values, while -INF is less than
/// all other values. The value NaN cannot be compared to any other values, although it equals
/// itself.
#[derive(Clone, Debug, Default, PartialEq, PartialOrd, serde::Deserialize, serde::Serialize)]
#[serde(transparent)]
pub struct XsdFloat(f64);
/// The error type produced when an XsdFloat cannot be parsed
#[derive(Clone, Debug, thiserror::Error)]
#[error("Error parsing Float")]
pub struct XsdFloatError;
impl XsdFloat {
/// Get an f64 from the XsdFloat
pub fn to_f64(&self) -> f64 {
self.0
}
/// Get an XsdFloat from an f64
pub fn from_f64(f: f64) -> Self {
f.into()
}
}
impl AsRef<f64> for XsdFloat {
fn as_ref(&self) -> &f64 {
&self.0
}
}
impl AsMut<f64> for XsdFloat {
fn as_mut(&mut self) -> &mut f64 {
&mut self.0
}
}
impl From<f64> for XsdFloat {
fn from(f: f64) -> Self {
XsdFloat(f)
}
}
impl From<XsdFloat> for f64 {
fn from(f: XsdFloat) -> Self {
f.0
}
}
impl std::convert::TryFrom<String> for XsdFloat {
type Error = XsdFloatError;
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<&str> for XsdFloat {
type Error = XsdFloatError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<&mut str> for XsdFloat {
type Error = XsdFloatError;
fn try_from(s: &mut str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::str::FromStr for XsdFloat {
type Err = XsdFloatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(XsdFloat(s.parse().map_err(|_| XsdFloatError)?))
}
}
impl std::fmt::Display for XsdFloat {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}

View file

@ -1,121 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
/// The type xsd:float represents an IEEE single-precision 32-bit floating-point number.
///
/// TODO: handle exponents, infinity, not-a-number
///
/// The format of xsd:float values is a mantissa (a number which conforms to the type decimal)
/// followed, optionally, by the character "E" or "e" followed by an exponent. The exponent must be
/// an integer. For example, 3E2 represents 3 times 10 to the 2nd power, or 300. The exponent must
/// be an integer.
///
/// In addition, the following values are valid: INF (infinity), -INF (negative infinity), and NaN
/// (Not a Number). INF is considered to be greater than all other values, while -INF is less than
/// all other values. The value NaN cannot be compared to any other values, although it equals
/// itself.
///
/// This type also validates that a float is at least 0.0
#[derive(Clone, Debug, PartialEq, PartialOrd, Default, serde::Deserialize, serde::Serialize)]
#[serde(transparent)]
pub struct XsdNonNegativeFloat(f64);
/// The error type produced when an XsdNonNegativeFloat cannot be parsed
#[derive(Clone, Debug, thiserror::Error)]
#[error("Error parsing NonNegativeFloat")]
pub struct XsdNonNegativeFloatError;
impl XsdNonNegativeFloat {
/// Get an f64 from this XsdNonNegativeFloat
pub fn to_float(&self) -> f64 {
self.0
}
/// Try to get an XsdNonNegativeFloat from an f64
pub fn from_float(f: f64) -> Result<Self, XsdNonNegativeFloatError> {
use std::convert::TryInto;
f.try_into()
}
}
impl AsRef<f64> for XsdNonNegativeFloat {
fn as_ref(&self) -> &f64 {
&self.0
}
}
impl From<XsdNonNegativeFloat> for f64 {
fn from(f: XsdNonNegativeFloat) -> Self {
f.0
}
}
impl std::convert::TryFrom<f64> for XsdNonNegativeFloat {
type Error = XsdNonNegativeFloatError;
fn try_from(f: f64) -> Result<Self, Self::Error> {
if f < 0.0 {
return Err(XsdNonNegativeFloatError);
}
Ok(XsdNonNegativeFloat(f))
}
}
impl std::convert::TryFrom<String> for XsdNonNegativeFloat {
type Error = XsdNonNegativeFloatError;
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<&str> for XsdNonNegativeFloat {
type Error = XsdNonNegativeFloatError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<&mut str> for XsdNonNegativeFloat {
type Error = XsdNonNegativeFloatError;
fn try_from(s: &mut str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::str::FromStr for XsdNonNegativeFloat {
type Err = XsdNonNegativeFloatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let f = s.parse().map_err(|_| XsdNonNegativeFloatError)?;
if f < 0.0 {
return Err(XsdNonNegativeFloatError);
}
Ok(XsdNonNegativeFloat(f))
}
}
impl std::fmt::Display for XsdNonNegativeFloat {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}

View file

@ -1,111 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
/// The type xsd:nonNegativeInteger represents an arbitrarily large non-negative integer.
///
/// An xsd:nonNegativeInteger is a sequence of digits, optionally preceded by a + sign. Leading
/// zeros are permitted, but decimal points are not.
#[derive(
Clone,
Debug,
Default,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
serde::Deserialize,
serde::Serialize,
)]
#[serde(transparent)]
pub struct XsdNonNegativeInteger(u64);
/// The error type produced when an XsdNonNegativeInteger cannot be parsed
#[derive(Clone, Debug, thiserror::Error)]
#[error("Error parsing NonNegativeInteger")]
pub struct XsdNonNegativeIntegerError;
impl XsdNonNegativeInteger {
/// Get a u64 from this XsdNonNegativeInteger
pub fn to_u64(&self) -> u64 {
self.0
}
/// Get an XsdNonNegativeInteger from a u64
pub fn from_u64(u: u64) -> Self {
u.into()
}
}
impl AsRef<u64> for XsdNonNegativeInteger {
fn as_ref(&self) -> &u64 {
&self.0
}
}
impl From<XsdNonNegativeInteger> for u64 {
fn from(i: XsdNonNegativeInteger) -> Self {
i.0
}
}
impl From<u64> for XsdNonNegativeInteger {
fn from(f: u64) -> Self {
XsdNonNegativeInteger(f)
}
}
impl std::convert::TryFrom<String> for XsdNonNegativeInteger {
type Error = XsdNonNegativeIntegerError;
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<&str> for XsdNonNegativeInteger {
type Error = XsdNonNegativeIntegerError;
fn try_from(s: &str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::convert::TryFrom<&mut str> for XsdNonNegativeInteger {
type Error = XsdNonNegativeIntegerError;
fn try_from(s: &mut str) -> Result<Self, Self::Error> {
s.parse()
}
}
impl std::str::FromStr for XsdNonNegativeInteger {
type Err = XsdNonNegativeIntegerError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let f = s.parse().map_err(|_| XsdNonNegativeIntegerError)?;
Ok(XsdNonNegativeInteger(f))
}
}
impl std::fmt::Display for XsdNonNegativeInteger {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}

View file

@ -1,124 +0,0 @@
/*
* This file is part of ActivityStreams.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with ActivityStreams. If not, see <http://www.gnu.org/licenses/>.
*/
/// A string type that conforms to the xsd:string specification.
///
/// TODO: Escape `<` and `&` when converting
///
/// The type xsd:string represents a character string that may contain any Unicode character
/// allowed by XML. Certain characters, namely the "less than" symbol (<) and the ampersand (&),
/// must be escaped (using the entities &lt; and &amp;, respectively) when used in strings in XML
/// instances.
///
/// The xsd:string type has a whiteSpace facet of preserve, which means that all whitespace
/// characters (spaces, tabs, carriage returns, and line feeds) are preserved by the processor.
/// This is in contrast to two types derived from it: normalizedString, and token.
#[derive(
Clone,
Debug,
Default,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd,
serde::Deserialize,
serde::Serialize,
)]
#[serde(transparent)]
pub struct XsdString(String);
impl XsdString {
/// Get an XsdString from a String
pub fn from_string(s: String) -> Self {
s.into()
}
/// Borrow an &str from an XsdString
pub fn as_str(&self) -> &str {
self.as_ref()
}
/// Consume the XsdString and get a String
pub fn into_string(self) -> String {
self.into()
}
}
impl std::str::FromStr for XsdString {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(s.into())
}
}
impl From<String> for XsdString {
fn from(s: String) -> Self {
XsdString(s)
}
}
impl From<&str> for XsdString {
fn from(s: &str) -> Self {
XsdString(s.to_owned())
}
}
impl From<&mut str> for XsdString {
fn from(s: &mut str) -> Self {
XsdString(s.to_owned())
}
}
impl From<XsdString> for String {
fn from(s: XsdString) -> Self {
s.0
}
}
impl AsRef<str> for XsdString {
fn as_ref(&self) -> &str {
&self.0
}
}
impl AsRef<String> for XsdString {
fn as_ref(&self) -> &String {
&self.0
}
}
impl AsMut<str> for XsdString {
fn as_mut(&mut self) -> &mut str {
&mut self.0
}
}
impl AsMut<String> for XsdString {
fn as_mut(&mut self) -> &mut String {
&mut self.0
}
}
impl std::fmt::Display for XsdString {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}

325
src/unparsed.rs Normal file
View file

@ -0,0 +1,325 @@
//! Types and Traits for dealing with Unparsed data
//!
//! Since ActivityStreams is extensible, and structured in a heirarchy of types, Data that was not
//! required to construct a given type might still come in handy when extending a into a subobject.
//!
//! For example, a `Create` activity is an extension of an `Object`, but if we need to store that
//! type on another object, we need to make the type system happy. Since any number of types both
//! provided and extended might need to be stored in the same key, a type called `AnyBase` exists.
//! AnyBase, when containing a `Base`, can be extended into a `Create` activity so long as the
//! required keys are present in it's `Unparsed` struct.
//!
//! For the most part, users of this library won't care about this module, but if you need to
//! create an Extension, it will come in handy.
//!
//! Let's implement a part of the Security extension to be compatible with Mastodon.
//!
//! ```rust
//! use activitystreams::{
//! actor::{AsApActor, ApActor},
//! base::{AsBase, Base, Extends},
//! markers,
//! object::{AsObject, Object},
//! prelude::*,
//! primitives::*,
//! unparsed::*,
//! url::Url,
//! };
//!
//! /// First, we'll define our public key types
//!
//! #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
//! #[serde(rename_all = "camelCase")]
//! pub struct PublicKeyValues {
//! pub id: Url,
//! pub owner: Url,
//! pub public_key_pem: String,
//! }
//!
//! #[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
//! #[serde(rename_all = "camelCase")]
//! pub struct PublicKey<Inner> {
//! pub public_key: PublicKeyValues,
//! pub inner: Inner,
//! }
//!
//!
//! /// Then, we'll implement Extends so we can produce a PublicKey<Object> from an AnyBase.
//!
//! impl<Inner, Kind> Extends<Kind> for PublicKey<Inner>
//! where
//! Inner: Extends<Kind, Error=serde_json::Error> + UnparsedMut,
//! {
//! type Error = serde_json::Error;
//!
//! fn extends(base: Base<Kind>) -> Result<Self, Self::Error> {
//! let mut inner = Inner::extends(base)?;
//!
//! Ok(PublicKey {
//! public_key: inner.unparsed_mut().remove("publicKey")?,
//! inner,
//! })
//! }
//!
//! fn retracts(self) -> Result<Base<Kind>, Self::Error> {
//! let PublicKey {
//! public_key,
//! mut inner,
//! } = self;
//!
//! inner.unparsed_mut().insert("publicKey", public_key)?;
//!
//! inner.retracts()
//! }
//! }
//!
//!
//! /// Auto-implement Base, Object, and Actor when Inner supports it
//! impl<Inner> markers::Base for PublicKey<Inner> where Inner: markers::Base {}
//! impl<Inner> markers::Object for PublicKey<Inner> where Inner: markers::Object {}
//! impl<Inner> markers::Actor for PublicKey<Inner> where Inner: markers::Actor {}
//!
//!
//! /// If we want to easily access getters and setters for internal types, we'll need to forward
//! /// those, too.
//!
//! /// Forward for base methods
//! ///
//! /// This allows us to access methods related to `context`, `id`, `kind`, `name`,
//! /// `media_type`, and `preview` directly from the PublicKey struct
//! impl<Inner, Kind> AsBase<Kind> for PublicKey<Inner>
//! where
//! Inner: AsBase<Kind>,
//! {
//! fn base_ref(&self) -> &Base<Kind> {
//! self.inner.base_ref()
//! }
//!
//! fn base_mut(&mut self) -> &mut Base<Kind> {
//! self.inner.base_mut()
//! }
//! }
//!
//! /// Forward for object methods
//! ///
//! /// This allows us to access methods related to `url`, `generator`, `start_time`, `duration`,
//! /// and more directly from the PublicKey struct
//! impl<Inner, Kind> AsObject<Kind> for PublicKey<Inner>
//! where
//! Inner: AsObject<Kind>,
//! {
//! fn object_ref(&self) -> &Object<Kind> {
//! self.inner.object_ref()
//! }
//!
//! fn object_mut(&mut self) -> &mut Object<Kind> {
//! self.inner.object_mut()
//! }
//! }
//!
//! /// Forward for ActivityPub actor methods
//! ///
//! /// This allows us to access methods related to `inbox`, `outbox`, `following`, `followers`,
//! /// `liked`, `streams`, `endpoints`, and more directly from the PublicKey struct
//! impl<Inner1, Inner2> AsApActor<Inner2> for PublicKey<Inner1>
//! where
//! Inner1: AsApActor<Inner2>,
//! {
//! fn ap_actor_ref(&self) -> &ApActor<Inner2> {
//! self.inner.ap_actor_ref()
//! }
//!
//! fn ap_actor_mut(&mut self) -> &mut ApActor<Inner2> {
//! self.inner.ap_actor_mut()
//! }
//! }
//!
//!
//! /// If we want to be able to extend from our own type, we'll need to forward some
//! /// implementations, and create some traits
//!
//! /// Make it easy for downstreams to get an Unparsed
//! impl<Inner> UnparsedMut for PublicKey<Inner>
//! where
//! Inner: UnparsedMut,
//! {
//! fn unparsed_mut(&mut self) -> &mut Unparsed {
//! self.inner.unparsed_mut()
//! }
//! }
//!
//! /// Create our own extensible trait
//! pub trait AsPublicKey<Inner> {
//! fn public_key_ref(&self) -> &PublicKey<Inner>;
//! fn public_key_mut(&mut self) -> &mut PublicKey<Inner>;
//! }
//!
//! /// Implement it
//! impl<Inner> AsPublicKey<Inner> for PublicKey<Inner> {
//! fn public_key_ref(&self) -> &Self {
//! self
//! }
//!
//! fn public_key_mut(&mut self) -> &mut Self {
//! self
//! }
//! }
//!
//! /// And now create helper methods
//! pub trait PublicKeyExt<Inner>: AsPublicKey<Inner> {
//! /// Borrow the public key's ID
//! fn key_id<'a>(&'a self) -> &'a Url
//! where
//! Inner: 'a,
//! {
//! &self.public_key_ref().public_key.id
//! }
//!
//! /// Set the public key's ID
//! fn set_key_id(&mut self, id: Url) -> &mut Self {
//! self.public_key_mut().public_key.id = id;
//! self
//! }
//!
//! /// Borrow the public key's Owner
//! fn key_owner<'a>(&'a self) -> &'a Url
//! where
//! Inner: 'a,
//! {
//! &self.public_key_ref().public_key.owner
//! }
//!
//! /// Set the public key's Owner
//! fn set_key_owner(&mut self, owner: Url) -> &mut Self {
//! self.public_key_mut().public_key.owner = owner;
//! self
//! }
//!
//! /// Borrow the public key's PEM encoded value
//! fn key_pem<'a>(&'a self) -> &'a str
//! where
//! Inner: 'a,
//! {
//! &self.public_key_ref().public_key.public_key_pem
//! }
//!
//! /// Set the public key's PEM encoded value
//! ///
//! /// In a real application, this might take a different type, such as RSA's RSAPublicKey, or
//! /// OpenSSL's or Ring's version
//! fn set_key_pem<T>(&mut self, pem: T) -> &mut Self
//! where
//! T: Into<String>,
//! {
//! self.public_key_mut().public_key.public_key_pem = pem.into();
//! self
//! }
//! }
//!
//! /// Finally, we'll automatically implement PublicKeyExt for any type implementing AsPublicKey
//! impl<T, Inner> PublicKeyExt<Inner> for T where T: AsPublicKey<Inner> {}
//!
//!
//! /// Now that eveything is implemented, we can use it like so:
//! use activitystreams::{actor::{kind::PersonType, Person}, uri};
//!
//! pub type ExtendedPerson = PublicKey<ApActor<Person>>;
//!
//! impl ExtendedPerson {
//! pub fn new(inbox: Url, mut owner: Url) -> Self {
//! let id = owner.clone();
//! owner.set_fragment(Some("main-key"));
//! PublicKey {
//! public_key: PublicKeyValues {
//! id,
//! owner,
//! public_key_pem: String::new(),
//! },
//! inner: ApActor::new(inbox, Person::new()),
//! }
//! }
//! }
//!
//! fn main() -> Result<(), anyhow::Error> {
//! let mut extended_person = ExtendedPerson::new(
//! uri!("https://example.com/user/inbox"),
//! uri!("https://example.com/user"),
//! );
//!
//! extended_person
//! .set_kind(PersonType::Person)
//! .set_id("https://example.com/user".parse()?)
//! .set_name("Demo User")
//! .set_preferred_username("user")
//! .set_outbox("https://example.com/user/outbox".parse()?)
//! .set_key_pem(
//! "------ BEGIN PUBLIC KEY ------\nasdfasdfasdfasdfasdfasdf..."
//! )
//! .set_key_owner("https://example.com/user".parse()?)
//! .set_key_id("https://example.com/user#main-key".parse()?);
//!
//! let string = serde_json::to_string(&extended_person)?;
//! println!("{}", string);
//! Ok(())
//! }
//! ```
/// A trait granting mutable access to an Unparsed struct
///
/// This is required for easy manipulation of Unparsed from potentially deeply nested structures.
pub trait UnparsedMut {
/// Get a mutable reference to Unparsed
fn unparsed_mut(&mut self) -> &mut Unparsed;
}
/// A helper trait providing two methods, 'insert' and 'remove', that is auto-implemented for
/// UnparsedMut types.
///
/// These methods are provided for easily pulling values from and inserting values into the
/// Unparsed struct.
pub trait UnparsedMutExt: UnparsedMut {
/// Remove a value from the Unparsed struct, provided it matches the expected type
fn remove<T>(&mut self, key: &str) -> Result<T, serde_json::Error>
where
T: serde::de::DeserializeOwned,
{
serde_json::from_value(self.unparsed_mut().remove(key))
}
/// Insert a value into the Unparsed struct if the value isn't Null
fn insert<T>(&mut self, key: &str, value: T) -> Result<&mut Self, serde_json::Error>
where
T: serde::ser::Serialize,
{
let value = serde_json::to_value(value)?;
if !value.is_null() {
self.unparsed_mut().insert(key.to_owned(), value);
}
Ok(self)
}
}
/// The Unparsed struct itself,
#[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
#[serde(transparent)]
pub struct Unparsed(std::collections::HashMap<String, serde_json::Value>);
impl Unparsed {
pub(crate) fn remove(&mut self, key: &str) -> serde_json::Value {
self.0.remove(key).unwrap_or(serde_json::Value::Null)
}
pub(crate) fn insert(&mut self, key: String, value: serde_json::Value) {
self.0.insert(key, value);
}
}
impl UnparsedMut for Unparsed {
fn unparsed_mut(&mut self) -> &mut Unparsed {
self
}
}
impl<T> UnparsedMutExt for T where T: UnparsedMut {}