asciidork_parser/tasks/
attr_refs.rs

1use std::any::Any;
2
3use crate::internal::*;
4
5pub trait AttrRefObserver: Any {
6  fn attr_defined(&mut self, name: &str, value: &AttrValue, name_loc: SourceLocation);
7  fn attr_ref_replaced(&mut self, attr_name: &str, loc: SourceLocation);
8  fn attr_ref_missing(&mut self, attr_name: &str, loc: SourceLocation);
9}
10
11impl<'arena> Parser<'arena> {
12  pub(crate) fn set_source_file_attrs(&mut self) {
13    let source_file = self.lexer.source_file().clone();
14    match source_file {
15      SourceFile::Stdin { .. } => {}
16      SourceFile::Path(path) => {
17        let file_stem = path.file_stem();
18        let ext = path.extension();
19        self.insert_job_attr("docfilesuffix", ext.to_string());
20        self.insert_job_attr("docname", file_stem.to_string());
21        self.insert_job_attr("asciidork-docfilename", format!("{file_stem}{ext}"));
22        match self.document.meta.safe_mode {
23          SafeMode::Server | SafeMode::Secure => {
24            self.insert_job_attr("docdir", "");
25            self.insert_job_attr("docfile", "");
26          }
27          SafeMode::Safe | SafeMode::Unsafe => {
28            self.insert_job_attr("docfile", path.to_string());
29            self.insert_job_attr("docdir", path.dirname().to_string());
30          }
31        }
32      }
33      SourceFile::Tmp => {}
34    };
35  }
36
37  pub(crate) fn push_token_replacing_attr_ref(
38    &mut self,
39    mut token: Token<'arena>,
40    line: &mut Line<'arena>,
41    drop_line: &mut bool,
42  ) -> Result<()> {
43    if token.kind(TokenKind::AttrRef) && self.ctx.subs.attr_refs() {
44      match self.document.meta.get(&token.lowercase_attr_name()) {
45        Some(AttrValue::String(attr_val)) => {
46          #[cfg(feature = "attr_ref_observation")]
47          if let Some(ref mut observer) = self.attr_ref_observer.as_mut() {
48            observer.attr_ref_replaced(token.attr_name(), token.loc);
49          }
50          if !attr_val.is_empty() {
51            self.lexer.set_tmp_buf(attr_val, BufLoc::Repeat(token.loc));
52          }
53          line.push(token);
54        }
55        _ => {
56          #[cfg(feature = "attr_ref_observation")]
57          if let Some(ref mut observer) = self.attr_ref_observer.as_mut() {
58            observer.attr_ref_missing(token.attr_name(), token.loc);
59          }
60          match self.document.meta.str("attribute-missing") {
61            Some("drop") => {}
62            Some("drop-line") => *drop_line = true,
63            val => {
64              token.kind = TokenKind::Word;
65              if val == Some("warn") {
66                self.err_token_full("Skipping reference to missing attribute", &token)?;
67              }
68              line.push(token);
69            }
70          }
71        }
72      }
73    } else {
74      line.push(token);
75    }
76    Ok(())
77  }
78
79  pub(crate) fn insert_job_attr(&mut self, key: &str, value: impl Into<AttrValue>) {
80    self
81      .document
82      .meta
83      .insert_job_attr(key, JobAttr::readonly(value))
84      .unwrap();
85  }
86
87  pub(crate) fn insert_doc_attr(
88    &mut self,
89    key: &str,
90    value: impl Into<AttrValue>,
91  ) -> std::result::Result<(), String> {
92    self.document.meta.insert_doc_attr(key, value)
93  }
94}