orchestra_toolkit/
string255.rs

1/* Copyright 2024-2025 LEDR Technologies Inc.
2* This file is part of the Orchestra library, which helps developer use our Orchestra technology which is based on AvesTerra, owned and developped by Georgetown University, under license agreement with LEDR Technologies Inc.
3*
4* The Orchestra library is a free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version.
5*
6* The Orchestra library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
7*
8* You should have received a copy of the GNU Lesser General Public License along with the Orchestra library. If not, see <https://www.gnu.org/licenses/>.
9*
10* If you have any questions, feedback or issues about the Orchestra library, you can contact us at support@ledr.io.
11*/
12
13use ascii::AsciiString;
14use serde::{Deserialize, Serialize};
15use std::fmt::Display;
16use std::ops::Deref;
17use thiserror::Error;
18
19/// String that is at most 255 bytes long.
20#[derive(Debug, Clone, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
21#[serde(try_from = "String", into = "String", expecting = "a string")]
22pub struct String255(String);
23
24#[derive(Error, Debug)]
25#[error("String is too long. Maximum size is 255 bytes but is {0}.")]
26pub struct String255TooLongError(pub usize);
27
28impl String255 {
29    pub const NULL: Self = Self(String::new());
30
31    /// Will panic if the value fails to get created.
32    /// Use this for constants, otherwise use `try_from`.
33    pub fn unchecked(from: &str) -> Self {
34        Self::try_from(from.to_string()).expect("ShortString::unchecked failed")
35    }
36
37    pub fn from_str_truncate(s: &str) -> Self {
38        if s.len() <= 255 {
39            Self::unchecked(s)
40        } else {
41            Self::unchecked(&s[..255])
42        }
43    }
44
45    // For consistency with other types
46    pub fn is_null(&self) -> bool {
47        self.0.is_empty()
48    }
49}
50
51impl TryFrom<String> for String255 {
52    type Error = String255TooLongError;
53
54    fn try_from(value: String) -> Result<Self, Self::Error> {
55        if value.len() > 255 {
56            Err(String255TooLongError(value.len()))
57        } else {
58            Ok(Self(value))
59        }
60    }
61}
62
63impl TryFrom<AsciiString> for String255 {
64    type Error = String255TooLongError;
65
66    fn try_from(value: AsciiString) -> Result<Self, Self::Error> {
67        value.to_string().try_into()
68    }
69}
70
71impl Deref for String255 {
72    type Target = String;
73
74    fn deref(&self) -> &Self::Target {
75        &self.0
76    }
77}
78
79// Needed for Serde
80impl From<String255> for String {
81    fn from(val: String255) -> Self {
82        val.0
83    }
84}
85
86impl Display for String255 {
87    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88        self.0.fmt(f)
89    }
90}
91
92#[cfg(test)]
93mod tests {
94    use super::*;
95
96    #[test]
97    fn test_truncate_short_string() {
98        let expected = String255::unchecked("Hello");
99        let actual = String255::from_str_truncate("Hello");
100        assert_eq!(expected, actual);
101    }
102
103    #[test]
104    fn test_truncate_len_254() {
105        let str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
106        assert_eq!(str.len(), 254);
107        let expected = String255::unchecked(str);
108
109        let actual = String255::from_str_truncate(str);
110        assert_eq!(expected, actual);
111    }
112
113    #[test]
114    fn test_truncate_len_255() {
115        let str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
116        assert_eq!(str.len(), 255);
117        let expected = String255::unchecked(str);
118
119        let actual = String255::from_str_truncate(str);
120        assert_eq!(expected, actual);
121    }
122    #[test]
123    fn test_truncate_len_256() {
124        let str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
125        assert_eq!(str.len(), 256);
126        let expected = String255::unchecked("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
127        assert_eq!(expected.len(), 255);
128
129        let actual = String255::from_str_truncate(str);
130        assert_eq!(expected, actual);
131    }
132}