use crate::database::models::DBProjectDisclosure; use ariadne::ids::UserId; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::collections::BTreeSet; use strum::{EnumDiscriminants, EnumString, IntoStaticStr}; use utoipa::ToSchema; #[derive(Debug, Clone, Serialize, Deserialize, ToSchema, EnumDiscriminants)] #[serde(rename_all = "snake_case", tag = "type")] #[strum_discriminants( name(ProjectDisclosureType), derive(IntoStaticStr, EnumString), strum(serialize_all = "snake_case") )] pub enum ProjectDisclosure { AiContent { note: Option, uses: BTreeSet, }, Advertisements { note: Option, }, EpilepsyTriggers { note: Option, }, SystemInteractions { note: Option, interactions: Vec, }, Telemetry { consent: TelemetryConsent, data_collected: Vec, }, DerivativeWork { sources: Vec, }, PaidFeatures { features: Vec, }, Archived { note: Option, }, } impl ProjectDisclosure { pub fn to_parts( &self, ) -> Result<(&'static str, serde_json::Value), serde_json::Error> { let serde_json::Value::Object(mut object) = serde_json::to_value(self)? else { return Err(serde::ser::Error::custom( "project disclosure must serialize to a JSON object", )); }; object.remove("type"); Ok(( ProjectDisclosureType::from(self).into(), serde_json::Value::Object(object), )) } pub fn from_parts( kind: &str, metadata: serde_json::Value, ) -> Result { let serde_json::Value::Object(mut object) = metadata else { return Err(serde::de::Error::custom( "project disclosure metadata must be a JSON object", )); }; object.insert( "type".to_owned(), serde_json::Value::String(kind.to_owned()), ); serde_json::from_value(serde_json::Value::Object(object)) } } #[derive( Debug, Clone, Copy, Default, Serialize, Deserialize, ToSchema, IntoStaticStr, EnumString, )] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] pub enum DisclosureLockStatus { #[default] Unlocked, CannotDisable, FullyLocked, } impl DisclosureLockStatus { pub fn allows_edit(self) -> bool { !matches!(self, Self::FullyLocked) } pub fn allows_removal(self) -> bool { matches!(self, Self::Unlocked) } } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct ProjectDisclosureData { #[serde(flatten)] pub disclosure: ProjectDisclosure, #[serde(skip_serializing_if = "Option::is_none")] pub set_by_moderator: Option, #[serde(skip_serializing_if = "Option::is_none")] pub lock_status: Option, pub updated_at: DateTime, #[serde(skip_serializing_if = "Option::is_none")] pub updated_by: Option, #[serde(skip_serializing_if = "Option::is_none")] pub deleted_at: Option>, } impl ProjectDisclosureData { pub fn from_db( value: DBProjectDisclosure, viewer_is_moderator: bool, viewer_is_member: bool, ) -> Self { Self { disclosure: value.disclosure, set_by_moderator: (viewer_is_member || viewer_is_moderator) .then_some(value.set_by_moderator), lock_status: (viewer_is_member || viewer_is_moderator) .then_some(value.lock_status), updated_at: value.updated_at, updated_by: ((!value.set_by_moderator && viewer_is_member) || viewer_is_moderator) .then_some(value.updated_by.into()), deleted_at: value.deleted_at, } } } #[derive( Debug, Clone, Serialize, Deserialize, ToSchema, IntoStaticStr, PartialEq, Eq, PartialOrd, Ord, )] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] pub enum AiUsages { Code, Assets, Text, Functionality, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema, IntoStaticStr)] #[serde(rename_all = "snake_case")] #[strum(serialize_all = "snake_case")] pub enum TelemetryConsent { OptIn, OptOut, AlwaysActive, } #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct DerivativeSource { pub link: Option, pub label: String, pub note: Option, }