diff --git a/CHANGELOG.md b/CHANGELOG.md index 4688d7d9..cdf15f22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - A way to customize resolving remote stylesheets. +- Non-blocking stylesheet resolving by default. ### Changed diff --git a/README.md b/README.md index 10325964..746db8c7 100644 --- a/README.md +++ b/README.md @@ -70,8 +70,21 @@ const HTML: &str = r#" "#; +#[tokio::main] +async fn main() -> css_inline::Result<()> { + let inlined = css_inline::inline(HTML).await?; + // Do something with inlined HTML, e.g. send an email + Ok(()) +} +``` + +There is also a "blocking" API for inlining: + +```rust +const HTML: &str = "..."; + fn main() -> css_inline::Result<()> { - let inlined = css_inline::inline(HTML)?; + let inlined = css_inline::blocking::inline(HTML)?; // Do something with inlined HTML, e.g. send an email Ok(()) } @@ -84,11 +97,12 @@ fn main() -> css_inline::Result<()> { ```rust const HTML: &str = "..."; -fn main() -> css_inline::Result<()> { +#[tokio::main] +async fn main() -> css_inline::Result<()> { let inliner = css_inline::CSSInliner::options() .load_remote_stylesheets(false) .build(); - let inlined = inliner.inline(HTML)?; + let inlined = inliner.inline(HTML).await?; // Do something with inlined HTML, e.g. send an email Ok(()) } @@ -131,12 +145,28 @@ If you'd like to load stylesheets from your filesystem, use the `file://` scheme ```rust const HTML: &str = "..."; -fn main() -> css_inline::Result<()> { +#[tokio::main] +async fn main() -> css_inline::Result<()> { let base_url = css_inline::Url::parse("file://styles/email/").expect("Invalid URL"); let inliner = css_inline::CSSInliner::options() .base_url(Some(base_url)) .build(); - let inlined = inliner.inline(HTML); + let inlined = inliner.inline(HTML).await?; + // Do something with inlined HTML, e.g. send an email + Ok(()) +} +``` + +The blocking version is available as well: + +```rust +const HTML: &str = "..."; + +fn main() -> css_inline::Result<()> { + let inliner = css_inline::blocking::CSSInliner::options() + .load_remote_stylesheets(false) + .build(); + let inlined = inliner.inline(HTML)?; // Do something with inlined HTML, e.g. send an email Ok(()) } @@ -149,7 +179,7 @@ For resolving remote stylesheets it is possible to implement a custom resolver: pub struct CustomStylesheetResolver; impl css_inline::StylesheetResolver for CustomStylesheetResolver { - fn retrieve(&self, location: &str) -> css_inline::Result { + fn retrieve_blocking(&self, location: &str) -> css_inline::Result { Err(self.unsupported("External stylesheets are not supported")) } } diff --git a/bindings/c/Cargo.toml b/bindings/c/Cargo.toml index 4d012086..f087d814 100644 --- a/bindings/c/Cargo.toml +++ b/bindings/c/Cargo.toml @@ -18,4 +18,4 @@ cbindgen = "0.26" path = "../../css-inline" version = "*" default-features = false -features = ["http", "file"] +features = ["http-blocking", "file"] diff --git a/bindings/c/src/lib.rs b/bindings/c/src/lib.rs index b40a5fde..490fbc60 100644 --- a/bindings/c/src/lib.rs +++ b/bindings/c/src/lib.rs @@ -1,4 +1,7 @@ -use css_inline::{CSSInliner, DefaultStylesheetResolver, InlineError, InlineOptions, Url}; +use css_inline::{ + blocking::{CSSInliner, InlineOptions}, + DefaultStylesheetResolver, InlineError, Url, +}; use libc::{c_char, size_t}; use std::{borrow::Cow, cmp, ffi::CStr, io::Write, ptr, sync::Arc}; diff --git a/bindings/javascript/Cargo.toml b/bindings/javascript/Cargo.toml index fbb0e945..fa109511 100644 --- a/bindings/javascript/Cargo.toml +++ b/bindings/javascript/Cargo.toml @@ -32,7 +32,7 @@ serde = { version = "1", features = ["derive"], default-features = false } path = "../../css-inline" version = "*" default-features = false -features = ["http", "file"] +features = ["http-blocking", "file"] [target.'cfg(target_arch = "wasm32")'.dependencies.css-inline] path = "../../css-inline" diff --git a/bindings/javascript/src/lib.rs b/bindings/javascript/src/lib.rs index d96e95d8..987853ea 100644 --- a/bindings/javascript/src/lib.rs +++ b/bindings/javascript/src/lib.rs @@ -60,6 +60,6 @@ export function inline(html: string, options?: InlineOptions): string; export function version(): string;"#; fn inline_inner(html: String, options: Options) -> std::result::Result { - let inliner = css_inline::CSSInliner::new(options.try_into()?); + let inliner = css_inline::blocking::CSSInliner::new(options.try_into()?); Ok(inliner.inline(&html).map_err(errors::InlineError)?) } diff --git a/bindings/javascript/src/options.rs b/bindings/javascript/src/options.rs index 2691df49..a07c57a9 100644 --- a/bindings/javascript/src/options.rs +++ b/bindings/javascript/src/options.rs @@ -42,11 +42,11 @@ pub struct Options { pub preallocate_node_capacity: Option, } -impl TryFrom for css_inline::InlineOptions<'_> { +impl TryFrom for css_inline::blocking::InlineOptions<'_> { type Error = JsError; fn try_from(value: Options) -> std::result::Result { - Ok(css_inline::InlineOptions { + Ok(css_inline::blocking::InlineOptions { inline_style_tags: value.inline_style_tags.unwrap_or(true), keep_style_tags: value.keep_style_tags.unwrap_or(false), keep_link_tags: value.keep_link_tags.unwrap_or(false), @@ -75,7 +75,7 @@ impl TryFrom for css_inline::InlineOptions<'_> { pub struct UnsupportedResolver; impl css_inline::StylesheetResolver for UnsupportedResolver { - fn retrieve(&self, location: &str) -> css_inline::Result { + fn retrieve_blocking(&self, location: &str) -> css_inline::Result { let message = if location.starts_with("https") | location.starts_with("http") { diff --git a/bindings/javascript/wasm/index.js b/bindings/javascript/wasm/index.js index a1d5a90c..5e3c0a2e 100644 --- a/bindings/javascript/wasm/index.js +++ b/bindings/javascript/wasm/index.js @@ -33,6 +33,18 @@ heap.push(void 0, null, true, false); function getObject(idx) { return heap[idx]; } +var heap_next = heap.length; +function dropObject(idx) { + if (idx < 132) + return; + heap[idx] = heap_next; + heap_next = idx; +} +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} var WASM_VECTOR_LEN = 0; var cachedUint8Memory0 = null; function getUint8Memory0() { @@ -104,7 +116,6 @@ function getStringFromWasm0(ptr, len) { ptr = ptr >>> 0; return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); } -var heap_next = heap.length; function addHeapObject(obj) { if (heap_next === heap.length) heap.push(heap.length + 1); @@ -113,17 +124,6 @@ function addHeapObject(obj) { heap[idx] = obj; return idx; } -function dropObject(idx) { - if (idx < 132) - return; - heap[idx] = heap_next; - heap_next = idx; -} -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} var cachedFloat64Memory0 = null; function getFloat64Memory0() { if (cachedFloat64Memory0 === null || cachedFloat64Memory0.byteLength === 0) { @@ -261,6 +261,9 @@ function __wbg_get_imports() { const ret = getObject(arg0) === void 0; return ret; }; + imports.wbg.__wbindgen_object_drop_ref = function(arg0) { + takeObject(arg0); + }; imports.wbg.__wbindgen_string_get = function(arg0, arg1) { const obj = getObject(arg1); const ret = typeof obj === "string" ? obj : void 0; @@ -303,9 +306,6 @@ function __wbg_get_imports() { const ret = +getObject(arg0); return ret; }; - imports.wbg.__wbindgen_object_drop_ref = function(arg0) { - takeObject(arg0); - }; imports.wbg.__wbg_length_1d25fa9e4ac21ce7 = function(arg0) { const ret = getObject(arg0).length; return ret; diff --git a/bindings/javascript/wasm/index.min.js b/bindings/javascript/wasm/index.min.js index eaf8dad1..a440b878 100644 --- a/bindings/javascript/wasm/index.min.js +++ b/bindings/javascript/wasm/index.min.js @@ -1,2 +1,2 @@ -"use strict";var cssInline=(()=>{var S=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var B=(e,n)=>{for(var t in n)S(e,t,{get:n[t],enumerable:!0})},q=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of D(n))!N.call(e,i)&&i!==t&&S(e,i,{get:()=>n[i],enumerable:!(r=$(n,i))||r.enumerable});return e};var z=e=>q(S({},"__esModule",{value:!0}),e);var Y={};B(Y,{initWasm:()=>K,inline:()=>Q,version:()=>X});var c,b=new Array(128).fill(void 0);b.push(void 0,null,!0,!1);function o(e){return b[e]}var h=0,g=null;function A(){return(g===null||g.byteLength===0)&&(g=new Uint8Array(c.memory.buffer)),g}var I=typeof TextEncoder<"u"?new TextEncoder("utf-8"):{encode:()=>{throw Error("TextEncoder not available")}},C=typeof I.encodeInto=="function"?function(e,n){return I.encodeInto(e,n)}:function(e,n){let t=I.encode(e);return n.set(t),{read:e.length,written:t.length}};function W(e,n,t){if(t===void 0){let _=I.encode(e),a=n(_.length,1)>>>0;return A().subarray(a,a+_.length).set(_),h=_.length,a}let r=e.length,i=n(r,1)>>>0,f=A(),s=0;for(;s127)break;f[i+s]=_}if(s!==r){s!==0&&(e=e.slice(s)),i=t(i,r,r=s+e.length*3,1)>>>0;let _=A().subarray(i+s,i+r),a=C(e,_);s+=a.written}return h=s,i}function j(e){return e==null}var w=null;function u(){return(w===null||w.byteLength===0)&&(w=new Int32Array(c.memory.buffer)),w}var M=typeof TextDecoder<"u"?new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}):{decode:()=>{throw Error("TextDecoder not available")}};typeof TextDecoder<"u"&&M.decode();function m(e,n){return e=e>>>0,M.decode(A().subarray(e,e+n))}var p=b.length;function l(e){p===b.length&&b.push(b.length+1);let n=p;return p=b[n],b[n]=e,n}function P(e){e<132||(b[e]=p,p=e)}function O(e){let n=o(e);return P(e),n}var y=null;function H(){return(y===null||y.byteLength===0)&&(y=new Float64Array(c.memory.buffer)),y}function E(e){let n=typeof e;if(n=="number"||n=="boolean"||e==null)return`${e}`;if(n=="string")return`"${e}"`;if(n=="symbol"){let i=e.description;return i==null?"Symbol":`Symbol(${i})`}if(n=="function"){let i=e.name;return typeof i=="string"&&i.length>0?`Function(${i})`:"Function"}if(Array.isArray(e)){let i=e.length,f="[";i>0&&(f+=E(e[0]));for(let s=1;s1)r=t[1];else return toString.call(e);if(r=="Object")try{return"Object("+JSON.stringify(e)+")"}catch{return"Object"}return e instanceof Error?`${e.name}: ${e.message} -${e.stack}`:r}function T(e,n){let t,r;try{let d=c.__wbindgen_add_to_stack_pointer(-16),L=W(e,c.__wbindgen_malloc,c.__wbindgen_realloc),R=h;c.inline(d,L,R,l(n));var i=u()[d/4+0],f=u()[d/4+1],s=u()[d/4+2],_=u()[d/4+3],a=i,x=f;if(_)throw a=0,x=0,O(s);return t=a,r=x,m(a,x)}finally{c.__wbindgen_add_to_stack_pointer(16),c.__wbindgen_free(t,r,1)}}function k(){let e,n;try{let i=c.__wbindgen_add_to_stack_pointer(-16);c.version(i);var t=u()[i/4+0],r=u()[i/4+1];return e=t,n=r,m(t,r)}finally{c.__wbindgen_add_to_stack_pointer(16),c.__wbindgen_free(e,n,1)}}async function J(e,n){if(typeof Response=="function"&&e instanceof Response){if(typeof WebAssembly.instantiateStreaming=="function")try{return await WebAssembly.instantiateStreaming(e,n)}catch(r){if(e.headers.get("Content-Type")!="application/wasm")console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",r);else throw r}let t=await e.arrayBuffer();return await WebAssembly.instantiate(t,n)}else{let t=await WebAssembly.instantiate(e,n);return t instanceof WebAssembly.Instance?{instance:t,module:e}:t}}function V(){let e={};return e.wbg={},e.wbg.__wbindgen_is_undefined=function(n){return o(n)===void 0},e.wbg.__wbindgen_string_get=function(n,t){let r=o(t),i=typeof r=="string"?r:void 0;var f=j(i)?0:W(i,c.__wbindgen_malloc,c.__wbindgen_realloc),s=h;u()[n/4+1]=s,u()[n/4+0]=f},e.wbg.__wbindgen_boolean_get=function(n){let t=o(n);return typeof t=="boolean"?t?1:0:2},e.wbg.__wbindgen_is_object=function(n){let t=o(n);return typeof t=="object"&&t!==null},e.wbg.__wbindgen_string_new=function(n,t){let r=m(n,t);return l(r)},e.wbg.__wbindgen_object_clone_ref=function(n){let t=o(n);return l(t)},e.wbg.__wbg_getwithrefkey_4a92a5eca60879b9=function(n,t){let r=o(n)[o(t)];return l(r)},e.wbg.__wbindgen_in=function(n,t){return o(n)in o(t)},e.wbg.__wbg_isSafeInteger_f93fde0dca9820f8=function(n){return Number.isSafeInteger(o(n))},e.wbg.__wbindgen_as_number=function(n){return+o(n)},e.wbg.__wbindgen_object_drop_ref=function(n){O(n)},e.wbg.__wbg_length_1d25fa9e4ac21ce7=function(n){return o(n).length},e.wbg.__wbindgen_memory=function(){let n=c.memory;return l(n)},e.wbg.__wbg_buffer_a448f833075b71ba=function(n){let t=o(n).buffer;return l(t)},e.wbg.__wbg_new_8f67e318f15d7254=function(n){let t=new Uint8Array(o(n));return l(t)},e.wbg.__wbg_set_2357bf09366ee480=function(n,t,r){o(n).set(o(t),r>>>0)},e.wbg.__wbindgen_error_new=function(n,t){let r=new Error(m(n,t));return l(r)},e.wbg.__wbindgen_jsval_loose_eq=function(n,t){return o(n)==o(t)},e.wbg.__wbindgen_number_get=function(n,t){let r=o(t),i=typeof r=="number"?r:void 0;H()[n/8+1]=j(i)?0:i,u()[n/4+0]=!j(i)},e.wbg.__wbg_instanceof_Uint8Array_bced6f43aed8c1aa=function(n){let t;try{t=o(n)instanceof Uint8Array}catch{t=!1}return t},e.wbg.__wbg_instanceof_ArrayBuffer_e7d53d51371448e2=function(n){let t;try{t=o(n)instanceof ArrayBuffer}catch{t=!1}return t},e.wbg.__wbindgen_debug_string=function(n,t){let r=E(o(t)),i=W(r,c.__wbindgen_malloc,c.__wbindgen_realloc),f=h;u()[n/4+1]=f,u()[n/4+0]=i},e.wbg.__wbindgen_throw=function(n,t){throw new Error(m(n,t))},e}function G(e,n){return c=e.exports,U.__wbindgen_wasm_module=n,y=null,w=null,g=null,c}async function U(e){if(c!==void 0)return c;typeof e>"u"&&(e=new URL("index_bg.wasm",void 0));let n=V();(typeof e=="string"||typeof Request=="function"&&e instanceof Request||typeof URL=="function"&&e instanceof URL)&&(e=fetch(e));let{instance:t,module:r}=await J(await e,n);return G(t,r)}var v=U;var F=!1,K=async e=>{if(F)throw new Error("Already initialized. The `initWasm()` function can be used only once.");await v(await e),F=!0};function Q(e,n){return T(e,n)}function X(){return k()}return z(Y);})(); +"use strict";var cssInline=(()=>{var S=Object.defineProperty;var $=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var N=Object.prototype.hasOwnProperty;var B=(e,n)=>{for(var t in n)S(e,t,{get:n[t],enumerable:!0})},q=(e,n,t,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let i of D(n))!N.call(e,i)&&i!==t&&S(e,i,{get:()=>n[i],enumerable:!(r=$(n,i))||r.enumerable});return e};var z=e=>q(S({},"__esModule",{value:!0}),e);var Y={};B(Y,{initWasm:()=>K,inline:()=>Q,version:()=>X});var c,b=new Array(128).fill(void 0);b.push(void 0,null,!0,!1);function o(e){return b[e]}var m=b.length;function C(e){e<132||(b[e]=m,m=e)}function M(e){let n=o(e);return C(e),n}var h=0,g=null;function A(){return(g===null||g.byteLength===0)&&(g=new Uint8Array(c.memory.buffer)),g}var I=typeof TextEncoder<"u"?new TextEncoder("utf-8"):{encode:()=>{throw Error("TextEncoder not available")}},P=typeof I.encodeInto=="function"?function(e,n){return I.encodeInto(e,n)}:function(e,n){let t=I.encode(e);return n.set(t),{read:e.length,written:t.length}};function W(e,n,t){if(t===void 0){let _=I.encode(e),a=n(_.length,1)>>>0;return A().subarray(a,a+_.length).set(_),h=_.length,a}let r=e.length,i=n(r,1)>>>0,f=A(),s=0;for(;s127)break;f[i+s]=_}if(s!==r){s!==0&&(e=e.slice(s)),i=t(i,r,r=s+e.length*3,1)>>>0;let _=A().subarray(i+s,i+r),a=P(e,_);s+=a.written}return h=s,i}function j(e){return e==null}var w=null;function u(){return(w===null||w.byteLength===0)&&(w=new Int32Array(c.memory.buffer)),w}var O=typeof TextDecoder<"u"?new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}):{decode:()=>{throw Error("TextDecoder not available")}};typeof TextDecoder<"u"&&O.decode();function p(e,n){return e=e>>>0,O.decode(A().subarray(e,e+n))}function l(e){m===b.length&&b.push(b.length+1);let n=m;return m=b[n],b[n]=e,n}var y=null;function H(){return(y===null||y.byteLength===0)&&(y=new Float64Array(c.memory.buffer)),y}function E(e){let n=typeof e;if(n=="number"||n=="boolean"||e==null)return`${e}`;if(n=="string")return`"${e}"`;if(n=="symbol"){let i=e.description;return i==null?"Symbol":`Symbol(${i})`}if(n=="function"){let i=e.name;return typeof i=="string"&&i.length>0?`Function(${i})`:"Function"}if(Array.isArray(e)){let i=e.length,f="[";i>0&&(f+=E(e[0]));for(let s=1;s1)r=t[1];else return toString.call(e);if(r=="Object")try{return"Object("+JSON.stringify(e)+")"}catch{return"Object"}return e instanceof Error?`${e.name}: ${e.message} +${e.stack}`:r}function T(e,n){let t,r;try{let d=c.__wbindgen_add_to_stack_pointer(-16),L=W(e,c.__wbindgen_malloc,c.__wbindgen_realloc),R=h;c.inline(d,L,R,l(n));var i=u()[d/4+0],f=u()[d/4+1],s=u()[d/4+2],_=u()[d/4+3],a=i,x=f;if(_)throw a=0,x=0,M(s);return t=a,r=x,p(a,x)}finally{c.__wbindgen_add_to_stack_pointer(16),c.__wbindgen_free(t,r,1)}}function k(){let e,n;try{let i=c.__wbindgen_add_to_stack_pointer(-16);c.version(i);var t=u()[i/4+0],r=u()[i/4+1];return e=t,n=r,p(t,r)}finally{c.__wbindgen_add_to_stack_pointer(16),c.__wbindgen_free(e,n,1)}}async function J(e,n){if(typeof Response=="function"&&e instanceof Response){if(typeof WebAssembly.instantiateStreaming=="function")try{return await WebAssembly.instantiateStreaming(e,n)}catch(r){if(e.headers.get("Content-Type")!="application/wasm")console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",r);else throw r}let t=await e.arrayBuffer();return await WebAssembly.instantiate(t,n)}else{let t=await WebAssembly.instantiate(e,n);return t instanceof WebAssembly.Instance?{instance:t,module:e}:t}}function V(){let e={};return e.wbg={},e.wbg.__wbindgen_is_undefined=function(n){return o(n)===void 0},e.wbg.__wbindgen_object_drop_ref=function(n){M(n)},e.wbg.__wbindgen_string_get=function(n,t){let r=o(t),i=typeof r=="string"?r:void 0;var f=j(i)?0:W(i,c.__wbindgen_malloc,c.__wbindgen_realloc),s=h;u()[n/4+1]=s,u()[n/4+0]=f},e.wbg.__wbindgen_boolean_get=function(n){let t=o(n);return typeof t=="boolean"?t?1:0:2},e.wbg.__wbindgen_is_object=function(n){let t=o(n);return typeof t=="object"&&t!==null},e.wbg.__wbindgen_string_new=function(n,t){let r=p(n,t);return l(r)},e.wbg.__wbindgen_object_clone_ref=function(n){let t=o(n);return l(t)},e.wbg.__wbg_getwithrefkey_4a92a5eca60879b9=function(n,t){let r=o(n)[o(t)];return l(r)},e.wbg.__wbindgen_in=function(n,t){return o(n)in o(t)},e.wbg.__wbg_isSafeInteger_f93fde0dca9820f8=function(n){return Number.isSafeInteger(o(n))},e.wbg.__wbindgen_as_number=function(n){return+o(n)},e.wbg.__wbg_length_1d25fa9e4ac21ce7=function(n){return o(n).length},e.wbg.__wbindgen_memory=function(){let n=c.memory;return l(n)},e.wbg.__wbg_buffer_a448f833075b71ba=function(n){let t=o(n).buffer;return l(t)},e.wbg.__wbg_new_8f67e318f15d7254=function(n){let t=new Uint8Array(o(n));return l(t)},e.wbg.__wbg_set_2357bf09366ee480=function(n,t,r){o(n).set(o(t),r>>>0)},e.wbg.__wbindgen_error_new=function(n,t){let r=new Error(p(n,t));return l(r)},e.wbg.__wbindgen_jsval_loose_eq=function(n,t){return o(n)==o(t)},e.wbg.__wbindgen_number_get=function(n,t){let r=o(t),i=typeof r=="number"?r:void 0;H()[n/8+1]=j(i)?0:i,u()[n/4+0]=!j(i)},e.wbg.__wbg_instanceof_Uint8Array_bced6f43aed8c1aa=function(n){let t;try{t=o(n)instanceof Uint8Array}catch{t=!1}return t},e.wbg.__wbg_instanceof_ArrayBuffer_e7d53d51371448e2=function(n){let t;try{t=o(n)instanceof ArrayBuffer}catch{t=!1}return t},e.wbg.__wbindgen_debug_string=function(n,t){let r=E(o(t)),i=W(r,c.__wbindgen_malloc,c.__wbindgen_realloc),f=h;u()[n/4+1]=f,u()[n/4+0]=i},e.wbg.__wbindgen_throw=function(n,t){throw new Error(p(n,t))},e}function G(e,n){return c=e.exports,U.__wbindgen_wasm_module=n,y=null,w=null,g=null,c}async function U(e){if(c!==void 0)return c;typeof e>"u"&&(e=new URL("index_bg.wasm",void 0));let n=V();(typeof e=="string"||typeof Request=="function"&&e instanceof Request||typeof URL=="function"&&e instanceof URL)&&(e=fetch(e));let{instance:t,module:r}=await J(await e,n);return G(t,r)}var v=U;var F=!1,K=async e=>{if(F)throw new Error("Already initialized. The `initWasm()` function can be used only once.");await v(await e),F=!0};function Q(e,n){return T(e,n)}function X(){return k()}return z(Y);})(); diff --git a/bindings/javascript/wasm/index.mjs b/bindings/javascript/wasm/index.mjs index 008fbb26..6ec93e90 100644 --- a/bindings/javascript/wasm/index.mjs +++ b/bindings/javascript/wasm/index.mjs @@ -5,6 +5,18 @@ heap.push(void 0, null, true, false); function getObject(idx) { return heap[idx]; } +var heap_next = heap.length; +function dropObject(idx) { + if (idx < 132) + return; + heap[idx] = heap_next; + heap_next = idx; +} +function takeObject(idx) { + const ret = getObject(idx); + dropObject(idx); + return ret; +} var WASM_VECTOR_LEN = 0; var cachedUint8Memory0 = null; function getUint8Memory0() { @@ -76,7 +88,6 @@ function getStringFromWasm0(ptr, len) { ptr = ptr >>> 0; return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len)); } -var heap_next = heap.length; function addHeapObject(obj) { if (heap_next === heap.length) heap.push(heap.length + 1); @@ -85,17 +96,6 @@ function addHeapObject(obj) { heap[idx] = obj; return idx; } -function dropObject(idx) { - if (idx < 132) - return; - heap[idx] = heap_next; - heap_next = idx; -} -function takeObject(idx) { - const ret = getObject(idx); - dropObject(idx); - return ret; -} var cachedFloat64Memory0 = null; function getFloat64Memory0() { if (cachedFloat64Memory0 === null || cachedFloat64Memory0.byteLength === 0) { @@ -233,6 +233,9 @@ function __wbg_get_imports() { const ret = getObject(arg0) === void 0; return ret; }; + imports.wbg.__wbindgen_object_drop_ref = function(arg0) { + takeObject(arg0); + }; imports.wbg.__wbindgen_string_get = function(arg0, arg1) { const obj = getObject(arg1); const ret = typeof obj === "string" ? obj : void 0; @@ -275,9 +278,6 @@ function __wbg_get_imports() { const ret = +getObject(arg0); return ret; }; - imports.wbg.__wbindgen_object_drop_ref = function(arg0) { - takeObject(arg0); - }; imports.wbg.__wbg_length_1d25fa9e4ac21ce7 = function(arg0) { const ret = getObject(arg0).length; return ret; diff --git a/bindings/javascript/wasm/index_bg.wasm b/bindings/javascript/wasm/index_bg.wasm index a61fbf8e..8fb999b9 100644 Binary files a/bindings/javascript/wasm/index_bg.wasm and b/bindings/javascript/wasm/index_bg.wasm differ diff --git a/bindings/python/Cargo.toml b/bindings/python/Cargo.toml index d64ceafc..98c6131e 100644 --- a/bindings/python/Cargo.toml +++ b/bindings/python/Cargo.toml @@ -23,7 +23,7 @@ url = "2" path = "../../css-inline" version = "*" default-features = false -features = ["http", "file"] +features = ["http-blocking", "file"] [profile.release] codegen-units = 1 diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index b2a86651..d7a232dd 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -74,12 +74,12 @@ fn parse_url(url: Option) -> PyResult> { /// Customizable CSS inliner. #[pyclass] struct CSSInliner { - inner: rust_inline::CSSInliner<'static>, + inner: rust_inline::blocking::CSSInliner<'static>, } macro_rules! inliner { ($inline_style_tags:expr, $keep_style_tags:expr, $keep_link_tags:expr, $base_url:expr, $load_remote_stylesheets:expr, $extra_css:expr, $preallocate_node_capacity:expr) => {{ - let options = rust_inline::InlineOptions { + rust_inline::blocking::InlineOptions { inline_style_tags: $inline_style_tags.unwrap_or(true), keep_style_tags: $keep_style_tags.unwrap_or(false), keep_link_tags: $keep_link_tags.unwrap_or(false), @@ -88,8 +88,8 @@ macro_rules! inliner { extra_css: $extra_css.map(Into::into), preallocate_node_capacity: $preallocate_node_capacity.unwrap_or(32), resolver: std::sync::Arc::new(rust_inline::DefaultStylesheetResolver), - }; - rust_inline::CSSInliner::new(options) + } + .build() }}; } @@ -198,7 +198,7 @@ fn inline_many( } fn inline_many_impl( - inliner: &rust_inline::CSSInliner<'_>, + inliner: &rust_inline::blocking::CSSInliner<'_>, htmls: &PyList, ) -> PyResult> { // Extract strings from the list. It will fail if there is any non-string value diff --git a/bindings/ruby/ext/css_inline/Cargo.toml b/bindings/ruby/ext/css_inline/Cargo.toml index a71fb898..97fc8e02 100644 --- a/bindings/ruby/ext/css_inline/Cargo.toml +++ b/bindings/ruby/ext/css_inline/Cargo.toml @@ -23,4 +23,4 @@ rayon = "1" path = "../../../../css-inline" version = "*" default-features = false -features = ["http", "file"] +features = ["http-blocking", "file"] diff --git a/bindings/ruby/ext/css_inline/src/lib.rs b/bindings/ruby/ext/css_inline/src/lib.rs index f5395eba..e2ced6c9 100644 --- a/bindings/ruby/ext/css_inline/src/lib.rs +++ b/bindings/ruby/ext/css_inline/src/lib.rs @@ -39,7 +39,7 @@ type RubyResult = Result; fn parse_options( args: &Args, -) -> RubyResult> { +) -> RubyResult> { let kwargs = get_kwargs::< _, (), @@ -67,7 +67,7 @@ fn parse_options( ], )?; let kwargs = kwargs.optional; - Ok(rust_inline::InlineOptions { + Ok(rust_inline::blocking::InlineOptions { inline_style_tags: kwargs.0.unwrap_or(true), keep_style_tags: kwargs.1.unwrap_or(false), keep_link_tags: kwargs.2.unwrap_or(false), @@ -81,7 +81,7 @@ fn parse_options( #[magnus::wrap(class = "CSSInline::CSSInliner")] struct CSSInliner { - inner: rust_inline::CSSInliner<'static>, + inner: rust_inline::blocking::CSSInliner<'static>, } struct InlineErrorWrapper(rust_inline::InlineError); @@ -133,7 +133,7 @@ impl CSSInliner { let args = scan_args::<(), _, _, _, _, _>(args)?; let options = parse_options(&args)?; Ok(CSSInliner { - inner: rust_inline::CSSInliner::new(options), + inner: rust_inline::blocking::CSSInliner::new(options), }) } @@ -152,7 +152,7 @@ fn inline(args: &[Value]) -> RubyResult { let args = scan_args::<(String,), _, _, _, _, _>(args)?; let options = parse_options(&args)?; let html = args.required.0; - Ok(rust_inline::CSSInliner::new(options) + Ok(rust_inline::blocking::CSSInliner::new(options) .inline(&html) .map_err(InlineErrorWrapper)?) } @@ -160,13 +160,13 @@ fn inline(args: &[Value]) -> RubyResult { fn inline_many(args: &[Value]) -> RubyResult> { let args = scan_args::<(Vec,), _, _, _, _, _>(args)?; let options = parse_options(&args)?; - let inliner = rust_inline::CSSInliner::new(options); + let inliner = rust_inline::blocking::CSSInliner::new(options); inline_many_impl(&args.required.0, &inliner) } fn inline_many_impl( htmls: &[String], - inliner: &rust_inline::CSSInliner<'static>, + inliner: &rust_inline::blocking::CSSInliner<'static>, ) -> RubyResult> { let output: Result, _> = htmls.par_iter().map(|html| inliner.inline(html)).collect(); Ok(output.map_err(InlineErrorWrapper)?) diff --git a/css-inline/Cargo.toml b/css-inline/Cargo.toml index 22cae200..a4e29a2e 100644 --- a/css-inline/Cargo.toml +++ b/css-inline/Cargo.toml @@ -22,18 +22,20 @@ rust-version = "1.65" name = "css-inline" [features] -default = ["cli", "http", "file"] +default = ["cli", "http", "http-blocking", "file"] cli = ["pico-args", "rayon"] http = ["reqwest"] +http-blocking = ["reqwest/blocking"] file = [] [dependencies] cssparser = "0.31.2" +futures-util = "0.3.30" html5ever = "0.26.0" indexmap = "2.1" pico-args = { version = "0.3", optional = true } rayon = { version = "1.7", optional = true } -reqwest = { version = "0.11.23", optional = true, default-features = false, features = ["rustls-tls", "blocking"] } +reqwest = { version = "0.11.23", optional = true, default-features = false, features = ["rustls-tls"] } rustc-hash = "1.1.0" selectors = "0.25.0" smallvec = "1" @@ -46,6 +48,7 @@ criterion = { version = "0.5.1", features = [], default-features = false } serde = { version = "1", features = ["derive"] } serde_json = "1" test-case = "3.3" +tokio = { version = "1.32", features = ["macros", "rt"] } [[bench]] name = "inliner" diff --git a/css-inline/benches/inliner.rs b/css-inline/benches/inliner.rs index e009b328..76ab8c3f 100644 --- a/css-inline/benches/inliner.rs +++ b/css-inline/benches/inliner.rs @@ -1,5 +1,5 @@ use codspeed_criterion_compat::{black_box, criterion_group, criterion_main, Criterion}; -use css_inline::inline_to; +use css_inline::blocking::inline_to; use std::fs; #[derive(serde::Deserialize, Debug)] diff --git a/css-inline/src/error.rs b/css-inline/src/error.rs index 5f21deb3..1d7ce97c 100644 --- a/css-inline/src/error.rs +++ b/css-inline/src/error.rs @@ -20,7 +20,7 @@ pub enum InlineError { /// from the filesystem. IO(io::Error), /// Network-related problem. E.g. resource is not available. - #[cfg(feature = "http")] + #[cfg(any(feature = "http", feature = "http-blocking"))] Network { /// Original network error. error: reqwest::Error, @@ -41,7 +41,7 @@ impl Error for InlineError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { InlineError::IO(error) => Some(error), - #[cfg(feature = "http")] + #[cfg(any(feature = "http", feature = "http-blocking"))] InlineError::Network { error, .. } => Some(error), InlineError::MissingStyleSheet { .. } | InlineError::ParseError(_) => None, } @@ -52,7 +52,7 @@ impl Display for InlineError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { Self::IO(error) => error.fmt(f), - #[cfg(feature = "http")] + #[cfg(any(feature = "http", feature = "http-blocking"))] Self::Network { error, location } => f.write_fmt(format_args!("{error}: {location}")), Self::ParseError(error) => f.write_str(error), Self::MissingStyleSheet { path } => { diff --git a/css-inline/src/lib.rs b/css-inline/src/lib.rs index 04aeeb63..56d4d253 100644 --- a/css-inline/src/lib.rs +++ b/css-inline/src/lib.rs @@ -34,8 +34,7 @@ mod parser; mod resolver; pub use error::InlineError; -use indexmap::IndexMap; -use std::{borrow::Cow, fmt::Formatter, hash::BuildHasherDefault, io::Write, sync::Arc}; +use std::{hash::BuildHasherDefault, io::Write}; use crate::html::ElementStyleMap; use hasher::BuildNoHashHasher; @@ -43,214 +42,32 @@ use html::Document; pub use resolver::{DefaultStylesheetResolver, StylesheetResolver}; pub use url::{ParseError, Url}; -/// Configuration options for CSS inlining process. -#[allow(clippy::struct_excessive_bools)] -pub struct InlineOptions<'a> { - /// Whether to inline CSS from "style" tags. - /// - /// Sometimes HTML may include a lot of boilerplate styles, that are not applicable in every - /// scenario and it is useful to ignore them and use `extra_css` instead. - pub inline_style_tags: bool, - /// Keep "style" tags after inlining. - pub keep_style_tags: bool, - /// Keep "link" tags after inlining. - pub keep_link_tags: bool, - /// Used for loading external stylesheets via relative URLs. - pub base_url: Option, - /// Whether remote stylesheets should be loaded or not. - pub load_remote_stylesheets: bool, - // The point of using `Cow` here is Python bindings, where it is problematic to pass a reference - // without dealing with memory leaks & unsafe. With `Cow` we can use moved values as `String` in - // Python wrapper for `CSSInliner` and `&str` in Rust & simple functions on the Python side - /// Additional CSS to inline. - pub extra_css: Option>, - /// Pre-allocate capacity for HTML nodes during parsing. - /// It can improve performance when you have an estimate of the number of nodes in your HTML document. - pub preallocate_node_capacity: usize, - /// A way to resolve stylesheets from various sources. - pub resolver: Arc, -} - -impl<'a> std::fmt::Debug for InlineOptions<'a> { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("InlineOptions") - .field("inline_style_tags", &self.inline_style_tags) - .field("keep_style_tags", &self.keep_style_tags) - .field("keep_link_tags", &self.keep_link_tags) - .field("base_url", &self.base_url) - .field("load_remote_stylesheets", &self.load_remote_stylesheets) - .field("extra_css", &self.extra_css) - .field("preallocate_node_capacity", &self.preallocate_node_capacity) - .finish_non_exhaustive() - } -} - -impl<'a> InlineOptions<'a> { - /// Override whether "style" tags should be inlined. - #[must_use] - pub fn inline_style_tags(mut self, inline_style_tags: bool) -> Self { - self.inline_style_tags = inline_style_tags; - self - } - - /// Override whether "style" tags should be kept after processing. - #[must_use] - pub fn keep_style_tags(mut self, keep_style_tags: bool) -> Self { - self.keep_style_tags = keep_style_tags; - self - } - - /// Override whether "link" tags should be kept after processing. - #[must_use] - pub fn keep_link_tags(mut self, keep_link_tags: bool) -> Self { - self.keep_link_tags = keep_link_tags; - self - } - - /// Set base URL that will be used for loading external stylesheets via relative URLs. - #[must_use] - pub fn base_url(mut self, base_url: Option) -> Self { - self.base_url = base_url; - self - } - - /// Override whether remote stylesheets should be loaded. - #[must_use] - pub fn load_remote_stylesheets(mut self, load_remote_stylesheets: bool) -> Self { - self.load_remote_stylesheets = load_remote_stylesheets; - self - } - - /// Set additional CSS to inline. - #[must_use] - pub fn extra_css(mut self, extra_css: Option>) -> Self { - self.extra_css = extra_css; - self - } - - /// Set the initial node capacity for HTML tree. - #[must_use] - pub fn preallocate_node_capacity(mut self, preallocate_node_capacity: usize) -> Self { - self.preallocate_node_capacity = preallocate_node_capacity; - self - } - - /// Set the way to resolve stylesheets from various sources. - #[must_use] - pub fn resolver(mut self, resolver: Arc) -> Self { - self.resolver = resolver; - self - } - - /// Create a new `CSSInliner` instance from this options. - #[must_use] - pub const fn build(self) -> CSSInliner<'a> { - CSSInliner::new(self) - } -} - -impl Default for InlineOptions<'_> { - #[inline] - fn default() -> Self { - InlineOptions { - inline_style_tags: true, - keep_style_tags: false, - keep_link_tags: false, - base_url: None, - load_remote_stylesheets: true, - extra_css: None, - preallocate_node_capacity: 32, - resolver: Arc::new(DefaultStylesheetResolver), - } - } -} - /// A specialized `Result` type for CSS inlining operations. pub type Result = std::result::Result; -/// Customizable CSS inliner. -#[derive(Debug)] -pub struct CSSInliner<'a> { - options: InlineOptions<'a>, -} - const GROWTH_COEFFICIENT: f64 = 1.5; // A rough coefficient to calculate the number of individual declarations based on the total CSS size. const DECLARATION_SIZE_COEFFICIENT: f64 = 30.0; -impl<'a> CSSInliner<'a> { - /// Create a new `CSSInliner` instance with given options. - #[must_use] - #[inline] - pub const fn new(options: InlineOptions<'a>) -> Self { - CSSInliner { options } - } - - /// Return a default `InlineOptions` that can fully configure the CSS inliner. - /// - /// # Examples - /// - /// Get default `InlineOptions`, then change base url - /// - /// ```rust - /// use css_inline::{CSSInliner, Url}; - /// # use url::ParseError; - /// # fn run() -> Result<(), ParseError> { - /// let url = Url::parse("https://api.example.com")?; - /// let inliner = CSSInliner::options() - /// .base_url(Some(url)) - /// .build(); - /// # Ok(()) - /// # } - /// # run().unwrap(); - /// ``` - #[must_use] - #[inline] - pub fn options() -> InlineOptions<'a> { - InlineOptions::default() - } - - /// Inline CSS styles from

