use alloc::string::String;
use alloc::vec::Vec;
use brink_format::{LineEntry, PluralResolver};
use super::{OutputBuffer, OutputPart, resolve_parts};
use crate::program::Program;
#[derive(Debug, Clone, PartialEq)]
pub struct Fragment {
pub parts: Vec<OutputPart>,
pub tags: Vec<String>,
}
impl OutputBuffer {
pub fn begin_fragment(&mut self) {
self.fragment_depth += 1;
self.fragment_capture.push(OutputPart::Checkpoint);
self.fragment_pending_tags.push(Vec::new());
}
#[expect(clippy::cast_possible_truncation)]
pub fn end_fragment(&mut self) -> Option<u32> {
let cp_idx = self
.fragment_capture
.iter()
.rposition(|p| matches!(p, OutputPart::Checkpoint))?;
let captured: Vec<OutputPart> = self.fragment_capture.drain(cp_idx..).collect();
let parts: Vec<OutputPart> = captured.into_iter().skip(1).collect();
let tags = self.fragment_pending_tags.pop().unwrap_or_default();
let idx = self.fragments.len() as u32;
self.fragments.push(Fragment { parts, tags });
self.fragment_depth = self.fragment_depth.saturating_sub(1);
Some(idx)
}
pub fn in_fragment_capture(&self) -> bool {
self.fragment_depth > 0
}
pub fn push_fragment_tag(&mut self, tag: String) {
if let Some(pending) = self.fragment_pending_tags.last_mut() {
pending.push(tag);
}
}
pub fn fragment_tags(&self, idx: u32) -> Option<&[String]> {
self.fragments.get(idx as usize).map(|f| f.tags.as_slice())
}
pub fn fragments(&self) -> &[Fragment] {
&self.fragments
}
pub fn fragment(&self, idx: u32) -> Option<&[OutputPart]> {
self.fragments.get(idx as usize).map(|f| f.parts.as_slice())
}
pub fn resolve_fragment(
&self,
idx: u32,
program: &Program,
line_tables: &[Vec<LineEntry>],
resolver: Option<&dyn PluralResolver>,
) -> String {
match self.fragment(idx) {
Some(parts) => resolve_parts(parts, program, line_tables, resolver, &self.fragments),
None => String::new(),
}
}
}