#activity-stream #activity-pub

activitystreams

一套用于activitystreams数据的核心类型和特质

60个版本

0.7.0-alpha.252023年7月6日
0.7.0-alpha.242022年12月19日
0.7.0-alpha.202022年3月31日
0.7.0-alpha.142021年11月19日
0.2.0 2018年5月19日

#48 in 视频

Download history 32/week @ 2024-03-11 24/week @ 2024-03-18 51/week @ 2024-03-25 77/week @ 2024-04-01 59/week @ 2024-04-08 32/week @ 2024-04-15 26/week @ 2024-04-22 24/week @ 2024-04-29 4/week @ 2024-05-06 5/week @ 2024-05-13 28/week @ 2024-05-20 1/week @ 2024-05-27 14/week @ 2024-06-03 21/week @ 2024-06-10 41/week @ 2024-06-17 22/week @ 2024-06-24

98 个月下载量
2 crate 中使用

GPL-3.0 许可证

570KB
8K SLoC

ActivityStreams

由ActivityStreams和ActivityPub规范组成的一套特质和类型

使用方法

首先,将ActivityStreams添加到您的依赖项中

[dependencies]
activitystreams = "0.7.0-alpha.19"

类型

该项目按Kind => Type布局

因此,要使用ActivityStreams视频,您会编写

use activitystreams::object::Video;
let video = Video::new();

要使用ActivityPub配置文件,您会编写

use activitystreams::object::{ApObject, Profile};
let inner = Profile::new();
let profile = ApObject::new(inner);

只有一个类型的Link

use activitystreams::link::Mention;
let mention = Mention::new();

字段

提供的类型中的许多字段都封装在OneOrMany<>或具有AnyBase类型。这是因为activitystreams规范对被认为有效的结构非常开放。

例如,ActivityStreams中的Object类型有一个summary字段,该字段可以表示为xsd:stringrdf:langString。它还指出,summary字段不是functional,这意味着可以出现任意数量的xsd:stringrdf:langString,或它们的组合。这个库将其表示为Option<OneOrMany<AnyString>>

此结果类型正好足够具体,可以匹配以下有效的ActivityStreams json,而不匹配任何无效的json。

没有摘要

{}

具有字符串摘要

{
    "summary": "A string"
}

具有rdf langstring

{
    "summary": {
        "@value": "A string",
        "@language": "en"
    }
}

具有多个值

{
    "summary": [
        {
            "@value": "A string",
            "@language": "en"
        },
        "An xsd:string this time"
    ]
}

虽然与这些类型交互可能会变得繁琐,但根据其内部内容,对OneOrMany类型实现了某些自定义方法。

fn from_xsd_string<T>(&mut self, T) -> Self;
fn from_rdf_lang_string<T>(&mut self, T) -> Self;

fn as_single_xsd_string(&self) -> Option<&str>;
fn as_single_rdf_langstring(&self) -> Option<&RdfLangString>;

fn single_xsd_string(self) -> Option<String>;
fn single_rdf_lang_string(self) -> Option<RdfLangString>;

fn add_xsd_string<T>(&mut self, T) -> &mut Self;
fn add_rdf_lang_string<T>(&mut self, T) -> &mut Self;

这些方法提供对设置和获取统一类型数据的访问,以及删除数据的功能。在设置方法中,类型参数 T 被绑定到 Into<String>Into<RdfLangString>。这允许将可以转换为这些类型的值传递给方法,而不是要求调用者执行转换。

RdfLangString 这样的类型可以在 primitives 模块中找到。除非你正在构建自己的自定义类型,否则通常不需要你自己导入它们。它们每个都实现了用于解析的 FromStr 和将字符串转换回的 Display,以及用于你可能期望的类型(例如 XsdNonNegativeInteger 实现了 From<u64>Into<u64>)的 FromIntoTryFromTryInto

特性

由于 ActivityStreams 是一种数据层次结构,它表示为包含其他结构体的结构体。这意味着任何 ActivityStreams 类型都可能出现的 context 字段将位于最内层的结构体中。为了避免编写像 ap_object.collection.object.base.context = Some(context()) 这样的代码,这个库提供了一些特性,这些特性会为提供的类型自动实现。

例如,BaseExt 特性为 context 提供以下方法,

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;

对于像 id 这样具有更具体界限的字段,

