Merge pull request #186 from snshn/code-improvements

Code improvements
pull/188/head
Sunshine 4 years ago committed by GitHub
commit 3ce26b5fdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -2,7 +2,8 @@ use cssparser::{ParseError, Parser, ParserInput, SourcePosition, Token};
use reqwest::blocking::Client; use reqwest::blocking::Client;
use std::collections::HashMap; use std::collections::HashMap;
use crate::utils::{data_to_data_url, get_url_fragment, is_http_url, resolve_url, retrieve_asset}; use crate::url::{data_to_data_url, get_url_fragment, is_http_url, resolve_url, url_with_fragment};
use crate::utils::retrieve_asset;
const CSS_PROPS_WITH_IMAGE_URLS: &[&str] = &[ const CSS_PROPS_WITH_IMAGE_URLS: &[&str] = &[
// Universal // Universal
@ -173,32 +174,34 @@ pub fn process_css<'a>(
let import_url_fragment = get_url_fragment(import_full_url.clone()); let import_url_fragment = get_url_fragment(import_full_url.clone());
match retrieve_asset(cache, client, &parent_url, &import_full_url, opt_silent) { match retrieve_asset(cache, client, &parent_url, &import_full_url, opt_silent) {
Ok((import_contents, import_final_url, _import_media_type)) => { Ok((import_contents, import_final_url, _import_media_type)) => {
result.push_str( let import_data_url = data_to_data_url(
enquote( "text/css",
data_to_data_url( embed_css(
"text/css", cache,
embed_css( client,
cache, &import_final_url,
client, &String::from_utf8_lossy(&import_contents),
&import_final_url, opt_no_fonts,
&String::from_utf8_lossy(&import_contents), opt_no_images,
opt_no_fonts, opt_silent,
opt_no_images,
opt_silent,
)
.as_bytes(),
&import_final_url,
&import_url_fragment,
),
false,
) )
.as_str(), .as_bytes(),
&import_final_url,
);
let assembled_url: String = url_with_fragment(
import_data_url.as_str(),
import_url_fragment.as_str(),
); );
result.push_str(enquote(assembled_url, false).as_str());
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(import_full_url.clone()) { if is_http_url(import_full_url.clone()) {
result.push_str(enquote(import_full_url, false).as_str()); let assembled_url: String = url_with_fragment(
import_full_url.as_str(),
import_url_fragment.as_str(),
);
result.push_str(enquote(assembled_url, false).as_str());
} }
} }
} }
@ -222,18 +225,19 @@ pub fn process_css<'a>(
opt_silent, opt_silent,
) { ) {
Ok((data, final_url, media_type)) => { Ok((data, final_url, media_type)) => {
let data_url = data_to_data_url( let data_url = data_to_data_url(&media_type, &data, &final_url);
&media_type, let assembled_url: String =
&data, url_with_fragment(data_url.as_str(), url_fragment.as_str());
&final_url, result.push_str(enquote(assembled_url, false).as_str());
&url_fragment,
);
result.push_str(enquote(data_url, false).as_str());
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(resolved_url.clone()) { if is_http_url(resolved_url.clone()) {
result.push_str(enquote(resolved_url, false).as_str()); let assembled_url: String = url_with_fragment(
resolved_url.as_str(),
url_fragment.as_str(),
);
result.push_str(enquote(assembled_url, false).as_str());
} }
} }
} }
@ -320,14 +324,17 @@ pub fn process_css<'a>(
) )
.as_bytes(), .as_bytes(),
&final_url, &final_url,
&url_fragment,
); );
result.push_str(enquote(data_url, false).as_str()); let assembled_url: String =
url_with_fragment(data_url.as_str(), url_fragment.as_str());
result.push_str(enquote(assembled_url, false).as_str());
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(full_url.clone()) { if is_http_url(full_url.clone()) {
result.push_str(enquote(full_url, false).as_str()); let assembled_url: String =
url_with_fragment(full_url.as_str(), url_fragment.as_str());
result.push_str(enquote(assembled_url, false).as_str());
} }
} }
} }
@ -339,14 +346,17 @@ pub fn process_css<'a>(
let url_fragment = get_url_fragment(full_url.clone()); let url_fragment = get_url_fragment(full_url.clone());
match retrieve_asset(cache, client, &parent_url, &full_url, opt_silent) { match retrieve_asset(cache, client, &parent_url, &full_url, opt_silent) {
Ok((data, final_url, media_type)) => { Ok((data, final_url, media_type)) => {
let data_url = let data_url = data_to_data_url(&media_type, &data, &final_url);
data_to_data_url(&media_type, &data, &final_url, &url_fragment); let assembled_url: String =
result.push_str(enquote(data_url, false).as_str()); url_with_fragment(data_url.as_str(), url_fragment.as_str());
result.push_str(enquote(assembled_url, false).as_str());
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(full_url.clone()) { if is_http_url(full_url.clone()) {
result.push_str(enquote(full_url, false).as_str()); let assembled_url: String =
url_with_fragment(full_url.as_str(), url_fragment.as_str());
result.push_str(enquote(assembled_url, false).as_str());
} }
} }
} }

