refactor: replace long arg lists with input/config structs and builder

- Thought::new_local → NewThought struct (7 args → 1)
- UserWriter::update_profile → UpdateProfileInput struct (6 args → 2)
- update_profile use case → UpdateProfileInput (8 args → 3)
- ActivityPubService::new → builder pattern (9 args → 5 required + 4 optional setters)
- accept_note → AcceptNoteInput struct (8 args → 1)
- ThoughtNote::new_public → ThoughtNoteInput struct (8 args → 1)

Remove all #[allow(clippy::too_many_arguments)] annotations.
This commit is contained in:
2026-05-17 12:25:53 +02:00
parent 2f5c89c381
commit d56d34cc27
31 changed files with 449 additions and 450 deletions

View File

@@ -5,7 +5,7 @@ use async_trait::async_trait;
use domain::{
errors::DomainError,
events::DomainEvent,
models::thought::{Thought, Visibility},
models::thought::{NewThought, Thought, Visibility},
models::user::User,
testing::TestStore,
value_objects::*,
@@ -92,15 +92,15 @@ fn alice() -> User {
}
fn local_thought(author_id: UserId) -> Thought {
Thought::new_local(
ThoughtId::new(),
author_id,
Content::new_local("hello").unwrap(),
None,
Visibility::Public,
None,
false,
)
Thought::new_local(NewThought {
id: ThoughtId::new(),
user_id: author_id,
content: Content::new_local("hello").unwrap(),
in_reply_to_id: None,
visibility: Visibility::Public,
content_warning: None,
sensitive: false,
})
}
fn svc(store: &TestStore, spy: Arc<SpyPort>) -> FederationEventService {
@@ -275,15 +275,15 @@ async fn boost_of_remote_thought_announces_remote_ap_id() {
async fn direct_thought_created_does_not_broadcast() {
let store = TestStore::default();
let alice = alice();
let thought = Thought::new_local(
ThoughtId::new(),
alice.id.clone(),
Content::new_local("private").unwrap(),
None,
Visibility::Direct,
None,
false,
);
let thought = Thought::new_local(NewThought {
id: ThoughtId::new(),
user_id: alice.id.clone(),
content: Content::new_local("private").unwrap(),
in_reply_to_id: None,
visibility: Visibility::Direct,
content_warning: None,
sensitive: false,
});
store.users.lock().unwrap().push(alice.clone());
store.thoughts.lock().unwrap().push(thought.clone());
@@ -304,15 +304,15 @@ async fn direct_thought_created_does_not_broadcast() {
async fn followers_only_thought_does_not_broadcast_publicly() {
let store = TestStore::default();
let alice = alice();
let thought = Thought::new_local(
ThoughtId::new(),
alice.id.clone(),
Content::new_local("for followers").unwrap(),
None,
Visibility::Followers,
None,
false,
);
let thought = Thought::new_local(NewThought {
id: ThoughtId::new(),
user_id: alice.id.clone(),
content: Content::new_local("for followers").unwrap(),
in_reply_to_id: None,
visibility: Visibility::Followers,
content_warning: None,
sensitive: false,
});
store.users.lock().unwrap().push(alice.clone());
store.thoughts.lock().unwrap().push(thought.clone());

View File

@@ -2,7 +2,7 @@ use super::*;
use domain::{
models::{
notification::NotificationKind,
thought::{Thought, Visibility},
thought::{NewThought, Thought, Visibility},
user::User,
},
testing::TestStore,
@@ -24,15 +24,15 @@ async fn like_creates_notification_for_thought_author() {
let store = TestStore::default();
let alice = alice();
let bob_id = UserId::new();
let thought = Thought::new_local(
ThoughtId::new(),
alice.id.clone(),
Content::new_local("hello").unwrap(),
None,
Visibility::Public,
None,
false,
);
let thought = Thought::new_local(NewThought {
id: ThoughtId::new(),
user_id: alice.id.clone(),
content: Content::new_local("hello").unwrap(),
in_reply_to_id: None,
visibility: Visibility::Public,
content_warning: None,
sensitive: false,
});
store.thoughts.lock().unwrap().push(thought.clone());
let svc = NotificationEventService {
thoughts: Arc::new(store.clone()),
@@ -54,15 +54,15 @@ async fn like_creates_notification_for_thought_author() {
async fn self_like_creates_no_notification() {
let store = TestStore::default();
let alice = alice();
let thought = Thought::new_local(
ThoughtId::new(),
alice.id.clone(),
Content::new_local("hello").unwrap(),
None,
Visibility::Public,
None,
false,
);
let thought = Thought::new_local(NewThought {
id: ThoughtId::new(),
user_id: alice.id.clone(),
content: Content::new_local("hello").unwrap(),
in_reply_to_id: None,
visibility: Visibility::Public,
content_warning: None,
sensitive: false,
});
store.thoughts.lock().unwrap().push(thought.clone());
let svc = NotificationEventService {
thoughts: Arc::new(store.clone()),
@@ -103,15 +103,15 @@ async fn reply_creates_notification_for_original_author() {
let store = TestStore::default();
let alice = alice();
let bob_id = UserId::new();
let original = Thought::new_local(
ThoughtId::new(),
alice.id.clone(),
Content::new_local("original").unwrap(),
None,
Visibility::Public,
None,
false,
);
let original = Thought::new_local(NewThought {
id: ThoughtId::new(),
user_id: alice.id.clone(),
content: Content::new_local("original").unwrap(),
in_reply_to_id: None,
visibility: Visibility::Public,
content_warning: None,
sensitive: false,
});
store.thoughts.lock().unwrap().push(original.clone());
let svc = NotificationEventService {
thoughts: Arc::new(store.clone()),
@@ -133,15 +133,15 @@ async fn reply_creates_notification_for_original_author() {
async fn self_reply_creates_no_notification() {
let store = TestStore::default();
let alice = alice();
let original = Thought::new_local(
ThoughtId::new(),
alice.id.clone(),
Content::new_local("original").unwrap(),
None,
Visibility::Public,
None,
false,
);
let original = Thought::new_local(NewThought {
id: ThoughtId::new(),
user_id: alice.id.clone(),
content: Content::new_local("original").unwrap(),
in_reply_to_id: None,
visibility: Visibility::Public,
content_warning: None,
sensitive: false,
});
store.thoughts.lock().unwrap().push(original.clone());
let svc = NotificationEventService {
thoughts: Arc::new(store.clone()),
@@ -161,15 +161,15 @@ async fn self_reply_creates_no_notification() {
async fn self_boost_creates_no_notification() {
let store = TestStore::default();
let alice = alice();
let thought = Thought::new_local(
ThoughtId::new(),
alice.id.clone(),
Content::new_local("hello").unwrap(),
None,
Visibility::Public,
None,
false,
);
let thought = Thought::new_local(NewThought {
id: ThoughtId::new(),
user_id: alice.id.clone(),
content: Content::new_local("hello").unwrap(),
in_reply_to_id: None,
visibility: Visibility::Public,
content_warning: None,
sensitive: false,
});
store.thoughts.lock().unwrap().push(thought.clone());
let svc = NotificationEventService {
thoughts: Arc::new(store.clone()),

View File

@@ -95,14 +95,7 @@ impl ActivityPubRepository for TestApRepo {
}
async fn accept_note(
&self,
_ap_id: &str,
_author_id: &UserId,
_content: &str,
_published: chrono::DateTime<chrono::Utc>,
_sensitive: bool,
_content_warning: Option<String>,
_visibility: &str,
_in_reply_to: Option<&str>,
_input: activitypub_base::AcceptNoteInput<'_>,
) -> Result<ThoughtId, DomainError> {
Ok(ThoughtId::from_uuid(uuid::Uuid::new_v4()))
}

View File

@@ -5,7 +5,7 @@ use domain::{
events::DomainEvent,
models::{
feed::{PageParams, Paginated, UserSummary},
user::User,
user::{UpdateProfileInput, User},
},
ports::{AuthService, GeneratedToken, PasswordHasher, UserReader, UserWriter},
testing::{NoOpEventPublisher, TestStore},
@@ -56,22 +56,9 @@ impl UserWriter for ConflictOnSaveStore {
async fn update_profile(
&self,
user_id: &UserId,
display_name: Option<String>,
bio: Option<String>,
avatar_url: Option<String>,
header_url: Option<String>,
custom_css: Option<String>,
input: UpdateProfileInput,
) -> Result<(), DomainError> {
self.0
.update_profile(
user_id,
display_name,
bio,
avatar_url,
header_url,
custom_css,
)
.await
self.0.update_profile(user_id, input).await
}
}
@@ -114,22 +101,9 @@ impl UserWriter for EmailConflictOnSaveStore {
async fn update_profile(
&self,
user_id: &UserId,
display_name: Option<String>,
bio: Option<String>,
avatar_url: Option<String>,
header_url: Option<String>,
custom_css: Option<String>,
input: UpdateProfileInput,
) -> Result<(), DomainError> {
self.0
.update_profile(
user_id,
display_name,
bio,
avatar_url,
header_url,
custom_css,
)
.await
self.0.update_profile(user_id, input).await
}
}

View File

@@ -3,7 +3,10 @@ const MAX_TOP_FRIENDS: usize = 8;
use domain::{
errors::DomainError,
events::DomainEvent,
models::{top_friend::TopFriend, user::User},
models::{
top_friend::TopFriend,
user::{UpdateProfileInput, User},
},
ports::{EventPublisher, TopFriendRepository, UserReader, UserWriter},
value_objects::{UserId, Username},
};
@@ -41,27 +44,13 @@ pub async fn get_user_by_id_or_username(
}
}
#[allow(clippy::too_many_arguments)]
pub async fn update_profile(
users: &dyn UserWriter,
events: &dyn EventPublisher,
user_id: &UserId,
display_name: Option<String>,
bio: Option<String>,
avatar_url: Option<String>,
header_url: Option<String>,
custom_css: Option<String>,
input: UpdateProfileInput,
) -> Result<(), DomainError> {
users
.update_profile(
user_id,
display_name,
bio,
avatar_url,
header_url,
custom_css,
)
.await?;
users.update_profile(user_id, input).await?;
events
.publish(&DomainEvent::ProfileUpdated {
user_id: user_id.clone(),

View File

@@ -1,7 +1,7 @@
use super::*;
use domain::{
models::{
thought::{Thought, Visibility},
thought::{NewThought, Thought, Visibility},
user::User,
},
testing::TestStore,
@@ -22,15 +22,19 @@ async fn like_and_unlike() {
let store = TestStore::default();
let alice = user("alice");
let tid = ThoughtId::new();
store.thoughts.lock().unwrap().push(Thought::new_local(
tid.clone(),
alice.id.clone(),
Content::new_local("hi").unwrap(),
None,
Visibility::Public,
None,
false,
));
store
.thoughts
.lock()
.unwrap()
.push(Thought::new_local(NewThought {
id: tid.clone(),
user_id: alice.id.clone(),
content: Content::new_local("hi").unwrap(),
in_reply_to_id: None,
visibility: Visibility::Public,
content_warning: None,
sensitive: false,
}));
like_thought(&store, &store, &alice.id, &tid).await.unwrap();
assert_eq!(store.likes.lock().unwrap().len(), 1);
unlike_thought(&store, &store, &alice.id, &tid)

View File

@@ -3,7 +3,7 @@ use domain::{
events::DomainEvent,
models::{
feed::{EngagementStats, FeedEntry},
thought::{Thought, Visibility},
thought::{NewThought, Thought, Visibility},
},
ports::{
EngagementRepository, EventPublisher, OutboxWriter, TagRepository, ThoughtRepository,
@@ -46,15 +46,15 @@ pub async fn create_thought(
Some("direct") => Visibility::Direct,
_ => Visibility::Public,
};
let thought = Thought::new_local(
ThoughtId::new(),
input.user_id,
content.clone(),
input.in_reply_to_id.clone(),
let thought = Thought::new_local(NewThought {
id: ThoughtId::new(),
user_id: input.user_id,
content: content.clone(),
in_reply_to_id: input.in_reply_to_id.clone(),
visibility,
input.content_warning,
input.sensitive,
);
content_warning: input.content_warning,
sensitive: input.sensitive,
});
thoughts.save(&thought).await?;
// Extract and attach hashtags from content.

View File

@@ -222,7 +222,7 @@ async fn create_reply_sets_in_reply_to_id() {
// enrichment_tests (combined from second cfg(test) block)
use domain::models::thought::{Thought, Visibility};
use domain::models::thought::{NewThought, Thought, Visibility};
use domain::ports::{ThoughtRepository, UserWriter};
fn make_user() -> User {
@@ -235,15 +235,15 @@ fn make_user() -> User {
}
fn make_thought(user_id: UserId) -> Thought {
Thought::new_local(
ThoughtId::new(),
Thought::new_local(NewThought {
id: ThoughtId::new(),
user_id,
Content::new_local(String::from("hello")).unwrap(),
None,
Visibility::Public,
None,
false,
)
content: Content::new_local(String::from("hello")).unwrap(),
in_reply_to_id: None,
visibility: Visibility::Public,
content_warning: None,
sensitive: false,
})
}
#[tokio::test]
@@ -287,15 +287,15 @@ async fn get_thread_views_batches_correctly() {
<TestStore as ThoughtRepository>::save(&store, &root)
.await
.unwrap();
let reply = Thought::new_local(
ThoughtId::new(),
user.id.clone(),
Content::new_local(String::from("reply")).unwrap(),
Some(root.id.clone()),
Visibility::Public,
None,
false,
);
let reply = Thought::new_local(NewThought {
id: ThoughtId::new(),
user_id: user.id.clone(),
content: Content::new_local(String::from("reply")).unwrap(),
in_reply_to_id: Some(root.id.clone()),
visibility: Visibility::Public,
content_warning: None,
sensitive: false,
});
<TestStore as ThoughtRepository>::save(&store, &reply)
.await
.unwrap();