summaryrefslogtreecommitdiff
path: root/src/eval/class.rs
blob: 456749333002048eed112fbdf819eb984d1e3806 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
use std::fmt::{self, Debug, Formatter, Write};
use std::marker::PhantomData;
use std::rc::Rc;

use super::{Args, EvalContext, Node, Styles};
use crate::diag::TypResult;
use crate::util::EcoString;

/// A class of nodes.
#[derive(Clone)]
pub struct Class(Rc<Inner<dyn Bounds>>);

/// The unsized structure behind the [`Rc`].
struct Inner<T: ?Sized> {
    name: EcoString,
    dispatch: T,
}

impl Class {
    /// Create a new class.
    pub fn new<T>(name: EcoString) -> Self
    where
        T: Construct + Set + 'static,
    {
        Self(Rc::new(Inner {
            name,
            dispatch: Dispatch::<T>(PhantomData),
        }))
    }

    /// The name of the class.
    pub fn name(&self) -> &EcoString {
        &self.0.name
    }

    /// Construct an instance of the class.
    pub fn construct(&self, ctx: &mut EvalContext, args: &mut Args) -> TypResult<Node> {
        self.0.dispatch.construct(ctx, args)
    }

    /// Execute the class's set rule.
    pub fn set(&self, styles: &mut Styles, args: &mut Args) -> TypResult<()> {
        self.0.dispatch.set(styles, args)
    }
}

impl Debug for Class {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        f.write_str("<class ")?;
        f.write_str(&self.0.name)?;
        f.write_char('>')
    }
}

impl PartialEq for Class {
    fn eq(&self, other: &Self) -> bool {
        // We cast to thin pointers for comparison.
        std::ptr::eq(
            Rc::as_ptr(&self.0) as *const (),
            Rc::as_ptr(&other.0) as *const (),
        )
    }
}

/// Construct an instance of a class.
pub trait Construct {
    /// Construct an instance of this class from the arguments.
    ///
    /// This is passed only the arguments that remain after execution of the
    /// class's set rule.
    fn construct(ctx: &mut EvalContext, args: &mut Args) -> TypResult<Node>;
}

/// Set style properties of a class.
pub trait Set {
    /// Parse the arguments and insert style properties of this class into the
    /// given style map.
    fn set(styles: &mut Styles, args: &mut Args) -> TypResult<()>;
}

/// Zero-sized struct whose vtable contains the constructor and set rule of a
/// class.
struct Dispatch<T>(PhantomData<T>);

trait Bounds {
    fn construct(&self, ctx: &mut EvalContext, args: &mut Args) -> TypResult<Node>;
    fn set(&self, styles: &mut Styles, args: &mut Args) -> TypResult<()>;
}

impl<T> Bounds for Dispatch<T>
where
    T: Construct + Set,
{
    fn construct(&self, ctx: &mut EvalContext, args: &mut Args) -> TypResult<Node> {
        T::construct(ctx, args)
    }

    fn set(&self, styles: &mut Styles, args: &mut Args) -> TypResult<()> {
        T::set(styles, args)
    }
}