cyclonedx_bom/external_models/
normalized_string.rs

1/*
2 * This file is part of CycloneDX Rust Cargo.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * SPDX-License-Identifier: Apache-2.0
17 */
18
19use crate::validation::ValidationError;
20use std::fmt::Display;
21use std::ops::Deref;
22
23/// A string that does not contain carriage return, line feed, or tab characters
24///
25/// Defined via the [XML schema](https://www.w3.org/TR/xmlschema-2/#normalizedString)
26#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
27pub struct NormalizedString(pub(crate) String);
28
29impl NormalizedString {
30    /// Construct a `NormalizedString` by replacing all of the invalid characters with spaces
31    /// ```
32    /// use cyclonedx_bom::prelude::*;
33    ///
34    /// let normalized_string = NormalizedString::new("A\r\nstring\rwith\ninvalid\tcharacters");
35    /// assert_eq!(normalized_string.to_string(), "A string with invalid characters".to_string());
36    /// ```
37    pub fn new(value: &str) -> Self {
38        let value = value.replace("\r\n", " ").replace(['\r', '\n', '\t'], " ");
39        NormalizedString(value)
40    }
41
42    /// Allow for the existence of invalid inputs from other data sources
43    pub(crate) fn new_unchecked(value: String) -> Self {
44        NormalizedString(value)
45    }
46}
47
48impl From<&str> for NormalizedString {
49    fn from(input: &str) -> Self {
50        NormalizedString::new(input)
51    }
52}
53
54impl From<NormalizedString> for String {
55    fn from(value: NormalizedString) -> Self {
56        value.0
57    }
58}
59
60impl Deref for NormalizedString {
61    type Target = str;
62
63    fn deref(&self) -> &Self::Target {
64        &self.0
65    }
66}
67
68impl AsRef<NormalizedString> for NormalizedString {
69    fn as_ref(&self) -> &NormalizedString {
70        self
71    }
72}
73
74impl AsRef<str> for NormalizedString {
75    fn as_ref(&self) -> &str {
76        &self.0
77    }
78}
79
80impl Display for NormalizedString {
81    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82        write!(f, "{}", self.0)
83    }
84}
85
86/// Validates a [`NormalizedString`].
87pub fn validate_normalized_string(
88    normalized_string: &NormalizedString,
89) -> Result<(), ValidationError> {
90    if normalized_string.contains("\r\n")
91        || normalized_string.contains('\r')
92        || normalized_string.contains('\n')
93        || normalized_string.contains('\t')
94    {
95        return Err(ValidationError::new(
96            "NormalizedString contains invalid characters \\r \\n \\t or \\r\\n",
97        ));
98    }
99
100    Ok(())
101}
102
103#[cfg(test)]
104mod test {
105    use super::*;
106    use pretty_assertions::assert_eq;
107
108    #[test]
109    fn it_should_normalize_strings() {
110        assert_eq!(
111            NormalizedString("no_whitespace".to_string()),
112            NormalizedString::new("no_whitespace")
113        );
114        assert_eq!(
115            NormalizedString("spaces and tabs".to_string()),
116            NormalizedString::new("spaces and\ttabs")
117        );
118        assert_eq!(
119            NormalizedString("carriage returns and linefeeds".to_string()),
120            NormalizedString::new("carriage\r\nreturns\rand\nlinefeeds")
121        );
122    }
123
124    #[test]
125    fn it_should_pass_validation() {
126        assert!(validate_normalized_string(&NormalizedString("no_whitespace".to_string())).is_ok());
127    }
128
129    #[test]
130    fn it_should_fail_validation() {
131        let result = validate_normalized_string(&NormalizedString("spaces and\ttabs".to_string()));
132
133        assert_eq!(
134            result,
135            Err(ValidationError::new(
136                "NormalizedString contains invalid characters \\r \\n \\t or \\r\\n",
137            ))
138        );
139    }
140}