#[derive(Debug, Clone, PartialEq)]
pub struct CodeBlockHeader {
pub name: Option<String>,
pub fragment: Option<String>,
pub parameters: Vec<String>,
}
impl CodeBlockHeader {
pub fn test_banner_string(name: &str) -> String {
format!("[](.test/{name}.log)")
}
pub(crate) fn is_test_banner(line: &str) -> bool {
line.starts_with("[![test]")
}
pub(crate) fn is_code_block_start(line: &str) -> bool {
microcad_lang_base::MICROCAD_EXTENSIONS
.iter()
.any(|ext| line.starts_with(&format!("```{ext}")))
|| Self::is_test_banner(line)
}
}
impl std::fmt::Display for CodeBlockHeader {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(name) = &self.name {
writeln!(f, "{}\n", Self::test_banner_string(name))?
}
write!(f, "```µcad")?;
if let Some(name) = &self.name {
write!(f, ",{name}")?
}
match &self.fragment {
None => {}
Some(fragment) => {
write!(f, "#{fragment}")?;
}
};
if !self.parameters.is_empty() {
write!(f, "({})", self.parameters.join(","))?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct CodeBlock {
pub header: CodeBlockHeader,
pub code: String,
pub line_offset: usize,
}
impl CodeBlock {
pub fn name(&self) -> &Option<String> {
&self.header.name
}
pub fn fragment(&self) -> &Option<String> {
&self.header.fragment
}
pub fn code(&self) -> &str {
&self.code
}
pub fn line_offset(&self) -> usize {
self.line_offset
}
pub fn can_format(&self) -> bool {
!self.header.parameters.contains(&String::from("no_format"))
}
}
impl std::fmt::Display for CodeBlock {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.header)?;
if !self.code.is_empty() {
writeln!(f, "{}", self.code)?;
}
write!(f, "```")
}
}