noodles_fasta/record/
definition.rs1use std::{
4 error, fmt,
5 str::{self, FromStr},
6};
7
8use bstr::{BStr, BString};
9
10const PREFIX: char = '>';
11
12#[derive(Clone, Debug, Eq, PartialEq)]
17pub struct Definition {
18 name: BString,
19 description: Option<BString>,
20}
21
22impl Definition {
23 pub fn new<N>(name: N, description: Option<BString>) -> Self
32 where
33 N: Into<BString>,
34 {
35 Self {
36 name: name.into(),
37 description,
38 }
39 }
40
41 pub fn name(&self) -> &BStr {
51 self.name.as_ref()
52 }
53
54 pub fn description(&self) -> Option<&BStr> {
69 self.description.as_ref().map(|s| s.as_ref())
70 }
71}
72
73impl fmt::Display for Definition {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 write!(f, "{PREFIX}{}", self.name)?;
76
77 if let Some(description) = self.description() {
78 write!(f, " {description}")?;
79 }
80
81 Ok(())
82 }
83}
84
85#[derive(Clone, Copy, Debug, Eq, PartialEq)]
87pub enum ParseError {
88 Empty,
90 MissingPrefix,
92 MissingName,
94}
95
96impl error::Error for ParseError {}
97
98impl fmt::Display for ParseError {
99 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
100 match self {
101 Self::Empty => f.write_str("empty input"),
102 Self::MissingPrefix => write!(f, "missing prefix ('{PREFIX}')"),
103 Self::MissingName => f.write_str("missing name"),
104 }
105 }
106}
107
108impl FromStr for Definition {
109 type Err = ParseError;
110
111 fn from_str(s: &str) -> Result<Self, Self::Err> {
112 if s.is_empty() {
113 return Err(ParseError::Empty);
114 } else if !s.starts_with(PREFIX) {
115 return Err(ParseError::MissingPrefix);
116 }
117
118 let line = &s[1..];
119 let mut components = line.splitn(2, |c: char| c.is_ascii_whitespace());
120
121 let name = components
122 .next()
123 .and_then(|s| if s.is_empty() { None } else { Some(s.into()) })
124 .ok_or(ParseError::MissingName)?;
125
126 let description = components.next().map(|s| s.trim().into());
127
128 Ok(Self { name, description })
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn test_fmt() {
138 let definition = Definition::new("sq0", None);
139 assert_eq!(definition.to_string(), ">sq0");
140
141 let definition = Definition::new("sq0", Some(BString::from("LN:13")));
142 assert_eq!(definition.to_string(), ">sq0 LN:13");
143 }
144
145 #[test]
146 fn test_from_str() {
147 assert_eq!(">sq0".parse(), Ok(Definition::new("sq0", None)));
148
149 assert_eq!(
150 ">sq0 LN:13".parse(),
151 Ok(Definition::new("sq0", Some(BString::from("LN:13"))))
152 );
153
154 assert_eq!("".parse::<Definition>(), Err(ParseError::Empty));
155 assert_eq!("sq0".parse::<Definition>(), Err(ParseError::MissingPrefix));
156 assert_eq!(">".parse::<Definition>(), Err(ParseError::MissingName));
157 }
158}