1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
//! Input source types for HTML content.
use std::path::PathBuf;
use crate::{Error, Result};
/// Source of HTML content to convert to PDF.
#[derive(Debug, Clone)]
pub enum InputSource {
/// Raw HTML string
Html(String),
/// Path to local HTML file
File(PathBuf),
/// URL to fetch and render
Url(String),
/// Template source with data (requires `templates` feature)
#[cfg(feature = "templates")]
Template {
/// Template source (inline or file)
source: TemplateSource,
/// Data to render the template with
data: serde_json::Value,
},
}
/// Template source for rendering.
#[cfg(feature = "templates")]
#[derive(Debug, Clone)]
pub enum TemplateSource {
/// Inline template string
Inline(String),
/// Path to template file
File(PathBuf),
}
impl InputSource {
/// Create an HTML string input source.
pub fn html<S: Into<String>>(content: S) -> Self {
Self::Html(content.into())
}
/// Create a file input source.
pub fn file<P: Into<PathBuf>>(path: P) -> Self {
Self::File(path.into())
}
/// Create a URL input source.
pub fn url<S: Into<String>>(url: S) -> Self {
Self::Url(url.into())
}
/// Create a template input source with inline template string.
#[cfg(feature = "templates")]
pub fn template<S, D>(template: S, data: D) -> Result<Self>
where
S: Into<String>,
D: serde::Serialize,
{
let data = serde_json::to_value(data)
.map_err(|e| Error::Template(format!("Failed to serialize data: {}", e)))?;
Ok(Self::Template {
source: TemplateSource::Inline(template.into()),
data,
})
}
/// Create a template input source with template file.
#[cfg(feature = "templates")]
pub fn template_file<P, D>(path: P, data: D) -> Result<Self>
where
P: Into<PathBuf>,
D: serde::Serialize,
{
let data = serde_json::to_value(data)
.map_err(|e| Error::Template(format!("Failed to serialize data: {}", e)))?;
Ok(Self::Template {
source: TemplateSource::File(path.into()),
data,
})
}
/// Resolve the input to an HTML string.
///
/// For templates, this renders the template with the provided data.
pub async fn resolve(&self) -> Result<String> {
match self {
InputSource::Html(html) => Ok(html.clone()),
InputSource::File(path) => {
if !path.exists() {
return Err(Error::InputSource(
format!("File not found: {}", path.display())
));
}
tokio::fs::read_to_string(path)
.await
.map_err(|e| Error::InputSource(
format!("Failed to read file {}: {}", path.display(), e)
))
}
InputSource::Url(url) => {
// For URLs, we don't fetch the content here - Chrome will navigate to it
Ok(url.clone())
}
#[cfg(feature = "templates")]
InputSource::Template { source, data } => {
use crate::template::render_template;
render_template(source, data).await
}
}
}
}