Skip to content

Commit 2b42c64

Browse files
committed
implement file name templating (title and timestamp)
1 parent de3b588 commit 2b42c64

File tree

4 files changed

+49
-14
lines changed

4 files changed

+49
-14
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ license = "CC0-1.0"
2525
[dependencies]
2626
atty = "0.2.14" # Used for highlighting network errors
2727
base64 = "0.22.1" # Used for integrity attributes
28-
chrono = "0.4.40" # Used for formatting output timestamp
28+
chrono = "0.4.40" # Used for formatting timestamps
2929
clap = { version = "4.5.32", features = ["derive"], optional = true } # Used for processing CLI arguments
3030
cssparser = "0.34.0" # Used for dealing with CSS
3131
encoding_rs = "0.8.35" # Used for parsing and converting document charsets

src/core.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use url::Url;
1515
use crate::cache::Cache;
1616
use crate::cookies::Cookie;
1717
use crate::html::{
18-
add_favicon, create_metadata_tag, get_base_url, get_charset, has_favicon, html_to_dom,
19-
serialize_document, set_base_url, set_charset, walk_and_embed_assets,
18+
add_favicon, create_metadata_tag, get_base_url, get_charset, get_title, has_favicon,
19+
html_to_dom, serialize_document, set_base_url, set_charset, walk_and_embed_assets,
2020
};
2121
use crate::url::{clean_url, create_data_url, get_referer_url, parse_data_url, resolve_url};
2222

@@ -122,7 +122,7 @@ pub fn create_monolithic_document(
122122
source: String,
123123
options: &Options,
124124
cache: &mut Option<Cache>,
125-
) -> Result<Vec<u8>, MonolithError> {
125+
) -> Result<(Vec<u8>, Option<String>), MonolithError> {
126126
// Check if source was provided
127127
if source.is_empty() {
128128
return Err(MonolithError::new("no target specified"));
@@ -235,7 +235,7 @@ pub fn create_monolithic_document(
235235
if !media_type.eq_ignore_ascii_case("text/html")
236236
&& !media_type.eq_ignore_ascii_case("application/xhtml+xml")
237237
{
238-
return Ok(retrieved_data);
238+
return Ok((retrieved_data, None));
239239
}
240240

241241
if options
@@ -353,6 +353,8 @@ pub fn create_monolithic_document(
353353
dom = set_charset(dom, document_encoding.clone());
354354
}
355355

356+
let document_title: Option<String> = get_title(&dom.document);
357+
356358
if options.output_format == MonolithOutputFormat::HTML {
357359
// Serialize DOM tree
358360
let mut result: Vec<u8> = serialize_document(dom, document_encoding, options);
@@ -364,9 +366,9 @@ pub fn create_monolithic_document(
364366
result.splice(0..0, metadata_comment.as_bytes().to_vec());
365367
}
366368

367-
Ok(result)
369+
Ok((result, document_title))
368370
} else {
369-
Ok(vec![])
371+
Ok((vec![], document_title))
370372
}
371373
}
372374

src/html.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ pub fn compose_csp(options: &Options) -> String {
124124
}
125125

126126
pub fn create_metadata_tag(url: &Url) -> String {
127-
let timestamp = Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true);
127+
let datetime: &str = &Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true);
128128
let mut clean_url: Url = clean_url(url.clone());
129129

130130
// Prevent credentials from getting into metadata
@@ -141,7 +141,7 @@ pub fn create_metadata_tag(url: &Url) -> String {
141141
} else {
142142
"local source"
143143
},
144-
timestamp,
144+
datetime,
145145
env!("CARGO_PKG_NAME"),
146146
env!("CARGO_PKG_VERSION"),
147147
)
@@ -339,6 +339,18 @@ pub fn get_parent_node(child: &Handle) -> Handle {
339339
parent.and_then(|node| node.upgrade()).unwrap()
340340
}
341341

342+
pub fn get_title(node: &Handle) -> Option<String> {
343+
for title_node in find_nodes(node, vec!["html", "head", "title"]).iter() {
344+
for child_node in title_node.children.borrow().iter() {
345+
if let NodeData::Text { ref contents } = child_node.data {
346+
return Some(contents.borrow().to_string());
347+
}
348+
}
349+
}
350+
351+
None
352+
}
353+
342354
pub fn has_favicon(handle: &Handle) -> bool {
343355
let mut found_favicon: bool = false;
344356

src/main.rs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::fs;
22
use std::io::{self, Error as IoError, Write};
33
use std::process;
44

5+
use chrono::prelude::*;
56
use clap::Parser;
67
use tempfile::Builder;
78

@@ -118,11 +119,28 @@ enum Output {
118119
}
119120

120121
impl Output {
121-
fn new(destination: &str) -> Result<Output, IoError> {
122+
fn new(destination: &str, document_title: &str) -> Result<Output, IoError> {
122123
if destination.is_empty() || destination.eq("-") {
123124
Ok(Output::Stdout(io::stdout()))
124125
} else {
125-
Ok(Output::File(fs::File::create(destination)?))
126+
let datetime: &str = &Utc::now().to_rfc3339_opts(SecondsFormat::Secs, true);
127+
let final_destination = &destination
128+
.replace("%timestamp%", &datetime.replace(':', "_"))
129+
.replace(
130+
"%title%",
131+
document_title
132+
.to_string()
133+
.replace('/', "∕")
134+
.replace('\\', "⧵")
135+
.replace('<', "[")
136+
.replace('>', "]")
137+
.replace(':', "_")
138+
.replace('\"', "''")
139+
.replace('|', "_")
140+
.replace('?', "")
141+
.trim_start_matches('.'),
142+
);
143+
Ok(Output::File(fs::File::create(final_destination)?))
126144
}
127145
}
128146

@@ -246,10 +264,13 @@ fn main() {
246264

247265
// Retrieve target from source and output result
248266
match create_monolithic_document(cli.target, &options, &mut cache) {
249-
Ok(result) => {
267+
Ok((result, title)) => {
250268
// Define output
251-
let mut output = Output::new(&destination.unwrap_or(String::new()))
252-
.expect("could not prepare output");
269+
let mut output = Output::new(
270+
&destination.unwrap_or(String::new()),
271+
&title.unwrap_or_default(),
272+
)
273+
.expect("could not prepare output");
253274

254275
// Write result into STDOUT or file
255276
output.write(&result).expect("could not write output");

0 commit comments

Comments
 (0)