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