use slack_morphism::prelude::*;
const SECTION_LIMIT: usize = 3000;
pub(crate) fn blocks_from_mrkdwn(text: &str) -> Vec<SlackBlock> {
let mut blocks: Vec<SlackBlock> = Vec::new();
let mut segment = String::new();
let mut in_fence = false;
let flush = |segment: &mut String, blocks: &mut Vec<SlackBlock>| {
let trimmed = segment.trim();
if !trimmed.is_empty() {
for piece in split_to_limit(trimmed) {
blocks.push(section(&piece));
}
}
segment.clear();
};
for line in text.lines() {
if line.trim_start().starts_with("```") {
in_fence = !in_fence;
}
if !in_fence && is_rule(line) {
flush(&mut segment, &mut blocks);
blocks.push(SlackBlock::Divider(SlackDividerBlock::new()));
continue;
}
segment.push_str(line);
segment.push('\n');
}
flush(&mut segment, &mut blocks);
blocks
}
pub(crate) fn context_footer(footer: &str) -> SlackBlock {
SlackBlock::Context(SlackContextBlock::new(vec![
SlackContextBlockElement::MarkDown(SlackBlockMarkDownText::new(format!("_{footer}_"))),
]))
}
fn is_rule(line: &str) -> bool {
let t = line.trim();
t.len() >= 3 && (t.chars().all(|c| c == '-') || t.chars().all(|c| c == '*'))
}
fn section(text: &str) -> SlackBlock {
SlackBlock::Section(SlackSectionBlock::new().with_text(SlackBlockText::MarkDown(
SlackBlockMarkDownText::new(text.to_string()),
)))
}
fn split_to_limit(text: &str) -> Vec<String> {
if text.chars().count() <= SECTION_LIMIT {
return vec![text.to_string()];
}
let mut out: Vec<String> = Vec::new();
let mut current = String::new();
for para in text.split("\n\n") {
let candidate_len = if current.is_empty() {
para.chars().count()
} else {
current.chars().count() + 2 + para.chars().count()
};
if candidate_len > SECTION_LIMIT && !current.is_empty() {
out.push(std::mem::take(&mut current));
}
if para.chars().count() > SECTION_LIMIT {
let chars: Vec<char> = para.chars().collect();
for chunk in chars.chunks(SECTION_LIMIT) {
out.push(chunk.iter().collect());
}
continue;
}
if !current.is_empty() {
current.push_str("\n\n");
}
current.push_str(para);
}
if !current.is_empty() {
out.push(current);
}
out
}