Skip to main content

smbcloud_model/
tenant.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use serde_repr::{Deserialize_repr, Serialize_repr};
4use std::fmt::Display;
5use 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]
12pub enum TenantKind {
13    #[default]
14    Personal = 0,
15    Organization = 1,
16}
17
18impl 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]
32pub enum TenantRole {
33    Owner = 0,
34    Admin = 1,
35    Member = 2,
36}
37
38impl 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]
50pub struct TenantProject {
51    pub id: i32,
52    pub name: String,
53}
54
55#[derive(Serialize, Deserialize, Debug, Clone)]
56#[tsync]
57pub 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
72impl 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]
82pub struct TenantCreate {
83    pub name: String,
84}
85
86#[derive(Serialize, Deserialize, Debug, Clone, Default)]
87#[tsync]
88pub struct TenantUpdate {
89    #[serde(skip_serializing_if = "Option::is_none")]
90    pub name: Option<String>,
91}
92
93impl TenantUpdate {
94    pub fn is_empty(&self) -> bool {
95        self.name.is_none()
96    }
97}
98
99#[cfg(test)]
100mod 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}