1use std::fmt;
2
3use noodles::gff::feature::record::Strand as GFFStrand;
4use rusqlite::{
5 ToSql,
6 types::{FromSql, FromSqlResult, ToSqlOutput, Value, ValueRef},
7};
8use serde::{Deserialize, Serialize};
9
10use crate::{errors::StrandError, gen_core_capnp};
11
12#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize, Ord, PartialOrd)]
13pub enum Strand {
14 Forward,
15 Reverse,
16 Unknown,
17 ImportantButUnknown,
18}
19
20impl Strand {
21 pub fn is_ambiguous(a: Strand) -> bool {
22 a == Strand::ImportantButUnknown || a == Strand::Unknown
23 }
24
25 pub fn is_compatible(a: Strand, b: Strand) -> bool {
26 let a_ambig = Strand::is_ambiguous(a);
27 let b_ambig = Strand::is_ambiguous(b);
28 (a_ambig || b_ambig) || a == b
29 }
30}
31
32impl ToSql for Strand {
34 fn to_sql(&self) -> rusqlite::Result<ToSqlOutput<'_>> {
35 let result = match self {
36 Strand::Forward => "+".into(),
37 Strand::Reverse => "-".into(),
38 Strand::Unknown => ".".into(),
39 Strand::ImportantButUnknown => "?".into(),
40 };
41 Ok(result)
42 }
43}
44
45impl From<Strand> for Value {
46 fn from(value: Strand) -> Value {
47 let result = match value {
48 Strand::Forward => "+",
49 Strand::Reverse => "-",
50 Strand::Unknown => ".",
51 Strand::ImportantButUnknown => "?",
52 };
53 Value::Text(result.to_string())
54 }
55}
56
57impl From<GFFStrand> for Strand {
58 fn from(value: GFFStrand) -> Strand {
59 match value {
60 GFFStrand::Forward => Strand::Forward,
61 GFFStrand::Unknown => Strand::Unknown,
62 GFFStrand::Reverse => Strand::Reverse,
63 GFFStrand::None => Strand::Unknown,
64 }
65 }
66}
67
68impl From<Strand> for GFFStrand {
69 fn from(value: Strand) -> GFFStrand {
70 match value {
71 Strand::Forward => GFFStrand::Forward,
72 Strand::Unknown => GFFStrand::Unknown,
73 Strand::Reverse => GFFStrand::Reverse,
74 Strand::ImportantButUnknown => GFFStrand::None,
75 }
76 }
77}
78
79impl FromSql for Strand {
80 fn column_result(value: ValueRef) -> FromSqlResult<Self> {
81 let result = match value.as_str() {
82 Ok("+") => Strand::Forward,
83 Ok("-") => Strand::Reverse,
84 Ok(".") => Strand::Unknown,
85 Ok("?") => Strand::ImportantButUnknown,
86 _ => panic!("Invalid entry in database"),
87 };
88 Ok(result)
89 }
90}
91
92impl TryFrom<&str> for Strand {
93 type Error = StrandError;
94
95 fn try_from(value: &str) -> Result<Self, Self::Error> {
96 match value {
97 "+" => Ok(Self::Forward),
98 "-" => Ok(Self::Reverse),
99 "." => Ok(Self::Unknown),
100 "?" => Ok(Self::ImportantButUnknown),
101 v => Err(StrandError::InvalidStrand(format!(
102 "Invalid strand entry: {v}"
103 ))),
104 }
105 }
106}
107
108impl From<Strand> for gen_core_capnp::Strand {
109 fn from(value: Strand) -> gen_core_capnp::Strand {
110 match value {
111 Strand::Forward => gen_core_capnp::Strand::Forward,
112 Strand::Unknown => gen_core_capnp::Strand::Unknown,
113 Strand::Reverse => gen_core_capnp::Strand::Reverse,
114 Strand::ImportantButUnknown => gen_core_capnp::Strand::ImportantButUnknown,
115 }
116 }
117}
118
119impl From<gen_core_capnp::Strand> for Strand {
120 fn from(value: gen_core_capnp::Strand) -> Strand {
121 match value {
122 gen_core_capnp::Strand::Forward => Strand::Forward,
123 gen_core_capnp::Strand::Unknown => Strand::Unknown,
124 gen_core_capnp::Strand::Reverse => Strand::Reverse,
125 gen_core_capnp::Strand::ImportantButUnknown => Strand::ImportantButUnknown,
126 }
127 }
128}
129
130impl fmt::Display for Strand {
131 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
132 let result = match self {
133 Strand::Forward => "+",
134 Strand::Reverse => "-",
135 Strand::Unknown => ".",
136 Strand::ImportantButUnknown => "?",
137 };
138 formatter.write_str(result)
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn test_formats() {
148 assert_eq!(format!("{v}", v = Strand::Forward), "+");
149 assert_eq!(format!("{v}", v = Strand::Reverse), "-");
150 assert_eq!(format!("{v}", v = Strand::ImportantButUnknown), "?");
151 assert_eq!(format!("{v}", v = Strand::Unknown), ".");
152 }
153
154 #[test]
155 fn test_column_conversion() {
156 assert_eq!(
157 Strand::column_result(ValueRef::Text("+".as_bytes())).unwrap(),
158 Strand::Forward
159 );
160 assert_eq!(
161 Strand::column_result(ValueRef::Text("-".as_bytes())).unwrap(),
162 Strand::Reverse
163 );
164 assert_eq!(
165 Strand::column_result(ValueRef::Text(".".as_bytes())).unwrap(),
166 Strand::Unknown
167 );
168 assert_eq!(
169 Strand::column_result(ValueRef::Text("?".as_bytes())).unwrap(),
170 Strand::ImportantButUnknown
171 );
172 }
173}