1use std::{
12 fmt::{Display, Write},
13 marker::PhantomData,
14 path::Path,
15};
16
17use super::{Change, Tagger, Text};
18use crate::{
19 buffer::PathKind,
20 form::FormId,
21 text::{FormTag, Ghost, Spacer},
22};
23
24#[derive(Clone)]
60pub struct Builder {
61 text: Text,
62 last_form: Option<(usize, FormTag)>,
63 buffer: String,
64 last_was_empty: bool,
65 pub no_space_after_empty: bool,
67}
68
69impl Builder {
70 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn build(mut self) -> Text {
90 if let Some((b, id)) = self.last_form
91 && b < self.text.last_point().byte()
92 {
93 self.text.insert_tag(Tagger::basic(), b.., id);
94 }
95
96 self.text
97 }
98
99 pub fn build_no_double_nl(self) -> Text {
109 let mut text = self.build();
110 if let Some(last_last_byte) = text.len().byte().checked_sub(2)
111 && let Some(strs) = text.strs(last_last_byte..)
112 && strs == "\n\n"
113 {
114 text.replace_range(last_last_byte..last_last_byte + 1, "");
115 }
116
117 text
118 }
119
120 pub fn push<D: Display, _T>(&mut self, part: impl AsBuilderPart<D, _T>) {
129 self.push_builder_part(part.as_builder_part());
130 }
131
132 #[doc(hidden)]
133 pub fn push_builder_part<_T>(&mut self, part: BuilderPart<impl Display, _T>) {
134 fn push_simple(builder: &mut Builder, part: BuilderPart) {
135 use BuilderPart as BP;
136
137 let end = builder.text.last_point().byte();
138 let tagger = Tagger::basic();
139
140 match part {
141 BP::Text(text) => builder.push_text(text),
142 BP::Builder(other) => builder.push_builder(other),
143 BP::Path(path) => builder.push_str(path.to_string_lossy()),
144 BP::PathKind(text) => builder.push_text(&text),
145 BP::Form(tag) => {
146 let last_form = if tag == crate::form::DEFAULT_ID.to_tag(0) {
147 builder.last_form.take()
148 } else {
149 builder.last_form.replace((end, tag))
150 };
151
152 if let Some((b, tag)) = last_form
153 && b < end
154 {
155 builder.text.insert_tag(tagger, b..end, tag);
156 }
157 }
158 BP::Spacer(_) => _ = builder.text.insert_tag(tagger, end, Spacer),
159 BP::Ghost(ghost) => _ = builder.text.insert_tag(tagger, end, ghost.clone()),
160 BP::ToString(_) => unsafe { std::hint::unreachable_unchecked() },
161 }
162 }
163
164 match part.try_to_basic() {
165 Ok(part_ref) => push_simple(self, part_ref),
166 Err(BuilderPart::ToString(display)) => self.push_str(display),
167 Err(_) => unsafe { std::hint::unreachable_unchecked() },
168 }
169 }
170
171 pub fn last_was_empty(&self) -> bool {
176 self.last_was_empty
177 }
178
179 pub fn push_str<D: Display>(&mut self, d: D) {
191 self.buffer.clear();
192 write!(self.buffer, "{d}").unwrap();
193 if self.buffer.is_empty()
194 || (self.no_space_after_empty && self.buffer == " " && self.last_was_empty)
195 {
196 self.last_was_empty = true;
197 } else {
198 self.last_was_empty = false;
199 let end = self.text.last_point();
200 self.text
201 .apply_change(0, Change::str_insert(&self.buffer, end));
202 }
203 }
204
205 pub fn reset_form(&mut self) {
211 let end = self.text.last_point().byte();
212 if let Some((b, last_form)) = self.last_form.take() {
213 self.text.insert_tag(Tagger::basic(), b..end, last_form);
214 }
215 }
216
217 fn push_text(&mut self, text: &Text) {
219 self.last_was_empty = text.is_empty();
220 self.text.insert_text(self.text.last_point(), text);
221 }
222
223 fn push_builder(&mut self, other: &Builder) {
225 self.last_was_empty = other.text.is_empty();
226
227 let offset = self.text.last_point().byte();
228 self.text.insert_text(offset, &other.text);
229 let end = self.text.last_point().byte();
230
231 if let Some((b, id)) = other.last_form
232 && b < other.text.last_point().byte()
233 {
234 self.text.insert_tag(Tagger::basic(), offset + b..end, id);
235 }
236 }
237}
238
239impl Default for Builder {
240 fn default() -> Self {
241 Builder {
242 text: Text::new(),
243 last_form: None,
244 buffer: String::with_capacity(50),
245 last_was_empty: false,
246 no_space_after_empty: false,
247 }
248 }
249}
250
251impl std::fmt::Debug for Builder {
252 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253 f.debug_struct("Builder")
254 .field("text", &self.text)
255 .finish_non_exhaustive()
256 }
257}
258
259impl From<Builder> for Text {
260 fn from(value: Builder) -> Self {
261 value.build()
262 }
263}
264
265#[derive(Clone)]
267pub enum BuilderPart<'a, D: Display = String, _T = ()> {
268 Text(&'a Text),
277 Builder(&'a Builder),
283 ToString(&'a D),
285 Path(&'a std::path::Path),
287 PathKind(Text),
289 Form(FormTag),
291 Spacer(PhantomData<_T>),
293 Ghost(&'a Ghost),
295}
296
297impl<'a, D: Display, _T> BuilderPart<'a, D, _T> {
298 fn try_to_basic(self) -> Result<BuilderPart<'a>, Self> {
299 match self {
300 BuilderPart::Text(text) => Ok(BuilderPart::Text(text)),
301 BuilderPart::Builder(builder) => Ok(BuilderPart::Builder(builder)),
302 BuilderPart::ToString(_) => Err(self),
303 BuilderPart::Path(path) => Ok(BuilderPart::Path(path)),
304 BuilderPart::PathKind(text) => Ok(BuilderPart::PathKind(text)),
305 BuilderPart::Form(form_id) => Ok(BuilderPart::Form(form_id)),
306 BuilderPart::Spacer(_) => Ok(BuilderPart::Spacer(PhantomData)),
307 BuilderPart::Ghost(ghost) => Ok(BuilderPart::Ghost(ghost)),
308 }
309 }
310}
311
312pub trait AsBuilderPart<D: Display = String, _T = ()> {
319 fn as_builder_part(&self) -> BuilderPart<'_, D, _T>;
321}
322
323macro_rules! implAsBuilderPart {
324 ($type:ident, $value:ident, $result:expr) => {
325 impl AsBuilderPart for $type {
326 fn as_builder_part(&self) -> BuilderPart<'_> {
327 let $value = self;
328 $result
329 }
330 }
331 };
332}
333
334implAsBuilderPart!(Builder, builder, BuilderPart::Builder(builder));
335implAsBuilderPart!(FormId, form_id, BuilderPart::Form(form_id.to_tag(0)));
336implAsBuilderPart!(FormTag, form_tag, BuilderPart::Form(*form_tag));
337implAsBuilderPart!(Spacer, _spacer, BuilderPart::Spacer(PhantomData));
338implAsBuilderPart!(Ghost, ghost, BuilderPart::Ghost(ghost));
339implAsBuilderPart!(Text, text, BuilderPart::Text(text));
340implAsBuilderPart!(Path, path, BuilderPart::Path(path));
341implAsBuilderPart!(PathKind, path, BuilderPart::PathKind(path.name_txt()));
342
343impl<D: Display> AsBuilderPart<D, D> for D {
344 fn as_builder_part(&self) -> BuilderPart<'_, D, D> {
345 BuilderPart::ToString(self)
346 }
347}
348
349#[macro_export]
355#[doc(hidden)]
356macro_rules! __txt__ {
357 ($($parts:tt)+) => {{
358 #[allow(unused_imports)]
359 use $crate::{
360 __parse_arg__, __parse_form__, __parse_str__, private_exports::format_like
361 };
362
363 let mut builder = $crate::text::Builder::new();
364 let _ = format_like!(
365 __parse_str__,
366 [('{', __parse_arg__, false), ('[', __parse_form__, true)],
367 &mut builder,
368 $($parts)*
369 );
370
371 builder.build()
372 }};
373}
374
375#[macro_export]
376#[doc(hidden)]
377macro_rules! __log__ {
378 ($lvl:expr, $($arg:tt)*) => {{
379 #[allow(unused_must_use)]
380 let text = $crate::text::txt!($($arg)*);
381
382 $crate::context::logs().push_record($crate::context::Record::new(
383 text,
384 $lvl,
385 ));
386 }}
387}
388
389#[macro_export]
390#[doc(hidden)]
391macro_rules! __parse_str__ {
392 ($builder:expr, $str:literal) => {{
393 let builder = $builder;
394 builder.push_str($str);
395 builder
396 }};
397}
398
399#[macro_export]
400#[doc(hidden)]
401macro_rules! __parse_arg__ {
402 ($builder:expr,"", $arg:expr) => {{
403 use $crate::text::AsBuilderPart;
404 let builder = $builder;
405 builder.push_builder_part($arg.as_builder_part());
406 builder
407 }};
408 ($builder:expr, $modif:literal, $arg:expr) => {{
409 let builder = $builder;
410 builder.push_str(format!(concat!("{:", $modif, "}"), &$arg));
411 builder
412 }};
413}
414
415#[macro_export]
416#[doc(hidden)]
417macro_rules! __parse_form__ {
418 ($builder:expr, $priority:literal,) => {{
419 use $crate::text::AsBuilderPart;
420 const PRIORITY: u8 = $crate::priority($priority);
421 let builder = $builder;
422 let id = $crate::form::DEFAULT_ID;
423 builder.push_builder_part(id.to_tag(PRIORITY).as_builder_part());
424 builder
425 }};
426 ($builder:expr, $priority:literal, a) => {{
427 use $crate::text::AsBuilderPart;
428 const PRIORITY: u8 = $crate::priority($priority);
429 let builder = $builder;
430 let id = $crate::form::ACCENT_ID;
431 builder.push_builder_part(id.to_tag(PRIORITY).as_builder_part());
432 builder
433 }};
434 ($builder:expr, $priority:literal, $($form:tt)*) => {{
435 use $crate::text::AsBuilderPart;
436 const PRIORITY: u8 = $crate::priority($priority);
437 let builder = $builder;
438 let id = $crate::form::id_of!(concat!($(stringify!($form)),*));
439 builder.push_builder_part(id.to_tag(PRIORITY).as_builder_part());
440 builder
441 }};
442}