mod apply;
mod split;
use apply::process_tag_block;
use crate::utils::RenderError;
use ass_core::analysis::events::TextAnalysis;
use ass_core::ExtensionRegistry;
#[cfg(feature = "nostd")]
use alloc::{
string::{String, ToString},
vec,
vec::Vec,
};
#[cfg(not(feature = "nostd"))]
use std::{
string::{String, ToString},
vec::Vec,
};
#[derive(Debug, Clone)]
pub struct TextSegment {
pub text: String,
pub start: usize,
pub end: usize,
pub tags: super::tag_processor::ProcessedTags,
}
pub fn segment_text_with_tags(
text: &str,
_registry: Option<&ExtensionRegistry>,
) -> Result<Vec<TextSegment>, RenderError> {
#[cfg(feature = "plugins")]
let analysis = TextAnalysis::analyze_with_registry(text, _registry)
.map_err(|e| RenderError::InvalidScript(e.to_string()))?;
#[cfg(not(feature = "plugins"))]
let analysis =
TextAnalysis::analyze(text).map_err(|e| RenderError::InvalidScript(e.to_string()))?;
let tags = analysis.override_tags();
if tags.is_empty() {
return Ok(vec![TextSegment {
text: analysis.plain_text().to_string(),
start: 0,
end: text.len(),
tags: super::tag_processor::ProcessedTags::default(),
}]);
}
let mut segments = Vec::new();
let mut current_tags = super::tag_processor::ProcessedTags::default();
let mut last_pos = 0;
let mut current_text = String::new();
let mut chars = text.chars();
let mut pos = 0;
let mut in_tag = false;
let mut tag_start = 0;
let mut brace_depth = 0;
while let Some(ch) = chars.next() {
if ch == '{' {
if !in_tag {
if !current_text.is_empty() {
segments.push(TextSegment {
text: current_text.clone(),
start: last_pos,
end: pos,
tags: current_tags.clone(),
});
current_text.clear();
last_pos = pos;
}
in_tag = true;
tag_start = pos;
brace_depth = 1;
} else {
brace_depth += 1;
}
} else if ch == '}' && in_tag {
brace_depth -= 1;
if brace_depth == 0 {
let tag_content = &text[tag_start + 1..pos];
let has_karaoke = tag_content.contains("\\k") || tag_content.contains("\\K");
if has_karaoke && !current_text.is_empty() {
segments.push(TextSegment {
text: current_text.clone(),
start: last_pos,
end: tag_start,
tags: current_tags.clone(),
});
current_text.clear();
}
process_tag_block(tag_content, &mut current_tags)?;
in_tag = false;
last_pos = pos + 1;
}
} else if !in_tag {
if ch == '\\' {
if let Some(next) = chars.next() {
pos += next.len_utf8();
match next {
'N' | 'n' => current_text.push('\n'),
'h' => current_text.push('\u{00A0}'),
_ => {
current_text.push(ch);
current_text.push(next);
}
}
} else {
current_text.push(ch);
}
} else {
current_text.push(ch);
}
}
pos += ch.len_utf8();
}
if !current_text.is_empty() || segments.is_empty() {
segments.push(TextSegment {
text: current_text,
start: last_pos,
end: text.len(),
tags: current_tags,
});
}
Ok(segments)
}
pub use super::tag_processor::{parse_alpha, parse_color, parse_move_args, parse_pos_args};