@ -1,8 +1,3 @@
use crate::css::embed_css;
use crate::js::attr_is_event_handler;
use crate::utils::{
data_to_data_url, get_url_fragment, is_http_url, resolve_url, retrieve_asset, url_has_protocol,
};
use base64; use base64;
use html5ever::interface::QualName; use html5ever::interface::QualName;
use html5ever::parse_document; use html5ever::parse_document;
@ -16,6 +11,14 @@ use sha2::{Digest, Sha256, Sha384, Sha512};
use std::collections::HashMap; use std::collections::HashMap;
use std::default::Default; use std::default::Default;
use crate::css::embed_css;
use crate::js::attr_is_event_handler;
use crate::url::{
data_to_data_url, get_url_fragment, is_http_url, resolve_url, url_has_protocol,
url_with_fragment,
};
use crate::utils::retrieve_asset;
struct SrcSetItem<'a> { struct SrcSetItem<'a> {
path: &'a str, path: &'a str,
descriptor: &'a str, descriptor: &'a str,
@ -91,19 +94,19 @@ pub fn embed_srcset(
let image_url_fragment = get_url_fragment(image_full_url.clone()); let image_url_fragment = get_url_fragment(image_full_url.clone());
match retrieve_asset(cache, client, &parent_url, &image_full_url, opt_silent) { match retrieve_asset(cache, client, &parent_url, &image_full_url, opt_silent) {
Ok((image_data, image_final_url, image_media_type)) => { Ok((image_data, image_final_url, image_media_type)) => {
let image_data_url = data_to_data_url( let image_data_url =
&image_media_type, data_to_data_url(&image_media_type, &image_data, &image_final_url);
&image_data,
&image_final_url,
&image_url_fragment,
);
// Append retreved asset as a data URL // Append retreved asset as a data URL
result.push_str(image_data_url.as_ref()); let assembled_url: String =
url_with_fragment(image_data_url.as_str(), image_url_fragment.as_str());
result.push_str(assembled_url.as_ref());
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(image_full_url.clone()) { if is_http_url(image_full_url.clone()) {
result.push_str(image_full_url.as_ref()); let assembled_url: String =
url_with_fragment(image_full_url.as_str(), image_url_fragment.as_str());
result.push_str(assembled_url.as_ref());
} else { } else {
// Avoid breaking the structure in case if not an HTTP(S) URL // Avoid breaking the structure in case if not an HTTP(S) URL
result.push_str(empty_image!()); result.push_str(empty_image!());
@ -246,33 +249,36 @@ pub fn walk_and_embed_assets(
&link_href_media_type, &link_href_media_type,
&link_href_data, &link_href_data,
&link_href_final_url, &link_href_final_url,
&link_href_url_fragment,
); );
// Add new data URL href attribute // Add new data URL href attribute
let assembled_url: String = url_with_fragment(
link_href_data_url.as_str(),
link_href_url_fragment.as_str(),
);
attrs_mut.push(Attribute { attrs_mut.push(Attribute {
name: QualName::new( name: QualName::new(
None, None,
ns!(), ns!(),
local_name!("href"), local_name!("href"),
), ),
value: Tendril::from_slice( value: Tendril::from_slice(assembled_url.as_ref()),
link_href_data_url.as_ref(),
),
}); });
} }
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(link_href_full_url.clone()) { if is_http_url(link_href_full_url.clone()) {
let assembled_url: String = url_with_fragment(
link_href_full_url.as_str(),
link_href_url_fragment.as_str(),
);
attrs_mut.push(Attribute { attrs_mut.push(Attribute {
name: QualName::new( name: QualName::new(
None, None,
ns!(), ns!(),
local_name!("href"), local_name!("href"),
), ),
value: Tendril::from_slice( value: Tendril::from_slice(assembled_url.as_ref()),
link_href_full_url.as_ref(),
),
}); });
} }
} }
@ -324,7 +330,6 @@ pub fn walk_and_embed_assets(
"text/css", "text/css",
css.as_bytes(), css.as_bytes(),
&link_href_final_url, &link_href_final_url,
"",
); );
// Add new data URL href attribute // Add new data URL href attribute
attrs_mut.push(Attribute { attrs_mut.push(Attribute {
@ -399,20 +404,27 @@ pub fn walk_and_embed_assets(
&background_media_type, &background_media_type,
&background_data, &background_data,
&background_final_url, &background_final_url,
&background_url_fragment,
); );
// Add new data URL background attribute // Add new data URL background attribute
let assembled_url: String = url_with_fragment(
background_data_url.as_str(),
background_url_fragment.as_str(),
);
attrs_mut.push(Attribute { attrs_mut.push(Attribute {
name: QualName::new(None, ns!(), local_name!("background")), name: QualName::new(None, ns!(), local_name!("background")),
value: Tendril::from_slice(background_data_url.as_ref()), value: Tendril::from_slice(assembled_url.as_ref()),
}); });
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(background_full_url.clone()) { if is_http_url(background_full_url.clone()) {
let assembled_url: String = url_with_fragment(
background_full_url.as_str(),
background_url_fragment.as_str(),
);
attrs_mut.push(Attribute { attrs_mut.push(Attribute {
name: QualName::new(None, ns!(), local_name!("background")), name: QualName::new(None, ns!(), local_name!("background")),
value: Tendril::from_slice(background_full_url.as_ref()), value: Tendril::from_slice(assembled_url.as_ref()),
}); });
} }
} }
@ -469,19 +481,26 @@ pub fn walk_and_embed_assets(
&img_media_type, &img_media_type,
&img_data, &img_data,
&img_final_url, &img_final_url,
&img_url_fragment, );
let assembled_url: String = url_with_fragment(
img_data_url.as_str(),
img_url_fragment.as_str(),
); );
attrs_mut.push(Attribute { attrs_mut.push(Attribute {
name: QualName::new(None, ns!(), local_name!("src")), name: QualName::new(None, ns!(), local_name!("src")),
value: Tendril::from_slice(img_data_url.as_ref()), value: Tendril::from_slice(assembled_url.as_ref()),
}); });
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(img_full_url.clone()) { if is_http_url(img_full_url.clone()) {
let assembled_url: String = url_with_fragment(
img_full_url.as_str(),
img_url_fragment.as_str(),
);
attrs_mut.push(Attribute { attrs_mut.push(Attribute {
name: QualName::new(None, ns!(), local_name!("src")), name: QualName::new(None, ns!(), local_name!("src")),
value: Tendril::from_slice(img_full_url.as_ref()), value: Tendril::from_slice(assembled_url.as_ref()),
}); });
} }
} }
@ -563,22 +582,27 @@ pub fn walk_and_embed_assets(
&input_image_media_type, &input_image_media_type,
&input_image_data, &input_image_data,
&input_image_final_url, &input_image_final_url,
&input_image_url_fragment,
); );
// Add data URL src attribute // Add data URL src attribute
let assembled_url: String = url_with_fragment(
input_image_data_url.as_str(),
input_image_url_fragment.as_str(),
);
attrs_mut.push(Attribute { attrs_mut.push(Attribute {
name: QualName::new(None, ns!(), local_name!("src")), name: QualName::new(None, ns!(), local_name!("src")),
value: Tendril::from_slice(input_image_data_url.as_ref()), value: Tendril::from_slice(assembled_url.as_ref()),
}); });
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(input_image_full_url.clone()) { if is_http_url(input_image_full_url.clone()) {
let assembled_url: String = url_with_fragment(
input_image_full_url.as_str(),
input_image_url_fragment.as_str(),
);
attrs_mut.push(Attribute { attrs_mut.push(Attribute {
name: QualName::new(None, ns!(), local_name!("src")), name: QualName::new(None, ns!(), local_name!("src")),
value: Tendril::from_slice( value: Tendril::from_slice(assembled_url.as_ref()),
input_image_full_url.as_ref(),
),
}); });
} }
} }
@ -610,20 +634,27 @@ pub fn walk_and_embed_assets(
&image_media_type, &image_media_type,
&image_data, &image_data,
&image_final_url, &image_final_url,
&image_url_fragment,
); );
// Add new data URL href attribute // Add new data URL href attribute
let assembled_url: String = url_with_fragment(
image_data_url.as_str(),
image_url_fragment.as_str(),
);
attrs_mut.push(Attribute { attrs_mut.push(Attribute {
name: QualName::new(None, ns!(), local_name!("href")), name: QualName::new(None, ns!(), local_name!("href")),
value: Tendril::from_slice(image_data_url.as_ref()), value: Tendril::from_slice(assembled_url.as_ref()),
}); });
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(image_full_url.clone()) { if is_http_url(image_full_url.clone()) {
let assembled_url: String = url_with_fragment(
image_full_url.as_str(),
image_url_fragment.as_str(),
);
attrs_mut.push(Attribute { attrs_mut.push(Attribute {
name: QualName::new(None, ns!(), local_name!("href")), name: QualName::new(None, ns!(), local_name!("href")),
value: Tendril::from_slice(image_full_url.as_ref()), value: Tendril::from_slice(assembled_url.as_ref()),
}); });
} }
} }
@ -661,21 +692,23 @@ pub fn walk_and_embed_assets(
&srcset_media_type, &srcset_media_type,
&srcset_data, &srcset_data,
&srcset_final_url, &srcset_final_url,
&srcset_url_fragment,
); );
attr.value.clear(); attr.value.clear();
attr.value.push_slice(srcset_data_url.as_str()); let assembled_url: String = url_with_fragment(
srcset_data_url.as_str(),
srcset_url_fragment.as_str(),
);
attr.value.push_slice(assembled_url.as_str());
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(srcset_full_url.clone()) { if is_http_url(srcset_full_url.clone()) {
attr.value.clear(); attr.value.clear();
attr.value.push_slice(srcset_full_url.as_str()); let assembled_url: String = url_with_fragment(
if !srcset_url_fragment.is_empty() { srcset_full_url.as_str(),
attr.value.push_slice("#"); srcset_url_fragment.as_str(),
attr.value );
.push_slice(srcset_url_fragment.as_str()); attr.value.push_slice(assembled_url.as_str());
}
} }
} }
} }
@ -739,7 +772,6 @@ pub fn walk_and_embed_assets(
"application/javascript", "application/javascript",
&script_data, &script_data,
&script_final_url, &script_final_url,
"",
); );
// Add new data URL src attribute // Add new data URL src attribute
attrs_mut.push(Attribute { attrs_mut.push(Attribute {
@ -844,16 +876,23 @@ pub fn walk_and_embed_assets(
&frame_media_type, &frame_media_type,
&frame_data, &frame_data,
&frame_final_url, &frame_final_url,
&frame_url_fragment,
); );
attr.value.clear(); attr.value.clear();
attr.value.push_slice(frame_data_url.as_str()); let assembled_url: String = url_with_fragment(
frame_data_url.as_str(),
frame_url_fragment.as_str(),
);
attr.value.push_slice(assembled_url.as_str());
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(frame_full_url.clone()) { if is_http_url(frame_full_url.clone()) {
attr.value.clear(); attr.value.clear();
attr.value.push_slice(frame_full_url.as_str()); let assembled_url: String = url_with_fragment(
frame_full_url.as_str(),
frame_url_fragment.as_str(),
);
attr.value.push_slice(assembled_url.as_str());
} }
} }
} }
@ -896,16 +935,23 @@ pub fn walk_and_embed_assets(
&video_poster_media_type, &video_poster_media_type,
&video_poster_data, &video_poster_data,
&video_poster_final_url, &video_poster_final_url,
&video_poster_url_fragment,
); );
attr.value.clear(); attr.value.clear();
attr.value.push_slice(video_poster_data_url.as_str()); let assembled_url: String = url_with_fragment(
video_poster_data_url.as_str(),
video_poster_url_fragment.as_str(),
);
attr.value.push_slice(assembled_url.as_str());
} }
Err(_) => { Err(_) => {
// Keep remote reference if unable to retrieve the asset // Keep remote reference if unable to retrieve the asset
if is_http_url(video_poster_full_url.clone()) { if is_http_url(video_poster_full_url.clone()) {
attr.value.clear(); attr.value.clear();
attr.value.push_slice(video_poster_full_url.as_str()); let assembled_url: String = url_with_fragment(
video_poster_full_url.as_str(),
video_poster_url_fragment.as_str(),
);
attr.value.push_slice(assembled_url.as_str());
} }
} }
} }

