Skip to main content

wdl_core/concern/
code.rs

1//! Codes for validation failures and lint warnings.
2
3use std::num::NonZeroUsize;
4
5use crate::Version;
6
7mod kind;
8
9pub use kind::Kind;
10use serde::Deserialize;
11use serde::Serialize;
12
13/// An error related to a [`Code`].
14#[derive(Debug)]
15pub enum Error {
16    /// Attempted to make a code with an invalid index.
17    InvalidIndex(usize),
18}
19
20impl std::fmt::Display for Error {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        match self {
23            Error::InvalidIndex(index) => write!(f, "invalid index: {index}"),
24        }
25    }
26}
27
28impl std::error::Error for Error {}
29
30/// A [`Result`](std::result::Result) with an [`Error`].
31type Result<T> = std::result::Result<T, Error>;
32
33/// A code.
34#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
35pub struct Code {
36    /// The kind of code.
37    kind: Kind,
38
39    /// The grammar for this code.
40    grammar: Version,
41
42    /// The index for this code.
43    index: NonZeroUsize,
44}
45
46impl Code {
47    /// Attempts to create a new [`Code`].
48    ///
49    /// # Examples
50    ///
51    /// ```
52    /// use wdl_core::concern::code::Kind;
53    /// use wdl_core::concern::Code;
54    /// use wdl_core::Version;
55    ///
56    /// let code = Code::try_new(Kind::Warning, Version::V1, 1)?;
57    /// assert_eq!(code.kind(), &Kind::Warning);
58    /// assert_eq!(code.grammar(), &Version::V1);
59    /// assert_eq!(code.index().get(), 1);
60    ///
61    /// # Ok::<(), Box<dyn std::error::Error>>(())
62    /// ```
63    pub fn try_new(kind: Kind, grammar: Version, index: usize) -> Result<Self> {
64        let index = NonZeroUsize::try_from(index).map_err(|_| Error::InvalidIndex(index))?;
65
66        Ok(Self {
67            kind,
68            grammar,
69            index,
70        })
71    }
72
73    /// Gets the [`Kind`] of concern for this [`Code`] by reference.
74    ///
75    /// # Examples
76    ///
77    /// ```
78    /// use wdl_core::concern::code::Kind;
79    /// use wdl_core::concern::Code;
80    /// use wdl_core::Version;
81    ///
82    /// let code = Code::try_new(Kind::Warning, Version::V1, 1)?;
83    /// assert_eq!(code.kind(), &Kind::Warning);
84    ///
85    /// # Ok::<(), Box<dyn std::error::Error>>(())
86    /// ```
87    pub fn kind(&self) -> &Kind {
88        &self.kind
89    }
90
91    /// Gets the grammar [`Version`] for this [`Code`] by reference.
92    ///
93    /// # Examples
94    ///
95    /// ```
96    /// use wdl_core::concern::code::Kind;
97    /// use wdl_core::concern::Code;
98    /// use wdl_core::Version;
99    ///
100    /// let code = Code::try_new(Kind::Warning, Version::V1, 1)?;
101    /// assert_eq!(code.grammar(), &Version::V1);
102    ///
103    /// # Ok::<(), Box<dyn std::error::Error>>(())
104    /// ```
105    pub fn grammar(&self) -> &Version {
106        &self.grammar
107    }
108
109    /// Gets the index of this [`Code`] by reference.
110    ///
111    /// # Examples
112    ///
113    /// ```
114    /// use wdl_core::concern::code::Kind;
115    /// use wdl_core::concern::Code;
116    /// use wdl_core::Version;
117    ///
118    /// let code = Code::try_new(Kind::Warning, Version::V1, 1)?;
119    /// assert_eq!(code.index().get(), 1);
120    ///
121    /// # Ok::<(), Box<dyn std::error::Error>>(())
122    /// ```
123    pub fn index(&self) -> NonZeroUsize {
124        self.index
125    }
126}
127
128impl std::fmt::Display for Code {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        write!(
131            f,
132            "{}::{}{:03}",
133            self.grammar.short_name(),
134            self.kind.prefix(),
135            self.index
136        )
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn zero_index() {
146        let err = Code::try_new(Kind::Error, Version::V1, 0).unwrap_err();
147        assert!(matches!(err, Error::InvalidIndex(0)));
148    }
149
150    #[test]
151    fn display() {
152        let identity = Code::try_new(Kind::Error, Version::V1, 1).unwrap();
153        assert_eq!(identity.to_string(), String::from("v1::E001"));
154
155        let identity = Code::try_new(Kind::Warning, Version::V1, 1).unwrap();
156        assert_eq!(identity.to_string(), String::from("v1::W001"));
157    }
158}