avina_wire/user/
project.rs

1use std::fmt::Display;
2
3use rand::{
4    Rng,
5    distr::{Distribution, StandardUniform},
6};
7use serde::{Deserialize, Serialize};
8#[cfg(feature = "sqlx")]
9use sqlx::{self, FromRow};
10use strum::EnumIter;
11#[cfg(feature = "tabled")]
12use tabled::Tabled;
13
14use crate::{
15    error::ConversionError, resources::FlavorGroupMinimal, user::UserMinimal,
16};
17
18#[cfg_attr(feature = "sqlx", derive(FromRow))]
19#[cfg_attr(feature = "tabled", derive(Tabled))]
20#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
21pub struct Project {
22    #[cfg_attr(feature = "sqlx", sqlx(try_from = "i32"))]
23    pub id: u32,
24    pub name: String,
25    pub openstack_id: String, // UUIDv4 without dashes
26    #[cfg_attr(feature = "sqlx", sqlx(try_from = "u32"))]
27    pub user_class: UserClass,
28}
29
30impl Display for Project {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        f.write_str(&format!("Project(id={}, name={}", self.id, self.name))
33    }
34}
35
36impl PartialEq<ProjectMinimal> for Project {
37    fn eq(&self, other: &ProjectMinimal) -> bool {
38        self.id == other.id
39            && self.name == other.name
40            && self.user_class == other.user_class
41    }
42}
43
44impl PartialEq<ProjectDetailed> for Project {
45    fn eq(&self, other: &ProjectDetailed) -> bool {
46        self.id == other.id
47            && self.name == other.name
48            && self.openstack_id == other.openstack_id
49            && self.user_class == other.user_class
50    }
51}
52
53#[cfg_attr(feature = "sqlx", derive(FromRow))]
54#[cfg_attr(feature = "tabled", derive(Tabled))]
55#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
56pub struct ProjectMinimal {
57    #[cfg_attr(
58        feature = "sqlx",
59        sqlx(try_from = "i32", rename = "project__id")
60    )]
61    pub id: u32,
62    #[cfg_attr(feature = "sqlx", sqlx(rename = "project__name"))]
63    pub name: String,
64    #[cfg_attr(
65        feature = "sqlx",
66        sqlx(try_from = "u32", rename = "project__user_class")
67    )]
68    pub user_class: UserClass,
69}
70
71impl PartialEq<Project> for ProjectMinimal {
72    fn eq(&self, other: &Project) -> bool {
73        self.id == other.id
74            && self.name == other.name
75            && self.user_class == other.user_class
76    }
77}
78
79impl PartialEq<ProjectDetailed> for ProjectMinimal {
80    fn eq(&self, other: &ProjectDetailed) -> bool {
81        self.id == other.id
82            && self.name == other.name
83            && self.user_class == other.user_class
84    }
85}
86
87impl Display for ProjectMinimal {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        f.write_str(&format!("Project(id={}, name={})", self.id, self.name))
90    }
91}
92
93#[cfg_attr(feature = "tabled", derive(Tabled))]
94#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
95pub struct ProjectDetailed {
96    pub id: u32,
97    pub name: String,
98    pub openstack_id: String, // UUIDv4 without dashes
99    pub user_class: UserClass,
100    // TODO: rethink list output in detailed structs:
101    // maybe we could have only the first few entries followed by ...
102    // in the output
103    #[cfg_attr(feature = "tabled", tabled(skip))]
104    pub users: Vec<UserMinimal>,
105    #[cfg_attr(feature = "tabled", tabled(skip))]
106    pub flavor_groups: Vec<FlavorGroupMinimal>,
107}
108
109impl PartialEq<ProjectMinimal> for ProjectDetailed {
110    fn eq(&self, other: &ProjectMinimal) -> bool {
111        self.id == other.id
112            && self.name == other.name
113            && self.user_class == other.user_class
114    }
115}
116
117impl PartialEq<Project> for ProjectDetailed {
118    fn eq(&self, other: &Project) -> bool {
119        self.id == other.id
120            && self.name == other.name
121            && self.openstack_id == other.openstack_id
122            && self.user_class == other.user_class
123    }
124}
125
126impl Display for ProjectDetailed {
127    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
128        f.write_str(&format!("Project(id={}, name={}", self.id, self.name))
129    }
130}
131
132#[cfg_attr(feature = "tabled", derive(Tabled))]
133#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
134#[serde(untagged)]
135pub enum ProjectRetrieved {
136    Detailed(ProjectDetailed),
137    Normal(Project),
138}
139
140#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
141pub struct ProjectListParams {
142    pub all: Option<bool>,
143    pub userclass: Option<UserClass>,
144}
145
146#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
147pub struct ProjectCreateData {
148    pub name: String,
149    pub openstack_id: String, // UUIDv4
150    #[serde(skip_serializing_if = "Option::is_none")]
151    pub user_class: Option<UserClass>,
152}
153
154impl ProjectCreateData {
155    pub fn new(name: String, openstack_id: String) -> Self {
156        Self {
157            name,
158            openstack_id,
159            user_class: None,
160        }
161    }
162}
163
164#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
165pub struct ProjectModifyData {
166    // TODO: why again is this here? since this is already a URL parameter
167    pub id: u32,
168
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub name: Option<String>,
171    #[serde(skip_serializing_if = "Option::is_none")]
172    pub openstack_id: Option<String>, // UUIDv4
173    #[serde(skip_serializing_if = "Option::is_none")]
174    pub user_class: Option<UserClass>,
175}
176
177impl ProjectModifyData {
178    pub fn new(id: u32) -> Self {
179        Self {
180            id,
181            name: None,
182            openstack_id: None,
183            user_class: None,
184        }
185    }
186}
187
188#[derive(
189    clap::ValueEnum,
190    Hash,
191    PartialEq,
192    Eq,
193    Clone,
194    EnumIter,
195    Debug,
196    Deserialize,
197    Serialize,
198    Copy,
199)]
200#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
201pub enum UserClass {
202    NA = 0,
203    UC1 = 1,
204    UC2 = 2,
205    UC3 = 3,
206    UC4 = 4,
207    UC5 = 5,
208    UC6 = 6,
209}
210
211impl Display for UserClass {
212    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
213        write!(f, "{self:?}")
214    }
215}
216
217impl TryFrom<u32> for UserClass {
218    type Error = ConversionError;
219
220    fn try_from(u: u32) -> Result<Self, Self::Error> {
221        match u {
222            0 => Ok(UserClass::NA),
223            1 => Ok(UserClass::UC1),
224            2 => Ok(UserClass::UC2),
225            3 => Ok(UserClass::UC3),
226            4 => Ok(UserClass::UC4),
227            5 => Ok(UserClass::UC5),
228            6 => Ok(UserClass::UC6),
229            _ => Err(ConversionError(
230                format!("Unknown user class value: {u}").to_string(),
231            )),
232        }
233    }
234}
235
236impl Distribution<UserClass> for StandardUniform {
237    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> UserClass {
238        match rng.random_range(0..6) {
239            0 => UserClass::NA,
240            1 => UserClass::UC1,
241            2 => UserClass::UC2,
242            3 => UserClass::UC3,
243            4 => UserClass::UC4,
244            5 => UserClass::UC5,
245            6 => UserClass::UC6,
246            _ => UserClass::NA,
247        }
248    }
249}