diff --git a/Cargo.lock b/Cargo.lock index 83de9e1cd..bc37231e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,6 +103,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base-x" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" + [[package]] name = "base64" version = "0.13.0" @@ -231,10 +237,16 @@ dependencies = [ "libc", "num-integer", "num-traits", - "time", + "time 0.1.44", "winapi 0.3.9", ] +[[package]] +name = "chunked_transfer" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7477065d45a8fe57167bf3cf8bcd3729b54cfcb81cca49bda2d038ea89ae82ca" + [[package]] name = "clap" version = "2.33.3" @@ -327,6 +339,33 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826" +[[package]] +name = "cookie" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f" +dependencies = [ + "percent-encoding", + "time 0.2.23", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3818dfca4b0cb5211a659bbcbb94225b7127407b2b135e650d717bfb78ab10d3" +dependencies = [ + "cookie", + "idna", + "log", + "publicsuffix", + "serde", + "serde_json", + "time 0.2.23", + "url", +] + [[package]] name = "core-foundation" version = "0.7.0" @@ -599,6 +638,12 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + [[package]] name = "dispatch" version = "0.2.0" @@ -671,6 +716,7 @@ dependencies = [ "glium", "serde", "serde_json", + "ureq", "webbrowser", ] @@ -703,18 +749,24 @@ dependencies = [ "serde_json", ] +[[package]] +name = "error-chain" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc" +dependencies = [ + "version_check", +] + [[package]] name = "example_web" version = "0.1.0" dependencies = [ "eframe", - "egui_web", "image", - "js-sys", "serde", "serde_json", "syntect", - "wasm-bindgen", ] [[package]] @@ -760,6 +812,16 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + [[package]] name = "fuchsia-zircon" version = "0.3.3" @@ -933,6 +995,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "image" version = "0.23.12" @@ -1102,6 +1175,12 @@ dependencies = [ "libc", ] +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + [[package]] name = "maybe-uninit" version = "2.0.0" @@ -1494,6 +1573,12 @@ dependencies = [ "toml", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + [[package]] name = "proc-macro2" version = "1.0.24" @@ -1503,6 +1588,28 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "publicsuffix" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bbaa49075179162b49acac1c6aa45fb4dafb5f13cf6794276d77bc7fd95757b" +dependencies = [ + "error-chain", + "idna", + "lazy_static", + "regex", + "url", +] + +[[package]] +name = "qstring" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" +dependencies = [ + "percent-encoding", +] + [[package]] name = "quote" version = "1.0.8" @@ -1589,6 +1696,21 @@ version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" +[[package]] +name = "ring" +version = "0.16.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi 0.3.9", +] + [[package]] name = "rustc-demangle" version = "0.1.18" @@ -1604,6 +1726,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustls" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b" +dependencies = [ + "base64", + "log", + "ring", + "sct", + "webpki", +] + [[package]] name = "rusttype" version = "0.9.2" @@ -1647,6 +1782,16 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "sct" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "semver" version = "0.9.0" @@ -1703,6 +1848,12 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + [[package]] name = "shared_library" version = "0.1.9" @@ -1745,6 +1896,70 @@ dependencies = [ "wayland-protocols", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "standback" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf906c8b8fc3f6ecd1046e01da1d8ddec83e48c8b08b84dcc02b585a6bedf5a8" +dependencies = [ + "version_check", +] + +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + [[package]] name = "strsim" version = "0.9.3" @@ -1839,6 +2054,44 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "time" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcdaeea317915d59b2b4cd3b5efcd156c309108664277793f5351700c02ce98b" +dependencies = [ + "const_fn", + "libc", + "standback", + "stdweb", + "time-macros", + "version_check", + "winapi 0.3.9", +] + +[[package]] +name = "time-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" +dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5c3be1edfad6027c69f5491cf4cb310d1a71ecd6af742788c6ff8bced86b8fa" +dependencies = [ + "proc-macro-hack", + "proc-macro2", + "quote", + "standback", + "syn", +] + [[package]] name = "tinytemplate" version = "1.1.0" @@ -1849,6 +2102,21 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tinyvec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + [[package]] name = "toml" version = "0.5.8" @@ -1864,6 +2132,24 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.8" @@ -1876,6 +2162,43 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "ureq" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "294b85ef5dbc3670a72e82a89971608a1fcc4ed5c7c5a2895230d31a95f0569b" +dependencies = [ + "base64", + "chunked_transfer", + "cookie", + "cookie_store", + "log", + "once_cell", + "qstring", + "rustls", + "url", + "webpki", + "webpki-roots", +] + +[[package]] +name = "url" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + [[package]] name = "version_check" version = "0.9.2" @@ -2075,6 +2398,25 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82015b7e0b8bad8185994674a13a93306bea76cf5a16c5a181382fd3a5ec2376" +dependencies = [ + "webpki", +] + [[package]] name = "widestring" version = "0.4.3" diff --git a/eframe/Cargo.toml b/eframe/Cargo.toml index b95bf89c2..7b3462caf 100644 --- a/eframe/Cargo.toml +++ b/eframe/Cargo.toml @@ -20,7 +20,7 @@ epi = { version = "0.6.0", path = "../epi", features = ["serde", "serde_json"] } # For compiling natively: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -egui_glium = { path = "../egui_glium" } +egui_glium = { path = "../egui_glium", features = ["http"] } # For compiling to web: [target.'cfg(target_arch = "wasm32")'.dependencies] diff --git a/eframe/src/lib.rs b/eframe/src/lib.rs index 63752f097..cc84fd85a 100644 --- a/eframe/src/lib.rs +++ b/eframe/src/lib.rs @@ -52,3 +52,34 @@ pub fn start_web(canvas_id: &str, app: Box) -> Result<(), wasm_bin pub fn run_native(app: Box) { egui_glium::run(app) } + +// ---------------------------------------------------------------------------- + +pub mod http { + pub use epi::http::*; + + /// Do a HTTP request and call the callback when done. + pub fn fetch( + request: Request, + on_done: impl 'static + Send + FnOnce(Result), + ) { + fetch_dyn(request, Box::new(on_done)) + } + + fn fetch_dyn(request: Request, on_done: Box) + Send>) { + #[cfg(target_arch = "wasm32")] + { + egui_web::spawn_future(async move { + let result = egui_web::http::fetch_async(&request).await; + on_done(result) + }); + } + #[cfg(not(target_arch = "wasm32"))] + { + std::thread::spawn(move || { + let result = egui_glium::http::fetch_blocking(&request); + on_done(result) + }); + } + } +} diff --git a/egui_glium/Cargo.toml b/egui_glium/Cargo.toml index 38b0684db..fc787705d 100644 --- a/egui_glium/Cargo.toml +++ b/egui_glium/Cargo.toml @@ -21,4 +21,9 @@ epi = { version = "0.6.0", path = "../epi", features = ["serde", "serde_json"] } glium = "0.29" serde = "1" serde_json = "1" +ureq = { version = "1.5", optional = true } webbrowser = "0.5" + +[features] +default = [] +http = ["ureq"] diff --git a/egui_glium/src/http.rs b/egui_glium/src/http.rs new file mode 100644 index 000000000..9752d5f70 --- /dev/null +++ b/egui_glium/src/http.rs @@ -0,0 +1,44 @@ +pub use epi::http::{Request, Response}; + +/// NOTE: Ok(..) is returned on network error. +/// Err is only for failure to use the fetch api. +pub fn fetch_blocking(request: &Request) -> Result { + let Request { method, url } = request; + + let resp = ureq::request(method, url).set("Accept", "*/*").call(); + if let Some(error) = resp.synthetic_error() { + return Err(error.to_string()); + } + + let url = resp.get_url().to_owned(); + let ok = resp.ok(); + let status = resp.status(); + let status_text = resp.status_text().to_owned(); + let header_content_type = resp.header("Content-Type").unwrap_or_default().to_owned(); + + let mut reader = resp.into_reader(); + let mut bytes = vec![]; + use std::io::Read; + reader + .read_to_end(&mut bytes) + .map_err(|err| err.to_string())?; + + let text = if header_content_type.starts_with("text") + || header_content_type == "application/javascript" + { + String::from_utf8(bytes.clone()).ok() + } else { + None + }; + + let response = Response { + url, + ok, + status, + status_text, + header_content_type, + bytes, + text, + }; + Ok(response) +} diff --git a/egui_glium/src/lib.rs b/egui_glium/src/lib.rs index cb8af676f..ca69532d0 100644 --- a/egui_glium/src/lib.rs +++ b/egui_glium/src/lib.rs @@ -4,6 +4,8 @@ #![allow(clippy::single_match)] mod backend; +#[cfg(feature = "http")] +pub mod http; mod painter; pub mod storage; diff --git a/egui_web/src/fetch.rs b/egui_web/src/http.rs similarity index 70% rename from egui_web/src/fetch.rs rename to egui_web/src/http.rs index 2debb7ea5..b81c7b712 100644 --- a/egui_web/src/fetch.rs +++ b/egui_web/src/http.rs @@ -1,39 +1,20 @@ use wasm_bindgen::prelude::*; -pub struct Response { - pub url: String, - pub ok: bool, - pub status: u16, - pub status_text: String, - - /// Content-Type header, or empty string if missing - pub header_content_type: String, - - /// The raw bytes - pub bytes: Vec, - - /// UTF-8 decoded version of bytes. - /// ONLY if `header_content_type` starts with "text" and bytes is UTF-8. - pub text: Option, -} +pub use epi::http::{Request, Response}; /// NOTE: Ok(..) is returned on network error. /// Err is only for failure to use the fetch api. -pub async fn fetch(method: &str, url: &str) -> Result { - fetch_jsvalue(method, url) +pub async fn fetch_async(request: &Request) -> Result { + fetch_jsvalue(request) .await .map_err(|err| err.as_string().unwrap_or_default()) } /// NOTE: Ok(..) is returned on network error. /// Err is only for failure to use the fetch api. -pub async fn get(url: &str) -> Result { - fetch("GET", url).await -} +async fn fetch_jsvalue(request: &Request) -> Result { + let Request { method, url } = request; -/// NOTE: Ok(..) is returned on network error. -/// Err is only for failure to use the fetch api. -async fn fetch_jsvalue(method: &str, url: &str) -> Result { // https://rustwasm.github.io/wasm-bindgen/examples/fetch.html use wasm_bindgen::JsCast; diff --git a/egui_web/src/lib.rs b/egui_web/src/lib.rs index e641a5e6e..5e8da8013 100644 --- a/egui_web/src/lib.rs +++ b/egui_web/src/lib.rs @@ -3,7 +3,7 @@ #![warn(clippy::all)] pub mod backend; -pub mod fetch; +pub mod http; pub mod webgl; pub use backend::*; diff --git a/epi/src/lib.rs b/epi/src/lib.rs index 7ff9854cf..63bb832b5 100644 --- a/epi/src/lib.rs +++ b/epi/src/lib.rs @@ -207,3 +207,42 @@ pub fn set_value(storage: &mut dyn Storage, key: &str, valu /// storage key used for app pub const APP_KEY: &str = "app"; + +// ---------------------------------------------------------------------------- + +pub mod http { + pub struct Request { + /// "GET", … + pub method: String, + /// https://… + pub url: String, + } + + impl Request { + pub fn get(url: String) -> Self { + Self { + method: "GET".to_owned(), + url, + } + } + } + + /// Response from an HTTP request for a very simple HTTP fetch API in `eframe`. + pub struct Response { + /// The URL we ended up at. This can differ from the request url when we have followed redirects. + pub url: String, + pub ok: bool, + pub status: u16, + pub status_text: String, + + /// Content-Type header, or empty string if missing. + pub header_content_type: String, + + /// The raw bytes. + pub bytes: Vec, + + /// UTF-8 decoded version of bytes. + /// ONLY if `header_content_type` starts with "text" and bytes is UTF-8. + pub text: Option, + } +} diff --git a/example_web/Cargo.toml b/example_web/Cargo.toml index f468879e6..6d4bc7957 100644 --- a/example_web/Cargo.toml +++ b/example_web/Cargo.toml @@ -10,10 +10,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] eframe = { path = "../eframe" } -egui_web = { path = "../egui_web" } image = { version = "0.23", default_features = false, features = ["jpeg", "png"] } -js-sys = "0.3" serde = { version = "1", features = ["derive"] } serde_json = "1" syntect = { version = "4", default_features = false, features = ["default-fancy"] } -wasm-bindgen = "0.2" diff --git a/example_web/src/example_app.rs b/example_web/src/example_app.rs index 0c616a205..9d8a94fcb 100644 --- a/example_web/src/example_app.rs +++ b/example_web/src/example_app.rs @@ -1,5 +1,5 @@ use eframe::{egui, epi}; -use egui_web::fetch::Response; +use epi::http::Response; use std::sync::mpsc::Receiver; struct Resource { @@ -76,8 +76,9 @@ impl epi::App for ExampleApp { let repaint_signal = integration_context.repaint_signal.clone(); let (sender, receiver) = std::sync::mpsc::channel(); self.in_progress = Some(receiver); - egui_web::spawn_future(async move { - sender.send(egui_web::fetch::get(&url).await).ok(); + + eframe::http::fetch(eframe::http::Request::get(url), move |response| { + sender.send(response).ok(); repaint_signal.request_repaint(); }); } diff --git a/example_web/src/lib.rs b/example_web/src/lib.rs index bd075e4f7..c89d1bc83 100644 --- a/example_web/src/lib.rs +++ b/example_web/src/lib.rs @@ -3,6 +3,7 @@ #![warn(clippy::all)] mod example_app; +pub use example_app::ExampleApp; #[cfg(target_arch = "wasm32")] use eframe::wasm_bindgen::{self, prelude::*}; diff --git a/example_web/src/main.rs b/example_web/src/main.rs new file mode 100644 index 000000000..6dad1db9d --- /dev/null +++ b/example_web/src/main.rs @@ -0,0 +1,9 @@ +#![forbid(unsafe_code)] +#![cfg_attr(not(debug_assertions), deny(warnings))] // Forbid warnings in release builds +#![warn(clippy::all)] + +// When compiling natively: +fn main() { + let app = example_web::ExampleApp::default(); + eframe::run_native(Box::new(app)); +}