use thiserror::Error;
const _: () = {
assert!(size_of::<Strand>() == 1);
const fn is_copy<T: Copy>() {}
is_copy::<Strand>();
};
#[derive(Error, Debug, PartialEq, Eq)]
pub enum ParseError {
#[error("invalid strand: {value}")]
Invalid {
value: String,
},
}
pub type ParseResult<T> = std::result::Result<T, ParseError>;
#[derive(Error, Debug, PartialEq, Eq)]
pub enum Error {
#[error("parse error: {0}")]
Parse(#[from] ParseError),
}
pub type Result<T> = std::result::Result<T, Error>;
#[repr(u8)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum Strand {
Positive,
Negative,
}
impl Strand {
pub fn complement(&self) -> Strand {
match self {
Strand::Positive => Strand::Negative,
Strand::Negative => Strand::Positive,
}
}
}
impl std::str::FromStr for Strand {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s {
"+" => Ok(Strand::Positive),
"-" => Ok(Strand::Negative),
_ => Err(Error::Parse(ParseError::Invalid {
value: s.to_string(),
})),
}
}
}
impl TryFrom<&str> for Strand {
type Error = Error;
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
value.parse()
}
}
impl std::fmt::Display for Strand {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Strand::Positive => write!(f, "+"),
Strand::Negative => write!(f, "-"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse() {
let s = "+".parse::<Strand>().unwrap();
assert_eq!(s, Strand::Positive);
let s = "-".parse::<Strand>().unwrap();
assert_eq!(s, Strand::Negative);
let err = "a".parse::<Strand>().unwrap_err();
assert_eq!(err.to_string(), "parse error: invalid strand: a");
}
#[test]
fn serialize() {
assert_eq!(Strand::Positive.to_string(), "+");
assert_eq!(Strand::Negative.to_string(), "-");
}
#[test]
fn complement() {
assert_eq!(Strand::Positive.complement(), Strand::Negative);
assert_eq!(Strand::Negative.complement(), Strand::Positive);
}
}