omics_coordinate/
contig.rs1use std::sync::Arc;
4
5use thiserror::Error;
6
7#[derive(Error, Debug, PartialEq, Eq)]
13pub enum Error {
14 #[error("contig name cannot be empty")]
16 Empty,
17}
18
19pub type Result<T> = std::result::Result<T, Error>;
21
22#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
39pub struct Contig(Arc<str>);
40
41impl Contig {
42 pub fn try_new(value: impl Into<String>) -> Result<Self> {
60 let s: String = value.into();
61 if s.is_empty() {
62 return Err(Error::Empty);
63 }
64 Ok(Self(Arc::from(s)))
65 }
66
67 pub fn new_unchecked(value: impl Into<String>) -> Self {
84 Self(Arc::from(value.into()))
85 }
86
87 pub fn as_str(&self) -> &str {
89 &self.0
90 }
91}
92
93impl std::fmt::Display for Contig {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 write!(f, "{}", self.0)
100 }
101}
102
103impl std::str::FromStr for Contig {
104 type Err = Error;
105
106 fn from_str(s: &str) -> Result<Self> {
107 Self::try_new(s)
108 }
109}
110
111impl TryFrom<&str> for Contig {
112 type Error = Error;
113
114 fn try_from(value: &str) -> Result<Self> {
115 Self::try_new(value)
116 }
117}
118
119impl TryFrom<String> for Contig {
120 type Error = Error;
121
122 fn try_from(value: String) -> Result<Self> {
123 Self::try_new(value)
124 }
125}
126
127impl std::ops::Deref for Contig {
128 type Target = str;
129
130 fn deref(&self) -> &Self::Target {
131 &self.0
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn try_new_valid() {
141 let contig = Contig::try_new("chr1").expect("valid contig name");
142 assert_eq!(contig.as_str(), "chr1");
143
144 let contig = Contig::try_new("seq0").expect("valid contig name");
145 assert_eq!(contig.as_str(), "seq0");
146
147 let contig = Contig::try_new("X").expect("valid contig name");
148 assert_eq!(contig.as_str(), "X");
149 }
150
151 #[test]
152 fn try_new_empty() {
153 let err = Contig::try_new("").expect_err("empty contig name should fail");
154 assert_eq!(err, Error::Empty);
155 assert_eq!(err.to_string(), "contig name cannot be empty");
156 }
157
158 #[test]
159 fn new_unchecked() {
160 let contig = Contig::new_unchecked("chr1");
161 assert_eq!(contig.as_str(), "chr1");
162
163 let contig = Contig::new_unchecked("");
165 assert_eq!(contig.as_str(), "");
166 }
167
168 #[test]
169 fn clone_is_shallow() {
170 let a = Contig::new_unchecked("chr1");
171 let b = a.clone();
172 assert_eq!(a, b);
173 assert!(std::ptr::eq(a.as_str(), b.as_str()));
175 }
176
177 #[test]
178 fn parse() {
179 let contig = "chr1".parse::<Contig>().expect("contig to parse");
180 assert_eq!(contig.as_str(), "chr1");
181 }
182
183 #[test]
184 fn parse_empty() {
185 let err = "".parse::<Contig>().expect_err("empty string should fail");
186 assert_eq!(err, Error::Empty);
187 }
188
189 #[test]
190 fn try_from_str() {
191 let contig = Contig::try_from("chr1").expect("valid contig");
192 assert_eq!(contig.as_str(), "chr1");
193
194 let err = Contig::try_from("").expect_err("empty should fail");
195 assert_eq!(err, Error::Empty);
196 }
197
198 #[test]
199 fn try_from_string() {
200 let contig = Contig::try_from(String::from("chr1")).expect("valid contig");
201 assert_eq!(contig.as_str(), "chr1");
202
203 let err = Contig::try_from(String::from("")).expect_err("empty should fail");
204 assert_eq!(err, Error::Empty);
205 }
206}