wsi_dicom_annotations/annotations/model/
algorithm.rs1use crate::{Error, Result};
2
3use super::code::{validate_text, DicomCode, MAX_LONG_TEXT_BYTES};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum GenerationType {
7 Manual,
8 Semiautomatic,
9 Automatic,
10}
11
12impl GenerationType {
13 #[must_use]
14 pub const fn dicom_value(self) -> &'static str {
15 match self {
16 Self::Manual => "MANUAL",
17 Self::Semiautomatic => "SEMIAUTOMATIC",
18 Self::Automatic => "AUTOMATIC",
19 }
20 }
21
22 pub(crate) fn from_dicom(value: &str) -> Result<Self> {
23 match value.trim() {
24 "MANUAL" => Ok(Self::Manual),
25 "SEMIAUTOMATIC" => Ok(Self::Semiautomatic),
26 "AUTOMATIC" => Ok(Self::Automatic),
27 other => Err(Error::Unsupported(format!(
28 "generation type {other:?} is not recognized"
29 ))),
30 }
31 }
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub struct AlgorithmIdentification {
36 family: DicomCode,
37 name_code: Option<DicomCode>,
38 name: String,
39 version: String,
40 parameters: Option<String>,
41 source: Option<String>,
42}
43
44impl AlgorithmIdentification {
45 pub fn new(
46 family: DicomCode,
47 name: impl Into<String>,
48 version: impl Into<String>,
49 ) -> Result<Self> {
50 let name = name.into();
51 let version = version.into();
52 validate_text("algorithm name", &name, 64)?;
53 validate_text("algorithm version", &version, 64)?;
54 Ok(Self {
55 family,
56 name_code: None,
57 name,
58 version,
59 parameters: None,
60 source: None,
61 })
62 }
63
64 #[must_use]
65 pub fn with_name_code(mut self, code: DicomCode) -> Self {
66 self.name_code = Some(code);
67 self
68 }
69
70 pub fn with_parameters(mut self, parameters: impl Into<String>) -> Result<Self> {
71 let parameters = parameters.into();
72 validate_text("algorithm parameters", ¶meters, MAX_LONG_TEXT_BYTES)?;
73 self.parameters = Some(parameters);
74 Ok(self)
75 }
76
77 pub fn with_source(mut self, source: impl Into<String>) -> Result<Self> {
78 let source = source.into();
79 validate_text("algorithm source", &source, 64)?;
80 self.source = Some(source);
81 Ok(self)
82 }
83
84 #[must_use]
85 pub fn family(&self) -> &DicomCode {
86 &self.family
87 }
88
89 #[must_use]
90 pub fn name_code(&self) -> Option<&DicomCode> {
91 self.name_code.as_ref()
92 }
93
94 #[must_use]
95 pub fn name(&self) -> &str {
96 &self.name
97 }
98
99 #[must_use]
100 pub fn version(&self) -> &str {
101 &self.version
102 }
103
104 #[must_use]
105 pub fn parameters(&self) -> Option<&str> {
106 self.parameters.as_deref()
107 }
108
109 #[must_use]
110 pub fn source(&self) -> Option<&str> {
111 self.source.as_deref()
112 }
113}
114
115pub(crate) fn validate_generation(
116 generation_type: GenerationType,
117 algorithms: &[AlgorithmIdentification],
118) -> Result<()> {
119 match generation_type {
120 GenerationType::Manual if !algorithms.is_empty() => Err(Error::InvalidInput(
121 "manual annotations cannot identify a generation algorithm".into(),
122 )),
123 GenerationType::Semiautomatic | GenerationType::Automatic if algorithms.is_empty() => {
124 Err(Error::InvalidInput(
125 "automatic and semiautomatic annotations require an algorithm identification"
126 .into(),
127 ))
128 }
129 _ => Ok(()),
130 }
131}