1use boxworks::ds;
43use boxworks::lang;
44use boxworks::lang::convert::ToBoxLang;
45
46#[macro_export]
47macro_rules! assert_box_eq {
48 ($left:expr, $right:expr$(,)?) => {{
49 let lhs: boxworks_testing::Value = $left.into();
50 let rhs: boxworks_testing::Value = $right.into();
51 boxworks_testing::assert_eq(lhs, rhs);
52 }};
53}
54
55#[macro_export]
56macro_rules! is_box_eq {
57 ($left:expr, $right:expr$(,)?) => {{
58 let lhs: boxworks_testing::Value = $left.into();
59 let rhs: boxworks_testing::Value = $right.into();
60 boxworks_testing::is_eq(lhs, rhs)
61 }};
62}
63
64#[macro_export]
65macro_rules! assert_box_lossy_eq {
66 ($left:expr, $right:expr$(,)?) => {{
67 let lhs: boxworks_testing::Value = $left.into();
68 let rhs: boxworks_testing::Value = $right.into();
69 boxworks_testing::assert_lossy_eq(lhs, rhs);
70 }};
71}
72
73#[macro_export]
74macro_rules! is_box_lossy_eq {
75 ($left:expr, $right:expr$(,)?) => {{
76 let lhs: boxworks_testing::Value = $left.into();
77 let rhs: boxworks_testing::Value = $right.into();
78 boxworks_testing::is_lossy_eq(lhs, rhs)
79 }};
80}
81
82pub enum Value {
83 String(String),
84 Box(Vec<ds::Horizontal>),
85}
86
87impl From<&str> for Value {
88 fn from(value: &str) -> Self {
89 Value::String(value.into())
90 }
91}
92
93impl From<String> for Value {
94 fn from(value: String) -> Self {
95 Value::String(value)
96 }
97}
98
99impl From<ds::VBox> for Value {
100 fn from(value: ds::VBox) -> Self {
101 Value::Box(vec![ds::Horizontal::VBox(value)])
102 }
103}
104
105impl From<ds::HBox> for Value {
106 fn from(value: ds::HBox) -> Self {
107 Value::Box(vec![ds::Horizontal::HBox(value)])
108 }
109}
110
111impl From<Vec<ds::Horizontal>> for Value {
112 fn from(value: Vec<ds::Horizontal>) -> Self {
113 Value::Box(value)
114 }
115}
116
117pub fn is_eq(lhs: Value, rhs: Value) -> bool {
118 is_eq_impl(lhs, rhs, false)
119}
120
121pub fn is_lossy_eq(lhs: Value, rhs: Value) -> bool {
122 is_eq_impl(lhs, rhs, true)
123}
124
125fn is_eq_impl(lhs: Value, rhs: Value, standardize_lossy: bool) -> bool {
126 let (lhs_list, _) = normalize(lhs, "lhs.box", standardize_lossy);
127 let (rhs_list, _) = normalize(rhs, "rhs.box", standardize_lossy);
128 lhs_list == rhs_list
129}
130
131pub fn assert_eq(lhs: Value, rhs: Value) {
132 assert_eq_impl(lhs, rhs, false);
133}
134
135pub fn assert_lossy_eq(lhs: Value, rhs: Value) {
136 assert_eq_impl(lhs, rhs, true);
137}
138
139fn assert_eq_impl(lhs: Value, rhs: Value, standardize_lossy: bool) {
140 let (lhs_list, lhs_s) = normalize(lhs, "lhs.box", standardize_lossy);
141 let (rhs_list, rhs_s) = normalize(rhs, "rhs.box", standardize_lossy);
142 use pretty_assertions::assert_eq;
144 assert_eq!(lhs_s, rhs_s);
145 assert_eq!(rhs_list, lhs_list);
148}
149
150fn normalize(val: Value, side: &str, standardize_lossy: bool) -> (Vec<ds::Horizontal>, String) {
151 let mut list = match val {
152 Value::String(s) => match lang::parse_horizontal_list(&s.clone()) {
153 Ok(v) => v,
154 Err(err) => {
155 let source = ariadne::Source::from(s);
156 let cache: (&str, _) = (side, source);
157 for err in err {
158 err.ariadne_report(side).eprint(cache.clone()).unwrap();
159 }
160 panic!("failed to parse boxlang input; errors printed above.")
161 }
162 },
163 Value::Box(v) => v,
164 };
165 if standardize_lossy {
166 standardize_lossy_h_list(&mut list);
167 }
168 let mut s = String::new();
169 for elem in &list {
170 use std::fmt::Write;
171 write!(&mut s, "{}", elem.to_box_lang()).unwrap();
172 }
173 (list, s)
174}
175
176fn standardize_lossy_h_list(v: &mut [ds::Horizontal]) {
177 for elem in v.iter_mut() {
178 use ds::Horizontal::*;
179 match elem {
180 HBox(hbox) => {
181 standardize_lossy_h_list(&mut hbox.list);
182 }
183 VBox(vbox) => {
184 standardize_lossy_v_list(&mut vbox.list);
185 }
186 Discretionary(disc) => {
187 standardize_lossy_d_list(&mut disc.pre_break);
188 standardize_lossy_d_list(&mut disc.post_break);
189 }
190 Ligature(ligature) => ligature.standardize_lossy(),
191 _ => {}
192 }
193 }
194}
195
196fn standardize_lossy_v_list(v: &mut [ds::Vertical]) {
197 for elem in v.iter_mut() {
198 use ds::Vertical::*;
199 match elem {
200 HBox(hbox) => {
201 standardize_lossy_h_list(&mut hbox.list);
202 }
203 VBox(vbox) => {
204 standardize_lossy_v_list(&mut vbox.list);
205 }
206 _ => {}
207 }
208 }
209}
210
211fn standardize_lossy_d_list(v: &mut [ds::DiscretionaryElem]) {
212 for elem in v.iter_mut() {
213 use ds::DiscretionaryElem::*;
214 match elem {
215 HBox(hbox) => {
216 standardize_lossy_h_list(&mut hbox.list);
217 }
218 VBox(vbox) => {
219 standardize_lossy_v_list(&mut vbox.list);
220 }
221 Ligature(ligature) => ligature.standardize_lossy(),
222 _ => {}
223 }
224 }
225}