use std::fmt::Display;
use rand::{
Rng,
distr::{Distribution, StandardUniform},
};
use serde::{Deserialize, Serialize};
#[cfg(feature = "sqlx")]
use sqlx::{self, FromRow};
use strum::EnumIter;
#[cfg(feature = "tabled")]
use tabled::Tabled;
use crate::{
error::ConversionError, resources::FlavorGroupMinimal, user::UserMinimal,
};
#[cfg_attr(feature = "sqlx", derive(FromRow))]
#[cfg_attr(feature = "tabled", derive(Tabled))]
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct Project {
#[cfg_attr(feature = "sqlx", sqlx(try_from = "i32"))]
pub id: u32,
pub name: String,
pub openstack_id: String, #[cfg_attr(feature = "sqlx", sqlx(try_from = "u32"))]
pub user_class: UserClass,
}
impl Display for Project {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&format!("Project(id={}, name={}", self.id, self.name))
}
}
impl PartialEq<ProjectMinimal> for Project {
fn eq(&self, other: &ProjectMinimal) -> bool {
self.id == other.id
&& self.name == other.name
&& self.user_class == other.user_class
}
}
impl PartialEq<ProjectDetailed> for Project {
fn eq(&self, other: &ProjectDetailed) -> bool {
self.id == other.id
&& self.name == other.name
&& self.openstack_id == other.openstack_id
&& self.user_class == other.user_class
}
}
#[cfg_attr(feature = "sqlx", derive(FromRow))]
#[cfg_attr(feature = "tabled", derive(Tabled))]
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct ProjectMinimal {
#[cfg_attr(
feature = "sqlx",
sqlx(try_from = "i32", rename = "project__id")
)]
pub id: u32,
#[cfg_attr(feature = "sqlx", sqlx(rename = "project__name"))]
pub name: String,
#[cfg_attr(
feature = "sqlx",
sqlx(try_from = "u32", rename = "project__user_class")
)]
pub user_class: UserClass,
}
impl PartialEq<Project> for ProjectMinimal {
fn eq(&self, other: &Project) -> bool {
self.id == other.id
&& self.name == other.name
&& self.user_class == other.user_class
}
}
impl PartialEq<ProjectDetailed> for ProjectMinimal {
fn eq(&self, other: &ProjectDetailed) -> bool {
self.id == other.id
&& self.name == other.name
&& self.user_class == other.user_class
}
}
impl Display for ProjectMinimal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&format!("Project(id={}, name={})", self.id, self.name))
}
}
#[cfg_attr(feature = "tabled", derive(Tabled))]
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
pub struct ProjectDetailed {
pub id: u32,
pub name: String,
pub openstack_id: String, pub user_class: UserClass,
#[cfg_attr(feature = "tabled", tabled(skip))]
pub users: Vec<UserMinimal>,
#[cfg_attr(feature = "tabled", tabled(skip))]
pub flavor_groups: Vec<FlavorGroupMinimal>,
}
impl PartialEq<ProjectMinimal> for ProjectDetailed {
fn eq(&self, other: &ProjectMinimal) -> bool {
self.id == other.id
&& self.name == other.name
&& self.user_class == other.user_class
}
}
impl PartialEq<Project> for ProjectDetailed {
fn eq(&self, other: &Project) -> bool {
self.id == other.id
&& self.name == other.name
&& self.openstack_id == other.openstack_id
&& self.user_class == other.user_class
}
}
impl Display for ProjectDetailed {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&format!("Project(id={}, name={}", self.id, self.name))
}
}
#[cfg_attr(feature = "tabled", derive(Tabled))]
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
#[serde(untagged)]
pub enum ProjectRetrieved {
Detailed(ProjectDetailed),
Normal(Project),
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
pub struct ProjectListParams {
pub all: Option<bool>,
pub userclass: Option<UserClass>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ProjectCreateData {
pub name: String,
pub openstack_id: String, #[serde(skip_serializing_if = "Option::is_none")]
pub user_class: Option<UserClass>,
}
impl ProjectCreateData {
pub fn new(name: String, openstack_id: String) -> Self {
Self {
name,
openstack_id,
user_class: None,
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ProjectModifyData {
pub id: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub openstack_id: Option<String>, #[serde(skip_serializing_if = "Option::is_none")]
pub user_class: Option<UserClass>,
}
impl ProjectModifyData {
pub fn new(id: u32) -> Self {
Self {
id,
name: None,
openstack_id: None,
user_class: None,
}
}
}
#[derive(
clap::ValueEnum,
Hash,
PartialEq,
Eq,
Clone,
EnumIter,
Debug,
Deserialize,
Serialize,
Copy,
)]
#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
pub enum UserClass {
NA = 0,
UC1 = 1,
UC2 = 2,
UC3 = 3,
UC4 = 4,
UC5 = 5,
UC6 = 6,
}
impl Display for UserClass {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl TryFrom<u32> for UserClass {
type Error = ConversionError;
fn try_from(u: u32) -> Result<Self, Self::Error> {
match u {
0 => Ok(UserClass::NA),
1 => Ok(UserClass::UC1),
2 => Ok(UserClass::UC2),
3 => Ok(UserClass::UC3),
4 => Ok(UserClass::UC4),
5 => Ok(UserClass::UC5),
6 => Ok(UserClass::UC6),
_ => Err(ConversionError(
format!("Unknown user class value: {u}").to_string(),
)),
}
}
}
impl Distribution<UserClass> for StandardUniform {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> UserClass {
match rng.random_range(0..6) {
0 => UserClass::NA,
1 => UserClass::UC1,
2 => UserClass::UC2,
3 => UserClass::UC3,
4 => UserClass::UC4,
5 => UserClass::UC5,
6 => UserClass::UC6,
_ => UserClass::NA,
}
}
}