eure_document/must_be.rs
1//! Zero-sized types for compile-time literal matching in `ParseDocument`.
2//!
3//! This module provides `MustBeText<M>`, a zero-sized type that only successfully
4//! parses from a specific Text value (content + language). It's similar to
5//! monostate's `MustBe!` macro but for Eure's `ParseDocument` trait.
6
7use core::marker::PhantomData;
8
9use crate::parse::{ParseContext, ParseDocument, ParseError, ParseErrorKind};
10use crate::text::{Language, Text};
11
12/// Marker trait for `MustBeText` types.
13///
14/// Types implementing this trait specify the expected content and language
15/// for text literal matching.
16pub trait MustBeTextMarker: Copy {
17 /// The expected text content.
18 const CONTENT: &'static str;
19 /// The expected language.
20 const LANGUAGE: Language;
21}
22
23/// Zero-sized type that only parses from a specific Text value.
24///
25/// This type implements `ParseDocument` and succeeds only when the parsed
26/// value matches the expected content and has a compatible language.
27///
28/// Use the `MustBeText!` macro to create instances of this type.
29#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
30pub struct MustBeText<M: MustBeTextMarker>(PhantomData<M>);
31
32impl<M: MustBeTextMarker> MustBeText<M> {
33 /// Create a new instance.
34 pub const fn new() -> Self {
35 MustBeText(PhantomData)
36 }
37}
38
39impl<M: MustBeTextMarker> ParseDocument<'_> for MustBeText<M> {
40 type Error = ParseError;
41
42 fn parse(ctx: &ParseContext<'_>) -> Result<Self, Self::Error> {
43 let text: Text = ctx.parse()?;
44 if text.content == M::CONTENT && text.language.is_compatible_with(&M::LANGUAGE) {
45 Ok(MustBeText(PhantomData))
46 } else {
47 Err(ParseError {
48 node_id: ctx.node_id(),
49 kind: ParseErrorKind::LiteralMismatch {
50 expected: alloc::format!("{:?} {:?}", M::LANGUAGE, M::CONTENT),
51 actual: alloc::format!("{:?} {:?}", text.language, text.content),
52 },
53 })
54 }
55 }
56}