fn id(&self) -> Option<&XsdAnyUri>;
fn set_id(&mut self, XsdAnyUri) -> &mut Self;
fn take_id(&self) -> Option<XsdAnyUri>;
fn delete_id(&mut self) -> &mut Self;

实现类似方法的扩展特性的完整列表可以在预览模块中找到。通过使用 use activitystreams::prelude::*;,所有方法都将为包含其字段的数据类型实现。

标记

这个库提供了一系列特性,如 ObjectLinkActorActivityCollectionCollectionPage。其中大多数特性仅仅是为了“标记”类型,这意味着它们在运行时没有提供任何值,但存在于编译时添加泛型约束。

如果你想创建一个只操作 Activity 而不是普通对象的函数,你可以这样绑定函数

use activitystreams::{base::BaseExt, context, markers::Activity, iri};

fn manipulator<T, Kind>(mut activity: T) -> Result<(), anyhow::Error>
where
    T: Activity + BaseExt<Kind>,
{
    activity
        .set_id(iri!("https://example.com"))
        .set_context(context());
    Ok(())
}

类型

这个库有一组单元结构体,可以将字符串序列化和反序列化。这是为了使不同的 ActivityPub 对象类型可以反序列化为不同的命名结构体。这些可以在 activitystreams::objects::kind 和类似路径中找到。

例如,要构建自己的 Person 结构体,你可以这样写

use activitystreams::actor::kind::PersonType;

#[derive(serde::Deserialize, serde::Serialize)]
pub struct MyPerson {
    // Do a rename since `type` is not a valid rust field name
    #[serde(rename = "type")]
    kind: PersonType,
}

这种类型只会在JSON中反序列化,其中"type":"Person"

示例

创建

use activitystreams::{
    context,
    object::{ApObject, Video},
    prelude::*,
    iri,
};

fn main() -> Result<(), anyhow::Error> {
    let mut video = ApObject::new(Video::new());

    video
        .set_context(context())
        .set_id(iri!("https://example.com/@example/lions"))
        .set_media_type("video/webm".parse()?)
        .set_url(iri!("https://example.com/@example/lions/video.webm"))
        .set_summary("A cool video")
        .set_duration("PT4M20S".parse()?)
        .set_shares(iri!("https://example.com/@example/lions/video.webm#shares"));

    println!("Video, {:#?}", video);

    let s = serde_json::to_string(&video)?;

    println!("json, {}", s);

    let v: ApObject<Video> = serde_json::from_str(&s)?;

    println!("Video again, {:#?}", v);

    Ok(())
}

解析

use activitystreams::{activity::ActorAndObject, prelude::*};

#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub enum AcceptedTypes {
    Accept,
    Announce,
    Create,
    Delete,
    Follow,
    Reject,
    Update,
    Undo,
}

pub type AcceptedActivity = ActorAndObject<AcceptedTypes>;

pub fn handle_activity(activity: AcceptedActivity) -> Result<(), anyhow::Error> {
    println!("Actor: {:?}", activity.actor());
    println!("Object: {:?}", activity.object());

    match activity.kind() {
        Some(AcceptedTypes::Accept) => println!("Accept"),
        Some(AcceptedTypes::Announce) => println!("Announce"),
        Some(AcceptedTypes::Create) => println!("Create"),
        Some(AcceptedTypes::Delete) => println!("Delete"),
        Some(AcceptedTypes::Follow) => println!("Follow"),
        Some(AcceptedTypes::Reject) => println!("Reject"),
        Some(AcceptedTypes::Update) => println!("Update"),
        Some(AcceptedTypes::Undo) => println!("Undo"),
        None => return Err(anyhow::Error::msg("No activity type provided")),
    }

    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)?)
}

贡献

如果您发现任何问题,请随时提交问题。请注意,任何贡献的代码都将根据GPLv3许可。

许可

版权 © 2020 Riley Trautman

ActivityStreams是自由软件:您可以按照自由软件基金会发布的GNU通用公共许可证的条款重新分发和/或修改它,许可证版本为3,或者(根据您的选择)任何更高版本。

ActivityStreams的发布是希望它将是有用的,但没有任何保证;甚至没有关于适销性或特定用途的隐含保证。有关详细信息,请参阅GNU通用公共许可证。此文件是ActivityStreams的一部分。

您应该已经收到ActivityStreams的GNU通用公共许可证副本。如果没有,请参阅http://www.gnu.org/licenses/

依赖项

~1.9–2.7MB
~54K SLoC