diff --git a/crates/domain/src/events.rs b/crates/domain/src/events.rs new file mode 100644 index 0000000..729d0f1 --- /dev/null +++ b/crates/domain/src/events.rs @@ -0,0 +1 @@ +// filled in Task 4 diff --git a/crates/domain/src/lib.rs b/crates/domain/src/lib.rs index fa5d165..af60305 100644 --- a/crates/domain/src/lib.rs +++ b/crates/domain/src/lib.rs @@ -1,3 +1,8 @@ pub mod errors; +pub mod events; +pub mod models; +pub mod ports; pub mod value_objects; -// remaining modules added in later tasks + +#[cfg(any(test, feature = "test-helpers"))] +pub mod testing; diff --git a/crates/domain/src/models/api_key.rs b/crates/domain/src/models/api_key.rs new file mode 100644 index 0000000..101029c --- /dev/null +++ b/crates/domain/src/models/api_key.rs @@ -0,0 +1,11 @@ +use chrono::{DateTime, Utc}; +use crate::value_objects::{ApiKeyId, UserId}; + +#[derive(Debug, Clone)] +pub struct ApiKey { + pub id: ApiKeyId, + pub user_id: UserId, + pub key_hash: String, + pub name: String, + pub created_at: DateTime, +} diff --git a/crates/domain/src/models/feed.rs b/crates/domain/src/models/feed.rs new file mode 100644 index 0000000..8cc226f --- /dev/null +++ b/crates/domain/src/models/feed.rs @@ -0,0 +1,40 @@ +use crate::models::{user::User, thought::Thought}; +use crate::value_objects::UserId; + +#[derive(Debug, Clone)] +pub struct UserSummary { + pub id: UserId, + pub username: String, + pub display_name: Option, + pub avatar_url: Option, + pub bio: Option, + pub thought_count: i64, + pub follower_count: i64, + pub following_count: i64, +} + +#[derive(Debug, Clone)] +pub struct FeedEntry { + pub thought: Thought, + pub author: User, + pub like_count: i64, + pub boost_count: i64, + pub reply_count: i64, + pub liked_by_viewer: bool, + pub boosted_by_viewer: bool, +} + +#[derive(Debug, Clone)] +pub struct PageParams { pub page: u64, pub per_page: u64 } +impl PageParams { + pub fn offset(&self) -> i64 { ((self.page.saturating_sub(1)) * self.per_page) as i64 } + pub fn limit(&self) -> i64 { self.per_page as i64 } +} + +#[derive(Debug, Clone)] +pub struct Paginated { + pub items: Vec, + pub total: i64, + pub page: u64, + pub per_page: u64, +} diff --git a/crates/domain/src/models/mod.rs b/crates/domain/src/models/mod.rs new file mode 100644 index 0000000..bb56c47 --- /dev/null +++ b/crates/domain/src/models/mod.rs @@ -0,0 +1,9 @@ +pub mod api_key; +pub mod feed; +pub mod notification; +pub mod remote_actor; +pub mod social; +pub mod tag; +pub mod thought; +pub mod top_friend; +pub mod user; diff --git a/crates/domain/src/models/notification.rs b/crates/domain/src/models/notification.rs new file mode 100644 index 0000000..6db91b8 --- /dev/null +++ b/crates/domain/src/models/notification.rs @@ -0,0 +1,24 @@ +use chrono::{DateTime, Utc}; +use crate::value_objects::{NotificationId, UserId, ThoughtId}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum NotificationType { Like, Boost, Follow, Mention, Reply } +impl NotificationType { + pub fn from_str(s: &str) -> Self { + match s { "like" => Self::Like, "boost" => Self::Boost, "follow" => Self::Follow, "mention" => Self::Mention, _ => Self::Reply } + } + pub fn as_str(&self) -> &str { + match self { Self::Like => "like", Self::Boost => "boost", Self::Follow => "follow", Self::Mention => "mention", Self::Reply => "reply" } + } +} + +#[derive(Debug, Clone)] +pub struct Notification { + pub id: NotificationId, + pub user_id: UserId, + pub notification_type: NotificationType, + pub from_user_id: Option, + pub thought_id: Option, + pub read: bool, + pub created_at: DateTime, +} diff --git a/crates/domain/src/models/remote_actor.rs b/crates/domain/src/models/remote_actor.rs new file mode 100644 index 0000000..f8d439c --- /dev/null +++ b/crates/domain/src/models/remote_actor.rs @@ -0,0 +1,12 @@ +use chrono::{DateTime, Utc}; + +#[derive(Debug, Clone)] +pub struct RemoteActor { + pub url: String, + pub handle: String, + pub display_name: Option, + pub inbox_url: String, + pub shared_inbox_url: Option, + pub public_key: String, + pub last_fetched_at: DateTime, +} diff --git a/crates/domain/src/models/social.rs b/crates/domain/src/models/social.rs new file mode 100644 index 0000000..82cb53d --- /dev/null +++ b/crates/domain/src/models/social.rs @@ -0,0 +1,47 @@ +use chrono::{DateTime, Utc}; +use crate::value_objects::{UserId, ThoughtId, LikeId, BoostId}; + +#[derive(Debug, Clone)] +pub struct Like { + pub id: LikeId, + pub user_id: UserId, + pub thought_id: ThoughtId, + pub ap_id: Option, + pub created_at: DateTime, +} + +#[derive(Debug, Clone)] +pub struct Boost { + pub id: BoostId, + pub user_id: UserId, + pub thought_id: ThoughtId, + pub ap_id: Option, + pub created_at: DateTime, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum FollowState { Pending, Accepted, Rejected } +impl FollowState { + pub fn from_str(s: &str) -> Self { + match s { "pending" => Self::Pending, "rejected" => Self::Rejected, _ => Self::Accepted } + } + pub fn as_str(&self) -> &str { + match self { Self::Pending => "pending", Self::Accepted => "accepted", Self::Rejected => "rejected" } + } +} + +#[derive(Debug, Clone)] +pub struct Follow { + pub follower_id: UserId, + pub following_id: UserId, + pub state: FollowState, + pub ap_id: Option, + pub created_at: DateTime, +} + +#[derive(Debug, Clone)] +pub struct Block { + pub blocker_id: UserId, + pub blocked_id: UserId, + pub created_at: DateTime, +} diff --git a/crates/domain/src/models/tag.rs b/crates/domain/src/models/tag.rs new file mode 100644 index 0000000..9c78590 --- /dev/null +++ b/crates/domain/src/models/tag.rs @@ -0,0 +1,2 @@ +#[derive(Debug, Clone)] +pub struct Tag { pub id: i32, pub name: String } diff --git a/crates/domain/src/models/thought.rs b/crates/domain/src/models/thought.rs new file mode 100644 index 0000000..56a1869 --- /dev/null +++ b/crates/domain/src/models/thought.rs @@ -0,0 +1,45 @@ +use chrono::{DateTime, Utc}; +use crate::value_objects::{ThoughtId, UserId, Content}; + +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +pub enum Visibility { + Public, Followers, Unlisted, Direct, +} +impl Visibility { + pub fn from_str(s: &str) -> Self { + match s { "followers" => Self::Followers, "unlisted" => Self::Unlisted, "direct" => Self::Direct, _ => Self::Public } + } + pub fn as_str(&self) -> &str { + match self { Self::Public => "public", Self::Followers => "followers", Self::Unlisted => "unlisted", Self::Direct => "direct" } + } +} + +#[derive(Debug, Clone)] +pub struct Thought { + pub id: ThoughtId, + pub user_id: UserId, + pub content: Content, + pub in_reply_to_id: Option, + pub in_reply_to_url: Option, + pub ap_id: Option, + pub visibility: Visibility, + pub content_warning: Option, + pub sensitive: bool, + pub local: bool, + pub created_at: DateTime, + pub updated_at: Option>, +} + +impl Thought { + pub fn new_local( + id: ThoughtId, user_id: UserId, content: Content, + in_reply_to_id: Option, visibility: Visibility, + content_warning: Option, sensitive: bool, + ) -> Self { + Self { + id, user_id, content, in_reply_to_id, in_reply_to_url: None, ap_id: None, + visibility, content_warning, sensitive, local: true, + created_at: Utc::now(), updated_at: None, + } + } +} diff --git a/crates/domain/src/models/top_friend.rs b/crates/domain/src/models/top_friend.rs new file mode 100644 index 0000000..d0d3279 --- /dev/null +++ b/crates/domain/src/models/top_friend.rs @@ -0,0 +1,4 @@ +use crate::value_objects::UserId; + +#[derive(Debug, Clone)] +pub struct TopFriend { pub user_id: UserId, pub friend_id: UserId, pub position: i16 } diff --git a/crates/domain/src/models/user.rs b/crates/domain/src/models/user.rs new file mode 100644 index 0000000..0b19f98 --- /dev/null +++ b/crates/domain/src/models/user.rs @@ -0,0 +1,35 @@ +use chrono::{DateTime, Utc}; +use crate::value_objects::{UserId, Username, Email, PasswordHash}; + +#[derive(Debug, Clone)] +pub struct User { + pub id: UserId, + pub username: Username, + pub email: Email, + pub password_hash: PasswordHash, + pub display_name: Option, + pub bio: Option, + pub avatar_url: Option, + pub header_url: Option, + pub custom_css: Option, + pub local: bool, + pub ap_id: Option, + pub inbox_url: Option, + pub public_key: Option, + pub private_key: Option, + pub created_at: DateTime, + pub updated_at: DateTime, +} + +impl User { + pub fn new_local(id: UserId, username: Username, email: Email, password_hash: PasswordHash) -> Self { + let now = Utc::now(); + Self { + id, username, email, password_hash, + display_name: None, bio: None, avatar_url: None, header_url: None, + custom_css: None, local: true, ap_id: None, inbox_url: None, + public_key: None, private_key: None, + created_at: now, updated_at: now, + } + } +} diff --git a/crates/domain/src/ports.rs b/crates/domain/src/ports.rs new file mode 100644 index 0000000..729d0f1 --- /dev/null +++ b/crates/domain/src/ports.rs @@ -0,0 +1 @@ +// filled in Task 4 diff --git a/crates/domain/src/testing.rs b/crates/domain/src/testing.rs new file mode 100644 index 0000000..729d0f1 --- /dev/null +++ b/crates/domain/src/testing.rs @@ -0,0 +1 @@ +// filled in Task 4