1use smol_str::SmolStr;
17
18#[derive(Copy, Clone, Debug, PartialEq, Eq)]
19pub struct BusBitChars {
20 pub open: char,
21 pub close: char,
22}
23
24impl Default for BusBitChars {
25 fn default() -> Self {
26 Self {
27 open: '[',
28 close: ']',
29 }
30 }
31}
32
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub struct BusName {
35 pub base: SmolStr,
36 pub lo: i32,
37 pub hi: i32,
38 pub ascending: bool,
42}
43
44impl BusName {
45 pub fn expand(&self, chars: BusBitChars) -> impl Iterator<Item = String> + '_ {
47 let (start, step): (i32, i32) = if self.ascending {
48 (self.lo, 1)
49 } else {
50 (self.hi, -1)
51 };
52 let count = (self.hi - self.lo).unsigned_abs() as usize + 1;
53 (0..count).map(move |k| {
54 let i = start + (k as i32) * step;
55 format!("{}{}{i}{}", self.base, chars.open, chars.close)
56 })
57 }
58
59 pub fn width(&self) -> u32 {
60 (self.hi - self.lo).unsigned_abs() + 1
61 }
62}
63
64pub fn parse_bus(s: &str, chars: BusBitChars) -> Option<BusName> {
67 let open = s.find(chars.open)?;
68 let close = s.find(chars.close)?;
69 if open >= close {
70 return None;
71 }
72 let base = SmolStr::from(s[..open].trim());
73 let inside = &s[open + 1..close];
74 let parts: Vec<&str> = inside.split(':').collect();
75 let (a, b) = match parts.as_slice() {
76 [single] => {
77 let n: i32 = single.trim().parse().ok()?;
79 (n, n)
80 }
81 [a, b] => {
82 let a: i32 = a.trim().parse().ok()?;
83 let b: i32 = b.trim().parse().ok()?;
84 (a, b)
85 }
86 _ => return None,
87 };
88 let lo = a.min(b);
89 let hi = a.max(b);
90 let ascending = a <= b;
91 Some(BusName {
92 base,
93 lo,
94 hi,
95 ascending,
96 })
97}
98
99pub fn format_bus_pin(base: &str, index: i32, chars: BusBitChars) -> String {
101 format!("{base}{}{index}{}", chars.open, chars.close)
102}
103
104#[derive(Clone, Debug)]
107pub struct Bus {
108 pub name: BusName,
109 pub member_pins: Vec<SmolStr>,
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn parse_descending_range() {
118 let b = parse_bus("DATA[3:0]", BusBitChars::default()).unwrap();
119 assert_eq!(b.base.as_str(), "DATA");
120 assert_eq!(b.lo, 0);
121 assert_eq!(b.hi, 3);
122 assert!(!b.ascending);
123 assert_eq!(b.width(), 4);
124 }
125
126 #[test]
127 fn parse_ascending_range() {
128 let b = parse_bus("Q[0:7]", BusBitChars::default()).unwrap();
129 assert!(b.ascending);
130 assert_eq!(b.width(), 8);
131 }
132
133 #[test]
134 fn parse_single_bit() {
135 let b = parse_bus("A[5]", BusBitChars::default()).unwrap();
136 assert_eq!(b.lo, 5);
137 assert_eq!(b.hi, 5);
138 assert_eq!(b.width(), 1);
139 }
140
141 #[test]
142 fn parse_returns_none_for_scalar() {
143 assert!(parse_bus("scalar_pin", BusBitChars::default()).is_none());
144 }
145
146 #[test]
147 fn alternate_bit_chars() {
148 let chars = BusBitChars {
149 open: '<',
150 close: '>',
151 };
152 let b = parse_bus("D<7:0>", chars).unwrap();
153 assert_eq!(b.width(), 8);
154 assert_eq!(b.base.as_str(), "D");
155 }
156
157 #[test]
158 fn expand_yields_scalar_names() {
159 let b = parse_bus("A[3:0]", BusBitChars::default()).unwrap();
160 let names: Vec<String> = b.expand(BusBitChars::default()).collect();
161 assert_eq!(names, vec!["A[3]", "A[2]", "A[1]", "A[0]"]);
162 }
163
164 #[test]
165 fn ascending_expand_preserves_direction() {
166 let b = parse_bus("A[0:3]", BusBitChars::default()).unwrap();
167 let names: Vec<String> = b.expand(BusBitChars::default()).collect();
168 assert_eq!(names, vec!["A[0]", "A[1]", "A[2]", "A[3]"]);
169 }
170
171 #[test]
172 fn format_bus_pin_uses_chosen_chars() {
173 let s = format_bus_pin("VAL", 42, BusBitChars { open: '<', close: '>' });
174 assert_eq!(s, "VAL<42>");
175 }
176}