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