Hello world!

" ) } -#[test] -fn do_not_process_style_tag_and_remove() { +#[tokio::test] +async fn do_not_process_style_tag_and_remove() { let html = html!("h1 {background-color: blue;}", "

Hello world!

"); let options = InlineOptions { keep_style_tags: false, @@ -436,15 +436,15 @@ fn do_not_process_style_tag_and_remove() { ..Default::default() }; let inliner = CSSInliner::new(options); - let result = inliner.inline(&html).unwrap(); + let result = inliner.inline(&html).await.unwrap(); assert_eq!( result, "

Hello world!

" ) } -#[test] -fn empty_style() { +#[tokio::test] +async fn empty_style() { // When the style tag is empty assert_inlined!( style = "", @@ -454,8 +454,8 @@ fn empty_style() { ) } -#[test] -fn media_query_ignore() { +#[tokio::test] +async fn media_query_ignore() { // When the style value includes @media query assert_inlined!( style = r#"@media screen and (max-width: 992px) { @@ -471,25 +471,26 @@ fn media_query_ignore() { #[test_case("@wrong { color: --- }", "Invalid @ rule: wrong")] #[test_case("ttt { 123 }", "Unexpected token: CurlyBracketBlock")] #[test_case("----", "End of input")] -fn invalid_rule(style: &str, expected: &str) { +#[tokio::test] +async fn invalid_rule(style: &str, expected: &str) { let html = html!( "h1 {background-color: blue;}", format!(r#"

Hello world!

"#, style) ); - let result = inline(&html); + let result = inline(&html).await; assert!(result.is_err()); assert_eq!(result.unwrap_err().to_string(), expected); } -#[test] -fn remove_style_tag() { +#[tokio::test] +async fn remove_style_tag() { let html = html!("h1 {background-color: blue;}", "

Hello world!

"); - let result = inline(&html).unwrap(); + let result = inline(&html).await.unwrap(); assert_eq!(result, "

Hello world!

") } -#[test] -fn remove_multiple_style_tags() { +#[tokio::test] +async fn remove_multiple_style_tags() { let html = r#" @@ -513,7 +514,7 @@ a { "#; - let result = inline(html).unwrap(); + let result = inline(html).await.unwrap(); assert_eq!( result, r#" @@ -529,8 +530,8 @@ a { ) } -#[test] -fn extra_css() { +#[tokio::test] +async fn extra_css() { let html = html!("h1 {background-color: blue;}", "

Hello world!

"); let options = InlineOptions { inline_style_tags: false, @@ -538,15 +539,15 @@ fn extra_css() { ..Default::default() }; let inliner = CSSInliner::new(options); - let result = inliner.inline(&html).unwrap(); + let result = inliner.inline(&html).await.unwrap(); assert_eq!( result, "

Hello world!

" ) } -#[test] -fn remote_file_stylesheet() { +#[tokio::test] +async fn remote_file_stylesheet() { let html = r#" @@ -561,7 +562,7 @@ h2 { color: red; }

Smaller Text

"#; - let inlined = inline(html); + let inlined = inline(html).await; assert_file( inlined, r#" @@ -572,8 +573,8 @@ h2 { color: red; } ); } -#[test] -fn missing_stylesheet() { +#[tokio::test] +async fn missing_stylesheet() { let html = r#" @@ -583,7 +584,7 @@ fn missing_stylesheet() {

