gxfkit_core/
attributes.rs1#[derive(Debug, Default, Clone, PartialEq, Eq)]
13pub struct Attributes {
14 pub pairs: Vec<(String, String)>,
15}
16
17impl Attributes {
18 pub fn parse_gff3(col: &str) -> Self {
20 let mut pairs = Vec::new();
21 if col == "." || col.is_empty() {
22 return Self { pairs };
23 }
24 for field in col.split(';') {
25 let field = field.trim();
26 if field.is_empty() {
27 continue;
28 }
29 match field.split_once('=') {
30 Some((k, v)) => pairs.push((k.trim().to_string(), v.to_string())),
31 None => pairs.push((field.to_string(), String::new())),
33 }
34 }
35 Self { pairs }
36 }
37
38 pub fn get(&self, key: &str) -> Option<&str> {
39 self.pairs
40 .iter()
41 .find(|(k, _)| k == key)
42 .map(|(_, v)| v.as_str())
43 }
44
45 pub fn first(&self, key: &str) -> Option<&str> {
47 self.get(key).map(|v| v.split(',').next().unwrap_or(v))
48 }
49
50 pub fn is_empty(&self) -> bool {
51 self.pairs.is_empty()
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn parses_basic() {
61 let a = Attributes::parse_gff3("ID=gene1;Name=BRCA2;Parent=x,y");
62 assert_eq!(a.get("ID"), Some("gene1"));
63 assert_eq!(a.first("Parent"), Some("x"));
64 assert_eq!(a.get("missing"), None);
65 }
66
67 #[test]
68 fn handles_dot_and_empty() {
69 assert!(Attributes::parse_gff3(".").is_empty());
70 assert!(Attributes::parse_gff3("").is_empty());
71 }
72}