1use regex::Regex;
4use serde::{Deserialize, Serialize};
5use std::fmt;
6use std::str::FromStr;
7use thiserror::Error;
8
9#[derive(Error, Debug, Clone, PartialEq)]
11pub enum IdError {
12 #[error("Invalid ID format: {0}")]
13 InvalidFormat(String),
14
15 #[error("Empty ID not allowed")]
16 EmptyId,
17
18 #[error("Invalid component: {0}")]
19 InvalidComponent(String),
20
21 #[error("ID overflow: cannot increment {0}")]
22 Overflow(String),
23
24 #[error("No parent for root ID: {0}")]
25 NoParent(String),
26
27 #[error("Parsing error: {0}")]
28 ParseError(String),
29}
30
31pub type IdResult<T> = Result<T, IdError>;
33
34#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
36pub enum IdComponent {
37 Numeric(u32),
39 Alpha(String),
41}
42
43impl IdComponent {
44 pub fn numeric(value: u32) -> Self {
46 Self::Numeric(value)
47 }
48
49 pub fn alpha<S: Into<String>>(value: S) -> IdResult<Self> {
51 let value = value.into();
52 if value.is_empty() {
53 return Err(IdError::InvalidComponent(
54 "Empty alphabetic component".to_string(),
55 ));
56 }
57
58 if !value.chars().all(|c| c.is_ascii_lowercase()) {
59 return Err(IdError::InvalidComponent(format!(
60 "Alphabetic component must contain only lowercase letters: {}",
61 value
62 )));
63 }
64
65 Ok(Self::Alpha(value))
66 }
67
68 pub fn increment(&self) -> IdResult<Self> {
70 match self {
71 Self::Numeric(n) => {
72 if *n == u32::MAX {
73 Err(IdError::Overflow(format!("Numeric component: {}", n)))
74 } else {
75 Ok(Self::Numeric(n + 1))
76 }
77 }
78 Self::Alpha(s) => {
79 let incremented = increment_alpha_string(s)?;
80 Ok(Self::Alpha(incremented))
81 }
82 }
83 }
84
85 pub fn is_numeric(&self) -> bool {
87 matches!(self, Self::Numeric(_))
88 }
89
90 pub fn is_alpha(&self) -> bool {
92 matches!(self, Self::Alpha(_))
93 }
94
95 pub fn as_str(&self) -> String {
97 match self {
98 Self::Numeric(n) => n.to_string(),
99 Self::Alpha(s) => s.clone(),
100 }
101 }
102}
103
104impl fmt::Display for IdComponent {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 match self {
107 Self::Numeric(n) => write!(f, "{}", n),
108 Self::Alpha(s) => write!(f, "{}", s),
109 }
110 }
111}
112
113impl FromStr for IdComponent {
114 type Err = IdError;
115
116 fn from_str(s: &str) -> IdResult<Self> {
117 if s.is_empty() {
118 return Err(IdError::InvalidComponent("Empty component".to_string()));
119 }
120
121 if let Ok(num) = s.parse::<u32>() {
123 return Ok(Self::Numeric(num));
124 }
125
126 Self::alpha(s)
128 }
129}
130
131#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
133pub struct Id {
134 components: Vec<IdComponent>,
135}
136
137impl Id {
138 pub fn new(components: Vec<IdComponent>) -> IdResult<Self> {
140 if components.is_empty() {
141 return Err(IdError::EmptyId);
142 }
143
144 for (i, component) in components.iter().enumerate() {
146 let should_be_numeric = i % 2 == 0; if should_be_numeric && !component.is_numeric() {
149 return Err(IdError::InvalidFormat(format!(
150 "Component {} should be numeric but got: {}",
151 i, component
152 )));
153 }
154
155 if !should_be_numeric && !component.is_alpha() {
156 return Err(IdError::InvalidFormat(format!(
157 "Component {} should be alphabetic but got: {}",
158 i, component
159 )));
160 }
161 }
162
163 Ok(Self { components })
164 }
165
166 pub fn from_number(n: u32) -> Self {
168 Self {
169 components: vec![IdComponent::Numeric(n)],
170 }
171 }
172
173 pub fn parse<S: AsRef<str>>(s: S) -> IdResult<Self> {
175 let s = s.as_ref();
176 if s.is_empty() {
177 return Err(IdError::EmptyId);
178 }
179
180 let components = parse_id_string(s)?;
181 Self::new(components)
182 }
183
184 pub fn components(&self) -> &[IdComponent] {
186 &self.components
187 }
188
189 pub fn depth(&self) -> usize {
191 self.components.len()
192 }
193
194 pub fn is_root(&self) -> bool {
196 self.components.len() == 1 && self.components[0].is_numeric()
197 }
198
199 pub fn parent(&self) -> IdResult<Option<Self>> {
201 if self.components.len() <= 1 {
202 return Ok(None); }
204
205 let mut parent_components = self.components.clone();
206 parent_components.pop();
207
208 Ok(Some(Self {
209 components: parent_components,
210 }))
211 }
212
213 pub fn next_sibling(&self) -> IdResult<Self> {
215 if self.components.is_empty() {
216 return Err(IdError::EmptyId);
217 }
218
219 let mut sibling_components = self.components.clone();
220 let last_idx = sibling_components.len() - 1;
221 sibling_components[last_idx] = sibling_components[last_idx].increment()?;
222
223 Ok(Self {
224 components: sibling_components,
225 })
226 }
227
228 pub fn first_child(&self) -> Self {
230 let mut child_components = self.components.clone();
231
232 let next_component = if self.components.len() % 2 == 0 {
234 IdComponent::Numeric(1)
236 } else {
237 IdComponent::Alpha("a".to_string())
239 };
240
241 child_components.push(next_component);
242 Self {
243 components: child_components,
244 }
245 }
246
247 pub fn is_ancestor_of(&self, other: &Id) -> bool {
249 if self.components.len() >= other.components.len() {
250 return false; }
252
253 self.components
255 .iter()
256 .zip(other.components.iter())
257 .all(|(a, b)| a == b)
258 }
259
260 pub fn is_descendant_of(&self, other: &Id) -> bool {
262 other.is_ancestor_of(self)
263 }
264
265 pub fn is_sibling_of(&self, other: &Id) -> bool {
267 if self.components.len() != other.components.len() {
268 return false; }
270
271 if self.components.len() <= 1 {
272 return true; }
274
275 self.components[..self.components.len() - 1]
277 .iter()
278 .zip(other.components[..other.components.len() - 1].iter())
279 .all(|(a, b)| a == b)
280 }
281
282 pub fn ancestors(&self) -> Vec<Id> {
284 let mut ancestors = Vec::new();
285
286 for i in 1..self.components.len() {
287 let ancestor_components = self.components[..i].to_vec();
288 ancestors.push(Id {
289 components: ancestor_components,
290 });
291 }
292
293 ancestors
294 }
295
296 pub fn to_string(&self) -> String {
298 self.components
299 .iter()
300 .map(|c| c.as_str())
301 .collect::<Vec<_>>()
302 .join("")
303 }
304}
305
306impl fmt::Display for Id {
307 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308 write!(f, "{}", self.to_string())
309 }
310}
311
312impl FromStr for Id {
313 type Err = IdError;
314
315 fn from_str(s: &str) -> IdResult<Self> {
316 Self::parse(s)
317 }
318}
319
320pub struct IdManager<F> {
322 config: IdConfig,
323 existence_checker: F,
324}
325
326pub use crate::config::IdConfig;
331
332impl<F> IdManager<F>
333where
334 F: Fn(&str) -> bool, {
336 pub fn new(config: IdConfig, existence_checker: F) -> Self {
338 Self {
339 config,
340 existence_checker,
341 }
342 }
343
344 pub fn extract_from_filename(&self, filename: &str) -> Option<Id> {
346 let patterns = self.get_filename_patterns();
347
348 for pattern in patterns {
349 if let Some(captures) = pattern.captures(filename) {
350 if let Some(id_match) = captures.get(1) {
351 if let Ok(id) = Id::parse(id_match.as_str()) {
352 return Some(id);
353 }
354 }
355 }
356 }
357
358 None
359 }
360
361 pub fn next_available_sibling(&self, current_id: &Id) -> IdResult<Id> {
363 let mut candidate = current_id.next_sibling()?;
364
365 while (self.existence_checker)(&candidate.to_string()) {
367 candidate = candidate.next_sibling()?;
368 }
369
370 Ok(candidate)
371 }
372
373 pub fn next_available_child(&self, parent_id: &Id) -> Id {
375 let mut candidate = parent_id.first_child();
376
377 while (self.existence_checker)(&candidate.to_string()) {
379 if let Ok(next) = candidate.next_sibling() {
381 candidate = next;
382 } else {
383 break;
385 }
386 }
387
388 candidate
389 }
390
391 pub fn validate_id(&self, id_str: &str) -> IdResult<Id> {
393 Id::parse(id_str)
394 }
395
396 pub fn id_exists(&self, id: &Id) -> bool {
398 (self.existence_checker)(&id.to_string())
399 }
400
401 fn get_filename_patterns(&self) -> Vec<Regex> {
403 let id_pattern = if self.config.allow_unicode {
404 r"([0-9\p{L}]+(?:[0-9\p{L}]*)*)"
405 } else {
406 r"([0-9a-z]+(?:[0-9a-z]*)*)"
407 };
408
409 let mut patterns = Vec::new();
410
411 match self.config.match_rule.as_str() {
412 "strict" => {
413 patterns.push(Regex::new(&format!(r"^{}$", id_pattern)).unwrap());
415 }
416 "separator" => {
417 let escaped_sep = regex::escape(&self.config.separator);
419 patterns.push(Regex::new(&format!(r"^{}{}.*", id_pattern, escaped_sep)).unwrap());
420 }
421 "fuzzy" => {
422 patterns.push(Regex::new(&format!(r"^{}[^0-9a-z].*", id_pattern)).unwrap());
424 patterns.push(Regex::new(&format!(r"^{}$", id_pattern)).unwrap());
426 }
427 _ => {
428 patterns.push(Regex::new(&format!(r"^{}$", id_pattern)).unwrap());
430 }
431 }
432
433 patterns
434 }
435}
436
437fn parse_id_string(s: &str) -> IdResult<Vec<IdComponent>> {
439 if s.is_empty() {
440 return Err(IdError::EmptyId);
441 }
442
443 let mut components = Vec::new();
444 let mut current = String::new();
445 let mut expecting_numeric = true; for ch in s.chars() {
448 if ch.is_ascii_digit() {
449 if !expecting_numeric && !current.is_empty() {
450 components.push(IdComponent::alpha(current.clone())?);
452 current.clear();
453 expecting_numeric = true;
454 }
455 current.push(ch);
456 } else if ch.is_ascii_lowercase() {
457 if expecting_numeric && !current.is_empty() {
458 let num: u32 = current
460 .parse()
461 .map_err(|_| IdError::ParseError(format!("Invalid number: {}", current)))?;
462 components.push(IdComponent::Numeric(num));
463 current.clear();
464 expecting_numeric = false;
465 }
466 current.push(ch);
467 } else {
468 return Err(IdError::InvalidFormat(format!(
469 "Invalid character '{}' in ID: {}",
470 ch, s
471 )));
472 }
473 }
474
475 if !current.is_empty() {
477 if expecting_numeric {
478 let num: u32 = current
479 .parse()
480 .map_err(|_| IdError::ParseError(format!("Invalid number: {}", current)))?;
481 components.push(IdComponent::Numeric(num));
482 } else {
483 components.push(IdComponent::alpha(current)?);
484 }
485 }
486
487 if components.is_empty() {
488 return Err(IdError::EmptyId);
489 }
490
491 Ok(components)
492}
493
494fn increment_alpha_string(s: &str) -> IdResult<String> {
496 if s.is_empty() {
497 return Err(IdError::InvalidComponent(
498 "Empty alphabetic string".to_string(),
499 ));
500 }
501
502 let mut chars: Vec<char> = s.chars().collect();
503 let mut carry = true;
504
505 for i in (0..chars.len()).rev() {
507 if !carry {
508 break;
509 }
510
511 if chars[i] == 'z' {
512 chars[i] = 'a';
513 } else {
515 chars[i] = (chars[i] as u8 + 1) as char;
516 carry = false;
517 }
518 }
519
520 if carry {
522 chars.insert(0, 'a');
523 }
524
525 Ok(chars.into_iter().collect())
526}
527
528#[cfg(test)]
529mod tests {
530 use super::*;
531
532 #[test]
533 fn test_id_component_creation() {
534 let num = IdComponent::numeric(42);
535 assert_eq!(num.as_str(), "42");
536 assert!(num.is_numeric());
537 assert!(!num.is_alpha());
538
539 let alpha = IdComponent::alpha("abc").unwrap();
540 assert_eq!(alpha.as_str(), "abc");
541 assert!(alpha.is_alpha());
542 assert!(!alpha.is_numeric());
543
544 assert!(IdComponent::alpha("").is_err());
546 assert!(IdComponent::alpha("ABC").is_err());
547 assert!(IdComponent::alpha("a1b").is_err());
548 assert!(IdComponent::alpha("123").is_err());
549 }
550
551 #[test]
552 fn test_component_increment() {
553 let num = IdComponent::numeric(5);
555 let next = num.increment().unwrap();
556 assert_eq!(next, IdComponent::numeric(6));
557
558 let max_num = IdComponent::numeric(u32::MAX);
560 assert!(max_num.increment().is_err());
561
562 let alpha = IdComponent::alpha("a").unwrap();
564 let next = alpha.increment().unwrap();
565 assert_eq!(next, IdComponent::alpha("b").unwrap());
566
567 let z = IdComponent::alpha("z").unwrap();
568 let next = z.increment().unwrap();
569 assert_eq!(next, IdComponent::alpha("aa").unwrap());
570
571 let az = IdComponent::alpha("az").unwrap();
572 let next = az.increment().unwrap();
573 assert_eq!(next, IdComponent::alpha("ba").unwrap());
574 }
575
576 #[test]
577 fn test_alpha_string_increment() {
578 assert_eq!(increment_alpha_string("a").unwrap(), "b");
579 assert_eq!(increment_alpha_string("z").unwrap(), "aa");
580 assert_eq!(increment_alpha_string("az").unwrap(), "ba");
581 assert_eq!(increment_alpha_string("zz").unwrap(), "aaa");
582 assert_eq!(increment_alpha_string("abc").unwrap(), "abd");
583 assert_eq!(increment_alpha_string("abz").unwrap(), "aca");
584
585 assert!(increment_alpha_string("").is_err());
587 }
588
589 #[test]
590 fn test_id_parsing() {
591 let id = Id::parse("1").unwrap();
593 assert_eq!(id.components().len(), 1);
594 assert!(id.is_root());
595 assert_eq!(id.depth(), 1);
596
597 let id = Id::parse("1a").unwrap();
598 assert_eq!(id.components().len(), 2);
599 assert!(!id.is_root());
600 assert_eq!(id.depth(), 2);
601
602 let id = Id::parse("1a2b3c").unwrap();
603 assert_eq!(id.components().len(), 6);
604 assert_eq!(id.depth(), 6);
605
606 let id = Id::parse("42z123a5").unwrap();
608 assert_eq!(id.components().len(), 4);
609 assert_eq!(id.components()[0], IdComponent::numeric(42));
610 assert_eq!(id.components()[1], IdComponent::alpha("z").unwrap());
611 assert_eq!(id.components()[2], IdComponent::numeric(123));
612 assert_eq!(id.components()[3], IdComponent::alpha("a").unwrap());
613
614 assert!(Id::parse("").is_err());
616 assert!(Id::parse("a").is_err()); assert!(Id::parse("1A").is_err()); assert!(Id::parse("1-2").is_err()); assert!(Id::parse("1 a").is_err()); }
621
622 #[test]
623 fn test_id_display() {
624 let id = Id::parse("1a2b").unwrap();
625 assert_eq!(id.to_string(), "1a2b");
626 assert_eq!(format!("{}", id), "1a2b");
627 }
628
629 #[test]
630 fn test_id_from_str() {
631 let id: Id = "1a2".parse().unwrap();
632 assert_eq!(id.to_string(), "1a2");
633
634 let result: Result<Id, _> = "invalid".parse();
635 assert!(result.is_err());
636 }
637
638 #[test]
639 fn test_id_relationships() {
640 let root = Id::parse("1").unwrap();
641 let child = Id::parse("1a").unwrap();
642 let grandchild = Id::parse("1a2").unwrap();
643 let sibling = Id::parse("2").unwrap();
644 let child_sibling = Id::parse("1b").unwrap();
645
646 assert_eq!(child.parent().unwrap(), Some(root.clone()));
648 assert_eq!(grandchild.parent().unwrap(), Some(child.clone()));
649 assert_eq!(root.parent().unwrap(), None);
650
651 assert!(root.is_ancestor_of(&child));
653 assert!(root.is_ancestor_of(&grandchild));
654 assert!(child.is_ancestor_of(&grandchild));
655 assert!(!child.is_ancestor_of(&root));
656 assert!(!root.is_ancestor_of(&sibling));
657
658 assert!(child.is_descendant_of(&root));
659 assert!(grandchild.is_descendant_of(&root));
660 assert!(grandchild.is_descendant_of(&child));
661 assert!(!root.is_descendant_of(&child));
662
663 assert!(root.is_sibling_of(&sibling));
665 assert!(child.is_sibling_of(&child_sibling));
666 assert!(!root.is_sibling_of(&child));
667 assert!(!child.is_sibling_of(&grandchild));
668 }
669
670 #[test]
671 fn test_ancestors() {
672 let id = Id::parse("1a2b").unwrap();
673 let ancestors = id.ancestors();
674
675 assert_eq!(ancestors.len(), 3);
676 assert_eq!(ancestors[0].to_string(), "1");
677 assert_eq!(ancestors[1].to_string(), "1a");
678 assert_eq!(ancestors[2].to_string(), "1a2");
679
680 let root = Id::parse("1").unwrap();
681 assert_eq!(root.ancestors().len(), 0);
682 }
683
684 #[test]
685 fn test_id_generation() {
686 let id = Id::parse("1a2").unwrap();
687
688 let sibling = id.next_sibling().unwrap();
690 assert_eq!(sibling.to_string(), "1a3");
691
692 let child = id.first_child();
694 assert_eq!(child.to_string(), "1a2a"); let grandchild = child.first_child();
698 assert_eq!(grandchild.to_string(), "1a2a1"); let root = Id::parse("1").unwrap();
702 let root_child = root.first_child();
703 assert_eq!(root_child.to_string(), "1a"); }
705
706 #[test]
707 fn test_id_from_number() {
708 let id = Id::from_number(42);
709 assert_eq!(id.to_string(), "42");
710 assert!(id.is_root());
711 assert_eq!(id.depth(), 1);
712 }
713
714 #[test]
715 fn test_id_manager_with_default_config() {
716 let config = IdConfig::default();
717 let manager = IdManager::new(config, |_| false);
718
719 assert_eq!(
721 manager
722 .extract_from_filename("1a2_note.md")
723 .map(|id| id.to_string()),
724 Some("1a2".to_string())
725 );
726 assert_eq!(
727 manager
728 .extract_from_filename("1a2-note.md")
729 .map(|id| id.to_string()),
730 Some("1a2".to_string())
731 );
732 assert_eq!(
733 manager
734 .extract_from_filename("1a2.md")
735 .map(|id| id.to_string()),
736 Some("1a2".to_string())
737 );
738 }
739
740 #[test]
741 fn test_id_manager_strict_matching() {
742 let config = IdConfig {
743 match_rule: "strict".to_string(),
744 separator: " - ".to_string(),
745 allow_unicode: false,
746 max_depth: 10,
747 };
748 let manager = IdManager::new(config, |_| false);
749
750 assert_eq!(
752 manager
753 .extract_from_filename("1a2")
754 .map(|id| id.to_string()),
755 Some("1a2".to_string())
756 );
757 assert_eq!(
758 manager.extract_from_filename("1a2.md"),
759 None );
761 assert_eq!(manager.extract_from_filename("1a2-note.md"), None);
762 }
763
764 #[test]
765 fn test_id_manager_separator_matching() {
766 let config = IdConfig {
767 match_rule: "separator".to_string(),
768 separator: " - ".to_string(),
769 allow_unicode: false,
770 max_depth: 10,
771 };
772 let manager = IdManager::new(config, |_| false);
773
774 assert_eq!(
775 manager
776 .extract_from_filename("1a2 - My Note.md")
777 .map(|id| id.to_string()),
778 Some("1a2".to_string())
779 );
780 assert_eq!(
781 manager
782 .extract_from_filename("1a2 - Another Note Title.md")
783 .map(|id| id.to_string()),
784 Some("1a2".to_string())
785 );
786 assert_eq!(manager.extract_from_filename("1a2.md"), None);
788 }
789
790 #[test]
791 fn test_id_manager_generation_with_existing_ids() {
792 use std::collections::HashSet;
793
794 let mut existing_ids = HashSet::new();
795 existing_ids.insert("1".to_string());
796 existing_ids.insert("1a".to_string());
797 existing_ids.insert("2".to_string());
798 existing_ids.insert("3".to_string());
799
800 let config = IdConfig::default();
801 let manager = IdManager::new(config, |id: &str| existing_ids.contains(id));
802
803 let current = Id::parse("1").unwrap();
805 let next_sibling = manager.next_available_sibling(¤t).unwrap();
806 assert_eq!(next_sibling.to_string(), "4");
807
808 let next_child = manager.next_available_child(¤t);
810 assert_eq!(next_child.to_string(), "1b");
811
812 existing_ids.insert("1b".to_string());
814 existing_ids.insert("1c".to_string());
815 let next_child = manager.next_available_child(¤t);
816 assert_eq!(next_child.to_string(), "1d");
817 }
818
819 #[test]
820 fn test_id_validation() {
821 let config = IdConfig::default();
822 let manager = IdManager::new(config, |_| false);
823
824 assert!(manager.validate_id("1").is_ok());
826 assert!(manager.validate_id("1a").is_ok());
827 assert!(manager.validate_id("1a2b3c").is_ok());
828 assert!(manager.validate_id("42z").is_ok());
829
830 assert!(manager.validate_id("").is_err());
832 assert!(manager.validate_id("a").is_err());
833 assert!(manager.validate_id("1A").is_err());
834 assert!(manager.validate_id("1-2").is_err());
835 }
836
837 #[test]
838 fn test_id_exists() {
839 use std::collections::HashSet;
840
841 let mut existing_ids = HashSet::new();
842 existing_ids.insert("1".to_string());
843 existing_ids.insert("1a2".to_string());
844
845 let config = IdConfig::default();
846 let manager = IdManager::new(config, |id: &str| existing_ids.contains(id));
847
848 let id1 = Id::parse("1").unwrap();
849 let id2 = Id::parse("1a2").unwrap();
850 let id3 = Id::parse("2").unwrap();
851
852 assert!(manager.id_exists(&id1));
853 assert!(manager.id_exists(&id2));
854 assert!(!manager.id_exists(&id3));
855 }
856
857 #[test]
858 fn test_edge_cases() {
859 let zz = IdComponent::alpha("zz").unwrap();
861 let incremented = zz.increment().unwrap();
862 assert_eq!(incremented, IdComponent::alpha("aaa").unwrap());
863
864 let zzz = IdComponent::alpha("zzz").unwrap();
865 let incremented = zzz.increment().unwrap();
866 assert_eq!(incremented, IdComponent::alpha("aaaa").unwrap());
867
868 let complex = Id::parse("999z999z999").unwrap();
870 assert_eq!(complex.depth(), 5);
871 assert!(complex.is_descendant_of(&Id::parse("999").unwrap()));
872 assert!(complex.is_descendant_of(&Id::parse("999z").unwrap()));
873 assert!(complex.is_descendant_of(&Id::parse("999z999").unwrap()));
874 assert!(complex.is_descendant_of(&Id::parse("999z999z").unwrap()));
875
876 let id = Id::parse("999z999z999").unwrap();
878 let sibling = id.next_sibling().unwrap();
879 assert_eq!(sibling.to_string(), "999z999z1000");
880 }
881
882 #[test]
883 fn test_component_parsing() {
884 let comp: IdComponent = "42".parse().unwrap();
886 assert_eq!(comp, IdComponent::numeric(42));
887
888 let comp: IdComponent = "0".parse().unwrap();
889 assert_eq!(comp, IdComponent::numeric(0));
890
891 let comp: IdComponent = "abc".parse().unwrap();
893 assert_eq!(comp, IdComponent::alpha("abc").unwrap());
894
895 let comp: IdComponent = "z".parse().unwrap();
896 assert_eq!(comp, IdComponent::alpha("z").unwrap());
897
898 let result: Result<IdComponent, _> = "".parse();
900 assert!(result.is_err());
901
902 let result: Result<IdComponent, _> = "ABC".parse();
903 assert!(result.is_err());
904
905 let result: Result<IdComponent, _> = "a1b".parse();
906 assert!(result.is_err());
907 }
908
909 #[test]
910 fn test_parse_id_string_function() {
911 let components = parse_id_string("1a2b").unwrap();
913 assert_eq!(components.len(), 4);
914 assert_eq!(components[0], IdComponent::numeric(1));
915 assert_eq!(components[1], IdComponent::alpha("a").unwrap());
916 assert_eq!(components[2], IdComponent::numeric(2));
917 assert_eq!(components[3], IdComponent::alpha("b").unwrap());
918
919 let components = parse_id_string("42").unwrap();
921 assert_eq!(components.len(), 1);
922 assert_eq!(components[0], IdComponent::numeric(42));
923
924 let components = parse_id_string("z").unwrap();
925 assert_eq!(components.len(), 1);
926 assert_eq!(components[0], IdComponent::alpha("z").unwrap());
927
928 assert!(parse_id_string("").is_err());
930 assert!(parse_id_string("1A").is_err());
931 assert!(parse_id_string("a1").is_err()); }
933
934 #[test]
935 fn test_id_new_validation() {
936 let components = vec![
938 IdComponent::numeric(1),
939 IdComponent::alpha("a").unwrap(),
940 IdComponent::numeric(2),
941 ];
942 assert!(Id::new(components).is_ok());
943
944 let components = vec![IdComponent::alpha("a").unwrap(), IdComponent::numeric(1)];
946 assert!(Id::new(components).is_err());
947
948 let components = vec![IdComponent::numeric(1), IdComponent::numeric(2)];
950 assert!(Id::new(components).is_err());
951
952 let components = vec![
954 IdComponent::numeric(1),
955 IdComponent::alpha("a").unwrap(),
956 IdComponent::alpha("b").unwrap(),
957 ];
958 assert!(Id::new(components).is_err());
959
960 assert!(Id::new(vec![]).is_err());
962 }
963
964 #[test]
965 fn test_overflow_handling() {
966 let large_comp = IdComponent::numeric(u32::MAX);
968 assert!(large_comp.increment().is_err());
969
970 let large_id = Id::from_number(u32::MAX);
972 assert_eq!(large_id.to_string(), u32::MAX.to_string());
973
974 assert!(large_id.next_sibling().is_err());
976 }
977}