shuttle_common/models/
project.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use strum::{Display, EnumString};
4
5#[cfg(feature = "display")]
6use crossterm::style::Stylize;
7#[cfg(feature = "display")]
8use std::fmt::Write;
9
10use super::deployment::DeploymentState;
11
12#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
13#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
14#[typeshare::typeshare]
15pub struct ProjectCreateRequest {
16    #[cfg_attr(feature = "utoipa", schema(pattern = "^[a-z0-9-]{1,32}$"))]
17    pub name: String,
18}
19
20#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
21#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
22#[typeshare::typeshare]
23pub struct ProjectResponse {
24    pub id: String,
25    /// Display name
26    pub name: String,
27    /// Project owner
28    pub user_id: String,
29    /// Team project belongs to
30    pub team_id: Option<String>,
31    pub created_at: DateTime<Utc>,
32    pub compute_tier: Option<ComputeTier>,
33    /// State of the current deployment if one exists (something has been deployed).
34    pub deployment_state: Option<DeploymentState>,
35    /// URIs where running deployments can be reached
36    pub uris: Vec<String>,
37}
38
39impl ProjectResponse {
40    #[cfg(feature = "display")]
41    pub fn to_string_colored(&self) -> String {
42        let mut s = String::new();
43        writeln!(&mut s, "{}", "Project info:".bold()).unwrap();
44        writeln!(&mut s, "  Project ID: {}", self.id).unwrap();
45        writeln!(&mut s, "  Project Name: {}", self.name).unwrap();
46        writeln!(&mut s, "  Owner: {}", self.user_id).unwrap();
47        writeln!(
48            &mut s,
49            "  Team: {}",
50            self.team_id.as_deref().unwrap_or("N/A")
51        )
52        .unwrap();
53        writeln!(
54            &mut s,
55            "  Created: {}",
56            self.created_at
57                .to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
58        )
59        .unwrap();
60        writeln!(&mut s, "  URIs:").unwrap();
61        for uri in &self.uris {
62            writeln!(&mut s, "    - {uri}").unwrap();
63        }
64
65        s
66    }
67}
68
69#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
70#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
71#[typeshare::typeshare]
72pub struct ProjectListResponse {
73    pub projects: Vec<ProjectResponse>,
74}
75
76/// Set wanted field(s) to Some to update those parts of the project
77#[derive(Debug, Default, Deserialize, Serialize, Clone)]
78#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
79#[typeshare::typeshare]
80pub struct ProjectUpdateRequest {
81    /// Change display name
82    #[cfg_attr(feature = "utoipa", schema(pattern = "^[a-z0-9-]{1,32}$"))]
83    pub name: Option<String>,
84    /// Transfer to other user
85    #[cfg_attr(feature = "utoipa", schema(pattern = "^user_[A-Z0-9]{26}$"))]
86    pub user_id: Option<String>,
87    /// Transfer to a team
88    #[cfg_attr(feature = "utoipa", schema(pattern = "^team_[A-Z0-9]{26}$"))]
89    pub team_id: Option<String>,
90    /// Transfer away from current team
91    pub remove_from_team: Option<bool>,
92    /// Project runtime configuration
93    pub config: Option<serde_json::Value>,
94}
95
96#[derive(Debug, Default, Clone, PartialEq, Eq, Display, Serialize, Deserialize, EnumString)]
97#[serde(rename_all = "lowercase")]
98#[strum(serialize_all = "lowercase")]
99#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
100#[typeshare::typeshare]
101pub enum ComputeTier {
102    #[default]
103    XS,
104    S,
105    M,
106    L,
107    XL,
108    XXL,
109
110    /// Forward compatibility
111    #[cfg(feature = "unknown-variants")]
112    #[doc(hidden)]
113    #[typeshare(skip)]
114    #[serde(untagged, skip_serializing)]
115    #[strum(default, to_string = "Unknown: {0}")]
116    Unknown(String),
117}