Files
obikmer/src/obitaxonomy/src/segment.rs
T
Eric Coissac 9356be4ec0 feat: introduce obitaxonomy crate for hierarchical taxonomy parsing
Adds the `obitaxonomy` crate to parse and validate hierarchical taxonomy paths using a strict `taxonomy:/name@rank/...` syntax. Replaces generic string-based path matching in predicates with structured `TaxPath` and `TaxPattern` types, enforcing explicit anchor constraints and rank-aware semantics. Updates filtering documentation to clarify optional leading slashes and segment-boundary matching rules.
2026-06-22 10:24:04 +02:00

50 lines
1.4 KiB
Rust

use std::fmt;
use crate::error::TaxError;
/// A single node in a taxonomy path: a name and an optional rank.
///
/// Neither `name` nor `rank` may contain `@` (reserved separator).
/// Serialised form: `name` or `name@rank`.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TaxSegment {
name: String,
rank: Option<String>,
}
impl TaxSegment {
pub fn parse(raw: &str) -> Result<Self, TaxError> {
let parts: Vec<&str> = raw.splitn(3, '@').collect();
let (name_raw, rank_raw) = match parts.as_slice() {
[name] => (*name, None),
[name, rank] => (*name, Some(*rank)),
_ => return Err(TaxError::AmbiguousRank { segment: raw.to_string() }),
};
if name_raw.is_empty() {
return Err(TaxError::EmptySegmentName);
}
let rank = match rank_raw {
None => None,
Some("") => return Err(TaxError::EmptyRankName { segment: raw.to_string() }),
Some(r) => Some(r.to_string()),
};
Ok(Self { name: name_raw.to_string(), rank })
}
pub fn name(&self) -> &str { &self.name }
pub fn rank(&self) -> Option<&str> { self.rank.as_deref() }
}
impl fmt::Display for TaxSegment {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.rank {
None => write!(f, "{}", self.name),
Some(r) => write!(f, "{}@{}", self.name, r),
}
}
}