use crate::codec::{DecompRef, Shape, Stroke};
const SEED_TXT: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/data/seed.txt"));
#[derive(Debug, Clone)]
pub struct Decomp {
pub zigen: Vec<char>,
pub strokes: Vec<Stroke>,
pub shape: Shape,
}
impl Decomp {
#[inline]
pub fn as_ref(&self) -> DecompRef<'_> {
DecompRef {
zigen: &self.zigen,
strokes: &self.strokes,
shape: self.shape,
}
}
pub fn first_stroke(&self) -> Option<Stroke> {
self.strokes.first().copied()
}
pub fn last_stroke(&self) -> Option<Stroke> {
self.strokes.last().copied()
}
}
pub fn parse_seed(src: &str) -> Vec<(char, Decomp)> {
let mut out = Vec::new();
for raw in src.lines() {
let line = raw.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let mut parts = line.splitn(4, '\t');
let (Some(ch), Some(zg), Some(strokes_field), Some(shape)) =
(parts.next(), parts.next(), parts.next(), parts.next())
else {
continue;
};
let ch = ch.trim();
if ch.chars().count() != 1 {
continue;
}
let ch = ch.chars().next().unwrap();
let zigen: Vec<char> = zg.split_whitespace().flat_map(|s| s.chars()).collect();
if zigen.is_empty() {
continue;
}
let strokes: Vec<Stroke> = strokes_field
.split_whitespace()
.filter_map(|s| s.parse::<u8>().ok().and_then(Stroke::from_u8))
.collect();
if strokes.is_empty() {
continue;
}
let Ok(p) = shape.trim().parse::<u8>() else {
continue;
};
let Some(shape) = Shape::from_u8(p) else {
continue;
};
out.push((
ch,
Decomp {
zigen,
strokes,
shape,
},
));
}
out
}
pub fn embedded_seed() -> Vec<(char, Decomp)> {
parse_seed(SEED_TXT)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parses_seed_entries() {
let entries = embedded_seed();
assert!(entries.len() >= 25);
let (ch, d) = &entries[0];
assert_eq!(*ch, '王');
assert_eq!(d.zigen, vec!['王']);
assert_eq!(d.first_stroke(), Some(Stroke::Heng));
}
}