use std::borrow::Cow;
use std::path::Path;
use base64::prelude::*;
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 dblquoted = delimited(tag("url(\""), is_not("\""), tag("\")"));
let sglquoted = delimited(tag("url('"), is_not("'"), tag("')"));
alt((sglquoted, dblquoted, 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_STANDARD.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;
}
}
}
})
}
pub fn inline_url<'a: 'b, 'b>(newpath: &'b str) -> impl Fn(&mut Css<'a>) + 'b {
|flat| inline_url_impl(newpath, flat)
}