activitystreams/
ext.rs

1/*
2 * This file is part of ActivityStreams.
3 *
4 * Copyright © 2020 Riley Trautman
5 *
6 * ActivityStreams is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * ActivityStreams is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with ActivityStreams.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20//! Defining extensibility in the ActivityStreams spec
21//!
22//! In ActivityStreams, there are many times you may want to use an extension. For example, to
23//! interact with Mastodon, you need to at least understand the `publicKey` field on their actor
24//! type. If not, you won't be able to use HTTP Signatures, and will have your messages rejected.
25//!
26//! But this library doesn't provide any of the security extensions to ActivityStreams. In order to
27//! support it, you could implment your own extensions to this library. Let's cover a basic
28//! example.
29//!
30//! ```rust
31//! // For this example, we'll use the Extensible trait, the Extension trait, the Actor trait, and
32//! // the Person type
33//! use activitystreams::{
34//!     actor::{Actor, Person},
35//!     ext::{Extensible, Extension},
36//! };
37//!
38//! /// Let's define the PublicKey type. The three fields in this PublicKey struct are how Mastodon
39//! /// represents Public Keys on actors. We'll need to derive Serialize and Deserialize for these
40//! /// in order for them to be useful.
41//! #[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
42//! #[serde(rename_all = "camelCase")]
43//! pub struct PublicKey {
44//!     /// The ID of the key.
45//!     ///
46//!     /// In mastodon, this is the same as the actor's URL with a #main-key on
47//!     /// the end.
48//!     pub id: String,
49//!
50//!     /// The ID of the actor who owns this key.
51//!     pub owner: String,
52//!
53//!     /// This is a PEM file with PKCS#8 encoded data.
54//!     pub public_key_pem: String,
55//! }
56//!
57//! /// Now, we'll need more than just a PublicKey struct to make this work. We'll need to define a
58//! /// second struct that declares the correct key to house this information inside of
59//! ///
60//! /// The information is represented as the following json:
61//! /// ```json
62//! /// {
63//! ///     "publicKey": {
64//! ///         "id": "key id",
65//! ///         "owner": "actor id",
66//! ///         "publicKeyPem": "pem string"
67//! ///     }
68//! /// }
69//! /// ```
70//! ///
71//! /// This means we'll need to define the 'publicKey' key
72//! #[derive(Clone, Debug, Default, serde::Deserialize, serde::Serialize)]
73//! #[serde(rename_all = "camelCase")]
74//! pub struct PublicKeyExtension {
75//!     /// The public key's information
76//!     pub public_key: PublicKey
77//! }
78//!
79//! impl PublicKey {
80//!     /// Let's add a convenience method to turn a PublicKey into a PublicKeyExtension
81//!     pub fn to_ext(self) -> PublicKeyExtension {
82//!         PublicKeyExtension { public_key: self }
83//!     }
84//! }
85//!
86//! // And finally, we'll implement the Extension trait for PublicKeyExtension
87//! //
88//! // We'll bound this extension by the Actor trait, since we don't care about non-actors having
89//! // keys. This means that you can put a key on a `Person`, but not on a `Video`.
90//! impl<T> Extension<T> for PublicKeyExtension where T: Actor {}
91//!
92//! // Now that these types are defined, we can put them to use!
93//! let person = Person::new();
94//!
95//! // let's just create a dummy key for this example
96//! let public_key = PublicKey {
97//!     id: "My ID".to_owned(),
98//!     owner: "Owner ID".to_owned(),
99//!     public_key_pem: "My Public Key".to_owned(),
100//! };
101//!
102//! // We're doing it! The person is being extended with a public key
103//! //
104//! // Note that calling `extend` on person here is possible because the Extensible trait is in
105//! // scope
106//! let person_with_key = person.extend(public_key.to_ext());
107//! ```
108
109use crate::{
110    activity::{Activity, ActivityBox, IntransitiveActivity, IntransitiveActivityBox},
111    actor::{Actor, ActorBox},
112    collection::{Collection, CollectionBox, CollectionPage, CollectionPageBox},
113    link::{Link, LinkBox},
114    object::{Object, ObjectBox},
115    Base, BaseBox,
116};
117use std::{convert::TryFrom, fmt::Debug};
118
119/// Defines an extension to an activitystreams object or link
120///
121/// This type provides two fields, the first field, `base`, should always the be base
122/// ActivityStreams type. As long as `base` implements an ActivityStreams trait, Ext will also.
123///
124/// For example, the type `Ext<Video, HashMap<String, String>>` will implement the `Object` trait,
125/// because `Video` implements that trait.
126///
127/// Additionally, AsRef and AsMut have been implemented for Ext. If type type
128/// `Ext<Ext<Follow, SomeTime>, AnotherType>` exists, that will implement
129/// `AsRef<ActivityProperties>` just like the innermost `Follow`.
130///
131/// Usage:
132/// ```rust
133/// use activitystreams::object::Video;
134///
135/// fn main() -> Result<(), Box<dyn std::error::Error>> {
136///     let mut video = Video::full();
137///
138///     // AsMut works even though this is an Ext<Video, ApObjectProperties>
139///     video
140///         .as_mut()
141///         .set_id("https://example.com")?;
142///
143///     // set information on the extension
144///     video
145///         .extension
146///         .set_source_xsd_any_uri("https://example.com")?;
147///
148///     Ok(())
149/// }
150/// ```
151#[derive(Clone, Debug, Default)]
152#[cfg_attr(feature = "derive", derive(serde::Deserialize, serde::Serialize))]
153pub struct Ext<T, U> {
154    /// The ActivityStreams base type, or another extension containing one
155    #[cfg_attr(feature = "derive", serde(flatten))]
156    pub base: T,
157
158    /// The extension being applied to the type
159    #[cfg_attr(feature = "derive", serde(flatten))]
160    pub extension: U,
161}
162
163/// A trait implemented by extensions to the ActivityStreams specification
164///
165/// This is implemented for a couple types by default, such as
166/// ApObjectProperties, and ApActorProperties.
167///
168/// Extensions are not intended to be used as trait objects
169pub trait Extension<T>
170where
171    T: Base,
172{
173    /// A default implementation that simply returns the Ext type with Self and the base type
174    /// inside of it.
175    fn extends(self, base: T) -> Ext<T, Self>
176    where
177        Self: Sized,
178    {
179        Ext {
180            base,
181            extension: self,
182        }
183    }
184}
185
186/// A trait implemented (automatically) by objects and links in the ActivityStreams specification
187///
188/// This is used to easily extend objects.
189///
190/// ```rust
191/// # use activitystreams::object::{Video, properties::ApObjectProperties};
192/// use activitystreams::ext::Extensible;
193/// let vid = Video::new();
194/// let ap_props = ApObjectProperties::default();
195///
196/// let extended_vid = vid.extend(ap_props);
197/// ```
198pub trait Extensible<U> {
199    fn extend(self, extension: U) -> Ext<Self, U>
200    where
201        Self: Sized;
202}
203
204impl<T, U, V> AsRef<V> for Ext<T, U>
205where
206    T: Base + AsRef<V>,
207    U: Extension<T> + Debug,
208{
209    fn as_ref(&self) -> &V {
210        self.base.as_ref()
211    }
212}
213
214impl<T, U, V> AsMut<V> for Ext<T, U>
215where
216    T: Base + AsMut<V>,
217    U: Extension<T> + Debug,
218{
219    fn as_mut(&mut self) -> &mut V {
220        self.base.as_mut()
221    }
222}
223
224impl<T, U> TryFrom<Ext<T, U>> for BaseBox
225where
226    T: Base + serde::ser::Serialize,
227    U: Extension<T> + serde::ser::Serialize + Debug,
228{
229    type Error = std::io::Error;
230
231    fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
232        BaseBox::from_concrete(e)
233    }
234}
235
236impl<T, U> TryFrom<Ext<T, U>> for ObjectBox
237where
238    T: Object + serde::ser::Serialize,
239    U: Extension<T> + serde::ser::Serialize + Debug,
240{
241    type Error = std::io::Error;
242
243    fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
244        ObjectBox::from_concrete(e)
245    }
246}
247
248impl<T, U> TryFrom<Ext<T, U>> for LinkBox
249where
250    T: Link + serde::ser::Serialize,
251    U: Extension<T> + serde::ser::Serialize + Debug,
252{
253    type Error = std::io::Error;
254
255    fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
256        LinkBox::from_concrete(e)
257    }
258}
259
260impl<T, U> TryFrom<Ext<T, U>> for CollectionBox
261where
262    T: Collection + serde::ser::Serialize,
263    U: Extension<T> + serde::ser::Serialize + Debug,
264{
265    type Error = std::io::Error;
266
267    fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
268        CollectionBox::from_concrete(e)
269    }
270}
271
272impl<T, U> TryFrom<Ext<T, U>> for CollectionPageBox
273where
274    T: CollectionPage + serde::ser::Serialize,
275    U: Extension<T> + serde::ser::Serialize + Debug,
276{
277    type Error = std::io::Error;
278
279    fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
280        CollectionPageBox::from_concrete(e)
281    }
282}
283
284impl<T, U> TryFrom<Ext<T, U>> for ActivityBox
285where
286    T: Activity + serde::ser::Serialize,
287    U: Extension<T> + serde::ser::Serialize + Debug,
288{
289    type Error = std::io::Error;
290
291    fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
292        ActivityBox::from_concrete(e)
293    }
294}
295
296impl<T, U> TryFrom<Ext<T, U>> for IntransitiveActivityBox
297where
298    T: IntransitiveActivity + serde::ser::Serialize,
299    U: Extension<T> + serde::ser::Serialize + Debug,
300{
301    type Error = std::io::Error;
302
303    fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
304        IntransitiveActivityBox::from_concrete(e)
305    }
306}
307
308impl<T, U> TryFrom<Ext<T, U>> for ActorBox
309where
310    T: Actor + serde::ser::Serialize,
311    U: Extension<T> + serde::ser::Serialize + Debug,
312{
313    type Error = std::io::Error;
314
315    fn try_from(e: Ext<T, U>) -> Result<Self, Self::Error> {
316        ActorBox::from_concrete(e)
317    }
318}
319
320impl<T, U> Extensible<U> for T
321where
322    T: crate::Base,
323    U: Extension<T>,
324{
325    fn extend(self, item: U) -> Ext<Self, U> {
326        item.extends(self)
327    }
328}
329
330impl<T, U> Base for Ext<T, U>
331where
332    T: Base,
333    U: Debug,
334{
335}
336
337impl<T, U> Object for Ext<T, U>
338where
339    T: Object,
340    U: Debug,
341{
342}
343
344impl<T, U> Link for Ext<T, U>
345where
346    T: Link,
347    U: Debug,
348{
349}
350
351impl<T, U> Actor for Ext<T, U>
352where
353    T: Actor,
354    U: Debug,
355{
356}
357
358impl<T, U> Collection for Ext<T, U>
359where
360    T: Collection,
361    U: Debug,
362{
363}
364
365impl<T, U> CollectionPage for Ext<T, U>
366where
367    T: CollectionPage,
368    U: Debug,
369{
370}
371
372impl<T, U> Activity for Ext<T, U>
373where
374    T: Activity,
375    U: Debug,
376{
377}
378
379impl<T, U> IntransitiveActivity for Ext<T, U>
380where
381    T: IntransitiveActivity,
382    U: Debug,
383{
384}