assemblyline_models/types/
classification.rs1use std::ops::Deref;
2use std::sync::{Arc, Mutex};
3
4use serde::{Serialize, Deserialize};
5use assemblyline_markings::classification::{ClassificationParser, NormalizeOptions};
6use struct_metadata::Described;
7
8use crate::types::JsonMap;
9use crate::{ElasticMeta, ModelError};
10
11enum ClassificationMode {
12 Uninitialized,
13 Disabled,
14 Configured(Arc<ClassificationParser>)
15}
16
17static GLOBAL_CLASSIFICATION: Mutex<ClassificationMode> = Mutex::new(ClassificationMode::Uninitialized);
18
19pub fn disable_global_classification() {
25 *GLOBAL_CLASSIFICATION.lock().unwrap() = ClassificationMode::Disabled;
26}
27
28pub fn set_global_classification(config: Arc<ClassificationParser>) {
29 *GLOBAL_CLASSIFICATION.lock().unwrap() = ClassificationMode::Configured(config);
30}
31
32pub fn unrestricted_classification() -> String {
33 match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
34 ClassificationMode::Uninitialized => panic!("classification handling without defining parser"),
35 ClassificationMode::Disabled => "".to_string(),
36 ClassificationMode::Configured(parser) => parser.unrestricted().to_owned(),
37 }
38}
39
40pub fn unrestricted_classification_string() -> ClassificationString {
41 match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
42 ClassificationMode::Uninitialized => panic!("classification handling without defining parser"),
43 ClassificationMode::Disabled => ClassificationString(Default::default()),
44 ClassificationMode::Configured(parser) => ClassificationString::unrestricted(parser),
45 }
46}
47
48pub fn unrestricted_expanding_classification() -> ExpandingClassification {
49 match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
50 ClassificationMode::Uninitialized => panic!("classification handling without defining parser"),
51 ClassificationMode::Disabled => ExpandingClassification {
52 classification: Default::default(),
53 __access_lvl__: Default::default(),
54 __access_req__: Default::default(),
55 __access_grp1__: Default::default(),
56 __access_grp2__: Default::default(),
57 },
58 ClassificationMode::Configured(parser) => ExpandingClassification::unrestricted(parser),
59 }
60}
61
62
63#[derive(Debug, Clone, Eq)]
72pub struct ExpandingClassification<const USER: bool=false> {
73 pub classification: String,
74 pub __access_lvl__: i32,
75 pub __access_req__: Vec<String>,
76 pub __access_grp1__: Vec<String>,
77 pub __access_grp2__: Vec<String>,
78}
79
80impl<const USER: bool> ExpandingClassification<USER> {
81 pub fn new(classification: String, parser: &ClassificationParser) -> Result<Self, ModelError> {
82 if parser.original_definition.enforce {
83
84 let parts = parser.get_classification_parts(&classification, false, true, !USER)?;
85 let classification = parser.get_normalized_classification_text(parts.clone(), false, false)?;
86
87 Ok(Self {
88 classification,
89 __access_lvl__: parts.level,
90 __access_req__: parts.required,
91 __access_grp1__: if parts.groups.is_empty() { vec!["__EMPTY__".to_owned()] } else { parts.groups },
92 __access_grp2__: if parts.subgroups.is_empty() { vec!["__EMPTY__".to_owned()] } else { parts.subgroups },
93 })
94 } else {
95 Ok(Self {
96 classification,
97 __access_lvl__: Default::default(),
98 __access_req__: Default::default(),
99 __access_grp1__: vec!["__EMPTY__".to_owned()],
100 __access_grp2__: vec!["__EMPTY__".to_owned()],
101 })
102 }
103 }
104
105 pub fn try_unrestricted() -> Option<Self> {
106 if let ClassificationMode::Configured(parser) = &*GLOBAL_CLASSIFICATION.lock().unwrap() {
107 Some(Self::unrestricted(parser))
108 } else {
109 None
110 }
111 }
112
113 pub fn unrestricted(parser: &ClassificationParser) -> Self {
114 Self::new(parser.unrestricted().to_string(), parser).unwrap()
115 }
116
117 pub fn as_str(&self) -> &str {
118 &self.classification
119 }
120
121 pub fn insert(parser: &ClassificationParser, output: &mut JsonMap, classification: &str) -> Result<(), ModelError> {
122 use serde_json::json;
123 if parser.original_definition.enforce {
124 let parts = parser.get_classification_parts(classification, true, true, !USER)?;
125 let classification = parser.get_normalized_classification_text(parts.clone(), true, false)?;
126
127 output.insert("classification".to_string(), json!(classification));
128 output.insert("__access_lvl__".to_string(), json!(parts.level));
129 output.insert("__access_req__".to_string(), json!(parts.required));
130 output.insert("__access_grp1__".to_string(), json!(if parts.groups.is_empty() { vec!["__EMPTY__".to_string()] } else { parts.groups }));
131 output.insert("__access_grp2__".to_string(), json!(if parts.subgroups.is_empty() { vec!["__EMPTY__".to_string()] } else { parts.subgroups }));
132 Ok(())
133 } else {
134 output.insert("classification".to_string(), json!(classification));
135 output.insert("__access_lvl__".to_string(), json!(0));
136 output.insert("__access_req__".to_string(), serde_json::Value::Array(Default::default()));
137 output.insert("__access_grp1__".to_string(), json!(&["__EMPTY__"]));
138 output.insert("__access_grp2__".to_string(), json!(&["__EMPTY__"]));
139 Ok(())
140 }
141 }
142}
143
144#[derive(Serialize, Deserialize)]
145struct RawClassification {
146 pub classification: String,
147 #[serde(default)]
148 pub __access_lvl__: i32,
149 #[serde(default)]
150 pub __access_req__: Vec<String>,
151 #[serde(default)]
152 pub __access_grp1__: Vec<String>,
153 #[serde(default)]
154 pub __access_grp2__: Vec<String>,
155}
156
157impl<const U: bool> From<&ExpandingClassification<U>> for RawClassification {
158 fn from(value: &ExpandingClassification<U>) -> Self {
159 Self {
160 classification: value.classification.clone(),
161 __access_lvl__: value.__access_lvl__,
162 __access_req__: value.__access_req__.clone(),
163 __access_grp1__: value.__access_grp1__.clone(),
164 __access_grp2__: value.__access_grp2__.clone(),
165 }
166 }
167}
168
169impl<const U: bool> From<RawClassification> for ExpandingClassification<U> {
170 fn from(value: RawClassification) -> Self {
171 Self {
172 classification: value.classification.clone(),
173 __access_lvl__: value.__access_lvl__,
174 __access_req__: value.__access_req__.clone(),
175 __access_grp1__: value.__access_grp1__.clone(),
176 __access_grp2__: value.__access_grp2__.clone(),
177 }
178 }
179}
180
181
182impl<const U: bool> Serialize for ExpandingClassification<U> {
183 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
184 where S: serde::Serializer
185 {
186 match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
187 ClassificationMode::Uninitialized => return Err(serde::ser::Error::custom("classification engine not initalized")),
188 ClassificationMode::Disabled |
190 ClassificationMode::Configured(_) => {},
192 }
193 RawClassification::from(self).serialize(serializer)
194 }
195}
196
197impl<'de, const U: bool> Deserialize<'de> for ExpandingClassification<U> {
198 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
199 where D: serde::Deserializer<'de>
200 {
201 let parser = match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
202 ClassificationMode::Uninitialized => return Err(serde::de::Error::custom("classification engine not initalized")),
203 ClassificationMode::Disabled => None,
205 ClassificationMode::Configured(parser) => Some(parser.clone()),
207 };
208
209 if let Some(parser) = parser {
210 let raw = RawClassification::deserialize(deserializer)?;
211 Self::new(raw.classification, &parser).map_err(serde::de::Error::custom)
212 } else {
213 Ok(RawClassification::deserialize(deserializer)?.into())
214 }
215 }
216}
217
218impl<const USER: bool> Described<ElasticMeta> for ExpandingClassification<USER> {
219 fn metadata() -> struct_metadata::Descriptor<ElasticMeta> {
220
221 struct_metadata::Descriptor {
228 docs: None,
229 metadata: ElasticMeta{mapping: Some("classification"), ..Default::default()},
230 kind: struct_metadata::Kind::new_struct("ExpandingClassification", vec![
231 struct_metadata::Entry { label: "classification", docs: None, metadata: ElasticMeta{mapping: Some("classification"), ..Default::default()}, type_info: String::metadata(), has_default: false, aliases: &["classification"] },
232 ], &mut [], &mut[]),
237 }
242 }
243}
244
245impl<const U: bool> PartialEq<&str> for ExpandingClassification<U> {
246 fn eq(&self, other: &&str) -> bool {
247 let parser = match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
248 ClassificationMode::Uninitialized | ClassificationMode::Disabled => None,
249 ClassificationMode::Configured(parser) => Some(parser.clone()),
250 };
251
252 if let Some(parser) = parser {
253 match (parser.normalize_classification(&self.classification), parser.normalize_classification(other)) {
254 (Ok(a), Ok(b)) => a == b,
255 _ => false
256 }
257 } else {
258 self.classification.as_str() == *other
259 }
260 }
261}
262
263impl<const U: bool> PartialEq for ExpandingClassification<U> {
264 fn eq(&self, other: &Self) -> bool {
265 self.eq(&other.classification.as_str())
266 }
267}
268
269
270#[derive(Described, Debug, Clone, Eq)]
274#[metadata_type(ElasticMeta)]
275#[metadata(mapping="classification_string")]
276pub struct ClassificationString(pub (crate) String);
277
278impl From<ClassificationString> for String {
279 fn from(value: ClassificationString) -> Self {
280 value.0
281 }
282}
283
284impl Deref for ClassificationString {
285 type Target = str;
286
287 fn deref(&self) -> &Self::Target {
288 &self.0
289 }
290}
291
292impl ClassificationString {
293 pub fn new(classification: String, parser: &Arc<ClassificationParser>) -> Result<Self, ModelError> {
294 Ok(Self(parser.normalize_classification_options(&classification, NormalizeOptions::short())?))
295 }
296
297 pub fn new_unchecked(classification: String) -> Self {
298 Self(classification)
299 }
300
301 pub fn try_unrestricted() -> Option<Self> {
302 if let ClassificationMode::Configured(parser) = &*GLOBAL_CLASSIFICATION.lock().unwrap() {
303 Some(Self::unrestricted(parser))
304 } else {
305 None
306 }
307 }
308
309 pub fn unrestricted(parser: &ClassificationParser) -> Self {
310 Self(parser.unrestricted().to_owned())
311 }
312
313 pub fn as_str(&self) -> &str {
314 &self.0
315 }
316
317 pub fn default_unrestricted() -> ClassificationString {
318 match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
319 ClassificationMode::Uninitialized => panic!("classification handling without defining parser"),
320 ClassificationMode::Disabled => ClassificationString(Default::default()),
321 ClassificationMode::Configured(parser) => ClassificationString::unrestricted(parser),
322 }
323 }
324}
325
326#[derive(Serialize, Deserialize)]
327struct RawClassificationString(String);
328
329
330impl From<&ClassificationString> for RawClassificationString {
331 fn from(value: &ClassificationString) -> Self {
332 Self(value.0.clone())
333 }
334}
335
336impl From<RawClassificationString> for ClassificationString {
337 fn from(value: RawClassificationString) -> Self {
338 Self(value.0.clone())
339 }
340}
341
342
343impl Serialize for ClassificationString {
344 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
345 where S: serde::Serializer
346 {
347 match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
348 ClassificationMode::Uninitialized => return Err(serde::ser::Error::custom("classification engine not initalized")),
349 ClassificationMode::Disabled |
351 ClassificationMode::Configured(_) => {},
353 }
354 RawClassificationString::from(self).serialize(serializer)
355 }
356}
357
358impl<'de> Deserialize<'de> for ClassificationString {
378 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
379 where D: serde::Deserializer<'de>
380 {
381 let parser = match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
382 ClassificationMode::Uninitialized => return Err(serde::de::Error::custom("classification engine not initalized")),
383 ClassificationMode::Disabled => None,
385 ClassificationMode::Configured(parser) => Some(parser.clone()),
387 };
388
389 if let Some(parser) = parser {
390 let raw = RawClassificationString::deserialize(deserializer)?;
391 Self::new(raw.0, &parser).map_err(serde::de::Error::custom)
392 } else {
393 Ok(RawClassificationString::deserialize(deserializer)?.into())
394 }
395 }
396}
397
398impl PartialEq for ClassificationString {
399 fn eq(&self, other: &Self) -> bool {
400 let parser = match &*GLOBAL_CLASSIFICATION.lock().unwrap() {
401 ClassificationMode::Uninitialized | ClassificationMode::Disabled => None,
402 ClassificationMode::Configured(parser) => Some(parser.clone()),
403 };
404
405 if let Some(parser) = parser {
406 match (parser.normalize_classification(&self.0), parser.normalize_classification(&other.0)) {
407 (Ok(a), Ok(b)) => a == b,
408 _ => false
409 }
410 } else {
411 self.0 == other.0
412 }
413 }
414}