Big Text

"#; - let inlined = inline(html); + let inlined = inline(html).await; #[cfg(feature = "file")] { assert_eq!( @@ -597,8 +598,8 @@ fn missing_stylesheet() { } } -#[test] -fn remote_file_stylesheet_disable() { +#[tokio::test] +async fn remote_file_stylesheet_disable() { let html = r#" @@ -613,7 +614,7 @@ h2 { color: red; }

Smaller Text

"#; - let inlined = inline(html); + let inlined = inline(html).await; assert_file( inlined, r#" @@ -624,8 +625,8 @@ h2 { color: red; } ); } -#[test] -fn remote_network_stylesheet() { +#[tokio::test] +async fn remote_network_stylesheet() { let html = r#" @@ -640,7 +641,7 @@ h2 { color: red; }

Smaller Text

"#; - let inlined = inline(html); + let inlined = inline(html).await; assert_http( inlined, r#" @@ -651,8 +652,8 @@ h2 { color: red; } ); } -#[test] -fn remote_network_stylesheet_invalid_url() { +#[tokio::test] +async fn remote_network_stylesheet_invalid_url() { let html = r#" @@ -661,7 +662,7 @@ fn remote_network_stylesheet_invalid_url() { "#; - let error = inline(html).expect_err("Should fail"); + let error = inline(html).await.expect_err("Should fail"); #[cfg(feature = "http")] let expected = "builder error: empty host: http:"; #[cfg(not(feature = "http"))] @@ -669,8 +670,8 @@ fn remote_network_stylesheet_invalid_url() { assert_eq!(error.to_string(), expected); } -#[test] -fn remote_network_stylesheet_same_scheme() { +#[tokio::test] +async fn remote_network_stylesheet_same_scheme() { let html = r#" @@ -688,7 +689,7 @@ h2 { color: red; } let inliner = CSSInliner::options() .base_url(Some(Url::parse("http://127.0.0.1:1234").unwrap())) .build(); - let inlined = inliner.inline(html); + let inlined = inliner.inline(html).await; assert_http( inlined, r#" @@ -699,8 +700,8 @@ h2 { color: red; } ); } -#[test] -fn remote_network_relative_stylesheet() { +#[tokio::test] +async fn remote_network_relative_stylesheet() { let html = r#" @@ -718,7 +719,7 @@ h2 { color: red; } let inliner = CSSInliner::options() .base_url(Some(Url::parse("http://127.0.0.1:1234").unwrap())) .build(); - let inlined = inliner.inline(html); + let inlined = inliner.inline(html).await; assert_http( inlined, r#" @@ -729,8 +730,8 @@ h2 { color: red; } ); } -#[test] -fn file_scheme() { +#[tokio::test] +async fn file_scheme() { let html = r#" @@ -750,7 +751,7 @@ h2 { color: red; } ..Default::default() }; let inliner = CSSInliner::new(options); - let inlined = inliner.inline(html); + let inlined = inliner.inline(html).await; assert_file( inlined, r#" @@ -761,8 +762,8 @@ h2 { color: red; } ); } -#[test] -fn customize_inliner() { +#[tokio::test] +async fn customize_inliner() { let options = InlineOptions { load_remote_stylesheets: false, ..Default::default() @@ -774,8 +775,8 @@ fn customize_inliner() { assert_eq!(options.preallocate_node_capacity, 25); } -#[test] -fn use_builder() { +#[tokio::test] +async fn use_builder() { let url = Url::parse("https://api.example.com").unwrap(); let _ = CSSInliner::options() .keep_style_tags(true) @@ -785,19 +786,19 @@ fn use_builder() { .build(); } -#[test] -fn inline_to() { +#[tokio::test] +async fn inline_to() { let html = html!("h1 { color: blue }", r#"

