Skip to content

Commit 02a18a4

Browse files
authored
Refine compatibility (#56)
1 parent 535030a commit 02a18a4

File tree

3 files changed

+174
-16
lines changed

3 files changed

+174
-16
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ missing-errors-doc = "allow"
3636
missing-panics-doc = "allow"
3737
needless-pass-by-value = "allow"
3838
pedantic = { level = "deny", priority = -1 }
39+
too-many-lines = "allow"
3940
wildcard-imports = "allow"
4041

4142
[workspace.lints.rust]

crates/boilerplate-parser/src/token.rs

Lines changed: 158 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,21 +117,28 @@ impl<'src> Token<'src> {
117117
}
118118

119119
#[must_use]
120-
pub fn is_compatible_with(self, other: &Self) -> bool {
120+
pub fn is_compatible_with(self, other: Self) -> bool {
121121
if self.code() != other.code() {
122122
return false;
123123
}
124124

125-
match (self, other) {
126-
(Self::Code { .. }, Self::Code { .. })
127-
| (Self::Interpolation { .. }, Self::Interpolation { .. })
128-
| (Self::Text { .. }, Self::Text { .. }) => true,
129-
(Self::CodeLine { closed, .. }, Self::CodeLine { closed: other, .. })
130-
| (Self::InterpolationLine { closed, .. }, Self::InterpolationLine { closed: other, .. }) => {
131-
closed == *other
125+
if self.block() != other.block() {
126+
for token in [self, other] {
127+
if !matches!(token, Self::Code { .. } | Self::CodeLine { .. }) {
128+
return false;
129+
}
130+
}
131+
}
132+
133+
if let Self::InterpolationLine { closed, .. } = self {
134+
if let Self::InterpolationLine { closed: other, .. } = other {
135+
if closed != other {
136+
return false;
137+
}
132138
}
133-
_ => false,
134139
}
140+
141+
true
135142
}
136143

137144
#[must_use]
@@ -148,6 +155,148 @@ impl<'src> Token<'src> {
148155
mod tests {
149156
use {super::*, pretty_assertions::assert_eq, Token::*};
150157

158+
#[test]
159+
fn compatibility() {
160+
#[track_caller]
161+
fn case(a: Token, b: Token) {
162+
assert!(a.is_compatible_with(b));
163+
}
164+
case(
165+
Text {
166+
contents: "foo",
167+
index: 0,
168+
},
169+
Text {
170+
contents: "bar",
171+
index: 1,
172+
},
173+
);
174+
case(Code { contents: "foo" }, Code { contents: "foo" });
175+
case(Code { contents: " foo" }, Code { contents: "foo" });
176+
case(Code { contents: "foo " }, Code { contents: "foo" });
177+
case(
178+
CodeLine {
179+
contents: "foo",
180+
closed: true,
181+
},
182+
CodeLine {
183+
contents: "foo",
184+
closed: true,
185+
},
186+
);
187+
case(
188+
CodeLine {
189+
contents: "foo",
190+
closed: false,
191+
},
192+
CodeLine {
193+
contents: "foo",
194+
closed: false,
195+
},
196+
);
197+
case(
198+
CodeLine {
199+
contents: "foo",
200+
closed: true,
201+
},
202+
CodeLine {
203+
contents: "foo",
204+
closed: false,
205+
},
206+
);
207+
case(
208+
CodeLine {
209+
contents: "foo",
210+
closed: false,
211+
},
212+
CodeLine {
213+
contents: "foo",
214+
closed: true,
215+
},
216+
);
217+
case(
218+
Code { contents: "foo" },
219+
CodeLine {
220+
contents: "foo",
221+
closed: true,
222+
},
223+
);
224+
case(
225+
CodeLine {
226+
contents: "foo",
227+
closed: true,
228+
},
229+
Code { contents: "foo" },
230+
);
231+
case(
232+
Interpolation { contents: "foo" },
233+
Interpolation { contents: "foo" },
234+
);
235+
case(
236+
InterpolationLine {
237+
contents: "foo",
238+
closed: true,
239+
},
240+
InterpolationLine {
241+
contents: "foo",
242+
closed: true,
243+
},
244+
);
245+
case(
246+
InterpolationLine {
247+
contents: "foo",
248+
closed: false,
249+
},
250+
InterpolationLine {
251+
contents: "foo",
252+
closed: false,
253+
},
254+
);
255+
}
256+
257+
#[test]
258+
fn incompatibility() {
259+
#[track_caller]
260+
fn case(a: Token, b: Token) {
261+
assert!(!a.is_compatible_with(b));
262+
}
263+
case(
264+
Text {
265+
contents: "foo",
266+
index: 0,
267+
},
268+
Code { contents: "bar" },
269+
);
270+
case(Code { contents: "foo" }, Interpolation { contents: "bar" });
271+
case(
272+
Interpolation { contents: "foo" },
273+
InterpolationLine {
274+
contents: "bar",
275+
closed: false,
276+
},
277+
);
278+
case(
279+
InterpolationLine {
280+
contents: "foo",
281+
closed: true,
282+
},
283+
InterpolationLine {
284+
contents: "bar",
285+
closed: true,
286+
},
287+
);
288+
case(
289+
InterpolationLine {
290+
contents: "foo",
291+
closed: true,
292+
},
293+
InterpolationLine {
294+
contents: "foo",
295+
closed: false,
296+
},
297+
);
298+
}
299+
151300
#[track_caller]
152301
fn assert_parse(expected: &str, expected_tokens: &[Token]) {
153302
let actual_tokens = Token::parse(expected).unwrap();

src/lib.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,14 @@
451451
//! "template blocks are not compatible: {{self.id}} != {{self.first}}",
452452
//! );
453453
//!
454-
//! // Try to reload an incompatible template with a different number of code blocks:
454+
//! // Text blocks cannot be removed entirely:
455+
//! let incompatible_template = "{{ self.first }}";
456+
//! assert_eq!(
457+
//! context.reload(incompatible_template).err().unwrap().to_string(),
458+
//! "new template has 1 blocks but old template has 3 blocks",
459+
//! );
460+
//!
461+
//! // Try to reload an incompatible template with a different number of blocks:
455462
//! let incompatible_template = "Goodbye, {{self.first}} {{self.last}}!";
456463
//! assert_eq!(
457464
//! context.reload(incompatible_template).err().unwrap().to_string(),
@@ -610,14 +617,15 @@ pub trait Boilerplate {
610617
fn reload<'a>(&self, src: &'a str) -> Result<Reload<&Self>, Error<'a>> {
611618
let tokens = Token::parse(src).map_err(Error::Parse)?;
612619

613-
let new = tokens.len();
614-
let old = Self::TOKENS.len();
615-
if new != old {
616-
return Err(Error::Length { new, old });
620+
if tokens.len() != Self::TOKENS.len() {
621+
return Err(Error::Length {
622+
new: tokens.len(),
623+
old: Self::TOKENS.len(),
624+
});
617625
}
618626

619-
for (&new, &old) in tokens.iter().zip(Self::TOKENS) {
620-
if !new.is_compatible_with(&old) {
627+
for (new, old) in tokens.iter().copied().zip(Self::TOKENS.iter().copied()) {
628+
if !new.is_compatible_with(old) {
621629
return Err(Error::Incompatible { new, old });
622630
}
623631
}

0 commit comments

Comments
 (0)