Jump to…
snowinitial commitqoxwzsukwmkx1mo
1//! Row types shared across crates.
2//!
3//! Only the types more than one crate needs live here. Query-specific shapes
4//! stay next to their queries.
5
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use uuid::Uuid;
9
10#[derive(Debug, Clone, sqlx::FromRow)]
11pub struct User {
12 pub id: Uuid,
13 pub subject: String,
14 pub handle: String,
15 pub display_name: Option<String>,
16 pub email: Option<String>,
17 pub avatar_url: Option<String>,
18 pub is_admin: bool,
19 pub created_at: DateTime<Utc>,
20}
21
22impl User {
23 /// What to show in the UI: the display name if we have one, else the handle.
24 pub fn label(&self) -> &str {
25 self.display_name
26 .as_deref()
27 .filter(|s| !s.trim().is_empty())
28 .unwrap_or(&self.handle)
29 }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, sqlx::Type, Serialize, Deserialize)]
33#[sqlx(type_name = "owner_kind", rename_all = "lowercase")]
34#[serde(rename_all = "lowercase")]
35pub enum OwnerKind {
36 User,
37 Org,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, sqlx::Type, Serialize, Deserialize)]
41#[sqlx(type_name = "visibility", rename_all = "lowercase")]
42#[serde(rename_all = "lowercase")]
43pub enum Visibility {
44 Public,
45 Private,
46}
47
48/// Repository access level.
49///
50/// Ordered least- to most-privileged; `PartialOrd` is derived from that order so
51/// permission checks read as `role >= RepoRole::Write`.
52#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sqlx::Type, Serialize, Deserialize)]
53#[sqlx(type_name = "repo_role", rename_all = "lowercase")]
54#[serde(rename_all = "lowercase")]
55pub enum RepoRole {
56 Read,
57 Write,
58 Maintain,
59 Admin,
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, sqlx::Type, Serialize, Deserialize)]
63#[sqlx(type_name = "org_role", rename_all = "lowercase")]
64#[serde(rename_all = "lowercase")]
65pub enum OrgRole {
66 Member,
67 Admin,
68}
69
70#[derive(Debug, Clone, Copy, PartialEq, Eq, sqlx::Type, Serialize, Deserialize)]
71#[sqlx(type_name = "change_state", rename_all = "lowercase")]
72#[serde(rename_all = "lowercase")]
73pub enum ChangeState {
74 Draft,
75 Open,
76 Merged,
77 Abandoned,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq, sqlx::Type, Serialize, Deserialize)]
81#[sqlx(type_name = "issue_state", rename_all = "lowercase")]
82#[serde(rename_all = "lowercase")]
83pub enum IssueState {
84 Open,
85 Closed,
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, sqlx::Type, Serialize, Deserialize)]
89#[sqlx(type_name = "anchor_state", rename_all = "lowercase")]
90#[serde(rename_all = "lowercase")]
91pub enum AnchorState {
92 Current,
93 Outdated,
94 Orphaned,
95}
96
97#[derive(Debug, Clone, sqlx::FromRow)]
98pub struct Repo {
99 pub id: Uuid,
100 pub owner_kind: OwnerKind,
101 pub owner_user_id: Option<Uuid>,
102 pub owner_org_id: Option<Uuid>,
103 pub name: String,
104 pub description: Option<String>,
105 pub visibility: Visibility,
106 pub default_bookmark: String,
107 pub fork_of_repo_id: Option<Uuid>,
108 pub size_bytes: i64,
109 pub pushed_at: Option<DateTime<Utc>>,
110 pub archived: bool,
111 pub created_at: DateTime<Utc>,
112}
113
114impl Repo {
115 pub fn is_public(&self) -> bool {
116 matches!(self.visibility, Visibility::Public)
117 }
118
119 /// Whether this repo has ever received a push. An empty repo shows clone
120 /// instructions rather than a file listing.
121 pub fn is_empty(&self) -> bool {
122 self.pushed_at.is_none()
123 }
124}
125
126#[derive(Debug, Clone)]
127pub struct Session {
128 pub id: Uuid,
129 pub user_id: Uuid,
130 pub expires_at: DateTime<Utc>,
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 #[test]
138 fn repo_roles_are_ordered_by_privilege() {
139 // Permission checks rely on this ordering, so it is worth pinning.
140 assert!(RepoRole::Read < RepoRole::Write);
141 assert!(RepoRole::Write < RepoRole::Maintain);
142 assert!(RepoRole::Maintain < RepoRole::Admin);
143 assert_eq!(
144 RepoRole::Admin.max(RepoRole::Read),
145 RepoRole::Admin,
146 "effective role is the maximum of the roles a user holds"
147 );
148 }
149
150 #[test]
151 fn user_label_falls_back_to_handle() {
152 let mut u = User {
153 id: Uuid::nil(),
154 subject: "s".into(),
155 handle: "alice".into(),
156 display_name: None,
157 email: None,
158 avatar_url: None,
159 is_admin: false,
160 created_at: Utc::now(),
161 };
162 assert_eq!(u.label(), "alice");
163 u.display_name = Some(" ".into());
164 assert_eq!(u.label(), "alice", "blank display names must not be shown");
165 u.display_name = Some("Alice Liddell".into());
166 assert_eq!(u.label(), "Alice Liddell");
167 }
168}

168 lines · Rust