use std::sync::Arc;
use thiserror::Error;
#[derive(Error, Debug, PartialEq, Eq)]
pub enum Error {
#[error("contig name cannot be empty")]
Empty,
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Contig(Arc<str>);
impl Contig {
pub fn try_new(value: impl Into<String>) -> Result<Self> {
let s: String = value.into();
if s.is_empty() {
return Err(Error::Empty);
}
Ok(Self(Arc::from(s)))
}
pub fn new_unchecked(value: impl Into<String>) -> Self {
Self(Arc::from(value.into()))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for Contig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::str::FromStr for Contig {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Self::try_new(s)
}
}
impl TryFrom<&str> for Contig {
type Error = Error;
fn try_from(value: &str) -> Result<Self> {
Self::try_new(value)
}
}
impl TryFrom<String> for Contig {
type Error = Error;
fn try_from(value: String) -> Result<Self> {
Self::try_new(value)
}
}
impl std::ops::Deref for Contig {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn try_new_valid() {
let contig = Contig::try_new("chr1").expect("valid contig name");
assert_eq!(contig.as_str(), "chr1");
let contig = Contig::try_new("seq0").expect("valid contig name");
assert_eq!(contig.as_str(), "seq0");
let contig = Contig::try_new("X").expect("valid contig name");
assert_eq!(contig.as_str(), "X");
}
#[test]
fn try_new_empty() {
let err = Contig::try_new("").expect_err("empty contig name should fail");
assert_eq!(err, Error::Empty);
assert_eq!(err.to_string(), "contig name cannot be empty");
}
#[test]
fn new_unchecked() {
let contig = Contig::new_unchecked("chr1");
assert_eq!(contig.as_str(), "chr1");
let contig = Contig::new_unchecked("");
assert_eq!(contig.as_str(), "");
}
#[test]
fn clone_is_shallow() {
let a = Contig::new_unchecked("chr1");
let b = a.clone();
assert_eq!(a, b);
assert!(std::ptr::eq(a.as_str(), b.as_str()));
}
#[test]
fn parse() {
let contig = "chr1".parse::<Contig>().expect("contig to parse");
assert_eq!(contig.as_str(), "chr1");
}
#[test]
fn parse_empty() {
let err = "".parse::<Contig>().expect_err("empty string should fail");
assert_eq!(err, Error::Empty);
}
#[test]
fn try_from_str() {
let contig = Contig::try_from("chr1").expect("valid contig");
assert_eq!(contig.as_str(), "chr1");
let err = Contig::try_from("").expect_err("empty should fail");
assert_eq!(err, Error::Empty);
}
#[test]
fn try_from_string() {
let contig = Contig::try_from(String::from("chr1")).expect("valid contig");
assert_eq!(contig.as_str(), "chr1");
let err = Contig::try_from(String::from("")).expect_err("empty should fail");
assert_eq!(err, Error::Empty);
}
}