Big Text

"#); let mut out = Vec::new(); - css_inline::inline_to(&html, &mut out).unwrap(); + css_inline::inline_to(&html, &mut out).await.unwrap(); assert_eq!( String::from_utf8_lossy(&out), "

Big Text

" ) } -#[test] -fn keep_style_tags() { +#[tokio::test] +async fn keep_style_tags() { let inliner = CSSInliner::options().keep_style_tags(true).build(); let html = r#" @@ -810,12 +811,12 @@ h2 { color: red; }

"#; - let inlined = inliner.inline(html).unwrap(); + let inlined = inliner.inline(html).await.unwrap(); assert_eq!(inlined, "\n\n\n\n

\n\n"); } -#[test] -fn keep_link_tags() { +#[tokio::test] +async fn keep_link_tags() { let inliner = CSSInliner::options() .base_url(Some(Url::parse("http://127.0.0.1:1234").unwrap())) .keep_link_tags(true) @@ -829,7 +830,7 @@ fn keep_link_tags() {

"#; - let inlined = inliner.inline(html); + let inlined = inliner.inline(html).await; assert_http( inlined, "\n\n\n\n

\n\n", diff --git a/css-inline/tests/test_selectors.rs b/css-inline/tests/test_selectors.rs index 75cd2f2d..f3e144ca 100644 --- a/css-inline/tests/test_selectors.rs +++ b/css-inline/tests/test_selectors.rs @@ -2,8 +2,8 @@ mod utils; // Most of the following tests are ported to Rust from https://github.com/rennat/pynliner -#[test] -fn identical_element() { +#[tokio::test] +async fn identical_element() { assert_inlined!( style = r#" .text-right { @@ -18,8 +18,8 @@ fn identical_element() { ) } -#[test] -fn is_or_prefixed_by() { +#[tokio::test] +async fn is_or_prefixed_by() { assert_inlined!( style = r#"[data-type|="thing"] {color: red;}"#, body = r#"1"#, @@ -32,8 +32,8 @@ fn is_or_prefixed_by() { ) } -#[test] -fn contains() { +#[tokio::test] +async fn contains() { assert_inlined!( style = r#"[data-type*="i"] {color: red;}"#, body = r#"1"#, @@ -41,8 +41,8 @@ fn contains() { ) } -#[test] -fn has_class_unicode() { +#[tokio::test] +async fn has_class_unicode() { assert_inlined!( style = r#".тест {color: red}"#, body = r#"1"#, @@ -50,8 +50,8 @@ fn has_class_unicode() { ) } -#[test] -fn has_class_short() { +#[tokio::test] +async fn has_class_short() { assert_inlined!( style = r#".t {color: red}"#, body = r#"1"#, @@ -59,8 +59,8 @@ fn has_class_short() { ) } -#[test] -fn has_class_multiple() { +#[tokio::test] +async fn has_class_multiple() { assert_inlined!( style = r#".t {color: red}"#, body = r#"1"#, @@ -68,24 +68,24 @@ fn has_class_multiple() { ) } -#[test] -fn ends_with() { +#[tokio::test] +async fn ends_with() { assert_inlined!( style = r#"[data-type$="ng"] {color: red;}"#, body = r#"1"#, expected = r#"1"# ) } -#[test] -fn starts_with() { +#[tokio::test] +async fn starts_with() { assert_inlined!( style = r#"[data-type^="th"] {color: red;}"#, body = r#"1"#, expected = r#"1"# ) } -#[test] -fn one_of() { +#[tokio::test] +async fn one_of() { assert_inlined!( style = r#"[data-type~="thing1"] {color: red;}"#, body = r#"1"#, @@ -97,8 +97,8 @@ fn one_of() { expected = r#"1"# ) } -#[test] -fn equals() { +#[tokio::test] +async fn equals() { assert_inlined!( style = r#"[data-type="thing"] {color: red;}"#, body = r#"1"#, @@ -110,16 +110,16 @@ fn equals() { expected = r#"1"# ) } -#[test] -fn exists() { +#[tokio::test] +async fn exists() { assert_inlined!( style = r#"[data-type] {color: red;}"#, body = r#"1"#, expected = r#"1"# ) } -#[test] -fn specificity() { +#[tokio::test] +async fn specificity() { assert_inlined!( style = r#"div,a,b,c,d,e,f,g,h,i,j { color: red; } .foo { color: blue; }"#, body = r#"
"#, @@ -127,88 +127,88 @@ fn specificity() { ) } -#[test] -fn first_child_descendant_selector_complex_dom() { +#[tokio::test] +async fn first_child_descendant_selector_complex_dom() { assert_inlined!( style = r#"h1 :first-child { color: red; }"#, body = r#"

