Skip to main content

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}