1use std::fs;
2use std::process::Command;
3
4use crate::error::Error;
5
6pub fn transform(xml: &str, xslt_files: &[(&str, &str)]) -> Result<String, Error> {
19 let tmp_dir = tempfile::tempdir().map_err(|e| Error::Xslt(format!("tempdir: {e}")))?;
20
21 for (name, content) in xslt_files {
23 fs::write(tmp_dir.path().join(name), content)?;
24 }
25
26 let xml_path = tmp_dir.path().join("input.xml");
28 fs::write(&xml_path, xml)?;
29
30 let main_xslt = xslt_files
32 .first()
33 .map(|(name, _)| name)
34 .ok_or_else(|| Error::Xslt("no XSLT files provided".into()))?;
35 let xslt_path = tmp_dir.path().join(main_xslt);
36
37 let saxon = find_saxon()?;
39
40 let output = Command::new(&saxon)
42 .arg(format!("-s:{}", xml_path.display()))
43 .arg(format!("-xsl:{}", xslt_path.display()))
44 .output()
45 .map_err(|e| Error::Xslt(format!("failed to run saxon: {e}")))?;
46
47 if !output.status.success() {
48 let stderr = String::from_utf8_lossy(&output.stderr);
49 return Err(Error::Xslt(format!("saxon failed: {stderr}")));
50 }
51
52 String::from_utf8(output.stdout)
53 .map_err(|e| Error::Xslt(format!("saxon output is not UTF-8: {e}")))
54}
55
56fn find_saxon() -> Result<String, Error> {
57 let check = Command::new("which").arg("saxon").output();
59 if let Ok(output) = check
60 && output.status.success()
61 {
62 let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
63 if path.is_empty() {
64 return Err(Error::Xslt(install_instructions()));
65 }
66 return Ok(path);
67 }
68
69 Err(Error::Xslt(install_instructions()))
70}
71
72fn install_instructions() -> String {
73 let os = std::env::consts::OS;
74 let mut msg = String::from("saxon not found in PATH.\n\nInstall instructions:\n");
75
76 match os {
77 "macos" => {
78 msg.push_str(" brew install saxon\n");
79 }
80 "linux" => {
81 msg.push_str(
82 " Download Saxon-HE from:\n \
83 https://github.com/Saxonica/Saxon-HE/releases\n \
84 Then add the `saxon` wrapper script to your PATH.\n",
85 );
86 }
87 "windows" => {
88 msg.push_str(
89 " Download Saxon-HE from:\n \
90 https://github.com/Saxonica/Saxon-HE/releases\n \
91 Then add saxon.exe to your PATH.\n",
92 );
93 }
94 _ => {
95 msg.push_str(
96 " Download Saxon-HE from:\n \
97 https://github.com/Saxonica/Saxon-HE/releases\n",
98 );
99 }
100 }
101
102 msg
103}