1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use super::*;

#[cfg(feature = "gui")]
use mktree::MkTree;

#[async_trait]
/// Responsible for modifications of [`ProductionAsset`].
pub trait AssetGov: DynClone + fmt::Debug + Send + Sync {
    async fn add_one(
        &self,
        project: &Project,
        asset: &ProductionAsset,
    ) -> Result<(), ModificationError>;

    async fn add_many(
        &self,
        project: &Project,
        assets: &[ProductionAsset],
    ) -> Result<(), ModificationError>;

    async fn edit_one(
        &self,
        project: &Project,
        asset: &ProductionAsset,
    ) -> Result<(), ModificationError>;

    async fn delete_one(
        &self,
        project: &Project,
        asset: &ProductionAsset,
    ) -> Result<(), ModificationError>;

    async fn delete_many(
        &self,
        project: &Project,
        assets: &[ProductionAsset],
    ) -> Result<(), ModificationError>;
}

dyn_clone::clone_trait_object!(AssetGov);

#[async_trait]
/// Responsible for building asset subtrees based on given grouping criteria,
/// i.e. by "semantic" or by assignees, with optional [`StatusFilter`].
pub trait AssetOrg: DynClone + fmt::Debug + Send + Sync {
    async fn assets(
        &self,
        project: &Project,
        status_filter: &StatusFilter,
    ) -> Result<Vec<ProductionAsset>, DatabaseError>;

    /// Gets an asset by ObjectId.
    async fn asset_from_bson_id(
        &self,
        project: &Project,
        id: &ObjectId,
    ) -> Result<ProductionAsset, DatabaseError>;

    async fn asset_excerpt_from_bson_id(
        &mut self,
        project: &Project,
        id: &ObjectId,
    ) -> Result<AssetExcerpt, DatabaseError>;

    /// All [`ProductionAsset`]s assigned to the given [`Staff`].
    async fn assets_assigned_to(
        &self,
        project: &Project,
        staff: &Staff,
    ) -> Result<HashSet<ProductionAsset>, DatabaseError>;

    #[cfg(feature = "gui")]
    /// Returns an asset tree for each main category type in the project.
    async fn category_trees(
        &self,
        project: &Project,
        status_filter: &StatusFilter,
    ) -> Result<Vec<MkTree<ProductionAsset>>, DatabaseError>;

    #[cfg(feature = "gui")]
    /// Returns an asset tree for each assignee group in the project.
    async fn assignee_group_trees(
        &self,
        project: &Project,
        status_filter: &StatusFilter,
        single_user: Option<Staff>,
    ) -> Result<Vec<MkTree<ProductionAsset>>, DatabaseError>;

    fn clear_cache(&mut self) {}
}

dyn_clone::clone_trait_object!(AssetOrg);

// ----------------------------------------------------------------------------
#[derive(Clone, Debug)]
/// Used to provide an option to only return the [`ProductionAsset`]s by filtering
/// them by a set of `AssetStatus`es.
pub enum StatusFilter {
    /// "AND" operation.
    All(BTreeSet<AssetStatus>),

    /// "OR" operation.
    Any(BTreeSet<AssetStatus>),
}

impl StatusFilter {
    pub fn all(filter: &BTreeSet<AssetStatus>) -> Self {
        Self::All(filter.clone())
    }

    pub fn any(filter: &BTreeSet<AssetStatus>) -> Self {
        Self::Any(filter.clone())
    }

    pub fn mongo_op_str(&self) -> &str {
        match &self {
            Self::All(_) => "$all",
            Self::Any(_) => "$in",
        }
    }

    pub fn is_empty(&self) -> bool {
        match &self {
            Self::All(inner) | Self::Any(inner) => inner.is_empty(),
        }
    }
    /// SAFETY: caller is responsible for ensuring `Self.0` `is_some`.
    pub fn to_vec_string(&self) -> Vec<String> {
        match &self {
            Self::All(inner) | Self::Any(inner) => inner.iter().map(|s| s.into()).collect(),
        }
    }
}