1use std::{
8 fmt::{Alignment, Display, Write},
9 marker::PhantomData,
10 path::PathBuf,
11};
12
13use super::{Change, Key, Tag, Text};
14use crate::{data::RwData, form::FormId};
15
16pub struct Builder {
42 text: Text,
43 last_form: Option<(usize, FormId)>,
44 last_align: Option<(usize, Alignment)>,
45 buffer: String,
46 last_was_empty: bool,
47}
48
49impl Builder {
50 pub fn new() -> Self {
55 Self::default()
56 }
57
58 pub fn finish(mut self) -> Text {
66 if (self.text.buffers(..).next_back()).is_none_or(|b| b != b'\n') {
67 self.push_str("\n");
68 self.text.0.forced_new_line = true;
69 }
70
71 let end = self.text.len().byte();
72 if let Some((b, id)) = self.last_form {
73 self.text.insert_tag(Key::basic(), Tag::Form(b..end, id, 0));
74 }
75 match self.last_align {
76 Some((b, Alignment::Center)) => {
77 self.text.insert_tag(Key::basic(), Tag::AlignCenter(b..end));
78 }
79 Some((b, Alignment::Right)) => {
80 self.text.insert_tag(Key::basic(), Tag::AlignRight(b..end));
81 }
82 Some(_) | None => {}
83 }
84
85 self.text
86 }
87
88 pub fn push<D: Display, _T>(&mut self, part: impl Into<BuilderPart<D, _T>>) {
96 let part = part.into();
97 let end = self.text.len().byte();
98 match part {
99 BuilderPart::Text(text) => self.push_text(text),
100 BuilderPart::ToString(display) => self.push_str(display),
101 BuilderPart::Form(id) => {
102 let last_form = match id == crate::form::DEFAULT_ID {
103 true => self.last_form.take(),
104 false => self.last_form.replace((end, id)),
105 };
106
107 if let Some((b, id)) = last_form {
108 self.text.insert_tag(Key::basic(), Tag::Form(b..end, id, 0));
109 }
110 }
111 BuilderPart::AlignLeft => match self.last_align.take() {
112 Some((b, Alignment::Center)) => {
113 self.text.insert_tag(Key::basic(), Tag::AlignCenter(b..end));
114 }
115 Some((b, Alignment::Right)) => {
116 self.text.insert_tag(Key::basic(), Tag::AlignRight(b..end));
117 }
118 _ => {}
119 },
120 BuilderPart::AlignCenter => match self.last_align.take() {
121 Some((b, Alignment::Center)) => self.last_align = Some((b, Alignment::Center)),
122 Some((b, Alignment::Right)) => {
123 self.text.insert_tag(Key::basic(), Tag::AlignRight(b..end));
124 }
125 None => self.last_align = Some((end, Alignment::Center)),
126 Some(_) => {}
127 },
128 BuilderPart::AlignRight => match self.last_align.take() {
129 Some((b, Alignment::Right)) => self.last_align = Some((b, Alignment::Right)),
130 Some((b, Alignment::Center)) => {
131 self.text.insert_tag(Key::basic(), Tag::AlignCenter(b..end));
132 }
133 None => self.last_align = Some((end, Alignment::Right)),
134 Some(_) => {}
135 },
136 BuilderPart::Spacer(_) => self.text.insert_tag(Key::basic(), Tag::Spacer(end)),
137 BuilderPart::Ghost(text) => self.text.insert_tag(Key::basic(), Tag::Ghost(end, text)),
138 }
139 }
140
141 pub fn last_was_empty(&self) -> bool {
146 self.last_was_empty
147 }
148
149 pub(crate) fn push_str<D: Display>(&mut self, d: D) {
153 self.buffer.clear();
154 write!(self.buffer, "{d}").unwrap();
155 if self.buffer.is_empty() {
156 self.last_was_empty = true;
157 } else {
158 self.last_was_empty = false;
159 let end = self.text.len();
160 self.text
161 .apply_change_inner(0, Change::str_insert(&self.buffer, end));
162 }
163 }
164
165 pub(crate) fn push_text(&mut self, mut text: Text) {
167 if text.0.forced_new_line {
168 let change = Change::remove_nl(text.last_point().unwrap());
169 text.apply_change_inner(0, change);
170 text.0.forced_new_line = false;
171 }
172 self.last_was_empty = text.is_empty();
173
174 if let Some((b, id)) = self.last_form.take() {
175 self.text
176 .insert_tag(Key::basic(), Tag::Form(b..self.text.len().byte(), id, 0));
177 }
178
179 self.text.0.bytes.extend(text.0.bytes);
180 self.text.0.tags.extend(text.0.tags);
181 }
182}
183
184impl Default for Builder {
185 fn default() -> Self {
186 Builder {
187 text: Text::empty(),
188 last_form: None,
189 last_align: None,
190 buffer: String::with_capacity(50),
191 last_was_empty: false,
192 }
193 }
194}
195
196#[derive(Clone)]
198pub struct AlignCenter;
199#[derive(Clone)]
201pub struct AlignLeft;
202#[derive(Clone)]
205pub struct AlignRight;
206#[derive(Clone)]
234pub struct Spacer;
235#[derive(Clone)]
240pub struct Ghost(pub Text);
241
242#[derive(Clone)]
244pub enum BuilderPart<D: Display, _T = String> {
245 Text(Text),
246 ToString(D),
247 Form(FormId),
248 AlignLeft,
249 AlignCenter,
250 AlignRight,
251 Spacer(PhantomData<_T>),
252 Ghost(Text),
253}
254
255impl From<FormId> for BuilderPart<String, FormId> {
256 fn from(value: FormId) -> Self {
257 Self::Form(value)
258 }
259}
260
261impl From<AlignLeft> for BuilderPart<String, AlignLeft> {
262 fn from(_: AlignLeft) -> Self {
263 BuilderPart::AlignLeft
264 }
265}
266
267impl From<AlignCenter> for BuilderPart<String, AlignCenter> {
268 fn from(_: AlignCenter) -> Self {
269 BuilderPart::AlignCenter
270 }
271}
272
273impl From<AlignRight> for BuilderPart<String, AlignRight> {
274 fn from(_: AlignRight) -> Self {
275 BuilderPart::AlignRight
276 }
277}
278
279impl From<Spacer> for BuilderPart<String, Spacer> {
280 fn from(_: Spacer) -> Self {
281 BuilderPart::Spacer(PhantomData)
282 }
283}
284
285impl From<Ghost> for BuilderPart<String, Ghost> {
286 fn from(value: Ghost) -> Self {
287 BuilderPart::Ghost(value.0)
288 }
289}
290
291impl From<Text> for BuilderPart<String, Text> {
292 fn from(value: Text) -> Self {
293 BuilderPart::Text(value)
294 }
295}
296
297impl<D: Display> From<&RwData<D>> for BuilderPart<String, D> {
298 fn from(value: &RwData<D>) -> Self {
299 BuilderPart::ToString(value.read().to_string())
300 }
301}
302
303impl<D: Display> From<D> for BuilderPart<D, D> {
304 fn from(value: D) -> Self {
305 BuilderPart::ToString(value)
306 }
307}
308
309impl From<PathBuf> for BuilderPart<String, PathBuf> {
310 fn from(value: PathBuf) -> Self {
311 BuilderPart::Text(Text::from(&value))
312 }
313}
314
315impl From<&PathBuf> for BuilderPart<String, PathBuf> {
316 fn from(value: &PathBuf) -> Self {
317 BuilderPart::Text(Text::from(value))
318 }
319}
320
321impl From<RwData<PathBuf>> for BuilderPart<String, PathBuf> {
322 fn from(value: RwData<PathBuf>) -> Self {
323 BuilderPart::Text(Text::from(&*value.read()))
324 }
325}
326
327pub macro text {
331 (@push $builder:expr, []) => {
333 let id = crate::form::id_of!("Default");
334 $builder.push(crate::text::Tag::PushForm(id, 0))
335 },
336 (@push $builder:expr, [*a]) => {
337 let id = crate::form::id_of!("Accent");
338 $builder.push(crate::text::Tag::PushForm(id, 0))
339 },
340 (@push $builder:expr, [$form:ident $(.$suffix:ident)*]) => {
341 let id = crate::form::id_of!(concat!(
342 stringify!($form) $(, stringify!(.), stringify!($suffix))*
343 ));
344 $builder.push(id)
345 },
346 (@push $builder:expr, [$($other:tt)+]) => {
347 compile_error!(concat!(
348 "Forms should be a list of identifiers separated by '.'s, received \"",
349 stringify!($($other)+),
350 "\" instead"
351 ))
352 },
353
354 (@push $builder:expr, $part:expr) => {
356 $builder.push($part)
357 },
358
359 (@parse $builder:expr, $part:tt $($parts:tt)*) => {{
360 text!(@push $builder, $part);
361 text!(@parse $builder, $($parts)*);
362 }},
363 (@parse $builder:expr,) => {},
364
365 ($builder:expr, $($parts:tt)+) => {{
366 let builder: &mut Builder = &mut $builder;
367 text!(@parse builder, $($parts)+);
368 }},
369 ($($parts:tt)+) => {{
370 let mut builder = Builder::new();
371 text!(builder, $($parts)+);
372 builder.finish()
373 }},
374}
375
376pub macro ok {
380 (@push $builder:expr, []) => {
382 $builder.push(crate::form::id_of!("DefaultOk"))
383 },
384 (@push $builder:expr, [*a]) => {
385 $builder.push(crate::form::id_of!("AccentOk"))
386 },
387 (@push $builder:expr, [$form:ident $(.$suffix:ident)*]) => {
388 $builder.push(crate::form::id_of!(concat!(
389 stringify!($form) $(, stringify!(.), stringify!($suffix))*
390 )));
391 },
392 (@push $builder:expr, [$($other:tt)+]) => {
393 compile_error!(concat!(
394 "Forms should be a list of identifiers separated by '.'s, received \"",
395 stringify!($($other)+),
396 "\" instead"
397 ))
398 },
399
400 (@push $builder:expr, $part:expr) => {
402 $builder.push($part)
403 },
404
405 (@parse $builder:expr, $part:tt $($parts:tt)*) => {{
406 ok!(@push $builder, $part);
407 ok!(@parse $builder, $($parts)*);
408 }},
409 (@parse $builder:expr,) => {},
410
411 ($builder:expr, $($parts:tt)+) => {{
412 let builder: &mut Builder = &mut $builder;
413 ok!(@parse builder, $($parts)+);
414 }},
415 ($($parts:tt)+) => {{
416 let mut builder = Builder::new();
417 ok!(builder, [DefaultOk] $($parts)+);
418 builder.finish()
419 }},
420}
421
422pub macro err {
426 (@push $builder:expr, []) => {
428 $builder.push(crate::form::id_of!("DefaultErr"))
429 },
430 (@push $builder:expr, [*a]) => {
431 $builder.push(crate::form::id_of!("AccentErr"))
432 },
433 (@push $builder:expr, [$form:ident $(.$suffix:ident)*]) => {
434 $builder.push(crate::form::id_of!(concat!(
435 stringify!($form) $(, stringify!(.), stringify!($suffix))*
436 )));
437 },
438 (@push $builder:expr, [$($other:tt)+]) => {
439 compile_error!(concat!(
440 "Forms should be a list of identifiers separated by '.'s, received \"",
441 stringify!($($other)+),
442 "\" instead"
443 ))
444 },
445
446 (@push $builder:expr, $part:expr) => {
448 $builder.push($part)
449 },
450
451 (@parse $builder:expr, $part:tt $($parts:tt)*) => {{
452 err!(@push $builder, $part);
453 err!(@parse $builder, $($parts)*);
454 }},
455 (@parse $builder:expr,) => {},
456
457 ($builder:expr, $($parts:tt)+) => {{
458 let builder: &mut Builder = &mut $builder;
459 err!(@parse builder, $($parts)+);
460 }},
461 ($($parts:tt)+) => {{
462 let mut builder = Builder::new();
463 err!(builder, [DefaultErr] $($parts)+);
464 builder.finish()
465 }},
466}
467
468pub macro hint {
472 (@push $builder:expr, []) => {
474 $builder.push(crate::form::id_of!("DefaultHint"))
475 },
476 (@push $builder:expr, [*a]) => {
477 $builder.push(crate::form::id_of!("AccentHint"))
478 },
479 (@push $builder:expr, [$form:ident $(.$suffix:ident)*]) => {
480 $builder.push(crate::form::id_of!(concat!(
481 stringify!($form) $(, stringify!(.), stringify!($suffix))*
482 )))
483 },
484 (@push $builder:expr, [$($other:tt)+]) => {
485 compile_error!(concat!(
486 "Forms should be a list of identifiers separated by '.'s, received \"",
487 stringify!($($other)+),
488 "\" instead"
489 ))
490 },
491
492 (@push $builder:expr, $part:expr) => {
494 $builder.push($part)
495 },
496
497 (@parse $builder:expr, $part:tt $($parts:tt)*) => {{
498 hint!(@push $builder, $part);
499 hint!(@parse $builder, $($parts)*);
500 }},
501 (@parse $builder:expr,) => {},
502
503 ($builder:expr, $($parts:tt)+) => {{
504 let builder: &mut Builder = &mut $builder;
505 hint!(@parse builder, $($parts)+);
506 }},
507 ($($parts:tt)+) => {{
508 let mut builder = Builder::new();
509 hint!(builder, [DefaultHint] $($parts)+);
510 builder.finish()
511 }},
512}