summaryrefslogtreecommitdiff
path: root/crates/typst-library/src/foundations/content/packed.rs
blob: e9916262920652c0e7c6307700ce68d0f0084e9f (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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};

use typst_syntax::Span;

use crate::foundations::{Content, Label, NativeElement};
use crate::introspection::Location;

/// A packed element of a static type.
#[derive(Clone)]
#[repr(transparent)]
pub struct Packed<T: NativeElement>(
    /// Invariant: Must be of type `T`.
    Content,
    PhantomData<T>,
);

impl<T: NativeElement> Packed<T> {
    /// Pack element while retaining its static type.
    pub fn new(element: T) -> Self {
        // Safety: The element is known to be of type `T`.
        Packed(element.pack(), PhantomData)
    }

    /// Try to cast type-erased content into a statically known packed element.
    pub fn from_ref(content: &Content) -> Option<&Self> {
        if content.is::<T>() {
            // Safety:
            // - We have checked the type.
            // - Packed<T> is repr(transparent).
            return Some(unsafe { std::mem::transmute::<&Content, &Packed<T>>(content) });
        }
        None
    }

    /// Try to cast type-erased content into a statically known packed element.
    pub fn from_mut(content: &mut Content) -> Option<&mut Self> {
        if content.is::<T>() {
            // Safety:
            // - We have checked the type.
            // - Packed<T> is repr(transparent).
            return Some(unsafe {
                std::mem::transmute::<&mut Content, &mut Packed<T>>(content)
            });
        }
        None
    }

    /// Try to cast type-erased content into a statically known packed element.
    pub fn from_owned(content: Content) -> Result<Self, Content> {
        if content.is::<T>() {
            // Safety:
            // - We have checked the type.
            // - Packed<T> is repr(transparent).
            return Ok(unsafe { std::mem::transmute::<Content, Packed<T>>(content) });
        }
        Err(content)
    }

    /// Pack back into content.
    pub fn pack(self) -> Content {
        self.0
    }

    /// Pack back into a reference to content.
    pub fn pack_ref(&self) -> &Content {
        &self.0
    }

    /// Pack back into a mutable reference to content.
    pub fn pack_mut(&mut self) -> &mut Content {
        &mut self.0
    }

    /// Extract the raw underlying element.
    pub fn unpack(self) -> T {
        // This function doesn't yet need owned self, but might in the future.
        (*self).clone()
    }

    /// The element's span.
    pub fn span(&self) -> Span {
        self.0.span()
    }

    /// Set the span of the element.
    pub fn spanned(self, span: Span) -> Self {
        Self(self.0.spanned(span), PhantomData)
    }

    /// Accesses the label of the element.
    pub fn label(&self) -> Option<Label> {
        self.0.label()
    }

    /// Accesses the location of the element.
    pub fn location(&self) -> Option<Location> {
        self.0.location()
    }

    /// Sets the location of the element.
    pub fn set_location(&mut self, location: Location) {
        self.0.set_location(location);
    }
}

impl<T: NativeElement> AsRef<T> for Packed<T> {
    fn as_ref(&self) -> &T {
        self
    }
}

impl<T: NativeElement> AsMut<T> for Packed<T> {
    fn as_mut(&mut self) -> &mut T {
        self
    }
}

impl<T: NativeElement> Deref for Packed<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        // Safety: Packed<T> guarantees that the content is of element type `T`.
        unsafe { (self.0).0.data::<T>() }
    }
}

impl<T: NativeElement> DerefMut for Packed<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        // Safety: Packed<T> guarantees that the content is of element type `T`.
        unsafe { (self.0).0.data_mut::<T>() }
    }
}

impl<T: NativeElement + Debug> Debug for Packed<T> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl<T: NativeElement> PartialEq for Packed<T> {
    fn eq(&self, other: &Self) -> bool {
        self.0 == other.0
    }
}

impl<T: NativeElement> Hash for Packed<T> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.hash(state);
    }
}