Hello World!

foo

bazbar

"#, expected = r#"

Hello World!

foo

bazbar

"# ) } -#[test] -fn last_child_descendant_selector() { +#[tokio::test] +async fn last_child_descendant_selector() { assert_inlined!( style = r#"h1 :last-child { color: red; }"#, body = r#"

Hello World!

"#, expected = r#"

Hello World!

"# ) } -#[test] -fn first_child_descendant_selector() { +#[tokio::test] +async fn first_child_descendant_selector() { assert_inlined!( style = r#"h1 :first-child { color: red; }"#, body = r#"

Hello World!

"#, expected = r#"

Hello World!

"# ) } -#[test] -fn child_with_first_child_and_unmatched_class_selector_complex_dom() { +#[tokio::test] +async fn child_with_first_child_and_unmatched_class_selector_complex_dom() { assert_inlined!( style = r#"h1 > .hello:first-child { color: green; }"#, body = r#"

Hello World!

foo

bazbar

"#, expected = r#"

Hello World!

foo

bazbar

"# ) } -#[test] -fn child_with_first_child_and_class_selector_complex_dom() { +#[tokio::test] +async fn child_with_first_child_and_class_selector_complex_dom() { assert_inlined!( style = r#"h1 > .hello:first-child { color: green; }"#, body = r#"

