use crate::detect_lang;
use crate::parser;
use mdbook_preprocessor::PreprocessorContext;
use std::fs;
fn wrap_content_in_code_block(content: String, language: String, lang_detected: String) -> String {
let backticks = if lang_detected == "markdown" {
let max_backticks = content
.lines()
.filter_map(|line| {
let trimmed = line.trim();
if trimmed.starts_with("```") {
Some(trimmed.chars().take_while(|&c| c == '`').count())
} else {
None
}
})
.max()
.unwrap_or(2) + 1;
"`".repeat(max_backticks)
} else {
"```".to_string()
};
format!("{}{}\n{}\n{}", backticks, language, content, backticks)
}
fn parse_include_range(input: String, max_line: usize) -> (usize, usize) {
let trimmed = input.trim();
if trimmed.is_empty() || trimmed == "-" {
return (1, max_line);
}
let parts: Vec<&str> = trimmed.split('-').collect();
if parts.len() != 2 {
return (1, max_line);
}
let start = parts[0].trim();
let end = parts[1].trim();
let start_line = if start.is_empty() {
1
} else {
match start.parse::<i32>() {
Ok(n) if n >= 1 && n as usize <= max_line => n as usize,
_ => 1, }
};
let end_line = if end.is_empty() {
max_line
} else {
match end.parse::<i32>() {
Ok(n) if n >= start_line as i32 && n as usize <= max_line => n as usize,
_ => max_line, }
};
(start_line, end_line)
}
pub fn include_script(
ctx: &PreprocessorContext,
options: Vec<parser::EmbedAppOption>,
) -> Result<String, String> {
let file_path_option = parser::get_option("file", options.clone());
if file_path_option.is_none() {
return Err("Option file is required".to_string());
}
let file_path = ctx
.root
.join(file_path_option.unwrap().value)
.to_string_lossy()
.to_string();
if !std::path::Path::new(&file_path).exists() {
return Err(format!("Cannot find file {}", file_path));
}
let content = fs::read_to_string(&file_path).unwrap_or_else(|_| String::new());
let range_option = parser::get_option("range", options.clone());
let max_line = content.lines().count();
let range = match range_option {
Some(option) => parse_include_range(option.value.clone(), max_line),
_ => (1, max_line), };
let start = range.0 - 1; let end = range.1; let lines: Vec<&str> = content.lines().collect();
let content = lines[start..end].join("\n");
let lang_option = parser::get_option("lang", options.clone());
let lang_detected = detect_lang::detect_lang(file_path.clone(), Some(&ctx.config));
let language = match lang_option {
Some(option) => option.value,
_ => lang_detected.clone(),
};
Ok(wrap_content_in_code_block(content, language, lang_detected))
}