diff --git a/.woodpecker.yml b/.woodpecker.yml index b82dd94..8462e1b 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,5 +1,5 @@ variables: - - &rust_image "rust:1.78-bullseye" + - &rust_image "rust:1.85-bullseye" steps: cargo_fmt: diff --git a/Cargo.toml b/Cargo.toml index d7a74dd..2a58253 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,6 @@ documentation = "https://docs.rs/activitypub_federation/" default = ["actix-web", "axum"] actix-web = ["dep:actix-web", "dep:http02"] axum = ["dep:axum", "dep:tower"] -diesel = ["dep:diesel"] [lints.rust] warnings = "deny" @@ -32,70 +31,66 @@ redundant_closure_for_method_calls = "deny" unwrap_used = "deny" [dependencies] -chrono = { version = "0.4.38", features = ["clock"], default-features = false } -serde = { version = "1.0.204", features = ["derive"] } -async-trait = "0.1.81" -url = { version = "2.5.2", features = ["serde"] } -serde_json = { version = "1.0.120", features = ["preserve_order"] } -reqwest = { version = "0.12.5", default-features = false, features = [ +chrono = { version = "0.4.39", features = ["clock"], default-features = false } +serde = { version = "1.0.217", features = ["derive"] } +async-trait = "0.1.85" +url = { version = "2.5.4", features = ["serde"] } +serde_json = { version = "1.0.137", features = ["preserve_order"] } +reqwest = { version = "0.12.12", default-features = false, features = [ "json", "stream", "rustls-tls", ] } -reqwest-middleware = "0.3.2" -tracing = "0.1.40" +reqwest-middleware = "0.4.0" +tracing = "0.1.41" base64 = "0.22.1" rand = "0.8.5" -rsa = "0.9.6" -once_cell = "1.19.0" -http = "1.1.0" +rsa = "0.9.7" +http = "1.2.0" sha2 = { version = "0.10.8", features = ["oid"] } -thiserror = "1.0.62" -derive_builder = "0.20.0" -itertools = "0.13.0" +thiserror = "2.0.11" +derive_builder = "0.20.2" +itertools = "0.14.0" dyn-clone = "1.0.17" enum_delegate = "0.2.0" httpdate = "1.0.3" -http-signature-normalization-reqwest = { version = "0.12.0", default-features = false, features = [ +http-signature-normalization-reqwest = { version = "0.13.0", default-features = false, features = [ "sha-2", "middleware", "default-spawner", ] } http-signature-normalization = "0.7.0" -bytes = "1.6.1" -futures-core = { version = "0.3.30", default-features = false } -pin-project-lite = "0.2.14" +bytes = "1.9.0" +futures-core = { version = "0.3.31", default-features = false } +pin-project-lite = "0.2.16" activitystreams-kinds = "0.3.0" -regex = { version = "1.10.5", default-features = false, features = [ +regex = { version = "1.11.1", default-features = false, features = [ "std", "unicode", ] } -tokio = { version = "1.38.0", features = [ +tokio = { version = "1.43.0", features = [ "sync", "rt", "rt-multi-thread", "time", ] } -diesel = { version = "2.2.1", features = [ - "postgres", -], default-features = false, optional = true } -futures = "0.3.30" -moka = { version = "0.12.8", features = ["future"] } +futures = "0.3.31" +moka = { version = "0.12.10", features = ["future"] } # Actix-web -actix-web = { version = "4.8.0", default-features = false, optional = true } +actix-web = { version = "4.9.0", default-features = false, optional = true } http02 = { package = "http", version = "0.2.12", optional = true } # Axum -axum = { version = "0.7.5", features = ["json"], default-features = false, optional = true } -tower = { version = "0.4.13", optional = true } +axum = { version = "0.8.1", features = ["json"], default-features = false, optional = true } +tower = { version = "0.5.2", optional = true } [dev-dependencies] -anyhow = "1.0.86" -axum = { version = "0.7.5", features = ["macros"] } -axum-extra = { version = "0.9.3", features = ["typed-header"] } -env_logger = "0.11.3" -tokio = { version = "1.38.0", features = ["full"] } +anyhow = "1.0.95" +axum = { version = "0.8.1", features = ["macros"] } +axum-extra = { version = "0.10.0", features = ["typed-header"] } +env_logger = "0.11.6" +tokio = { version = "1.43.0", features = ["full"] } [profile.dev] strip = "symbols" diff --git a/docs/10_fetching_objects_with_unknown_type.md b/docs/10_fetching_objects_with_unknown_type.md index 96392d4..8a15953 100644 --- a/docs/10_fetching_objects_with_unknown_type.md +++ b/docs/10_fetching_objects_with_unknown_type.md @@ -26,7 +26,7 @@ pub enum SearchableObjects { Note(Note) } -#[async_trait::async_trait] + impl Object for SearchableDbObjects { type DataType = DbConnection; type Kind = SearchableObjects; diff --git a/examples/live_federation/objects/person.rs b/examples/live_federation/objects/person.rs index d9439ea..e7e334b 100644 --- a/examples/live_federation/objects/person.rs +++ b/examples/live_federation/objects/person.rs @@ -63,7 +63,6 @@ pub struct Person { public_key: PublicKey, } -#[async_trait::async_trait] impl Object for DbUser { type DataType = DatabaseHandle; type Kind = Person; diff --git a/examples/live_federation/objects/post.rs b/examples/live_federation/objects/post.rs index 1b19fac..31edeb1 100644 --- a/examples/live_federation/objects/post.rs +++ b/examples/live_federation/objects/post.rs @@ -44,7 +44,6 @@ pub struct Mention { pub kind: MentionType, } -#[async_trait::async_trait] impl Object for DbPost { type DataType = DatabaseHandle; type Kind = Note; diff --git a/examples/local_federation/axum/http.rs b/examples/local_federation/axum/http.rs index dd9d002..e010a73 100644 --- a/examples/local_federation/axum/http.rs +++ b/examples/local_federation/axum/http.rs @@ -30,8 +30,8 @@ pub fn listen(config: &FederationConfig) -> Result<(), Error> { info!("Listening with axum on {hostname}"); let config = config.clone(); let app = Router::new() - .route("/:user/inbox", post(http_post_user_inbox)) - .route("/:user", get(http_get_user)) + .route("/{user}/inbox", post(http_post_user_inbox)) + .route("/{user}", get(http_get_user)) .route("/.well-known/webfinger", get(webfinger)) .layer(FederationMiddleware::new(config)); diff --git a/examples/local_federation/objects/person.rs b/examples/local_federation/objects/person.rs index 0ae402f..57b4ec2 100644 --- a/examples/local_federation/objects/person.rs +++ b/examples/local_federation/objects/person.rs @@ -128,7 +128,6 @@ impl DbUser { } } -#[async_trait::async_trait] impl Object for DbUser { type DataType = DatabaseHandle; type Kind = Person; diff --git a/examples/local_federation/objects/post.rs b/examples/local_federation/objects/post.rs index cbdf8e8..7868e07 100644 --- a/examples/local_federation/objects/post.rs +++ b/examples/local_federation/objects/post.rs @@ -41,7 +41,6 @@ pub struct Note { content: String, } -#[async_trait::async_trait] impl Object for DbPost { type DataType = DatabaseHandle; type Kind = Note; diff --git a/src/actix_web/inbox.rs b/src/actix_web/inbox.rs index 9bce475..0912e72 100644 --- a/src/actix_web/inbox.rs +++ b/src/actix_web/inbox.rs @@ -122,14 +122,14 @@ mod test { let (_, _, config) = setup_receive_test().await; let actor = Url::parse("http://ds9.lemmy.ml/u/lemmy_alpha").unwrap(); - let id = "http://localhost:123/1"; + let activity_id = "http://localhost:123/1"; let activity = json!({ "actor": actor.as_str(), "to": ["https://www.w3.org/ns/activitystreams#Public"], "object": "http://ds9.lemmy.ml/post/1", "cc": ["http://enterprise.lemmy.ml/c/main"], "type": "Delete", - "id": id + "id": activity_id } ); let body: Bytes = serde_json::to_vec(&activity).unwrap().into(); @@ -144,8 +144,8 @@ mod test { .await; match res { - Err(Error::ParseReceivedActivity(_, url)) => { - assert_eq!(id, url.expect("has url").as_str()); + Err(Error::ParseReceivedActivity { err: _, id }) => { + assert_eq!(activity_id, id.expect("has url").as_str()); } _ => unreachable!(), } diff --git a/src/axum/inbox.rs b/src/axum/inbox.rs index 1767c10..84567e0 100644 --- a/src/axum/inbox.rs +++ b/src/axum/inbox.rs @@ -10,7 +10,6 @@ use crate::{ traits::{ActivityHandler, Actor, Object}, }; use axum::{ - async_trait, body::Body, extract::FromRequest, http::{Request, StatusCode}, @@ -58,7 +57,6 @@ pub struct ActivityData { body: Vec, } -#[async_trait] impl FromRequest for ActivityData where S: Send + Sync, diff --git a/src/axum/middleware.rs b/src/axum/middleware.rs index 290dc94..2f8b0e9 100644 --- a/src/axum/middleware.rs +++ b/src/axum/middleware.rs @@ -1,5 +1,5 @@ use crate::config::{Data, FederationConfig, FederationMiddleware}; -use axum::{async_trait, body::Body, extract::FromRequestParts, http::Request, response::Response}; +use axum::{body::Body, extract::FromRequestParts, http::Request, response::Response}; use http::{request::Parts, StatusCode}; use std::task::{Context, Poll}; use tower::{Layer, Service}; @@ -43,7 +43,6 @@ where } } -#[async_trait] impl FromRequestParts for Data where S: Send + Sync, diff --git a/src/error.rs b/src/error.rs index 4a53fd8..0661071 100644 --- a/src/error.rs +++ b/src/error.rs @@ -44,11 +44,16 @@ pub enum Error { #[error("Failed to parse object {1} with content {2}: {0}")] ParseFetchedObject(serde_json::Error, Url, String), /// Failed to parse an activity received from another instance - #[error("Failed to parse incoming activity {}: {0}", match .1 { + #[error("Failed to parse incoming activity {}: {0}", match .id { Some(t) => format!("with id {t}"), None => String::new(), })] - ParseReceivedActivity(serde_json::Error, Option), + ParseReceivedActivity { + /// The parse error + err: serde_json::Error, + /// ID of the Activitypub object which caused this error + id: Option, + }, /// Reqwest Middleware Error #[error(transparent)] ReqwestMiddleware(#[from] reqwest_middleware::Error), diff --git a/src/fetch/collection_id.rs b/src/fetch/collection_id.rs index 8c796f4..ae17ca0 100644 --- a/src/fetch/collection_id.rs +++ b/src/fetch/collection_id.rs @@ -102,92 +102,3 @@ where self.0.eq(&other.0) && self.1 == other.1 } } - -#[cfg(feature = "diesel")] -const _IMPL_DIESEL_NEW_TYPE_FOR_COLLECTION_ID: () = { - use diesel::{ - backend::Backend, - deserialize::{FromSql, FromStaticSqlRow}, - expression::AsExpression, - internal::derives::as_expression::Bound, - pg::Pg, - query_builder::QueryId, - serialize, - serialize::{Output, ToSql}, - sql_types::{HasSqlType, SingleValue, Text}, - Expression, - Queryable, - }; - - // TODO: this impl only works for Postgres db because of to_string() call which requires reborrow - impl ToSql for CollectionId - where - Kind: Collection, - for<'de2> ::Kind: Deserialize<'de2>, - String: ToSql, - { - fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { - let v = self.0.to_string(); - >::to_sql(&v, &mut out.reborrow()) - } - } - impl<'expr, Kind, ST> AsExpression for &'expr CollectionId - where - Kind: Collection, - for<'de2> ::Kind: Deserialize<'de2>, - Bound: Expression, - ST: SingleValue, - { - type Expression = Bound; - fn as_expression(self) -> Self::Expression { - Bound::new(self.0.as_str()) - } - } - impl AsExpression for CollectionId - where - Kind: Collection, - for<'de2> ::Kind: Deserialize<'de2>, - Bound: Expression, - ST: SingleValue, - { - type Expression = Bound; - fn as_expression(self) -> Self::Expression { - Bound::new(self.0.to_string()) - } - } - impl FromSql for CollectionId - where - Kind: Collection + Send + 'static, - for<'de2> ::Kind: Deserialize<'de2>, - String: FromSql, - DB: Backend, - DB: HasSqlType, - { - fn from_sql( - raw: DB::RawValue<'_>, - ) -> Result> { - let string: String = FromSql::::from_sql(raw)?; - Ok(CollectionId::parse(&string)?) - } - } - impl Queryable for CollectionId - where - Kind: Collection + Send + 'static, - for<'de2> ::Kind: Deserialize<'de2>, - String: FromStaticSqlRow, - DB: Backend, - DB: HasSqlType, - { - type Row = String; - fn build(row: Self::Row) -> diesel::deserialize::Result { - Ok(CollectionId::parse(&row)?) - } - } - impl QueryId for CollectionId - where - Kind: Collection + 'static, - for<'de2> ::Kind: Deserialize<'de2>, - { - type QueryId = Self; - } -}; diff --git a/src/fetch/mod.rs b/src/fetch/mod.rs index 424a115..0365b3e 100644 --- a/src/fetch/mod.rs +++ b/src/fetch/mod.rs @@ -109,6 +109,7 @@ async fn fetch_object_http_with_accept( let mut counter = data.request_counter.fetch_add(1, Ordering::SeqCst); // fetch_add returns old value so we need to increment manually here counter += 1; + url.to_string(); if counter > config.http_fetch_limit { return Err(Error::RequestLimit); } diff --git a/src/fetch/object_id.rs b/src/fetch/object_id.rs index b6aafc6..4792cd9 100644 --- a/src/fetch/object_id.rs +++ b/src/fetch/object_id.rs @@ -144,7 +144,7 @@ where data: &Data<::DataType>, ) -> Result, ::Error> { let id = self.0.clone(); - Object::read_from_id(*id, data).await + ::read_from_id(*id, data).await } /// Fetch object from origin instance over HTTP, then verify and parse it. @@ -271,95 +271,6 @@ where } } -#[cfg(feature = "diesel")] -const _IMPL_DIESEL_NEW_TYPE_FOR_OBJECT_ID: () = { - use diesel::{ - backend::Backend, - deserialize::{FromSql, FromStaticSqlRow}, - expression::AsExpression, - internal::derives::as_expression::Bound, - pg::Pg, - query_builder::QueryId, - serialize, - serialize::{Output, ToSql}, - sql_types::{HasSqlType, SingleValue, Text}, - Expression, - Queryable, - }; - - // TODO: this impl only works for Postgres db because of to_string() call which requires reborrow - impl ToSql for ObjectId - where - Kind: Object, - for<'de2> ::Kind: Deserialize<'de2>, - String: ToSql, - { - fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { - let v = self.0.to_string(); - >::to_sql(&v, &mut out.reborrow()) - } - } - impl<'expr, Kind, ST> AsExpression for &'expr ObjectId - where - Kind: Object, - for<'de2> ::Kind: Deserialize<'de2>, - Bound: Expression, - ST: SingleValue, - { - type Expression = Bound; - fn as_expression(self) -> Self::Expression { - Bound::new(self.0.as_str()) - } - } - impl AsExpression for ObjectId - where - Kind: Object, - for<'de2> ::Kind: Deserialize<'de2>, - Bound: Expression, - ST: SingleValue, - { - type Expression = Bound; - fn as_expression(self) -> Self::Expression { - Bound::new(self.0.to_string()) - } - } - impl FromSql for ObjectId - where - Kind: Object + Send + 'static, - for<'de2> ::Kind: Deserialize<'de2>, - String: FromSql, - DB: Backend, - DB: HasSqlType, - { - fn from_sql( - raw: DB::RawValue<'_>, - ) -> Result> { - let string: String = FromSql::::from_sql(raw)?; - Ok(ObjectId::parse(&string)?) - } - } - impl Queryable for ObjectId - where - Kind: Object + Send + 'static, - for<'de2> ::Kind: Deserialize<'de2>, - String: FromStaticSqlRow, - DB: Backend, - DB: HasSqlType, - { - type Row = String; - fn build(row: Self::Row) -> diesel::deserialize::Result { - Ok(ObjectId::parse(&row)?) - } - } - impl QueryId for ObjectId - where - Kind: Object + 'static, - for<'de2> ::Kind: Deserialize<'de2>, - { - type QueryId = Self; - } -}; - /// Internal only #[cfg(test)] #[allow(clippy::unwrap_used)] diff --git a/src/fetch/webfinger.rs b/src/fetch/webfinger.rs index 8d53078..b73e28c 100644 --- a/src/fetch/webfinger.rs +++ b/src/fetch/webfinger.rs @@ -7,10 +7,9 @@ use crate::{ }; use http::HeaderValue; use itertools::Itertools; -use once_cell::sync::Lazy; use regex::Regex; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fmt::Display}; +use std::{collections::HashMap, fmt::Display, sync::LazyLock}; use tracing::debug; use url::Url; @@ -130,8 +129,8 @@ pub fn extract_webfinger_name<'i, T>(query: &'i str, data: &Data) -> Result<& where T: Clone, { - static WEBFINGER_REGEX: Lazy = - Lazy::new(|| Regex::new(r"^acct:([\p{L}0-9_\.\-]+)@(.*)$").expect("compile regex")); + static WEBFINGER_REGEX: LazyLock = + LazyLock::new(|| Regex::new(r"^acct:([\p{L}0-9_\.\-]+)@(.*)$").expect("compile regex")); // Regex to extract usernames from webfinger query. Supports different alphabets using `\p{L}`. // TODO: This should use a URL parser let captures = WEBFINGER_REGEX diff --git a/src/http_signatures.rs b/src/http_signatures.rs index f30e75c..95d0a12 100644 --- a/src/http_signatures.rs +++ b/src/http_signatures.rs @@ -19,7 +19,6 @@ use http_signature_normalization_reqwest::{ prelude::{Config, SignExt}, DefaultSpawner, }; -use once_cell::sync::Lazy; use reqwest::Request; use reqwest_middleware::RequestBuilder; use rsa::{ @@ -30,7 +29,7 @@ use rsa::{ }; use serde::Deserialize; use sha2::{Digest, Sha256}; -use std::{collections::BTreeMap, fmt::Debug, time::Duration}; +use std::{collections::BTreeMap, fmt::Debug, sync::LazyLock, time::Duration}; use tracing::debug; use url::Url; @@ -82,9 +81,9 @@ pub(crate) async fn sign_request( private_key: RsaPrivateKey, http_signature_compat: bool, ) -> Result { - static CONFIG: Lazy> = - Lazy::new(|| Config::new().set_expiration(EXPIRES_AFTER)); - static CONFIG_COMPAT: Lazy = Lazy::new(|| { + static CONFIG: LazyLock> = + LazyLock::new(|| Config::new().set_expiration(EXPIRES_AFTER)); + static CONFIG_COMPAT: LazyLock = LazyLock::new(|| { Config::new() .mastodon_compat() .set_expiration(EXPIRES_AFTER) @@ -185,7 +184,7 @@ fn verify_signature_inner( uri: &Uri, public_key: &str, ) -> Result<(), Error> { - static CONFIG: Lazy = Lazy::new(|| { + static CONFIG: LazyLock = LazyLock::new(|| { http_signature_normalization::Config::new() .set_expiration(EXPIRES_AFTER) .require_digest() @@ -288,9 +287,10 @@ pub mod test { use rsa::{pkcs1::DecodeRsaPrivateKey, pkcs8::DecodePrivateKey}; use std::str::FromStr; - static ACTOR_ID: Lazy = Lazy::new(|| Url::parse("https://example.com/u/alice").unwrap()); - static INBOX_URL: Lazy = - Lazy::new(|| Url::parse("https://example.com/u/alice/inbox").unwrap()); + static ACTOR_ID: LazyLock = + LazyLock::new(|| Url::parse("https://example.com/u/alice").unwrap()); + static INBOX_URL: LazyLock = + LazyLock::new(|| Url::parse("https://example.com/u/alice/inbox").unwrap()); #[tokio::test] async fn test_sign() { diff --git a/src/lib.rs b/src/lib.rs index 0a44fc9..b7fe013 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,10 +52,10 @@ where ::Error: From, Datatype: Clone, { - let activity: Activity = serde_json::from_slice(body).map_err(|e| { + let activity: Activity = serde_json::from_slice(body).map_err(|err| { // Attempt to include activity id in error message let id = extract_id(body).ok(); - Error::ParseReceivedActivity(e, id) + Error::ParseReceivedActivity { err, id } })?; data.config.verify_url_and_domain(&activity).await?; let actor = ObjectId::::from(activity.actor().clone()) diff --git a/src/traits.rs b/src/traits.rs index 9976bda..de318ff 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -43,7 +43,7 @@ use url::Url; /// content: String, /// } /// -/// #[async_trait::async_trait] +/// /// impl Object for DbPost { /// type DataType = DbConnection; /// type Kind = Note; @@ -93,7 +93,6 @@ use url::Url; /// } /// /// } -#[async_trait] pub trait Object: Sized + Debug { /// App data type passed to handlers. Must be identical to /// [crate::config::FederationConfigBuilder::app_data] type. @@ -119,23 +118,29 @@ pub trait Object: Sized + Debug { /// Try to read the object with given `id` from local database. /// /// Should return `Ok(None)` if not found. - async fn read_from_id( + fn read_from_id( object_id: Url, data: &Data, - ) -> Result, Self::Error>; + ) -> impl std::future::Future, Self::Error>> + Send; /// Mark remote object as deleted in local database. /// /// Called when a `Delete` activity is received, or if fetch returns a `Tombstone` object. - async fn delete(self, _data: &Data) -> Result<(), Self::Error> { - Ok(()) + fn delete( + self, + _data: &Data, + ) -> impl std::future::Future> + Send { + async { Ok(()) } } /// Convert database type to Activitypub type. /// /// Called when a local object gets fetched by another instance over HTTP, or when an object /// gets sent in an activity. - async fn into_json(self, data: &Data) -> Result; + fn into_json( + self, + data: &Data, + ) -> impl std::future::Future> + Send; /// Verifies that the received object is valid. /// @@ -144,18 +149,21 @@ pub trait Object: Sized + Debug { /// /// It is necessary to use a separate method for this, because it might be used for activities /// like `Delete/Note`, which shouldn't perform any database write for the inner `Note`. - async fn verify( + fn verify( json: &Self::Kind, expected_domain: &Url, data: &Data, - ) -> Result<(), Self::Error>; + ) -> impl std::future::Future> + Send; /// Convert object from ActivityPub type to database type. /// /// Called when an object is received from HTTP fetch or as part of an activity. This method /// should write the received object to database. Note that there is no distinction between /// create and update, so an `upsert` operation should be used. - async fn from_json(json: Self::Kind, data: &Data) -> Result; + fn from_json( + json: Self::Kind, + data: &Data, + ) -> impl std::future::Future> + Send; } /// Handler for receiving incoming activities. @@ -292,7 +300,6 @@ where } /// Trait for federating collections -#[async_trait] pub trait Collection: Sized { /// Actor or object that this collection belongs to type Owner; @@ -305,31 +312,31 @@ pub trait Collection: Sized { type Error; /// Reads local collection from database and returns it as Activitypub JSON. - async fn read_local( + fn read_local( owner: &Self::Owner, data: &Data, - ) -> Result; + ) -> impl std::future::Future> + Send; /// Verifies that the received object is valid. /// /// You should check here that the domain of id matches `expected_domain`. Additionally you /// should perform any application specific checks. - async fn verify( + fn verify( json: &Self::Kind, expected_domain: &Url, data: &Data, - ) -> Result<(), Self::Error>; + ) -> impl std::future::Future> + Send; /// Convert object from ActivityPub type to database type. /// /// Called when an object is received from HTTP fetch or as part of an activity. This method /// should also write the received object to database. Note that there is no distinction /// between create and update, so an `upsert` operation should be used. - async fn from_json( + fn from_json( json: Self::Kind, owner: &Self::Owner, data: &Data, - ) -> Result; + ) -> impl std::future::Future> + Send; } /// Some impls of these traits for use in tests. Dont use this from external crates. @@ -338,7 +345,7 @@ pub trait Collection: Sized { #[doc(hidden)] #[allow(clippy::unwrap_used)] pub mod tests { - use super::{async_trait, ActivityHandler, Actor, Data, Debug, Object, PublicKey, Url}; + use super::{ActivityHandler, Actor, Data, Debug, Object, PublicKey, Url}; use crate::{ error::Error, fetch::object_id::ObjectId, @@ -346,8 +353,9 @@ pub mod tests { protocol::verification::verify_domains_match, }; use activitystreams_kinds::{activity::FollowType, actor::PersonType}; - use once_cell::sync::Lazy; + use async_trait::async_trait; use serde::{Deserialize, Serialize}; + use std::sync::LazyLock; #[derive(Clone)] pub struct DbConnection; @@ -389,9 +397,10 @@ pub mod tests { pub local: bool, } - pub static DB_USER_KEYPAIR: Lazy = Lazy::new(|| generate_actor_keypair().unwrap()); + pub static DB_USER_KEYPAIR: LazyLock = + LazyLock::new(|| generate_actor_keypair().unwrap()); - pub static DB_USER: Lazy = Lazy::new(|| DbUser { + pub static DB_USER: LazyLock = LazyLock::new(|| DbUser { name: String::new(), federation_id: "https://localhost/123".parse().unwrap(), inbox: "https://localhost/123/inbox".parse().unwrap(), @@ -401,7 +410,6 @@ pub mod tests { local: false, }); - #[async_trait] impl Object for DbUser { type DataType = DbConnection; type Kind = Person; @@ -505,7 +513,6 @@ pub mod tests { #[derive(Debug, Clone)] pub struct DbPost {} - #[async_trait] impl Object for DbPost { type DataType = DbConnection; type Kind = Note;