Hello World!

foo

bazbar

"#, expected = r#"

Hello World!

foo

bazbar

"# ) } -#[test] -fn nested_child_with_first_child_override_selector_complex_dom() { +#[tokio::test] +async fn nested_child_with_first_child_override_selector_complex_dom() { assert_inlined!( style = r#"div > div > * { color: green; } div > div > :first-child { color: red; }"#, body = r#"
Hello World!

foo

bazbar
"#, expected = r#"
Hello World!

foo

bazbar
"# ) } -#[test] -fn child_with_first_and_last_child_override_selector() { +#[tokio::test] +async fn child_with_first_and_last_child_override_selector() { assert_inlined!( style = r#"p > * { color: green; } p > :first-child:last-child { color: red; }"#, body = r#"

Hello World!

"#, expected = r#"

Hello World!

"# ) } -#[test] -fn id_el_child_with_first_child_override_selector_complex_dom() { +#[tokio::test] +async fn id_el_child_with_first_child_override_selector_complex_dom() { assert_inlined!( style = r#"#abc > * { color: green; } #abc > :first-child { color: red; }"#, body = r#"
Hello World!

foo

bazbar
"#, expected = r#"
Hello World!

foo

bazbar
"# ) } -#[test] -fn child_with_first_child_override_selector_complex_dom() { +#[tokio::test] +async fn child_with_first_child_override_selector_complex_dom() { assert_inlined!( style = r#"div > * { color: green; } div > :first-child { color: red; }"#, body = r#"
Hello World!

