asciidork-parser 0.36.0

Asciidork parser
Documentation
use std::any::Any;

use crate::internal::*;

pub trait AttrRefObserver: Any {
  fn attr_defined(&mut self, name: &str, value: &AttrValue, name_loc: SourceLocation);
  fn attr_ref_replaced(&mut self, attr_name: &str, loc: SourceLocation);
  fn attr_ref_missing(&mut self, attr_name: &str, loc: SourceLocation);
}

impl<'arena> Parser<'arena> {
  pub(crate) fn set_source_file_attrs(&mut self) {
    let source_file = self.lexer.source_file().clone();
    match source_file {
      SourceFile::Stdin { .. } => {}
      SourceFile::Path(path) => {
        let file_stem = path.file_stem();
        let ext = path.extension();
        let filename = format!("{file_stem}{ext}");
        self.insert_job_attr("docfilesuffix", ext.to_string());
        self.insert_job_attr("docname", file_stem.to_string());
        self.insert_job_attr("asciidork-docfilename", filename.clone());
        match self.document.meta.safe_mode {
          SafeMode::Server | SafeMode::Secure => {
            self.insert_job_attr("docdir", "");
            self.insert_job_attr("docfile", filename);
            self.insert_job_attr("user-home", ".");
          }
          SafeMode::Safe | SafeMode::Unsafe => {
            self.insert_job_attr("docfile", path.to_string());
            self.insert_job_attr("docdir", path.dirname().to_string());
            if let Some(home) = std::env::home_dir() {
              self.insert_job_attr("user-home", home.to_str().unwrap_or("."));
            }
          }
        }
      }
      SourceFile::Tmp => {}
    };
  }

  pub(crate) fn push_token_replacing_attr_ref(
    &mut self,
    mut token: Token<'arena>,
    line: &mut Line<'arena>,
    drop_line: &mut bool,
  ) -> Result<()> {
    if self.ctx.replacing_attr {
      token.attr_replacement = true;
      if self.lexer.tmp_buf_is_empty() {
        self.ctx.replacing_attr = false;
      }
    }
    if token.kind(TokenKind::AttrRef) && self.ctx.subs.attr_refs() {
      match self.document.meta.get(&token.lowercase_attr_name()) {
        Some(AttrValue::String(attr_val)) => {
          #[cfg(feature = "attr_ref_observation")]
          if let Some(ref mut observer) = self.attr_ref_observer.as_mut() {
            observer.attr_ref_replaced(token.attr_name(), token.loc);
          }
          if !attr_val.is_empty() {
            let mut buf_loc = BufLoc::Repeat(token.loc);
            // special case: attr def macro wrapped in pass macro, we need
            // the replaced attr to have real locs for formatting
            if attr_val.starts_with("\x1Bass:")
              && let Some(def) = self
                .ctx
                .attr_defs
                .iter()
                .find(|d| d.name == token.attr_name())
            {
              buf_loc = BufLoc::From(def.val_loc);
            }
            self.lexer.set_tmp_buf(attr_val, buf_loc);
          }
          line.push(token);
          self.ctx.replacing_attr = true;
        }
        _ => {
          #[cfg(feature = "attr_ref_observation")]
          if let Some(ref mut observer) = self.attr_ref_observer.as_mut() {
            observer.attr_ref_missing(token.attr_name(), token.loc);
          }
          match self.document.meta.str("attribute-missing") {
            Some("drop") => {}
            Some("drop-line") => *drop_line = true,
            val => {
              token.kind = TokenKind::Word;
              if val == Some("warn") {
                self.err_token_full("Skipping reference to missing attribute", &token)?;
              }
              line.push(token);
            }
          }
        }
      }
    } else {
      line.push(token);
    }
    Ok(())
  }

  pub(crate) fn insert_job_attr(&mut self, key: &str, value: impl Into<AttrValue>) {
    self
      .document
      .meta
      .insert_job_attr(key, JobAttr::readonly(value))
      .unwrap();
  }

  pub(crate) fn insert_doc_attr(
    &mut self,
    key: &str,
    value: impl Into<AttrValue>,
  ) -> std::result::Result<(), String> {
    self.document.meta.insert_doc_attr(key, value)
  }
}