1 use chrono::{DateTime, Utc};
2 use serde::{Deserialize, Serialize};
3 use serde_repr::{Deserialize_repr, Serialize_repr};
4 use std::fmt::Display;
5 use tsync::tsync;
6
7 /// Personal — bootstrapped on signup, one per user, never created/deleted via
8 /// the API. Organization — a team workspace created explicitly.
9 #[derive(Deserialize_repr, Serialize_repr, Debug, Clone, Copy, PartialEq, Eq, Default)]
10 #[repr(u8)]
11 #[tsync]
12 pub enum TenantKind {
13 #[default]
14 Personal = 0,
15 Organization = 1,
16 }
17
18 impl Display for TenantKind {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 match self {
21 TenantKind::Personal => write!(f, "personal"),
22 TenantKind::Organization => write!(f, "organization"),
23 }
24 }
25 }
26
27 /// The current user's role within a tenant, as returned alongside the tenant
28 /// in list/show responses (kept off the bare `Tenant` record itself).
29 #[derive(Deserialize_repr, Serialize_repr, Debug, Clone, Copy, PartialEq, Eq)]
30 #[repr(u8)]
31 #[tsync]
32 pub enum TenantRole {
33 Owner = 0,
34 Admin = 1,
35 Member = 2,
36 }
37
38 impl Display for TenantRole {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 match self {
41 TenantRole::Owner => write!(f, "owner"),
42 TenantRole::Admin => write!(f, "admin"),
43 TenantRole::Member => write!(f, "member"),
44 }
45 }
46 }
47
48 #[derive(Serialize, Deserialize, Debug, Clone)]
49 #[tsync]
50 pub struct TenantProject {
51 pub id: i32,
52 pub name: String,
53 }
54
55 #[derive(Serialize, Deserialize, Debug, Clone)]
56 #[tsync]
57 pub struct Tenant {
58 pub id: i64,
59 pub name: String,
60 pub slug: String,
61 pub kind: TenantKind,
62 /// The requesting user's role in this tenant.
63 pub role: TenantRole,
64 pub projects_count: i64,
65 pub default_project: Option<TenantProject>,
66 /// Whether this is the tenant currently selected for the CLI session.
67 #[serde(default)]
68 pub current: bool,
69 pub created_at: DateTime<Utc>,
70 }
71
72 impl Display for Tenant {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 write!(f, "ID: {}, Name: {}", self.id, self.name)
75 }
76 }
77
78 /// Payload for creating an organization tenant. Personal tenants are
79 /// bootstrapped on signup and can't be created through this endpoint.
80 #[derive(Serialize, Deserialize, Debug, Clone)]
81 #[tsync]
82 pub struct TenantCreate {
83 pub name: String,
84 }
85
86 #[derive(Serialize, Deserialize, Debug, Clone, Default)]
87 #[tsync]
88 pub struct TenantUpdate {
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub name: Option<String>,
91 }
92
93 impl TenantUpdate {
94 pub fn is_empty(&self) -> bool {
95 self.name.is_none()
96 }
97 }
98
99 #[cfg(test)]
100 mod tests {
101 use super::*;
102 use serde_json::json;
103
104 #[test]
105 fn test_tenant_create() {
106 let tenant_create = TenantCreate {
107 name: "Acme".to_owned(),
108 };
109 let json = json!({ "name": "Acme" });
110 assert_eq!(serde_json::to_value(tenant_create).unwrap(), json);
111 }
112
113 #[test]
114 fn test_tenant_update_is_empty() {
115 assert!(TenantUpdate::default().is_empty());
116 assert!(!TenantUpdate {
117 name: Some("Acme".to_owned())
118 }
119 .is_empty());
120 }
121 }