foo

bazbar
"#, expected = r#"
Hello World!

foo

bazbar
"# ) } -#[test] -fn child_follow_by_last_child_selector_complex_dom() { +#[tokio::test] +async fn child_follow_by_last_child_selector_complex_dom() { assert_inlined!( style = r#"h1 > :last-child { color: red; }"#, body = r#"

Hello World!

foo

bazbar

"#, expected = r#"

Hello World!

foo

bazbar

"# ) } -#[test] -fn parent_pseudo_selector() { +#[tokio::test] +async fn parent_pseudo_selector() { assert_inlined!( style = r#"span:last-child span { color: red; }"#, body = r#"

Hello World!

"#, @@ -225,8 +225,8 @@ fn parent_pseudo_selector() { expected = r#"

Hello World!nope

"# ) } -#[test] -fn multiple_pseudo_selectors() { +#[tokio::test] +async fn multiple_pseudo_selectors() { assert_inlined!( style = r#"span:first-child:last-child { color: red; }"#, body = r#"

Hello World!

"#, @@ -238,32 +238,32 @@ fn multiple_pseudo_selectors() { expected = r#"

Hello World!again!

"# ) } -#[test] -fn last_child_selector() { +#[tokio::test] +async fn last_child_selector() { assert_inlined!( style = r#"h1 > :last-child { color: red; }"#, body = r#"

