Skip to content

Commit 537b0b9

Browse files
n-tux-tmpNewbyteen-tux-tmpn-tux-tmp
authored
add get_hash function (#1962)
Co-authored-by: Newbyte <[email protected]> Co-authored-by: n-tux-tmp <[email protected]> Co-authored-by: n-tux-tmp <[email protected]>
1 parent fd0e25e commit 537b0b9

File tree

5 files changed

+176
-59
lines changed

5 files changed

+176
-59
lines changed

components/site/src/tpls.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,13 @@ pub fn register_early_global_fns(site: &mut Site) -> TeraResult<()> {
5353
),
5454
);
5555
site.tera.register_function(
56-
"get_file_hash",
57-
global_fns::GetFileHash::new(
56+
"get_hash",
57+
global_fns::GetHash::new(
5858
site.base_path.clone(),
5959
site.config.theme.clone(),
6060
site.output_path.clone(),
6161
),
6262
);
63-
6463
site.tera.register_filter(
6564
"markdown",
6665
filters::MarkdownFilter::new(

components/templates/src/global_fns/files.rs

+163-49
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::HashMap;
22
use std::path::PathBuf;
3-
use std::{fs, io, result};
3+
use std::fs;
4+
use std::io::Read;
45

56
use crate::global_fns::helpers::search_for_file;
67
use config::Config;
@@ -10,20 +11,20 @@ use libs::tera::{from_value, to_value, Function as TeraFn, Result, Value};
1011
use libs::url;
1112
use utils::site::resolve_internal_link;
1213

13-
fn compute_file_hash<D: digest::Digest>(
14-
mut file: fs::File,
14+
fn compute_hash<D: digest::Digest>(
15+
literal: String,
1516
as_base64: bool,
16-
) -> result::Result<String, io::Error>
17+
) -> String
1718
where
1819
digest::Output<D>: core::fmt::LowerHex,
1920
D: std::io::Write,
2021
{
2122
let mut hasher = D::new();
22-
io::copy(&mut file, &mut hasher)?;
23+
hasher.update(literal);
2324
if as_base64 {
24-
Ok(encode_b64(hasher.finalize()))
25+
encode_b64(hasher.finalize())
2526
} else {
26-
Ok(format!("{:x}", hasher.finalize()))
27+
format!("{:x}", hasher.finalize())
2728
}
2829
}
2930

@@ -128,7 +129,13 @@ impl TeraFn for GetUrl {
128129
)
129130
.map_err(|e| format!("`get_url`: {}", e))?
130131
.and_then(|(p, _)| fs::File::open(&p).ok())
131-
.and_then(|f| compute_file_hash::<Sha256>(f, false).ok())
132+
.and_then(|mut f| {
133+
let mut contents = String::new();
134+
135+
f.read_to_string(&mut contents).ok()?;
136+
137+
Some(compute_hash::<Sha256>(contents, false))
138+
})
132139
{
133140
Some(hash) => {
134141
permalink = format!("{}?h={}", permalink, hash);
@@ -166,71 +173,99 @@ impl TeraFn for GetUrl {
166173
}
167174

168175
#[derive(Debug)]
169-
pub struct GetFileHash {
176+
pub struct GetHash {
170177
base_path: PathBuf,
171178
theme: Option<String>,
172179
output_path: PathBuf,
173180
}
174-
impl GetFileHash {
181+
impl GetHash {
175182
pub fn new(base_path: PathBuf, theme: Option<String>, output_path: PathBuf) -> Self {
176183
Self { base_path, theme, output_path }
177184
}
178185
}
179186

180-
impl TeraFn for GetFileHash {
187+
impl TeraFn for GetHash {
181188
fn call(&self, args: &HashMap<String, Value>) -> Result<Value> {
182-
let path = required_arg!(
189+
let path = optional_arg!(
183190
String,
184191
args.get("path"),
185-
"`get_file_hash` requires a `path` argument with a string value"
192+
"`get_hash` requires either a `path` or a `literal` argument with a string value"
193+
);
194+
195+
let literal = optional_arg!(
196+
String,
197+
args.get("literal"),
198+
"`get_hash` requires either a `path` or a `literal` argument with a string value"
186199
);
200+
201+
let contents = match (path, literal) {
202+
(Some(_), Some(_)) => {
203+
return Err("`get_hash`: must have only one of `path` or `literal` argument".into());
204+
},
205+
(None, None) => {
206+
return Err("`get_hash`: must have at least one of `path` or `literal` argument".into());
207+
},
208+
(Some(path_v), None) => {
209+
let file_path =
210+
match search_for_file(&self.base_path, &path_v, &self.theme, &self.output_path)
211+
.map_err(|e| format!("`get_hash`: {}", e))?
212+
{
213+
Some((f, _)) => f,
214+
None => {
215+
return Err(format!("`get_hash`: Cannot find file: {}", path_v).into());
216+
}
217+
};
218+
219+
let mut f = match std::fs::File::open(file_path) {
220+
Ok(f) => f,
221+
Err(e) => {
222+
return Err(format!("File {} could not be open: {}", path_v, e).into());
223+
}
224+
};
225+
226+
let mut contents = String::new();
227+
228+
match f.read_to_string(&mut contents) {
229+
Ok(f) => f,
230+
Err(e) => {
231+
return Err(format!("File {} could not be read: {}", path_v, e).into());
232+
}
233+
};
234+
235+
contents
236+
}
237+
(None, Some(literal_v)) => { literal_v }
238+
};
239+
240+
187241
let sha_type = optional_arg!(
188242
u16,
189243
args.get("sha_type"),
190-
"`get_file_hash`: `sha_type` must be 256, 384 or 512"
244+
"`get_hash`: `sha_type` must be 256, 384 or 512"
191245
)
192246
.unwrap_or(384);
247+
193248
let base64 = optional_arg!(
194249
bool,
195250
args.get("base64"),
196-
"`get_file_hash`: `base64` must be true or false"
251+
"`get_hash`: `base64` must be true or false"
197252
)
198253
.unwrap_or(true);
199254

200-
let file_path =
201-
match search_for_file(&self.base_path, &path, &self.theme, &self.output_path)
202-
.map_err(|e| format!("`get_file_hash`: {}", e))?
203-
{
204-
Some((f, _)) => f,
205-
None => {
206-
return Err(format!("`get_file_hash`: Cannot find file: {}", path).into());
207-
}
208-
};
209-
210-
let f = match std::fs::File::open(file_path) {
211-
Ok(f) => f,
212-
Err(e) => {
213-
return Err(format!("File {} could not be open: {}", path, e).into());
214-
}
215-
};
216-
217255
let hash = match sha_type {
218-
256 => compute_file_hash::<Sha256>(f, base64),
219-
384 => compute_file_hash::<Sha384>(f, base64),
220-
512 => compute_file_hash::<Sha512>(f, base64),
221-
_ => return Err("`get_file_hash`: Invalid sha value".into()),
256+
256 => compute_hash::<Sha256>(contents, base64),
257+
384 => compute_hash::<Sha384>(contents, base64),
258+
512 => compute_hash::<Sha512>(contents, base64),
259+
_ => return Err("`get_hash`: Invalid sha value".into()),
222260
};
223261

224-
match hash {
225-
Ok(digest) => Ok(to_value(digest).unwrap()),
226-
Err(_) => Err("`get_file_hash`: could no compute hash".into()),
227-
}
262+
Ok(to_value(hash).unwrap())
228263
}
229264
}
230265

231266
#[cfg(test)]
232267
mod tests {
233-
use super::{GetFileHash, GetUrl};
268+
use super::{GetHash, GetUrl};
234269

235270
use std::collections::HashMap;
236271
use std::fs::create_dir;
@@ -456,7 +491,7 @@ title = "A title"
456491
#[test]
457492
fn can_get_file_hash_sha256_no_base64() {
458493
let dir = create_temp_dir();
459-
let static_fn = GetFileHash::new(dir.into_path(), None, PathBuf::new());
494+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
460495
let mut args = HashMap::new();
461496
args.insert("path".to_string(), to_value("app.css").unwrap());
462497
args.insert("sha_type".to_string(), to_value(256).unwrap());
@@ -470,7 +505,7 @@ title = "A title"
470505
#[test]
471506
fn can_get_file_hash_sha256_base64() {
472507
let dir = create_temp_dir();
473-
let static_fn = GetFileHash::new(dir.into_path(), None, PathBuf::new());
508+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
474509
let mut args = HashMap::new();
475510
args.insert("path".to_string(), to_value("app.css").unwrap());
476511
args.insert("sha_type".to_string(), to_value(256).unwrap());
@@ -481,7 +516,7 @@ title = "A title"
481516
#[test]
482517
fn can_get_file_hash_sha384_no_base64() {
483518
let dir = create_temp_dir();
484-
let static_fn = GetFileHash::new(dir.into_path(), None, PathBuf::new());
519+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
485520
let mut args = HashMap::new();
486521
args.insert("path".to_string(), to_value("app.css").unwrap());
487522
args.insert("base64".to_string(), to_value(false).unwrap());
@@ -494,7 +529,7 @@ title = "A title"
494529
#[test]
495530
fn can_get_file_hash_sha384() {
496531
let dir = create_temp_dir();
497-
let static_fn = GetFileHash::new(dir.into_path(), None, PathBuf::new());
532+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
498533
let mut args = HashMap::new();
499534
args.insert("path".to_string(), to_value("app.css").unwrap());
500535
assert_eq!(
@@ -506,7 +541,7 @@ title = "A title"
506541
#[test]
507542
fn can_get_file_hash_sha512_no_base64() {
508543
let dir = create_temp_dir();
509-
let static_fn = GetFileHash::new(dir.into_path(), None, PathBuf::new());
544+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
510545
let mut args = HashMap::new();
511546
args.insert("path".to_string(), to_value("app.css").unwrap());
512547
args.insert("sha_type".to_string(), to_value(512).unwrap());
@@ -520,7 +555,7 @@ title = "A title"
520555
#[test]
521556
fn can_get_file_hash_sha512() {
522557
let dir = create_temp_dir();
523-
let static_fn = GetFileHash::new(dir.into_path(), None, PathBuf::new());
558+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
524559
let mut args = HashMap::new();
525560
args.insert("path".to_string(), to_value("app.css").unwrap());
526561
args.insert("sha_type".to_string(), to_value(512).unwrap());
@@ -530,6 +565,85 @@ title = "A title"
530565
);
531566
}
532567

568+
#[test]
569+
fn can_get_hash_sha256_no_base64() {
570+
let dir = create_temp_dir();
571+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
572+
let mut args = HashMap::new();
573+
args.insert("literal".to_string(), to_value("Hello World").unwrap());
574+
args.insert("sha_type".to_string(), to_value(256).unwrap());
575+
args.insert("base64".to_string(), to_value(false).unwrap());
576+
assert_eq!(
577+
static_fn.call(&args).unwrap(),
578+
"a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"
579+
);
580+
}
581+
582+
#[test]
583+
fn can_get_hash_sha256_base64() {
584+
let dir = create_temp_dir();
585+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
586+
let mut args = HashMap::new();
587+
args.insert("literal".to_string(), to_value("Hello World").unwrap());
588+
args.insert("sha_type".to_string(), to_value(256).unwrap());
589+
args.insert("base64".to_string(), to_value(true).unwrap());
590+
assert_eq!(
591+
static_fn.call(&args).unwrap(),
592+
"pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=");
593+
}
594+
595+
#[test]
596+
fn can_get_hash_sha384_no_base64() {
597+
let dir = create_temp_dir();
598+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
599+
let mut args = HashMap::new();
600+
args.insert("literal".to_string(), to_value("Hello World").unwrap());
601+
args.insert("base64".to_string(), to_value(false).unwrap());
602+
assert_eq!(
603+
static_fn.call(&args).unwrap(),
604+
"99514329186b2f6ae4a1329e7ee6c610a729636335174ac6b740f9028396fcc803d0e93863a7c3d90f86beee782f4f3f"
605+
);
606+
}
607+
608+
#[test]
609+
fn can_get_hash_sha384() {
610+
let dir = create_temp_dir();
611+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
612+
let mut args = HashMap::new();
613+
args.insert("literal".to_string(), to_value("Hello World").unwrap());
614+
assert_eq!(
615+
static_fn.call(&args).unwrap(),
616+
"mVFDKRhrL2rkoTKefubGEKcpY2M1F0rGt0D5AoOW/MgD0Ok4Y6fD2Q+Gvu54L08/"
617+
);
618+
}
619+
620+
#[test]
621+
fn can_get_hash_sha512_no_base64() {
622+
let dir = create_temp_dir();
623+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
624+
let mut args = HashMap::new();
625+
args.insert("literal".to_string(), to_value("Hello World").unwrap());
626+
args.insert("sha_type".to_string(), to_value(512).unwrap());
627+
args.insert("base64".to_string(), to_value(false).unwrap());
628+
assert_eq!(
629+
static_fn.call(&args).unwrap(),
630+
"2c74fd17edafd80e8447b0d46741ee243b7eb74dd2149a0ab1b9246fb30382f27e853d8585719e0e67cbda0daa8f51671064615d645ae27acb15bfb1447f459b"
631+
);
632+
}
633+
634+
#[test]
635+
fn can_get_hash_sha512() {
636+
let dir = create_temp_dir();
637+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
638+
let mut args = HashMap::new();
639+
args.insert("literal".to_string(), to_value("Hello World").unwrap());
640+
args.insert("sha_type".to_string(), to_value(512).unwrap());
641+
assert_eq!(
642+
static_fn.call(&args).unwrap(),
643+
"LHT9F+2v2A6ER7DUZ0HuJDt+t03SFJoKsbkkb7MDgvJ+hT2FhXGeDmfL2g2qj1FnEGRhXWRa4nrLFb+xRH9Fmw=="
644+
);
645+
}
646+
533647
#[test]
534648
fn can_resolve_asset_path_to_valid_url() {
535649
let config = Config::parse(CONFIG_DATA).unwrap();
@@ -540,7 +654,7 @@ title = "A title"
540654
args.insert(
541655
"path".to_string(),
542656
to_value(dir.path().join("app.css").strip_prefix(std::env::temp_dir()).unwrap())
543-
.unwrap(),
657+
.unwrap(),
544658
);
545659
assert_eq!(
546660
static_fn.call(&args).unwrap(),
@@ -554,7 +668,7 @@ title = "A title"
554668
#[test]
555669
fn error_when_file_not_found_for_hash() {
556670
let dir = create_temp_dir();
557-
let static_fn = GetFileHash::new(dir.into_path(), None, PathBuf::new());
671+
let static_fn = GetHash::new(dir.into_path(), None, PathBuf::new());
558672
let mut args = HashMap::new();
559673
args.insert("path".to_string(), to_value("doesnt-exist").unwrap());
560674
let err = format!("{}", static_fn.call(&args).unwrap_err());

components/templates/src/global_fns/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ mod images;
99
mod load_data;
1010

1111
pub use self::content::{GetPage, GetSection, GetTaxonomy, GetTaxonomyTerm, GetTaxonomyUrl};
12-
pub use self::files::{GetFileHash, GetUrl};
12+
pub use self::files::{GetHash, GetUrl};
1313
pub use self::i18n::Trans;
1414
pub use self::images::{GetImageMetadata, ResizeImage};
1515
pub use self::load_data::LoadData;

0 commit comments

Comments
 (0)