use super::*;
#[async_trait]
pub trait ProjectDefaultDelivery: DynClone + fmt::Debug + Send + Sync {
async fn default_workflow(
&self,
project: &Project,
) -> Result<ProductionDefaultWorkflow, DatabaseError>;
async fn make_default(
&self,
project: &Project,
default: ProductionDefault,
) -> Result<(), ModificationError>;
async fn unlink_default(
&self,
project: &Project,
template: DeliveryTemplate,
) -> Result<(), ModificationError>;
}
dyn_clone::clone_trait_object!(ProjectDefaultDelivery);
#[async_trait]
pub trait AssetStage: DynClone + fmt::Debug + Send + Sync {
async fn stage_indices(
&mut self,
project: &Project,
asset: &ProductionAsset,
) -> Result<StageRef, DatabaseError>;
async fn set_stage(
&mut self,
project: &Project,
asset: &ProductionAsset,
index: StageIndex,
) -> Result<(), ModificationError>;
async fn unset_stage(
&mut self,
project: &Project,
asset: &ProductionAsset,
index: StageIndex,
) -> Result<(), ModificationError>;
}
dyn_clone::clone_trait_object!(AssetStage);
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "mongo", derive(Serialize, Deserialize))]
pub struct ProductionDefaultWorkflow {
#[cfg_attr(
feature = "mongo",
serde(rename = "_id", skip_serializing_if = "Option::is_none")
)]
id: Option<ObjectId>,
default_workflow: bool,
#[cfg_attr(feature = "mongo", serde(rename = "internal"))]
pub internal_oid: Option<ObjectId>,
#[cfg_attr(feature = "mongo", serde(skip))]
pub internal: Option<Result<DeliveryTemplate, DatabaseError>>,
#[cfg_attr(feature = "mongo", serde(rename = "outgoing"))]
pub outoing_oid: Option<ObjectId>,
#[cfg_attr(feature = "mongo", serde(skip))]
pub outgoing: Option<Result<DeliveryTemplate, DatabaseError>>,
}
impl ProductionDefaultWorkflow {
pub fn empty() -> Self {
Self {
default_workflow: true,
..Default::default()
}
}
pub fn match_stage_ref(&mut self, stage: &StageRef) {
if let Some(indices) = &stage.internal_indices {
self.match_internal_stage_ref(indices);
};
if let Some(indices) = &stage.outgoing_indices {
self.match_outgoing_stage_ref(indices);
};
}
pub fn unmatch_stage_ref(&mut self) {
if let Some(Ok(workflow)) = self.internal.as_mut() {
workflow.reset_reference();
};
if let Some(Ok(workflow)) = self.outgoing.as_mut() {
workflow.reset_reference();
};
}
fn match_internal_stage_ref(&mut self, indices: &[StageIndex]) {
if let Some(Ok(workflow)) = self.internal.as_mut() {
match_stage_ref(&mut workflow.template.stage_order, indices);
};
}
fn match_outgoing_stage_ref(&mut self, indices: &[StageIndex]) {
if let Some(Ok(workflow)) = self.outgoing.as_mut() {
match_stage_ref(&mut workflow.template.stage_order, indices);
};
}
}
fn match_stage_ref(stage_order: &mut Vec<Stage>, indices: &[StageIndex]) {
stage_order.iter_mut().for_each(|stage| {
if let Some(idx) = &stage.index {
stage.has_reference_to = indices.contains(idx);
};
});
}
impl BsonId for ProductionDefaultWorkflow {
fn bson_id_as_ref(&self) -> Option<&ObjectId> {
self.id.as_ref()
}
fn bson_id(&self) -> AnyResult<&ObjectId> {
self.id
.as_ref()
.context("ProductionDefaultWorkflow without BSON ObjectId")
}
}
#[derive(Debug, Clone, strum::AsRefStr)]
pub enum ProductionDefault {
#[strum(serialize = "🎨 Internal Production")]
Internal(DeliveryTemplate),
#[strum(serialize = "🚚 Outgoing Delivery")]
Outgoing(DeliveryTemplate),
}
impl BsonId for ProductionDefault {
fn bson_id_as_ref(&self) -> Option<&ObjectId> {
match self {
Self::Internal(t) | Self::Outgoing(t) => t.bson_id_as_ref(),
}
}
fn bson_id(&self) -> AnyResult<&ObjectId> {
match self {
Self::Internal(t) | Self::Outgoing(t) => t.bson_id(),
}
}
}
impl ProductionDefault {
pub fn internal_as_str() -> &'static str {
"🎨 Internal Production"
}
pub fn outgoing_as_str() -> &'static str {
"🚚 Outgoing Delivery"
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "mongo", derive(Serialize, Deserialize))]
pub enum StageIndex {
Generic(u32),
Internal(u32),
Outgoing(u32),
}
impl StageIndex {
pub fn field_key(&self) -> &str {
match &self {
Self::Generic(_) => "stage.generic",
Self::Internal(_) => "stage.internal",
Self::Outgoing(_) => "stage.outgoing",
}
}
pub fn value(&self) -> u32 {
match &self {
Self::Generic(inner) | Self::Internal(inner) | Self::Outgoing(inner) => *inner,
}
}
}
#[derive(Clone, Default)]
#[cfg_attr(feature = "mongo", derive(Serialize, Deserialize))]
pub struct StageRef {
#[cfg_attr(feature = "mongo", serde(rename = "internal"))]
internal_indices: Option<Vec<StageIndex>>,
#[cfg(feature = "gui")]
#[cfg_attr(feature = "mongo", serde(skip))]
internal: Option<Result<Vec<RichText>, DatabaseError>>,
#[cfg_attr(feature = "mongo", serde(rename = "outgoing"))]
outgoing_indices: Option<Vec<StageIndex>>,
#[cfg(feature = "gui")]
#[cfg_attr(feature = "mongo", serde(skip))]
outgoing: Option<Result<Vec<RichText>, DatabaseError>>,
}
impl fmt::Debug for StageRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("StageRef")
.field("internal_indices", &self.internal_indices)
.field("outgoing_indices", &self.outgoing_indices)
.finish()
}
}
impl StageRef {
pub fn contains_index(&self, idx: &StageIndex) -> bool {
match idx {
StageIndex::Generic(_) => true,
StageIndex::Internal(_) => match &self.internal_indices {
Some(indices) => indices.contains(idx),
None => false,
},
StageIndex::Outgoing(_) => match &self.outgoing_indices {
Some(indices) => indices.contains(idx),
None => false,
},
}
}
}
#[cfg(feature = "gui")]
impl StageRef {
fn internal_mut(&mut self, template: Option<&Result<DeliveryTemplate, DatabaseError>>) {
if let Some(template) = template {
epxand_indices(self.internal_indices.as_ref(), template, &mut self.internal);
};
}
fn outgoing_mut(&mut self, template: Option<&Result<DeliveryTemplate, DatabaseError>>) {
if let Some(template) = template {
epxand_indices(self.outgoing_indices.as_ref(), template, &mut self.outgoing);
};
}
pub fn ui(&self, ui: &mut egui::Ui) {
ui.group(|ui| {
stages_name_ui(self.internal.as_ref(), ui);
stages_name_ui(self.outgoing.as_ref(), ui);
});
}
}
#[cfg(feature = "gui")]
#[derive(Debug)]
pub struct StageRefReadBuilder(StageRef);
#[cfg(feature = "gui")]
impl StageRefReadBuilder {
pub fn new(stage: StageRef) -> Self {
Self(stage)
}
pub fn epxand_indices(mut self, project_definition: &ProductionDefaultWorkflow) -> Self {
self.0.internal_mut(project_definition.internal.as_ref());
self.0.outgoing_mut(project_definition.outgoing.as_ref());
self
}
pub fn finish(self) -> StageRef {
self.0
}
}
#[cfg(feature = "gui")]
fn epxand_indices(
indices: Option<&Vec<StageIndex>>,
template: &Result<DeliveryTemplate, DatabaseError>,
expanded: &mut Option<Result<Vec<RichText>, DatabaseError>>,
) {
if let Some(indices) = indices {
match template {
Ok(template) => {
let mut expanding = vec![];
indices.iter().for_each(|i| {
if let Some(Some(stage)) = template.stages.get(i.value() as usize) {
expanding.push(RichText::new(&stage.name).color(stage.color));
};
});
*expanded = Some(Ok(expanding));
}
Err(e) => {
*expanded = Some(Err(anyhow!(
"Project's DefaultWorkflow Definition Error: {}",
e
)
.into()));
}
}
};
}
#[cfg(feature = "gui")]
fn stages_name_ui(stages: Option<&Result<Vec<RichText>, DatabaseError>>, ui: &mut egui::Ui) {
match stages {
Some(Ok(stages)) => {
ui.horizontal(|ui| {
stages.iter().for_each(|s| {
ui.label(s.clone());
});
});
}
Some(Err(e)) => {
ui.colored_label(Color32::RED, "🚫 Stages")
.on_hover_text(e.to_string());
}
None => {
}
}
}