Hello World!

"#, expected = r#"

Hello World!

"# ) } -#[test] -fn child_follow_by_first_child_selector_complex_dom() { +#[tokio::test] +async fn child_follow_by_first_child_selector_complex_dom() { assert_inlined!( style = r#"h1 > :first-child { color: red; }"#, body = r#"

Hello World!

foo

bazbar

"#, expected = r#"

Hello World!

foo

bazbar

"# ) } -#[test] -fn child_follow_by_first_child_selector_with_comments() { +#[tokio::test] +async fn child_follow_by_first_child_selector_with_comments() { assert_inlined!( style = r#"h1 > :first-child { color: red; }"#, body = r#"

Hello World!

foo

bazbar

"#, expected = r#"

Hello World!

foo

bazbar

"# ) } -#[test] -fn child_follow_by_first_child_selector_with_white_spaces() { +#[tokio::test] +async fn child_follow_by_first_child_selector_with_white_spaces() { assert_inlined!( style = r#"h1 > :first-child { color: red; }"#, body = r#"

Hello World!

foo

bazbar

"#, @@ -271,8 +271,8 @@ fn child_follow_by_first_child_selector_with_white_spaces() { ) } -#[test] -fn child_follow_by_adjacent_selector_complex_dom() { +#[tokio::test] +async fn child_follow_by_adjacent_selector_complex_dom() { assert_inlined!( style = r#"h1 > span + p { color: red; }"#, body = r#"

Hello World!

foo

bazbar

"#, @@ -280,88 +280,88 @@ fn child_follow_by_adjacent_selector_complex_dom() { ) } -#[test] -fn unknown_pseudo_selector() { +#[tokio::test] +async fn unknown_pseudo_selector() { assert_inlined!( style = r#"h1 > span:css4-selector { color: red; }"#, body = r#"

Hello World!

foo

bazbar

"#, expected = r#"

Hello World!

foo

bazbar

"# ) } -#[test] -fn adjacent_selector() { +#[tokio::test] +async fn adjacent_selector() { assert_inlined!( style = r#"h1 + h2 { color: red; }"#, body = r#"

Hello World!

How are you?

"#, expected = r#"

Hello World!

How are you?

"# ) } -#[test] -fn child_all_selector_complex_dom() { +#[tokio::test] +async fn child_all_selector_complex_dom() { assert_inlined!( style = r#"h1 > * { color: red; }"#, body = r#"

Hello World!

foo

bazbar

"#, expected = r#"

Hello World!

foo

bazbar

"# ) } -#[test] -fn child_selector_complex_dom() { +#[tokio::test] +async fn child_selector_complex_dom() { assert_inlined!( style = r#"h1 > span { color: red; }"#, body = r#"

Hello World!

foo

bazbar

"#, expected = r#"

Hello World!

foo

bazbar

"# ) } -#[test] -fn nested_child_selector() { +#[tokio::test] +async fn nested_child_selector() { assert_inlined!( style = r#"div > h1 > span { color: red; }""#, body = r#"

Hello World!

"#, expected = r#"

Hello World!

"# ) } -#[test] -fn child_selector() { +#[tokio::test] +async fn child_selector() { assert_inlined!( style = r#"h1 > span { color: red; }"#, body = r#"

Hello World!

"#, expected = r#"

Hello World!

"# ) } -#[test] -fn descendant_selector() { +#[tokio::test] +async fn descendant_selector() { assert_inlined!( style = r#"h1 span { color: red; }"#, body = r#"

Hello World!

"#, expected = r#"

Hello World!

"# ) } -#[test] -fn combination_selector() { +#[tokio::test] +async fn combination_selector() { assert_inlined!( style = r#"h1#a.b { color: red; }"#, body = r#"

Hello World!

"#, expected = r#"

Hello World!

"# ) } -#[test] -fn conflicting_multiple_class_selector() { +#[tokio::test] +async fn conflicting_multiple_class_selector() { assert_inlined!( style = r#"h1.a.b { color: red; }"#, body = r#"

Hello World!

I should not be changed

"#, expected = r#"

Hello World!

I should not be changed

"# ) } -#[test] -fn multiple_class_selector() { +#[tokio::test] +async fn multiple_class_selector() { assert_inlined!( style = r#"h1.a.b { color: red; }"#, body = r#"

Hello World!

"#, expected = r#"

Hello World!

"# ) } -#[test] -fn missing_link_descendant_selector() { +#[tokio::test] +async fn missing_link_descendant_selector() { assert_inlined!( style = r#"#a b i { color: red }"#, body = r#"
x
"#, @@ -369,8 +369,8 @@ fn missing_link_descendant_selector() { ) } -#[test] -fn comma_specificity() { +#[tokio::test] +async fn comma_specificity() { assert_inlined!( style = r#"i, i { color: red; } i { color: blue; }"#, body = r#"howdy"#, @@ -378,8 +378,8 @@ fn comma_specificity() { ) } -#[test] -fn overwrite_comma() { +#[tokio::test] +async fn overwrite_comma() { assert_inlined!( style = r#"h1,h2,h3 {color: #000;}"#, body = r#"

Foo

Foo

"#, diff --git a/css-inline/tests/utils.rs b/css-inline/tests/utils.rs index bebc768b..4fa4358e 100644 --- a/css-inline/tests/utils.rs +++ b/css-inline/tests/utils.rs @@ -15,7 +15,7 @@ macro_rules! html { macro_rules! assert_inlined { (style = $style: expr, body = $body: expr, expected = $expected: expr) => {{ let html = html!($style, $body); - let inlined = css_inline::inline(&html).unwrap(); + let inlined = css_inline::inline(&html).await.unwrap(); assert_eq!(inlined, html!($expected)) }}; } diff --git a/profiler/src/main.rs b/profiler/src/main.rs index 79eac16b..c709dacc 100644 --- a/profiler/src/main.rs +++ b/profiler/src/main.rs @@ -35,7 +35,7 @@ fn main() -> Result<(), Box> { for _ in 0..args.iterations { #[cfg(feature = "dhat-heap")] let _profiler = dhat::Profiler::new_heap(); - css_inline::inline_to(&benchmark.html, &mut output).expect("Inlining failed"); + css_inline::blocking::inline_to(&benchmark.html, &mut output).expect("Inlining failed"); output.clear(); } } else {