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}