@ -4,6 +4,7 @@ mod macros;
pub mod css; pub mod css;
pub mod html; pub mod html;
pub mod js; pub mod js;
pub mod url;
pub mod utils; pub mod utils;
#[cfg(test)] #[cfg(test)]

@ -1,6 +1,4 @@
use chrono::prelude::*; use chrono::prelude::*;
use monolith::html::{html_to_dom, stringify_document, walk_and_embed_assets};
use monolith::utils::{data_url_to_data, is_data_url, is_file_url, is_http_url, retrieve_asset};
use reqwest::blocking::Client; use reqwest::blocking::Client;
use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT}; use reqwest::header::{HeaderMap, HeaderValue, USER_AGENT};
use reqwest::Url; use reqwest::Url;
@ -12,6 +10,10 @@ use std::path::Path;
use std::process; use std::process;
use std::time::Duration; use std::time::Duration;
use monolith::html::{html_to_dom, stringify_document, walk_and_embed_assets};
use monolith::url::{data_url_to_data, is_data_url, is_file_url, is_http_url};
use monolith::utils::retrieve_asset;
mod args; mod args;
mod macros; mod macros;

@ -7,12 +7,12 @@
#[cfg(test)] #[cfg(test)]
mod passing { mod passing {
use crate::utils; use crate::url;
#[test] #[test]
fn removes_fragment() { fn removes_fragment() {
assert_eq!( assert_eq!(
utils::clean_url("https://somewhere.com/font.eot#iefix"), url::clean_url("https://somewhere.com/font.eot#iefix"),
"https://somewhere.com/font.eot" "https://somewhere.com/font.eot"
); );
} }
@ -20,7 +20,7 @@ mod passing {
#[test] #[test]
fn removes_empty_fragment() { fn removes_empty_fragment() {
assert_eq!( assert_eq!(
utils::clean_url("https://somewhere.com/font.eot#"), url::clean_url("https://somewhere.com/font.eot#"),
"https://somewhere.com/font.eot" "https://somewhere.com/font.eot"
); );
} }
@ -28,7 +28,7 @@ mod passing {
#[test] #[test]
fn removes_empty_query_and_empty_fragment() { fn removes_empty_query_and_empty_fragment() {
assert_eq!( assert_eq!(
utils::clean_url("https://somewhere.com/font.eot?#"), url::clean_url("https://somewhere.com/font.eot?#"),
"https://somewhere.com/font.eot" "https://somewhere.com/font.eot"
); );
} }
@ -36,7 +36,7 @@ mod passing {
#[test] #[test]
fn removes_empty_query_amp_and_empty_fragment() { fn removes_empty_query_amp_and_empty_fragment() {
assert_eq!( assert_eq!(
utils::clean_url("https://somewhere.com/font.eot?a=b&#"), url::clean_url("https://somewhere.com/font.eot?a=b&#"),
"https://somewhere.com/font.eot?a=b" "https://somewhere.com/font.eot?a=b"
); );
} }
@ -44,7 +44,7 @@ mod passing {
#[test] #[test]
fn keeps_credentials() { fn keeps_credentials() {
assert_eq!( assert_eq!(
utils::clean_url("https://cookie:monster@gibson.internet/"), url::clean_url("https://cookie:monster@gibson.internet/"),
"https://cookie:monster@gibson.internet/" "https://cookie:monster@gibson.internet/"
); );
} }

@ -7,13 +7,13 @@
#[cfg(test)] #[cfg(test)]
mod passing { mod passing {
use crate::utils; use crate::url;
#[test] #[test]
fn encode_string_with_specific_media_type() { fn encode_string_with_specific_media_type() {
let mime = "application/javascript"; let mime = "application/javascript";
let data = "var word = 'hello';\nalert(word);\n"; let data = "var word = 'hello';\nalert(word);\n";
let data_url = utils::data_to_data_url(mime, data.as_bytes(), "", ""); let data_url = url::data_to_data_url(mime, data.as_bytes(), "");
assert_eq!( assert_eq!(
&data_url, &data_url,
@ -24,8 +24,8 @@ mod passing {
#[test] #[test]
fn encode_append_fragment() { fn encode_append_fragment() {
let data = "<svg></svg>\n"; let data = "<svg></svg>\n";
let data_url = utils::data_to_data_url("text/css", data.as_bytes(), "", "fragment"); let data_url = url::data_to_data_url("image/svg+xml", data.as_bytes(), "");
assert_eq!(&data_url, "data:text/css;base64,PHN2Zz48L3N2Zz4K#fragment"); assert_eq!(&data_url, "data:image/svg+xml;base64,PHN2Zz48L3N2Zz4K");
} }
} }

@ -7,11 +7,11 @@
#[cfg(test)] #[cfg(test)]
mod passing { mod passing {
use crate::utils; use crate::url;
#[test] #[test]
fn parse_text_html_base64() { fn parse_text_html_base64() {
let (media_type, data) = utils::data_url_to_data("data:text/html;base64,V29yayBleHBhbmRzIHNvIGFzIHRvIGZpbGwgdGhlIHRpbWUgYXZhaWxhYmxlIGZvciBpdHMgY29tcGxldGlvbg=="); let (media_type, data) = url::data_url_to_data("data:text/html;base64,V29yayBleHBhbmRzIHNvIGFzIHRvIGZpbGwgdGhlIHRpbWUgYXZhaWxhYmxlIGZvciBpdHMgY29tcGxldGlvbg==");
assert_eq!(media_type, "text/html"); assert_eq!(media_type, "text/html");
assert_eq!( assert_eq!(
@ -22,7 +22,7 @@ mod passing {
#[test] #[test]
fn parse_text_html_utf8() { fn parse_text_html_utf8() {
let (media_type, data) = utils::data_url_to_data( let (media_type, data) = url::data_url_to_data(
"data:text/html;utf8,Work expands so as to fill the time available for its completion", "data:text/html;utf8,Work expands so as to fill the time available for its completion",
); );
@ -35,7 +35,7 @@ mod passing {
#[test] #[test]
fn parse_text_html_plaintext() { fn parse_text_html_plaintext() {
let (media_type, data) = utils::data_url_to_data( let (media_type, data) = url::data_url_to_data(
"data:text/html,Work expands so as to fill the time available for its completion", "data:text/html,Work expands so as to fill the time available for its completion",
); );
@ -48,7 +48,7 @@ mod passing {
#[test] #[test]
fn parse_text_html_charset_utf_8_between_two_whitespaces() { fn parse_text_html_charset_utf_8_between_two_whitespaces() {
let (media_type, data) = utils::data_url_to_data(" data:text/html;charset=utf-8,Work expands so as to fill the time available for its completion "); let (media_type, data) = url::data_url_to_data(" data:text/html;charset=utf-8,Work expands so as to fill the time available for its completion ");
assert_eq!(media_type, "text/html"); assert_eq!(media_type, "text/html");
assert_eq!( assert_eq!(
@ -60,7 +60,7 @@ mod passing {
#[test] #[test]
fn parse_text_css_url_encoded() { fn parse_text_css_url_encoded() {
let (media_type, data) = let (media_type, data) =
utils::data_url_to_data("data:text/css,div{background-color:%23000}"); url::data_url_to_data("data:text/css,div{background-color:%23000}");
assert_eq!(media_type, "text/css"); assert_eq!(media_type, "text/css");
assert_eq!(String::from_utf8_lossy(&data), "div{background-color:#000}"); assert_eq!(String::from_utf8_lossy(&data), "div{background-color:#000}");
@ -68,7 +68,7 @@ mod passing {
#[test] #[test]
fn parse_no_media_type_base64() { fn parse_no_media_type_base64() {
let (media_type, data) = utils::data_url_to_data("data:;base64,dGVzdA=="); let (media_type, data) = url::data_url_to_data("data:;base64,dGVzdA==");
assert_eq!(media_type, ""); assert_eq!(media_type, "");
assert_eq!(String::from_utf8_lossy(&data), "test"); assert_eq!(String::from_utf8_lossy(&data), "test");
@ -76,7 +76,7 @@ mod passing {
#[test] #[test]
fn parse_no_media_type_no_encoding() { fn parse_no_media_type_no_encoding() {
let (media_type, data) = utils::data_url_to_data("data:;,test%20test"); let (media_type, data) = url::data_url_to_data("data:;,test%20test");
assert_eq!(media_type, ""); assert_eq!(media_type, "");
assert_eq!(String::from_utf8_lossy(&data), "test test"); assert_eq!(String::from_utf8_lossy(&data), "test test");
@ -92,11 +92,11 @@ mod passing {
#[cfg(test)] #[cfg(test)]
mod failing { mod failing {
use crate::utils; use crate::url;
#[test] #[test]
fn just_word_data() { fn just_word_data() {
let (media_type, data) = utils::data_url_to_data("data"); let (media_type, data) = url::data_url_to_data("data");
assert_eq!(media_type, ""); assert_eq!(media_type, "");
assert_eq!(String::from_utf8_lossy(&data), ""); assert_eq!(String::from_utf8_lossy(&data), "");

@ -7,12 +7,12 @@
#[cfg(test)] #[cfg(test)]
mod passing { mod passing {
use crate::utils; use crate::url;
#[test] #[test]
fn decode_unicode_characters() { fn decode_unicode_characters() {
assert_eq!( assert_eq!(
utils::decode_url(str!( url::decode_url(str!(
"%E6%A4%9C%E3%83%92%E3%83%A0%E8%A7%A3%E5%A1%97%E3%82%83%E3%83%83%20%3D%20%E3%82%B5" "%E6%A4%9C%E3%83%92%E3%83%A0%E8%A7%A3%E5%A1%97%E3%82%83%E3%83%83%20%3D%20%E3%82%B5"
)), )),
"検ヒム解塗ゃッ = サ" "検ヒム解塗ゃッ = サ"
@ -22,7 +22,7 @@ mod passing {
#[test] #[test]
fn decode_file_url() { fn decode_file_url() {
assert_eq!( assert_eq!(
utils::decode_url(str!("file:///tmp/space%20here/test%231.html")), url::decode_url(str!("file:///tmp/space%20here/test%231.html")),
"file:///tmp/space here/test#1.html" "file:///tmp/space here/test#1.html"
); );
} }
@ -30,7 +30,7 @@ mod passing {
#[test] #[test]
fn plus_sign() { fn plus_sign() {
assert_eq!( assert_eq!(
utils::decode_url(str!( url::decode_url(str!(
"fonts.somewhere.com/css?family=Open+Sans:300,400,400italic,600,600italic" "fonts.somewhere.com/css?family=Open+Sans:300,400,400italic,600,600italic"
)), )),
"fonts.somewhere.com/css?family=Open+Sans:300,400,400italic,600,600italic" "fonts.somewhere.com/css?family=Open+Sans:300,400,400italic,600,600italic"

@ -7,18 +7,18 @@
#[cfg(test)] #[cfg(test)]
mod passing { mod passing {
use crate::utils; use crate::url;
#[test] #[test]
fn remove_protocl_and_fragment() { fn remove_protocl_and_fragment() {
if cfg!(windows) { if cfg!(windows) {
assert_eq!( assert_eq!(
utils::file_url_to_fs_path("file:///C:/documents/some-path/some-file.svg#fragment"), url::file_url_to_fs_path("file:///C:/documents/some-path/some-file.svg#fragment"),
"C:\\documents\\some-path\\some-file.svg" "C:\\documents\\some-path\\some-file.svg"
); );
} else { } else {
assert_eq!( assert_eq!(
utils::file_url_to_fs_path("file:///tmp/some-path/some-file.svg#fragment"), url::file_url_to_fs_path("file:///tmp/some-path/some-file.svg#fragment"),
"/tmp/some-path/some-file.svg" "/tmp/some-path/some-file.svg"
); );
} }
@ -28,12 +28,12 @@ mod passing {
fn decodes_urls() { fn decodes_urls() {
if cfg!(windows) { if cfg!(windows) {
assert_eq!( assert_eq!(
utils::file_url_to_fs_path("file:///C:/Documents%20and%20Settings/some-file.html"), url::file_url_to_fs_path("file:///C:/Documents%20and%20Settings/some-file.html"),
"C:\\Documents and Settings\\some-file.html" "C:\\Documents and Settings\\some-file.html"
); );
} else { } else {
assert_eq!( assert_eq!(
utils::file_url_to_fs_path("file:///home/user/My%20Documents"), url::file_url_to_fs_path("file:///home/user/My%20Documents"),
"/home/user/My Documents" "/home/user/My Documents"
); );
} }

@ -7,12 +7,12 @@
#[cfg(test)] #[cfg(test)]
mod passing { mod passing {
use crate::utils; use crate::url;
#[test] #[test]
fn data_url() { fn data_url() {
assert_eq!( assert_eq!(
utils::get_url_fragment( url::get_url_fragment(
"data:image/svg+xml;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h#test" "data:image/svg+xml;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h#test"
), ),
"test" "test"
@ -21,6 +21,6 @@ mod passing {
#[test] #[test]
fn https_empty() { fn https_empty() {
assert_eq!(utils::get_url_fragment("https://kernel.org#"), ""); assert_eq!(url::get_url_fragment("https://kernel.org#"), "");
} }
} }

@ -7,18 +7,18 @@
#[cfg(test)] #[cfg(test)]
mod passing { mod passing {
use crate::utils; use crate::url;
#[test] #[test]
fn data_url_text_html() { fn data_url_text_html() {
assert!(utils::is_data_url( assert!(url::is_data_url(
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h" "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h"
)); ));
} }
#[test] #[test]
fn data_url_no_media_type() { fn data_url_no_media_type() {
assert!(utils::is_data_url( assert!(url::is_data_url(
"data:;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h" "data:;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h"
)); ));
} }
@ -33,20 +33,20 @@ mod passing {
#[cfg(test)] #[cfg(test)]
mod failing { mod failing {
use crate::utils; use crate::url;
#[test] #[test]
fn https_url() { fn https_url() {
assert!(!utils::is_data_url("https://kernel.org")); assert!(!url::is_data_url("https://kernel.org"));
} }
#[test] #[test]
fn no_protocol_url() { fn no_protocol_url() {
assert!(!utils::is_data_url("//kernel.org")); assert!(!url::is_data_url("//kernel.org"));
} }
#[test] #[test]
fn empty_string() { fn empty_string() {
assert!(!utils::is_data_url("")); assert!(!url::is_data_url(""));
} }
} }

@ -7,32 +7,32 @@
#[cfg(test)] #[cfg(test)]
mod passing { mod passing {
use crate::utils; use crate::url;
#[test] #[test]
fn unix_file_url() { fn unix_file_url() {
assert!(utils::is_file_url( assert!(url::is_file_url(
"file:///home/user/Websites/my-website/index.html" "file:///home/user/Websites/my-website/index.html"
)); ));
} }
#[test] #[test]
fn windows_file_url() { fn windows_file_url() {
assert!(utils::is_file_url( assert!(url::is_file_url(
"file:///C:/Documents%20and%20Settings/user/Websites/my-website/assets/images/logo.png" "file:///C:/Documents%20and%20Settings/user/Websites/my-website/assets/images/logo.png"
)); ));
} }
#[test] #[test]
fn unix_url_with_backslashes() { fn unix_url_with_backslashes() {
assert!(utils::is_file_url( assert!(url::is_file_url(
"file:\\\\\\home\\user\\Websites\\my-website\\index.html" "file:\\\\\\home\\user\\Websites\\my-website\\index.html"
)); ));
} }
#[test] #[test]
fn windows_file_url_with_backslashes() { fn windows_file_url_with_backslashes() {
assert!(utils::is_file_url( assert!(url::is_file_url(
"file:\\\\\\C:\\Documents%20and%20Settings\\user\\Websites\\my-website\\assets\\images\\logo.png" "file:\\\\\\C:\\Documents%20and%20Settings\\user\\Websites\\my-website\\assets\\images\\logo.png"
)); ));
} }
@ -47,37 +47,37 @@ mod passing {
#[cfg(test)] #[cfg(test)]
mod failing { mod failing {
use crate::utils; use crate::url;
#[test] #[test]
fn url_with_no_protocl() { fn url_with_no_protocl() {
assert!(!utils::is_file_url("//kernel.org")); assert!(!url::is_file_url("//kernel.org"));
} }
#[test] #[test]
fn dot_slash_filename() { fn dot_slash_filename() {
assert!(!utils::is_file_url("./index.html")); assert!(!url::is_file_url("./index.html"));
} }
#[test] #[test]
fn just_filename() { fn just_filename() {
assert!(!utils::is_file_url("some-local-page.htm")); assert!(!url::is_file_url("some-local-page.htm"));
} }
#[test] #[test]
fn https_ip_port_url() { fn https_ip_port_url() {
assert!(!utils::is_file_url("https://1.2.3.4:80/www/index.html")); assert!(!url::is_file_url("https://1.2.3.4:80/www/index.html"));
} }
#[test] #[test]
fn data_url() { fn data_url() {
assert!(!utils::is_file_url( assert!(!url::is_file_url(
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h" "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h"
)); ));
} }
#[test] #[test]
fn just_word_file() { fn just_word_file() {
assert!(!utils::is_file_url("file")); assert!(!url::is_file_url("file"));
} }
} }

@ -7,21 +7,21 @@
#[cfg(test)] #[cfg(test)]
mod passing { mod passing {
use crate::utils; use crate::url;
#[test] #[test]
fn http_url() { fn http_url() {
assert!(utils::is_http_url("http://kernel.org")); assert!(url::is_http_url("http://kernel.org"));
} }
#[test] #[test]
fn https_url() { fn https_url() {
assert!(utils::is_http_url("https://www.rust-lang.org/")); assert!(url::is_http_url("https://www.rust-lang.org/"));
} }
#[test] #[test]
fn http_url_with_backslashes() { fn http_url_with_backslashes() {
assert!(utils::is_http_url("http:\\\\freebsd.org\\")); assert!(url::is_http_url("http:\\\\freebsd.org\\"));
} }
} }
@ -34,31 +34,31 @@ mod passing {
#[cfg(test)] #[cfg(test)]
mod failing { mod failing {
use crate::utils; use crate::url;
#[test] #[test]
fn url_with_no_protocol() { fn url_with_no_protocol() {
assert!(!utils::is_http_url("//kernel.org")); assert!(!url::is_http_url("//kernel.org"));
} }
#[test] #[test]
fn dot_slash_filename() { fn dot_slash_filename() {
assert!(!utils::is_http_url("./index.html")); assert!(!url::is_http_url("./index.html"));
} }
#[test] #[test]
fn just_filename() { fn just_filename() {
assert!(!utils::is_http_url("some-local-page.htm")); assert!(!url::is_http_url("some-local-page.htm"));
} }
#[test] #[test]
fn https_ip_port_url() { fn https_ip_port_url() {
assert!(!utils::is_http_url("ftp://1.2.3.4/www/index.html")); assert!(!url::is_http_url("ftp://1.2.3.4/www/index.html"));
} }
#[test] #[test]
fn data_url() { fn data_url() {
assert!(!utils::is_http_url( assert!(!url::is_http_url(
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h" "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h"
)); ));
} }

@ -0,0 +1,12 @@
mod clean_url;
mod data_to_data_url;
mod data_url_to_data;
mod decode_url;
mod file_url_to_fs_path;
mod get_url_fragment;
mod is_data_url;
mod is_file_url;
mod is_http_url;
mod resolve_url;
mod url_has_protocol;
mod url_with_fragment;

@ -7,13 +7,13 @@
#[cfg(test)] #[cfg(test)]
mod passing { mod passing {
use crate::utils; use crate::url;
use url::ParseError; use url::ParseError;
#[test] #[test]
fn from_https_to_level_up_relative() -> Result<(), ParseError> { fn from_https_to_level_up_relative() -> Result<(), ParseError> {
let resolved_url = let resolved_url =
utils::resolve_url("https://www.kernel.org", "../category/signatures.html")?; url::resolve_url("https://www.kernel.org", "../category/signatures.html")?;
assert_eq!( assert_eq!(
resolved_url.as_str(), resolved_url.as_str(),
@ -25,7 +25,7 @@ mod passing {
#[test] #[test]
fn from_just_filename_to_full_https_url() -> Result<(), ParseError> { fn from_just_filename_to_full_https_url() -> Result<(), ParseError> {
let resolved_url = utils::resolve_url( let resolved_url = url::resolve_url(
"saved_page.htm", "saved_page.htm",
"https://www.kernel.org/category/signatures.html", "https://www.kernel.org/category/signatures.html",
)?; )?;
@ -40,7 +40,7 @@ mod passing {
#[test] #[test]
fn from_https_url_to_url_with_no_protocol() -> Result<(), ParseError> { fn from_https_url_to_url_with_no_protocol() -> Result<(), ParseError> {
let resolved_url = utils::resolve_url( let resolved_url = url::resolve_url(
"https://www.kernel.org", "https://www.kernel.org",
"//www.kernel.org/theme/images/logos/tux.png", "//www.kernel.org/theme/images/logos/tux.png",
)?; )?;
@ -56,7 +56,7 @@ mod passing {
#[test] #[test]
fn from_https_url_to_url_with_no_protocol_and_on_different_hostname() -> Result<(), ParseError> fn from_https_url_to_url_with_no_protocol_and_on_different_hostname() -> Result<(), ParseError>
{ {
let resolved_url = utils::resolve_url( let resolved_url = url::resolve_url(
"https://www.kernel.org", "https://www.kernel.org",
"//another-host.org/theme/images/logos/tux.png", "//another-host.org/theme/images/logos/tux.png",
)?; )?;
@ -71,7 +71,7 @@ mod passing {
#[test] #[test]
fn from_https_url_to_relative_root_path() -> Result<(), ParseError> { fn from_https_url_to_relative_root_path() -> Result<(), ParseError> {
let resolved_url = utils::resolve_url( let resolved_url = url::resolve_url(
"https://www.kernel.org/category/signatures.html", "https://www.kernel.org/category/signatures.html",
"/theme/images/logos/tux.png", "/theme/images/logos/tux.png",
)?; )?;
@ -86,7 +86,7 @@ mod passing {
#[test] #[test]
fn from_https_to_just_filename() -> Result<(), ParseError> { fn from_https_to_just_filename() -> Result<(), ParseError> {
let resolved_url = utils::resolve_url( let resolved_url = url::resolve_url(
"https://www.w3schools.com/html/html_iframe.asp", "https://www.w3schools.com/html/html_iframe.asp",
"default.asp", "default.asp",
)?; )?;
@ -101,7 +101,7 @@ mod passing {
#[test] #[test]
fn from_data_url_to_https() -> Result<(), ParseError> { fn from_data_url_to_https() -> Result<(), ParseError> {
let resolved_url = utils::resolve_url( let resolved_url = url::resolve_url(
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h", "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h",
"https://www.kernel.org/category/signatures.html", "https://www.kernel.org/category/signatures.html",
)?; )?;
@ -116,7 +116,7 @@ mod passing {
#[test] #[test]
fn from_data_url_to_data_url() -> Result<(), ParseError> { fn from_data_url_to_data_url() -> Result<(), ParseError> {
let resolved_url = utils::resolve_url( let resolved_url = url::resolve_url(
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h", "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h",
"data:text/html;base64,PGEgaHJlZj0iaW5kZXguaHRtbCI+SG9tZTwvYT4K", "data:text/html;base64,PGEgaHJlZj0iaW5kZXguaHRtbCI+SG9tZTwvYT4K",
)?; )?;
@ -131,7 +131,7 @@ mod passing {
#[test] #[test]
fn from_file_url_to_relative_path() -> Result<(), ParseError> { fn from_file_url_to_relative_path() -> Result<(), ParseError> {
let resolved_url = utils::resolve_url( let resolved_url = url::resolve_url(
"file:///home/user/Websites/my-website/index.html", "file:///home/user/Websites/my-website/index.html",
"assets/images/logo.png", "assets/images/logo.png",
) )
@ -147,7 +147,7 @@ mod passing {
#[test] #[test]
fn from_file_url_to_relative_path_with_backslashes() -> Result<(), ParseError> { fn from_file_url_to_relative_path_with_backslashes() -> Result<(), ParseError> {
let resolved_url = utils::resolve_url( let resolved_url = url::resolve_url(
"file:\\\\\\home\\user\\Websites\\my-website\\index.html", "file:\\\\\\home\\user\\Websites\\my-website\\index.html",
"assets\\images\\logo.png", "assets\\images\\logo.png",
) )
@ -163,7 +163,7 @@ mod passing {
#[test] #[test]
fn from_data_url_to_file_url() -> Result<(), ParseError> { fn from_data_url_to_file_url() -> Result<(), ParseError> {
let resolved_url = utils::resolve_url( let resolved_url = url::resolve_url(
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h", "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h",
"file:///etc/passwd", "file:///etc/passwd",
) )
@ -176,7 +176,7 @@ mod passing {
#[test] #[test]
fn preserve_fragment() -> Result<(), ParseError> { fn preserve_fragment() -> Result<(), ParseError> {
let resolved_url = utils::resolve_url( let resolved_url = url::resolve_url(
"http://doesnt-matter.local/", "http://doesnt-matter.local/",
"css/fonts/fontmarvelous.svg#fontmarvelous", "css/fonts/fontmarvelous.svg#fontmarvelous",
) )
@ -193,9 +193,9 @@ mod passing {
#[test] #[test]
fn resolve_from_file_url_to_file_url() -> Result<(), ParseError> { fn resolve_from_file_url_to_file_url() -> Result<(), ParseError> {
let resolved_url = if cfg!(windows) { let resolved_url = if cfg!(windows) {
utils::resolve_url("file:///c:/index.html", "file:///c:/image.png").unwrap_or(str!()) url::resolve_url("file:///c:/index.html", "file:///c:/image.png").unwrap_or(str!())
} else { } else {
utils::resolve_url("file:///tmp/index.html", "file:///tmp/image.png").unwrap_or(str!()) url::resolve_url("file:///tmp/index.html", "file:///tmp/image.png").unwrap_or(str!())
}; };
assert_eq!( assert_eq!(
@ -220,12 +220,12 @@ mod passing {
#[cfg(test)] #[cfg(test)]
mod failing { mod failing {
use crate::utils; use crate::url;
use url::ParseError; use url::ParseError;
#[test] #[test]
fn from_data_url_to_url_with_no_protocol() -> Result<(), ParseError> { fn from_data_url_to_url_with_no_protocol() -> Result<(), ParseError> {
let resolved_url = utils::resolve_url( let resolved_url = url::resolve_url(
"data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h", "data:text/html;base64,V2VsY29tZSBUbyBUaGUgUGFydHksIDxiPlBhbDwvYj4h",
"//www.w3schools.com/html/html_iframe.asp", "//www.w3schools.com/html/html_iframe.asp",
) )

@ -7,50 +7,50 @@
#[cfg(test)] #[cfg(test)]
mod passing { mod passing {
use crate::utils; use crate::url;
#[test] #[test]
fn mailto() { fn mailto() {
assert!(utils::url_has_protocol( assert!(url::url_has_protocol(
"mailto:somebody@somewhere.com?subject=hello" "mailto:somebody@somewhere.com?subject=hello"
)); ));
} }
#[test] #[test]
fn tel() { fn tel() {
assert!(utils::url_has_protocol("tel:5551234567")); assert!(url::url_has_protocol("tel:5551234567"));
} }
#[test] #[test]
fn ftp_no_slashes() { fn ftp_no_slashes() {
assert!(utils::url_has_protocol("ftp:some-ftp-server.com")); assert!(url::url_has_protocol("ftp:some-ftp-server.com"));
} }
#[test] #[test]
fn ftp_with_credentials() { fn ftp_with_credentials() {
assert!(utils::url_has_protocol( assert!(url::url_has_protocol(
"ftp://user:password@some-ftp-server.com" "ftp://user:password@some-ftp-server.com"
)); ));
} }
#[test] #[test]
fn javascript() { fn javascript() {
assert!(utils::url_has_protocol("javascript:void(0)")); assert!(url::url_has_protocol("javascript:void(0)"));
} }
#[test] #[test]
fn http() { fn http() {
assert!(utils::url_has_protocol("http://news.ycombinator.com")); assert!(url::url_has_protocol("http://news.ycombinator.com"));
} }
#[test] #[test]
fn https() { fn https() {
assert!(utils::url_has_protocol("https://github.com")); assert!(url::url_has_protocol("https://github.com"));
} }
#[test] #[test]
fn mailto_uppercase() { fn mailto_uppercase() {
assert!(utils::url_has_protocol( assert!(url::url_has_protocol(
"MAILTO:somebody@somewhere.com?subject=hello" "MAILTO:somebody@somewhere.com?subject=hello"
)); ));
} }
@ -69,23 +69,23 @@ mod failing {
#[test] #[test]
fn url_with_no_protocol() { fn url_with_no_protocol() {
assert!(!utils::url_has_protocol( assert!(!url::url_has_protocol(
"//some-hostname.com/some-file.html" "//some-hostname.com/some-file.html"
)); ));
} }
#[test] #[test]
fn relative_path() { fn relative_path() {
assert!(!utils::url_has_protocol("some-hostname.com/some-file.html")); assert!(!url::url_has_protocol("some-hostname.com/some-file.html"));
} }
#[test] #[test]
fn relative_to_root_path() { fn relative_to_root_path() {
assert!(!utils::url_has_protocol("/some-file.html")); assert!(!url::url_has_protocol("/some-file.html"));
} }
#[test] #[test]
fn empty_string() { fn empty_string() {
assert!(!utils::url_has_protocol("")); assert!(!url::url_has_protocol(""));
} }
} }

@ -0,0 +1,40 @@
// ██████╗ █████╗ ███████╗███████╗██╗███╗ ██╗ ██████╗
// ██╔══██╗██╔══██╗██╔════╝██╔════╝██║████╗ ██║██╔════╝
// ██████╔╝███████║███████╗███████╗██║██╔██╗ ██║██║ ███╗
// ██╔═══╝ ██╔══██║╚════██║╚════██║██║██║╚██╗██║██║ ██║
// ██║ ██║ ██║███████║███████║██║██║ ╚████║╚██████╔╝
// ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚═╝╚═╝ ╚═══╝ ╚═════╝
#[cfg(test)]
mod passing {
use crate::url;
#[test]
fn url_with_fragment_url() {
let url = "https://localhost.localdomain/path/";
let fragment = "test";
let assembled_url = url::url_with_fragment(url, fragment);
assert_eq!(&assembled_url, "https://localhost.localdomain/path/#test");
}
#[test]
fn url_with_fragment_empty_url() {
let url = "https://localhost.localdomain/path/";
let fragment = "";
let assembled_url = url::url_with_fragment(url, fragment);
assert_eq!(&assembled_url, "https://localhost.localdomain/path/");
}
#[test]
fn url_with_fragment_data_url() {
let url = "data:image/svg+xml;base64,PHN2Zz48L3N2Zz4K";
let fragment = "fragment";
let assembled_url = url::url_with_fragment(url, fragment);
assert_eq!(
&assembled_url,
"data:image/svg+xml;base64,PHN2Zz48L3N2Zz4K#fragment"
);
}
}

@ -1,13 +1,2 @@
mod clean_url;
mod data_to_data_url;
mod data_url_to_data;
mod decode_url;
mod detect_media_type; mod detect_media_type;
mod file_url_to_fs_path;
mod get_url_fragment;
mod is_data_url;
mod is_file_url;
mod is_http_url;
mod resolve_url;
mod retrieve_asset; mod retrieve_asset;
mod url_has_protocol;

@ -7,11 +7,13 @@
#[cfg(test)] #[cfg(test)]
mod passing { mod passing {
use crate::utils;
use reqwest::blocking::Client; use reqwest::blocking::Client;
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
use crate::url;
use crate::utils;
#[test] #[test]
fn read_data_url() { fn read_data_url() {
let cache = &mut HashMap::new(); let cache = &mut HashMap::new();
@ -28,12 +30,12 @@ mod passing {
) )
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
utils::data_to_data_url(&media_type, &data, &final_url, ""), url::data_to_data_url(&media_type, &data, &final_url),
utils::data_to_data_url("text/html", "target".as_bytes(), "", "") url::data_to_data_url("text/html", "target".as_bytes(), "")
); );
assert_eq!( assert_eq!(
final_url, final_url,
utils::data_to_data_url("text/html", "target".as_bytes(), "", "") url::data_to_data_url("text/html", "target".as_bytes(), "")
); );
assert_eq!(&media_type, "text/html"); assert_eq!(&media_type, "text/html");
} }
@ -63,7 +65,7 @@ mod passing {
false, false,
) )
.unwrap(); .unwrap();
assert_eq!(utils::data_to_data_url("application/javascript", &data, &final_url, ""), "data:application/javascript;base64,ZG9jdW1lbnQuYm9keS5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IgPSAiZ3JlZW4iOwpkb2N1bWVudC5ib2R5LnN0eWxlLmNvbG9yID0gInJlZCI7Cg=="); assert_eq!(url::data_to_data_url("application/javascript", &data, &final_url), "data:application/javascript;base64,ZG9jdW1lbnQuYm9keS5zdHlsZS5iYWNrZ3JvdW5kQ29sb3IgPSAiZ3JlZW4iOwpkb2N1bWVudC5ib2R5LnN0eWxlLmNvbG9yID0gInJlZCI7Cg==");
assert_eq!( assert_eq!(
&final_url, &final_url,
&format!( &format!(

@ -0,0 +1,168 @@
use base64;
use url::{form_urlencoded, ParseError, Url};
use crate::utils::detect_media_type;
pub fn clean_url<T: AsRef<str>>(input: T) -> String {
let mut url = Url::parse(input.as_ref()).unwrap();
// Clear fragment
url.set_fragment(None);
// Get rid of stray question mark
if url.query() == Some("") {
url.set_query(None);
}
// Remove empty trailing ampersand(s)
let mut result: String = url.to_string();
while result.ends_with("&") {
result.pop();
}
result
}
pub fn data_to_data_url(media_type: &str, data: &[u8], url: &str) -> String {
let media_type: String = if media_type.is_empty() {
detect_media_type(data, &url)
} else {
media_type.to_string()
};
format!("data:{};base64,{}", media_type, base64::encode(data))
}
pub fn data_url_to_data<T: AsRef<str>>(url: T) -> (String, Vec<u8>) {
let parsed_url: Url = Url::parse(url.as_ref()).unwrap_or(Url::parse("data:,").unwrap());
let path: String = parsed_url.path().to_string();
let comma_loc: usize = path.find(',').unwrap_or(path.len());
let meta_data: String = path.chars().take(comma_loc).collect();
let raw_data: String = path.chars().skip(comma_loc + 1).collect();
let text: String = decode_url(raw_data);
let meta_data_items: Vec<&str> = meta_data.split(';').collect();
let mut media_type: String = str!();
let mut encoding: &str = "";
let mut i: i8 = 0;
for item in &meta_data_items {
if i == 0 {
media_type = str!(item);
} else {
if item.eq_ignore_ascii_case("base64")
|| item.eq_ignore_ascii_case("utf8")
|| item.eq_ignore_ascii_case("charset=UTF-8")
{
encoding = item;
}
}
i = i + 1;
}
let data: Vec<u8> = if encoding.eq_ignore_ascii_case("base64") {
base64::decode(&text).unwrap_or(vec![])
} else {
text.as_bytes().to_vec()
};
(media_type, data)
}
pub fn decode_url(input: String) -> String {
let input: String = input.replace("+", "%2B");
form_urlencoded::parse(input.as_bytes())
.map(|(key, val)| {
[
key.to_string(),
if val.to_string().len() == 0 {
str!()
} else {
str!('=')
},
val.to_string(),
]
.concat()
})
.collect()
}
pub fn file_url_to_fs_path(url: &str) -> String {
if !is_file_url(url) {
return str!();
}
let cutoff_l = if cfg!(windows) { 8 } else { 7 };
let mut fs_file_path: String = decode_url(url.to_string()[cutoff_l..].to_string());
let url_fragment = get_url_fragment(url);
if url_fragment != "" {
let max_len = fs_file_path.len() - 1 - url_fragment.len();
fs_file_path = fs_file_path[0..max_len].to_string();
}
if cfg!(windows) {
fs_file_path = fs_file_path.replace("/", "\\");
}
// File paths should not be %-encoded
decode_url(fs_file_path)
}
pub fn get_url_fragment<T: AsRef<str>>(url: T) -> String {
if Url::parse(url.as_ref()).unwrap().fragment() == None {
str!()
} else {
str!(Url::parse(url.as_ref()).unwrap().fragment().unwrap())
}
}
pub fn is_data_url<T: AsRef<str>>(url: T) -> bool {
Url::parse(url.as_ref())
.and_then(|u| Ok(u.scheme() == "data"))
.unwrap_or(false)
}
pub fn is_file_url<T: AsRef<str>>(url: T) -> bool {
Url::parse(url.as_ref())
.and_then(|u| Ok(u.scheme() == "file"))
.unwrap_or(false)
}
pub fn is_http_url<T: AsRef<str>>(url: T) -> bool {
Url::parse(url.as_ref())
.and_then(|u| Ok(u.scheme() == "http" || u.scheme() == "https"))
.unwrap_or(false)
}
pub fn resolve_url<T: AsRef<str>, U: AsRef<str>>(from: T, to: U) -> Result<String, ParseError> {
let result = if is_http_url(to.as_ref()) {
to.as_ref().to_string()
} else {
Url::parse(from.as_ref())?
.join(to.as_ref())?
.as_ref()
.to_string()
};
Ok(result)
}
pub fn url_has_protocol<T: AsRef<str>>(url: T) -> bool {
Url::parse(url.as_ref())
.and_then(|u| Ok(u.scheme().len() > 0))
.unwrap_or(false)
}
pub fn url_with_fragment(url: &str, fragment: &str) -> String {
let mut result = str!(&url);
if !fragment.is_empty() {
result += "#";
result += fragment;
}
result
}

@ -1,10 +1,10 @@
use base64;
use reqwest::blocking::Client; use reqwest::blocking::Client;
use reqwest::header::CONTENT_TYPE; use reqwest::header::CONTENT_TYPE;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
use std::path::Path; use std::path::Path;
use url::{form_urlencoded, ParseError, Url};
use crate::url::{clean_url, data_url_to_data, file_url_to_fs_path, is_data_url, is_file_url};
const MAGIC: [[&[u8]; 2]; 18] = [ const MAGIC: [[&[u8]; 2]; 18] = [
// Image // Image
@ -38,26 +38,6 @@ const PLAINTEXT_MEDIA_TYPES: &[&str] = &[
"text/plain", "text/plain",
]; ];
pub fn data_to_data_url(media_type: &str, data: &[u8], url: &str, fragment: &str) -> String {
let media_type: String = if media_type.is_empty() {
detect_media_type(data, &url)
} else {
media_type.to_string()
};
let hash: String = if fragment != "" {
format!("#{}", fragment)
} else {
str!()
};
format!(
"data:{};base64,{}{}",
media_type,
base64::encode(data),
hash
)
}
pub fn detect_media_type(data: &[u8], url: &str) -> String { pub fn detect_media_type(data: &[u8], url: &str) -> String {
for item in MAGIC.iter() { for item in MAGIC.iter() {
if data.starts_with(item[0]) { if data.starts_with(item[0]) {
@ -72,153 +52,10 @@ pub fn detect_media_type(data: &[u8], url: &str) -> String {
str!() str!()
} }
pub fn url_has_protocol<T: AsRef<str>>(url: T) -> bool {
Url::parse(url.as_ref())
.and_then(|u| Ok(u.scheme().len() > 0))
.unwrap_or(false)
}
pub fn is_data_url<T: AsRef<str>>(url: T) -> bool {
Url::parse(url.as_ref())
.and_then(|u| Ok(u.scheme() == "data"))
.unwrap_or(false)
}
pub fn is_file_url<T: AsRef<str>>(url: T) -> bool {
Url::parse(url.as_ref())
.and_then(|u| Ok(u.scheme() == "file"))
.unwrap_or(false)
}
pub fn is_http_url<T: AsRef<str>>(url: T) -> bool {
Url::parse(url.as_ref())
.and_then(|u| Ok(u.scheme() == "http" || u.scheme() == "https"))
.unwrap_or(false)
}
pub fn is_plaintext_media_type(media_type: &str) -> bool { pub fn is_plaintext_media_type(media_type: &str) -> bool {
PLAINTEXT_MEDIA_TYPES.contains(&media_type.to_lowercase().as_str()) PLAINTEXT_MEDIA_TYPES.contains(&media_type.to_lowercase().as_str())
} }
pub fn resolve_url<T: AsRef<str>, U: AsRef<str>>(from: T, to: U) -> Result<String, ParseError> {
let result = if is_http_url(to.as_ref()) {
to.as_ref().to_string()
} else {
Url::parse(from.as_ref())?
.join(to.as_ref())?
.as_ref()
.to_string()
};
Ok(result)
}
pub fn get_url_fragment<T: AsRef<str>>(url: T) -> String {
if Url::parse(url.as_ref()).unwrap().fragment() == None {
str!()
} else {
str!(Url::parse(url.as_ref()).unwrap().fragment().unwrap())
}
}
pub fn clean_url<T: AsRef<str>>(input: T) -> String {
let mut url = Url::parse(input.as_ref()).unwrap();
// Clear fragment
url.set_fragment(None);
// Get rid of stray question mark
if url.query() == Some("") {
url.set_query(None);
}
// Remove empty trailing ampersand(s)
let mut result: String = url.to_string();
while result.ends_with("&") {
result.pop();
}
result
}
pub fn data_url_to_data<T: AsRef<str>>(url: T) -> (String, Vec<u8>) {
let parsed_url: Url = Url::parse(url.as_ref()).unwrap_or(Url::parse("data:,").unwrap());
let path: String = parsed_url.path().to_string();
let comma_loc: usize = path.find(',').unwrap_or(path.len());
let meta_data: String = path.chars().take(comma_loc).collect();
let raw_data: String = path.chars().skip(comma_loc + 1).collect();
let text: String = decode_url(raw_data);
let meta_data_items: Vec<&str> = meta_data.split(';').collect();
let mut media_type: String = str!();
let mut encoding: &str = "";
let mut i: i8 = 0;
for item in &meta_data_items {
if i == 0 {
media_type = str!(item);
} else {
if item.eq_ignore_ascii_case("base64")
|| item.eq_ignore_ascii_case("utf8")
|| item.eq_ignore_ascii_case("charset=UTF-8")
{
encoding = item;
}
}
i = i + 1;
}
let data: Vec<u8> = if encoding.eq_ignore_ascii_case("base64") {
base64::decode(&text).unwrap_or(vec![])
} else {
text.as_bytes().to_vec()
};
(media_type, data)
}
pub fn decode_url(input: String) -> String {
let input: String = input.replace("+", "%2B");
form_urlencoded::parse(input.as_bytes())
.map(|(key, val)| {
[
key.to_string(),
if val.to_string().len() == 0 {
str!()
} else {
str!('=')
},
val.to_string(),
]
.concat()
})
.collect()
}
pub fn file_url_to_fs_path(url: &str) -> String {
if !is_file_url(url) {
return str!();
}
let cutoff_l = if cfg!(windows) { 8 } else { 7 };
let mut fs_file_path: String = decode_url(url.to_string()[cutoff_l..].to_string());
let url_fragment = get_url_fragment(url);
if url_fragment != "" {
let max_len = fs_file_path.len() - 1 - url_fragment.len();
fs_file_path = fs_file_path[0..max_len].to_string();
}
if cfg!(windows) {
fs_file_path = fs_file_path.replace("/", "\\");
}
// File paths should not be %-encoded
decode_url(fs_file_path)
}
pub fn retrieve_asset( pub fn retrieve_asset(
cache: &mut HashMap<String, Vec<u8>>, cache: &mut HashMap<String, Vec<u8>>,
client: &Client, client: &Client,

Loading…
Cancel
Save