1use mf2_model::{Dir, MarkupKind};
5
6use crate::function::{FnContext, Function, OptionEntries};
7use crate::sink::{Sink, SubPartSink};
8use crate::text::write_escaped_literal;
9use crate::unannotated;
10use crate::value::Value;
11
12pub trait PartSink {
14 fn part(&mut self, part: Part<'_>);
16}
17
18#[non_exhaustive]
21pub enum Part<'p> {
22 Text(&'p str),
24 BidiIsolation(Isolation),
26 Expression(ExpressionPart<'p>),
28 Markup(MarkupPart<'p>),
30 Fallback(FallbackSource<'p>),
32}
33
34#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
36pub enum Isolation {
37 Lri,
39 Rli,
41 Fsi,
43 Pdi,
45}
46
47impl Isolation {
48 pub const fn as_str(self) -> &'static str {
50 match self {
51 Isolation::Lri => "\u{2066}",
52 Isolation::Rli => "\u{2067}",
53 Isolation::Fsi => "\u{2068}",
54 Isolation::Pdi => "\u{2069}",
55 }
56 }
57}
58
59pub struct ExpressionPart<'p> {
61 pub(crate) value: &'p Value<'p>,
62 pub(crate) handler: Option<&'static dyn Function>,
63 pub(crate) cx: FnContext<'p>,
64 pub(crate) dir: Dir,
65 pub(crate) id: Option<&'p str>,
66}
67
68impl<'p> ExpressionPart<'p> {
69 pub fn kind(&self) -> &'p str {
72 match self.handler {
73 Some(h) => h.part_kind(),
74 None => unannotated::kind(self.value),
75 }
76 }
77
78 pub fn locale(&self) -> &'p str {
80 self.cx.locale()
81 }
82
83 pub fn dir(&self) -> Dir {
85 self.dir
86 }
87
88 pub fn id(&self) -> Option<&'p str> {
90 self.id
91 }
92
93 pub fn value(&self) -> &'p Value<'p> {
95 self.value
96 }
97
98 pub fn write(&self, out: &mut dyn Sink) {
100 match self.handler {
101 Some(h) => h.format(&self.cx, self.value, out),
102 None => unannotated::format(self.value, self.cx.host(), out),
103 }
104 }
105
106 pub fn sub_parts(&self, out: &mut dyn SubPartSink) {
109 match self.handler {
110 Some(h) => h.format_parts(&self.cx, self.value, out),
111 None => unannotated::format_parts(self.value, self.cx.host(), out),
112 }
113 }
114}
115
116pub struct MarkupPart<'p> {
118 pub(crate) kind: MarkupKind,
119 pub(crate) name: &'p str,
120 pub(crate) id: Option<&'p str>,
121 pub(crate) options: OptionEntries<'p, 'p>,
122}
123
124impl<'p> MarkupPart<'p> {
125 pub fn kind(&self) -> MarkupKind {
127 self.kind
128 }
129
130 pub fn name(&self) -> &'p str {
132 self.name
133 }
134
135 pub fn id(&self) -> Option<&'p str> {
137 self.id
138 }
139
140 pub fn options(&self) -> MarkupOptions<'p> {
142 MarkupOptions {
143 entries: self.options,
144 at: 0,
145 }
146 }
147}
148
149pub struct MarkupOptions<'p> {
151 entries: OptionEntries<'p, 'p>,
152 at: usize,
153}
154
155impl<'p> Iterator for MarkupOptions<'p> {
156 type Item = (&'p str, &'p Value<'p>);
157
158 fn next(&mut self) -> Option<Self::Item> {
159 let e = self.entries.get(self.at)?;
160 self.at += 1;
161 Some((e.0, e.1.value))
162 }
163}
164
165#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
167#[non_exhaustive]
168pub enum FallbackSource<'p> {
169 Variable(&'p str),
171 Literal(&'p str),
173 Function(&'p str),
175 Unknown,
177}
178
179impl FallbackSource<'_> {
180 pub fn write(&self, out: &mut dyn Sink) {
182 match *self {
183 FallbackSource::Variable(n) => {
184 out.push_str("$");
185 out.push_str(n);
186 }
187 FallbackSource::Literal(v) => {
188 out.push_str("|");
189 write_escaped_literal(v, out);
190 out.push_str("|");
191 }
192 FallbackSource::Function(n) => {
193 out.push_str(":");
194 out.push_str(n);
195 }
196 FallbackSource::Unknown => out.push_str("\u{FFFD}"),
197 }
198 }
199}