-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathinline_url.rs
More file actions
87 lines (74 loc) · 3.56 KB
/
inline_url.rs
File metadata and controls
87 lines (74 loc) · 3.56 KB
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
// ┌───────────────────────────────────────────────────────────────────────────┐
// │ │
// │ ██████╗ ██████╗ ██████╗ Copyright (C) 2022, The Prospective Company │
// │ ██╔══██╗██╔══██╗██╔═══██╗ │
// │ ██████╔╝██████╔╝██║ ██║ This file is part of the Procss library, │
// │ ██╔═══╝ ██╔══██╗██║ ██║ distributed under the terms of the │
// │ ██║ ██║ ██║╚██████╔╝ Apache License 2.0. The full license can │
// │ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ be found in the LICENSE file. │
// │ │
// └───────────────────────────────────────────────────────────────────────────┘
use std::borrow::Cow;
use std::path::Path;
use nom::branch::alt;
use nom::bytes::complete::{is_not, tag};
use nom::sequence::delimited;
use crate::ast::{Css, Rule};
#[cfg(not(target_arch = "wasm32"))]
use crate::utils::fs;
#[cfg(feature = "iotest")]
use crate::utils::IoTestFs;
#[cfg(not(target_arch = "wasm32"))]
fn read_file_sync(path: &Path) -> Option<Vec<u8>> {
fs::read(path).ok()
}
#[cfg(target_arch = "wasm32")]
fn read_file_sync(path: &Path) -> Option<Vec<u8>> {
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "fs")]
extern "C" {
#[wasm_bindgen(catch)]
fn readFileSync(path: &str) -> Result<Vec<u8>, JsValue>;
}
readFileSync(&*path.to_string_lossy()).ok()
}
fn parse_url(input: &str) -> nom::IResult<&str, &str> {
let unquoted = delimited(tag("url("), is_not(")"), tag(")"));
let quoted = delimited(tag("url(\""), is_not("\""), tag("\")"));
alt((quoted, unquoted))(input)
}
fn into_data_uri<'a>(path: &Path) -> Option<Cow<'a, str>> {
if path.starts_with("data:") {
return None;
}
let contents = read_file_sync(path)?;
let encoded = base64::encode(contents);
let fff = path.extension().unwrap_or_default().to_string_lossy();
let ggg = path.to_string_lossy();
let fmt = match fff.as_ref() {
"png" => "png",
"gif" => "gif",
"svg" => "svg+xml",
_ => ggg.as_ref(),
};
Some(format!("url(\"data:image/{};base64,{}\")", fmt, encoded).into())
}
fn inline_url_impl<'a>(newpath: &str, flat: &mut Css<'a>) {
flat.transform::<Rule<'a>>(|rule| {
let path = parse_url(&rule.value)
.ok()
.and_then(|x| x.0.is_empty().then_some(Path::new(newpath).join(x.1)));
if let Some(path) = &path {
if path.starts_with(".") || path.starts_with("/") {
if let Some(value) = into_data_uri(path) {
rule.value = value;
}
}
}
})
}
/// Inline `url()` rule properties containing local paths to be replace with the
/// base64 encoded contents of their respective files.
pub fn inline_url<'a: 'b, 'b>(newpath: &'b str) -> impl Fn(&mut Css<'a>) + 'b {
|flat| inline_url_impl(newpath, flat)
}