We were running into issues where the documents grew too large for being passed as a string to emacs, and we need to handle #+setupfile so we need to start handling org-mode documents as files and not just as anonymous streams of text. The anonymous stream of text handling will remain because the automated tests use it.
124 lines
3.3 KiB
Rust
124 lines
3.3 KiB
Rust
use std::path::Path;
|
|
use std::process::Command;
|
|
|
|
pub fn emacs_parse_anonymous_org_document<C>(
|
|
file_contents: C,
|
|
) -> Result<String, Box<dyn std::error::Error>>
|
|
where
|
|
C: AsRef<str>,
|
|
{
|
|
let escaped_file_contents = escape_elisp_string(file_contents);
|
|
let elisp_script = format!(
|
|
r#"(progn
|
|
(erase-buffer)
|
|
(insert "{escaped_file_contents}")
|
|
(org-mode)
|
|
(message "%s" (pp-to-string (org-element-parse-buffer)))
|
|
)"#,
|
|
escaped_file_contents = escaped_file_contents
|
|
);
|
|
let mut cmd = Command::new("emacs");
|
|
let cmd = cmd
|
|
.arg("-q")
|
|
.arg("--no-site-file")
|
|
.arg("--no-splash")
|
|
.arg("--batch")
|
|
.arg("--eval")
|
|
.arg(elisp_script);
|
|
let out = cmd.output()?;
|
|
out.status.exit_ok()?;
|
|
let org_sexp = out.stderr;
|
|
Ok(String::from_utf8(org_sexp)?)
|
|
}
|
|
|
|
pub fn emacs_parse_file_org_document<P>(file_path: P) -> Result<String, Box<dyn std::error::Error>>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
let file_path = file_path.as_ref().canonicalize()?;
|
|
let containing_directory = file_path.parent().ok_or(format!(
|
|
"Failed to get containing directory for path {}",
|
|
file_path.display()
|
|
))?;
|
|
let elisp_script = format!(
|
|
r#"(progn
|
|
(org-mode)
|
|
(message "%s" (pp-to-string (org-element-parse-buffer)))
|
|
)"#
|
|
);
|
|
let mut cmd = Command::new("emacs");
|
|
let cmd = cmd
|
|
.current_dir(containing_directory)
|
|
.arg("-q")
|
|
.arg("--no-site-file")
|
|
.arg("--no-splash")
|
|
.arg("--batch")
|
|
.arg("--insert")
|
|
.arg(file_path.as_os_str())
|
|
.arg("--eval")
|
|
.arg(elisp_script);
|
|
let out = cmd.output()?;
|
|
out.status.exit_ok()?;
|
|
let org_sexp = out.stderr;
|
|
Ok(String::from_utf8(org_sexp)?)
|
|
}
|
|
|
|
fn escape_elisp_string<C>(file_contents: C) -> String
|
|
where
|
|
C: AsRef<str>,
|
|
{
|
|
let source = file_contents.as_ref();
|
|
let source_len = source.len();
|
|
// We allocate a string 10% larger than the source to account for escape characters. Without this, we would have more allocations during processing.
|
|
let mut output = String::with_capacity(source_len + (source_len / 10));
|
|
for c in source.chars() {
|
|
match c {
|
|
'"' | '\\' => {
|
|
output.push('\\');
|
|
output.push(c);
|
|
}
|
|
_ => {
|
|
output.push(c);
|
|
}
|
|
}
|
|
}
|
|
output
|
|
}
|
|
|
|
pub fn get_emacs_version() -> Result<String, Box<dyn std::error::Error>> {
|
|
let elisp_script = r#"(progn
|
|
(message "%s" (version))
|
|
)"#;
|
|
let mut cmd = Command::new("emacs");
|
|
let cmd = cmd
|
|
.arg("-q")
|
|
.arg("--no-site-file")
|
|
.arg("--no-splash")
|
|
.arg("--batch")
|
|
.arg("--eval")
|
|
.arg(elisp_script);
|
|
|
|
let out = cmd.output()?;
|
|
out.status.exit_ok()?;
|
|
Ok(String::from_utf8(out.stderr)?)
|
|
}
|
|
|
|
pub fn get_org_mode_version() -> Result<String, Box<dyn std::error::Error>> {
|
|
let elisp_script = r#"(progn
|
|
(org-mode)
|
|
(message "%s" (org-version nil t nil))
|
|
)"#;
|
|
let mut cmd = Command::new("emacs");
|
|
let cmd = cmd
|
|
.arg("-q")
|
|
.arg("--no-site-file")
|
|
.arg("--no-splash")
|
|
.arg("--batch")
|
|
.arg("--eval")
|
|
.arg(elisp_script);
|
|
|
|
let out = cmd.output()?;
|
|
out.status.exit_ok()?;
|
|
Ok(String::from_utf8(out.stderr)?)
|
|
}
|