diff --git a/.github/workflows/build_static.sh b/.github/workflows/build_static.sh index 9171b536..71d04244 100644 --- a/.github/workflows/build_static.sh +++ b/.github/workflows/build_static.sh @@ -25,7 +25,10 @@ main() { $cargo build -p livesplit --target $TARGET --release $FEATURES ;; wasm32-unknown-unknown) - $cargo build -p cdylib --target $TARGET --release $FEATURES + $cargo build -p cdylib --target $TARGET $release_flag $FEATURES + ;; + wasm32-wasi) + $cargo build -p cdylib --target $TARGET $release_flag $FEATURES ;; *) $cargo build -p staticlib --target $TARGET $release_flag $FEATURES diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 2362cd10..564a4123 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -30,11 +30,13 @@ jobs: - Bare Metal RISC-V 64 gc - Bare Metal RISC-V 64 imac - # Web + # WebAssembly # FIXME: emscripten is broken at the moment. cross' emscripten is too old. # - Web asmjs # - Web wasm32 emscripten - - Web wasm32 + - WebAssembly Unknown + - WebAssembly Web + - WebAssembly WASI # Windows - Windows aarch64 @@ -253,7 +255,7 @@ jobs: no_std: true install_target: true - # Web + # WebAssembly # - label: Web asmjs # target: asmjs-unknown-emscripten # os: ubuntu-latest @@ -269,9 +271,27 @@ jobs: # tests: skip # dylib: skip - - label: Web wasm32 + - label: WebAssembly Unknown target: wasm32-unknown-unknown os: ubuntu-latest + cross: skip + tests: skip + dylib: skip + install_target: true + + - label: WebAssembly Web + target: wasm32-unknown-unknown + os: ubuntu-latest + cross: skip + tests: skip + dylib: skip + install_target: true + features: "--features wasm-web" + + - label: WebAssembly WASI + target: wasm32-wasi + os: ubuntu-latest + cross: skip tests: skip dylib: skip install_target: true diff --git a/Cargo.toml b/Cargo.toml index 3a8f1f0b..155fc7a3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,9 @@ smallvec = { version = "1.0.0", default-features = false, optional = true } euc = { version = "0.4.2", default-features = false, optional = true } vek = { version = "0.9.8", default-features = false, optional = true } +# WebAssembly in the Web +web-sys = { version = "0.3.28", default-features = false, features = ["Performance", "Window"], optional = true } + [dev-dependencies] memmem = "0.1.1" criterion = "0.3.0" @@ -83,6 +86,7 @@ more-image-formats = ["image/webp", "image/pnm", "image/ico", "image/jpeg", "ima image-shrinking = ["more-image-formats"] rendering = ["std", "more-image-formats", "euclid", "livesplit-title-abbreviations", "lyon", "rusttype", "smallvec"] software-rendering = ["rendering", "euc", "vek", "derive_more/mul"] +wasm-web = ["std", "web-sys", "chrono/wasmbind", "livesplit-hotkey/wasm-web"] # FIXME: Some targets don't have atomics, but we can't test for this properly # yet. So there's a feature you explicitly have to opt into to deactivate the diff --git a/capi/Cargo.toml b/capi/Cargo.toml index 9f3759aa..a304ae37 100644 --- a/capi/Cargo.toml +++ b/capi/Cargo.toml @@ -11,3 +11,4 @@ serde_json = "1.0.8" [features] default = ["image-shrinking"] image-shrinking = ["livesplit-core/image-shrinking"] +wasm-web = ["livesplit-core/wasm-web"] diff --git a/capi/cdylib/Cargo.toml b/capi/cdylib/Cargo.toml index 9476c603..325fb2a0 100644 --- a/capi/cdylib/Cargo.toml +++ b/capi/cdylib/Cargo.toml @@ -14,3 +14,4 @@ crate-type = ["cdylib"] [features] default = ["image-shrinking"] image-shrinking = ["livesplit-core-capi/image-shrinking"] +wasm-web = ["livesplit-core-capi/wasm-web"] diff --git a/capi/src/hotkey_config.rs b/capi/src/hotkey_config.rs index 83aceed4..453d9ed0 100644 --- a/capi/src/hotkey_config.rs +++ b/capi/src/hotkey_config.rs @@ -1,7 +1,7 @@ //! The configuration to use for a Hotkey System. It describes with keys to use //! as hotkeys for the different actions. -use super::{output_vec, str, Json, get_file, release_file}; +use super::{get_file, output_vec, release_file, str, Json}; use crate::setting_value::OwnedSettingValue; use livesplit_core::HotkeyConfig; use serde_json; @@ -59,7 +59,7 @@ pub unsafe extern "C" fn HotkeyConfig_parse_json(settings: Json) -> NullableOwne HotkeyConfig::from_json(settings).ok().map(Box::new) } -/// Attempts to parse a hotkey configuration from a given file. is +/// Attempts to parse a hotkey configuration from a given file. is /// returned it couldn't be parsed. This will not close the file descriptor / /// handle. #[no_mangle] diff --git a/capi/src/layout.rs b/capi/src/layout.rs index 435cfe53..3b559b59 100644 --- a/capi/src/layout.rs +++ b/capi/src/layout.rs @@ -1,7 +1,7 @@ //! A Layout allows you to combine multiple components together to visualize a //! variety of information the runner is interested in. -use super::{output_vec, str, Json, get_file, release_file}; +use super::{get_file, output_vec, release_file, str, Json}; use crate::component::OwnedComponent; use livesplit_core::layout::{parser, LayoutSettings}; use livesplit_core::{Layout, Timer}; diff --git a/capi/src/lib.rs b/capi/src/lib.rs index 55d2b3b1..c77b1d06 100644 --- a/capi/src/lib.rs +++ b/capi/src/lib.rs @@ -121,7 +121,7 @@ unsafe fn str(s: *const c_char) -> &'static str { } } -//raw file descriptor handling +// raw file descriptor handling #[cfg(unix)] unsafe fn get_file(fd: i64) -> File { use std::os::unix::io::FromRawFd; @@ -153,3 +153,28 @@ unsafe fn release_file(file: File) { #[cfg(not(any(windows, unix)))] unsafe fn release_file(_: File) {} + +/// Allocate memory. +#[cfg(all( + target_arch = "wasm32", + not(any(target_os = "emscripten", target_os = "wasi", feature = "wasm-web")), +))] +#[no_mangle] +pub extern "C" fn alloc(size: usize) -> *mut u8 { + let mut buf = Vec::with_capacity(size); + let ptr = buf.as_mut_ptr(); + core::mem::forget(buf); + ptr +} + +/// Deallocate memory. +#[cfg(all( + target_arch = "wasm32", + not(any(target_os = "emscripten", target_os = "wasi", feature = "wasm-web")), +))] +#[no_mangle] +pub extern "C" fn dealloc(ptr: *mut u8, cap: usize) { + unsafe { + let _buf = Vec::from_raw_parts(ptr, 0, cap); + } +} diff --git a/capi/src/run.rs b/capi/src/run.rs index 527a3c31..77098c78 100644 --- a/capi/src/run.rs +++ b/capi/src/run.rs @@ -1,6 +1,6 @@ //! A Run stores the split times for a specific game and category of a runner. -use super::{output_str, output_time_span, output_vec, str, get_file, release_file}; +use super::{get_file, output_str, output_time_span, output_vec, release_file, str}; use crate::parse_run_result::OwnedParseRunResult; use crate::segment::OwnedSegment; use livesplit_core::run::{parser, saver}; diff --git a/capi/staticlib/Cargo.toml b/capi/staticlib/Cargo.toml index 7df62841..1a06e6ee 100644 --- a/capi/staticlib/Cargo.toml +++ b/capi/staticlib/Cargo.toml @@ -14,3 +14,4 @@ crate-type = ["staticlib"] [features] default = ["image-shrinking"] image-shrinking = ["livesplit-core-capi/image-shrinking"] +wasm-web = ["livesplit-core-capi/wasm-web"] diff --git a/crates/livesplit-hotkey/Cargo.toml b/crates/livesplit-hotkey/Cargo.toml index 8692303b..d8c3a87b 100644 --- a/crates/livesplit-hotkey/Cargo.toml +++ b/crates/livesplit-hotkey/Cargo.toml @@ -26,11 +26,16 @@ promising-future = { version = "0.2.4", optional = true } stdweb = { version = "0.3.0", optional = true } parking_lot = { version = "0.9.0", optional = true } +[target.'cfg(all(target_arch = "wasm32", target_os = "unknown"))'.dependencies] +wasm-bindgen = { version = "0.2.54", optional = true } +web-sys = { version = "0.3.28", default-features = false, features = ["KeyboardEvent", "EventTarget", "Window"], optional = true } + [dependencies] cfg-if = "0.1.10" -snafu = { version = "0.6.0", default-features = false } serde = { version = "1.0.98", default-features = false, features = ["derive", "alloc"] } +snafu = { version = "0.6.0", default-features = false } [features] default = ["std"] std = ["snafu/std", "serde/std", "stdweb", "parking_lot", "x11-dl", "mio", "promising-future", "winapi", "parking_lot"] +wasm-web = ["wasm-bindgen", "web-sys"] diff --git a/crates/livesplit-hotkey/src/lib.rs b/crates/livesplit-hotkey/src/lib.rs index eac5dd1c..2d1cbe07 100644 --- a/crates/livesplit-hotkey/src/lib.rs +++ b/crates/livesplit-hotkey/src/lib.rs @@ -18,8 +18,15 @@ cfg_if::cfg_if! { #[macro_use] extern crate stdweb; } else if #[cfg(all(target_arch = "wasm32", target_os = "unknown"))] { - pub mod wasm; - pub use crate::wasm::*; + cfg_if::cfg_if! { + if #[cfg(feature = "wasm-web")] { + mod wasm_web; + pub use self::wasm_web::*; + } else { + mod wasm_unknown; + pub use self::wasm_unknown::*; + } + } } else { pub mod other; pub use crate::other::*; diff --git a/crates/livesplit-hotkey/src/wasm/key_code.rs b/crates/livesplit-hotkey/src/wasm_unknown/key_code.rs similarity index 100% rename from crates/livesplit-hotkey/src/wasm/key_code.rs rename to crates/livesplit-hotkey/src/wasm_unknown/key_code.rs diff --git a/crates/livesplit-hotkey/src/wasm/mod.rs b/crates/livesplit-hotkey/src/wasm_unknown/mod.rs similarity index 100% rename from crates/livesplit-hotkey/src/wasm/mod.rs rename to crates/livesplit-hotkey/src/wasm_unknown/mod.rs diff --git a/crates/livesplit-hotkey/src/wasm_web/key_code.rs b/crates/livesplit-hotkey/src/wasm_web/key_code.rs new file mode 100644 index 00000000..7128a339 --- /dev/null +++ b/crates/livesplit-hotkey/src/wasm_web/key_code.rs @@ -0,0 +1,361 @@ +use std::str::FromStr; + +// Based on https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code + +#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone, serde::Serialize, serde::Deserialize)] +pub enum KeyCode { + Again, + AltLeft, + AltRight, + ArrowDown, + ArrowLeft, + ArrowRight, + ArrowUp, + AudioVolumeDown, + AudioVolumeMute, + AudioVolumeUp, + Backquote, + Backslash, + Backspace, + BracketLeft, + BracketRight, + BrowserBack, + BrowserFavorites, + BrowserForward, + BrowserHome, + BrowserRefresh, + BrowserSearch, + BrowserStop, + Cancel, + CapsLock, + Comma, + ContextMenu, + ControlLeft, + ControlRight, + Convert, + Copy, + Cut, + Delete, + Digit0, + Digit1, + Digit2, + Digit3, + Digit4, + Digit5, + Digit6, + Digit7, + Digit8, + Digit9, + Eject, + End, + Enter, + Equal, + Escape, + F1, + F10, + F11, + F12, + F13, + F14, + F15, + F16, + F17, + F18, + F19, + F2, + F20, + F21, + F22, + F23, + F24, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + Find, + Fn, + HangulMode, + Hanja, + Help, + Home, + Insert, + IntlBackslash, + IntlRo, + IntlYen, + KanaMode, + KeyA, + KeyB, + KeyC, + KeyD, + KeyE, + KeyF, + KeyG, + KeyH, + KeyI, + KeyJ, + KeyK, + KeyL, + KeyM, + KeyN, + KeyO, + KeyP, + KeyQ, + KeyR, + KeyS, + KeyT, + KeyU, + KeyV, + KeyW, + KeyX, + KeyY, + KeyZ, + Lang1, + Lang2, + LaunchApp1, + LaunchApp2, + LaunchMail, + LaunchMediaPlayer, + MediaPlayPause, + MediaSelect, + MediaStop, + MediaTrackNext, + MediaTrackPrevious, + MetaLeft, + MetaRight, + Minus, + NonConvert, + NumLock, + Numpad0, + Numpad1, + Numpad2, + Numpad3, + Numpad4, + Numpad5, + Numpad6, + Numpad7, + Numpad8, + Numpad9, + NumpadAdd, + NumpadChangeSign, + NumpadComma, + NumpadDecimal, + NumpadDivide, + NumpadEnter, + NumpadEqual, + NumpadMultiply, + NumpadParenLeft, + NumpadParenRight, + NumpadSubtract, + Open, + OSLeft, + OSRight, + PageDown, + PageUp, + Paste, + Pause, + Period, + Power, + PrintScreen, + Props, + Quote, + RomanCharacters, + ScrollLock, + Select, + Semicolon, + ShiftLeft, + ShiftRight, + Slash, + Sleep, + Space, + Tab, + Undo, + VolumeDown, + VolumeMute, + VolumeUp, + WakeUp, +} + +impl FromStr for KeyCode { + type Err = (); + fn from_str(s: &str) -> Result { + use self::KeyCode::*; + Ok(match s { + "Again" => Again, + "AltLeft" => AltLeft, + "AltRight" => AltRight, + "ArrowDown" => ArrowDown, + "ArrowLeft" => ArrowLeft, + "ArrowRight" => ArrowRight, + "ArrowUp" => ArrowUp, + "AudioVolumeDown" => AudioVolumeDown, + "AudioVolumeMute" => AudioVolumeMute, + "AudioVolumeUp" => AudioVolumeUp, + "Backquote" => Backquote, + "Backslash" => Backslash, + "Backspace" => Backspace, + "BracketLeft" => BracketLeft, + "BracketRight" => BracketRight, + "BrowserBack" => BrowserBack, + "BrowserFavorites" => BrowserFavorites, + "BrowserForward" => BrowserForward, + "BrowserHome" => BrowserHome, + "BrowserRefresh" => BrowserRefresh, + "BrowserSearch" => BrowserSearch, + "BrowserStop" => BrowserStop, + "Cancel" => Cancel, + "CapsLock" => CapsLock, + "Comma" => Comma, + "ContextMenu" => ContextMenu, + "ControlLeft" => ControlLeft, + "ControlRight" => ControlRight, + "Convert" => Convert, + "Copy" => Copy, + "Cut" => Cut, + "Delete" => Delete, + "Digit0" => Digit0, + "Digit1" => Digit1, + "Digit2" => Digit2, + "Digit3" => Digit3, + "Digit4" => Digit4, + "Digit5" => Digit5, + "Digit6" => Digit6, + "Digit7" => Digit7, + "Digit8" => Digit8, + "Digit9" => Digit9, + "Eject" => Eject, + "End" => End, + "Enter" => Enter, + "Equal" => Equal, + "Escape" => Escape, + "F1" => F1, + "F10" => F10, + "F11" => F11, + "F12" => F12, + "F13" => F13, + "F14" => F14, + "F15" => F15, + "F16" => F16, + "F17" => F17, + "F18" => F18, + "F19" => F19, + "F2" => F2, + "F20" => F20, + "F21" => F21, + "F22" => F22, + "F23" => F23, + "F24" => F24, + "F3" => F3, + "F4" => F4, + "F5" => F5, + "F6" => F6, + "F7" => F7, + "F8" => F8, + "F9" => F9, + "Find" => Find, + "Fn" => Fn, + "HangulMode" => HangulMode, + "Hanja" => Hanja, + "Help" => Help, + "Home" => Home, + "Insert" => Insert, + "IntlBackslash" => IntlBackslash, + "IntlRo" => IntlRo, + "IntlYen" => IntlYen, + "KanaMode" => KanaMode, + "KeyA" => KeyA, + "KeyB" => KeyB, + "KeyC" => KeyC, + "KeyD" => KeyD, + "KeyE" => KeyE, + "KeyF" => KeyF, + "KeyG" => KeyG, + "KeyH" => KeyH, + "KeyI" => KeyI, + "KeyJ" => KeyJ, + "KeyK" => KeyK, + "KeyL" => KeyL, + "KeyM" => KeyM, + "KeyN" => KeyN, + "KeyO" => KeyO, + "KeyP" => KeyP, + "KeyQ" => KeyQ, + "KeyR" => KeyR, + "KeyS" => KeyS, + "KeyT" => KeyT, + "KeyU" => KeyU, + "KeyV" => KeyV, + "KeyW" => KeyW, + "KeyX" => KeyX, + "KeyY" => KeyY, + "KeyZ" => KeyZ, + "Lang1" => Lang1, + "Lang2" => Lang2, + "LaunchApp1" => LaunchApp1, + "LaunchApp2" => LaunchApp2, + "LaunchMail" => LaunchMail, + "LaunchMediaPlayer" => LaunchMediaPlayer, + "MediaPlayPause" => MediaPlayPause, + "MediaSelect" => MediaSelect, + "MediaStop" => MediaStop, + "MediaTrackNext" => MediaTrackNext, + "MediaTrackPrevious" => MediaTrackPrevious, + "MetaLeft" => MetaLeft, + "MetaRight" => MetaRight, + "Minus" => Minus, + "NonConvert" => NonConvert, + "NumLock" => NumLock, + "Numpad0" => Numpad0, + "Numpad1" => Numpad1, + "Numpad2" => Numpad2, + "Numpad3" => Numpad3, + "Numpad4" => Numpad4, + "Numpad5" => Numpad5, + "Numpad6" => Numpad6, + "Numpad7" => Numpad7, + "Numpad8" => Numpad8, + "Numpad9" => Numpad9, + "NumpadAdd" => NumpadAdd, + "NumpadChangeSign" => NumpadChangeSign, + "NumpadComma" => NumpadComma, + "NumpadDecimal" => NumpadDecimal, + "NumpadDivide" => NumpadDivide, + "NumpadEnter" => NumpadEnter, + "NumpadEqual" => NumpadEqual, + "NumpadMultiply" => NumpadMultiply, + "NumpadParenLeft" => NumpadParenLeft, + "NumpadParenRight" => NumpadParenRight, + "NumpadSubtract" => NumpadSubtract, + "Open" => Open, + "OSLeft" => OSLeft, + "OSRight" => OSRight, + "PageDown" => PageDown, + "PageUp" => PageUp, + "Paste" => Paste, + "Pause" => Pause, + "Period" => Period, + "Power" => Power, + "PrintScreen" => PrintScreen, + "Props" => Props, + "Quote" => Quote, + "RomanCharacters" => RomanCharacters, + "ScrollLock" => ScrollLock, + "Select" => Select, + "Semicolon" => Semicolon, + "ShiftLeft" => ShiftLeft, + "ShiftRight" => ShiftRight, + "Slash" => Slash, + "Sleep" => Sleep, + "Space" => Space, + "Tab" => Tab, + "Undo" => Undo, + "VolumeDown" => VolumeDown, + "VolumeMute" => VolumeMute, + "VolumeUp" => VolumeUp, + "WakeUp" => WakeUp, + _ => return Err(()), + }) + } +} diff --git a/crates/livesplit-hotkey/src/wasm_web/mod.rs b/crates/livesplit-hotkey/src/wasm_web/mod.rs new file mode 100644 index 00000000..150e7634 --- /dev/null +++ b/crates/livesplit-hotkey/src/wasm_web/mod.rs @@ -0,0 +1,80 @@ +mod key_code; +pub use self::key_code::KeyCode; + +use wasm_bindgen::prelude::*; +use wasm_bindgen::JsCast; +use web_sys::{window, KeyboardEvent}; + +use std::collections::hash_map::{Entry, HashMap}; +use std::sync::{Arc, Mutex}; + +#[derive(Debug, snafu::Snafu)] +pub enum Error { + AlreadyRegistered, + NotRegistered, + FailedToCreateHook, +} + +pub type Result = std::result::Result; + +pub struct Hook { + hotkeys: Arc>>>, + callback: Closure, +} + +impl Drop for Hook { + fn drop(&mut self) { + if let Some(window) = window() { + let _ = window.remove_event_listener_with_callback( + "keypress", + self.callback.as_ref().unchecked_ref(), + ); + } + } +} + +impl Hook { + pub fn new() -> Result { + let hotkeys = Arc::new(Mutex::new(HashMap::< + KeyCode, + Box, + >::new())); + + let window = window().ok_or(Error::FailedToCreateHook)?; + + let hotkey_map = hotkeys.clone(); + let callback = Closure::wrap(Box::new(move |event: KeyboardEvent| { + if let Ok(code) = event.code().parse() { + if let Some(callback) = hotkey_map.lock().unwrap().get_mut(&code) { + callback(); + } + } + }) as Box); + + window + .add_event_listener_with_callback("keypress", callback.as_ref().unchecked_ref()) + .map_err(|_| Error::FailedToCreateHook)?; + + Ok(Hook { hotkeys, callback }) + } + + pub fn register(&self, hotkey: KeyCode, callback: F) -> Result<()> + where + F: FnMut() + Send + 'static, + { + if let Entry::Vacant(vacant) = self.hotkeys.lock().unwrap().entry(hotkey) { + vacant.insert(Box::new(callback)); + Ok(()) + } else { + Err(Error::AlreadyRegistered) + } + } + + pub fn unregister(&self, hotkey: KeyCode) -> Result<()> { + if self.hotkeys.lock().unwrap().remove(&hotkey).is_some() { + Ok(()) + } else { + Err(Error::NotRegistered) + } + } +} diff --git a/src/analysis/pb_chance/mod.rs b/src/analysis/pb_chance/mod.rs index 8c8d065b..59fe06d1 100644 --- a/src/analysis/pb_chance/mod.rs +++ b/src/analysis/pb_chance/mod.rs @@ -8,8 +8,8 @@ //! The PB chance is currently calculated through the Balanced PB algorithm. The //! PB chance is the percentile at which the Balanced PB algorithm finds the PB. -use crate::{comparison, Run, Segment, TimeSpan, Timer, TimingMethod}; use crate::platform::prelude::*; +use crate::{comparison, Run, Segment, TimeSpan, Timer, TimingMethod}; #[cfg(test)] mod tests; diff --git a/src/analysis/sum_of_segments/mod.rs b/src/analysis/sum_of_segments/mod.rs index 528f0a7e..e85e13fc 100644 --- a/src/analysis/sum_of_segments/mod.rs +++ b/src/analysis/sum_of_segments/mod.rs @@ -13,8 +13,8 @@ pub mod worst; #[cfg(test)] mod tests; -use crate::{Segment, Time, TimeSpan, TimingMethod}; use crate::platform::prelude::*; +use crate::{Segment, Time, TimeSpan, TimingMethod}; /// Describes the shortest amount of time it takes to reach a certain segment. /// Since there is the possibility that the shortest path is actually skipping diff --git a/src/comparison/balanced_pb.rs b/src/comparison/balanced_pb.rs index 398f0bdb..f3324d4a 100644 --- a/src/comparison/balanced_pb.rs +++ b/src/comparison/balanced_pb.rs @@ -12,8 +12,8 @@ //! be smoothed out throughout the whole comparison. use super::{goal, ComparisonGenerator}; -use crate::{Attempt, Segment, TimingMethod}; use crate::platform::prelude::*; +use crate::{Attempt, Segment, TimingMethod}; /// The Comparison Generator for calculating a comparison which has the same /// final time as the runner's Personal Best. Unlike the Personal Best however, diff --git a/src/comparison/best_segments.rs b/src/comparison/best_segments.rs index fa4d85d5..4ece45d6 100644 --- a/src/comparison/best_segments.rs +++ b/src/comparison/best_segments.rs @@ -2,8 +2,8 @@ use super::ComparisonGenerator; use crate::analysis::sum_of_segments::best::calculate; -use crate::{Attempt, Segment, Time, TimingMethod}; use crate::platform::prelude::*; +use crate::{Attempt, Segment, Time, TimingMethod}; /// The Comparison Generator for calculating the Best Segments of a Run. #[derive(Copy, Clone, Debug)] diff --git a/src/comparison/goal.rs b/src/comparison/goal.rs index e6c5533d..efc32909 100644 --- a/src/comparison/goal.rs +++ b/src/comparison/goal.rs @@ -5,9 +5,9 @@ //! Balanced PB comparison however is based on this, which uses the Personal //! Best as a goal time to balance the mistakes that happened in the Personal Best. +use crate::platform::prelude::*; use crate::{Segment, Time, TimeSpan, TimingMethod}; use ordered_float::OrderedFloat; -use crate::platform::prelude::*; /// The default name of the goal comparison. pub const NAME: &str = "Goal"; diff --git a/src/comparison/median_segments.rs b/src/comparison/median_segments.rs index 13c5ffc2..aff4bfc0 100644 --- a/src/comparison/median_segments.rs +++ b/src/comparison/median_segments.rs @@ -4,9 +4,9 @@ //! suited to represent the current performance of a runner. use super::ComparisonGenerator; +use crate::platform::prelude::*; use crate::{Attempt, Segment, TimeSpan, TimingMethod}; use ordered_float::OrderedFloat; -use crate::platform::prelude::*; /// The Comparison Generator for calculating the Median Segments of a Run. The /// Median Segments are calculated through a weighted median that gives more diff --git a/src/comparison/mod.rs b/src/comparison/mod.rs index cd3b213f..cc63889b 100644 --- a/src/comparison/mod.rs +++ b/src/comparison/mod.rs @@ -25,9 +25,9 @@ pub use self::median_segments::MedianSegments; pub use self::none::None; pub use self::worst_segments::WorstSegments; +use crate::platform::prelude::*; use crate::{Attempt, Segment, Timer}; use core::fmt::Debug; -use crate::platform::prelude::*; /// Defines the Personal Best comparison. This module mostly just serves for /// providing the names of the comparison, as the Personal Best is not a diff --git a/src/comparison/worst_segments.rs b/src/comparison/worst_segments.rs index 7f44bda1..858b72b7 100644 --- a/src/comparison/worst_segments.rs +++ b/src/comparison/worst_segments.rs @@ -2,8 +2,8 @@ use super::ComparisonGenerator; use crate::analysis::sum_of_segments::worst::calculate; -use crate::{Attempt, Segment, Time, TimingMethod}; use crate::platform::prelude::*; +use crate::{Attempt, Segment, Time, TimingMethod}; /// The Comparison Generator for calculating the Worst Segments of a Run. #[derive(Copy, Clone, Debug)] diff --git a/src/component/detailed_timer/mod.rs b/src/component/detailed_timer/mod.rs index 81535095..0d15ac4c 100644 --- a/src/component/detailed_timer/mod.rs +++ b/src/component/detailed_timer/mod.rs @@ -6,6 +6,7 @@ use super::timer; use crate::comparison::{self, best_segments, none}; +use crate::platform::prelude::*; use crate::settings::{ CachedImageId, Field, Gradient, ImageData, SemanticColor, SettingsDescription, Value, }; @@ -15,7 +16,6 @@ use crate::timing::formatter::{ }; use crate::{GeneralLayoutSettings, Segment, TimeSpan, Timer, TimerPhase, TimingMethod}; use serde::{Deserialize, Serialize}; -use crate::platform::prelude::*; #[cfg(test)] mod tests; diff --git a/src/component/graph.rs b/src/component/graph.rs index c303e527..9b5eabfc 100644 --- a/src/component/graph.rs +++ b/src/component/graph.rs @@ -3,11 +3,11 @@ //! the chosen comparison throughout the whole attempt. All the individual //! deltas are shown as points in a graph. +use crate::platform::prelude::*; use crate::settings::{Color, Field, SettingsDescription, Value}; use crate::{analysis, comparison, GeneralLayoutSettings, TimeSpan, Timer, TimerPhase}; use alloc::borrow::Cow; use serde::{Deserialize, Serialize}; -use crate::platform::prelude::*; const GRAPH_EDGE_VALUE: f32 = 200.0; const GRAPH_EDGE_MIN: f32 = 5.0; diff --git a/src/component/pb_chance.rs b/src/component/pb_chance.rs index d3ff26a4..94a4f1c3 100644 --- a/src/component/pb_chance.rs +++ b/src/component/pb_chance.rs @@ -5,10 +5,10 @@ //! how well the attempt is going. use super::key_value; +use crate::platform::prelude::*; use crate::settings::{Color, Field, Gradient, SettingsDescription, Value}; use crate::{analysis::pb_chance, Timer}; use serde::{Deserialize, Serialize}; -use crate::platform::prelude::*; /// The PB Chance Component is a component that shows how likely it is to beat /// the Personal Best. If there is no active attempt it shows the general chance diff --git a/src/component/possible_time_save.rs b/src/component/possible_time_save.rs index 7de1810b..8509347a 100644 --- a/src/component/possible_time_save.rs +++ b/src/component/possible_time_save.rs @@ -6,13 +6,13 @@ use super::key_value; use crate::analysis::possible_time_save; +use crate::platform::prelude::*; use crate::settings::{Color, Field, Gradient, SettingsDescription, Value}; use crate::timing::formatter::{Accuracy, PossibleTimeSave, TimeFormatter}; use crate::{comparison, Timer, TimerPhase}; -use serde::{Deserialize, Serialize}; use alloc::borrow::Cow; use core::fmt::Write as FmtWrite; -use crate::platform::prelude::*; +use serde::{Deserialize, Serialize}; /// The Possible Time Save Component is a component that shows how much time the /// chosen comparison could've saved for the current segment, based on the Best diff --git a/src/component/previous_segment.rs b/src/component/previous_segment.rs index e268d3fd..f65523bb 100644 --- a/src/component/previous_segment.rs +++ b/src/component/previous_segment.rs @@ -6,13 +6,13 @@ //! active time loss whenever the runner is losing time on the current segment. use super::key_value; +use crate::platform::prelude::*; use crate::settings::{Color, Field, Gradient, SemanticColor, SettingsDescription, Value}; use crate::timing::formatter::{Accuracy, Delta, PossibleTimeSave, TimeFormatter}; use crate::{analysis, comparison, GeneralLayoutSettings, Timer, TimerPhase}; -use serde::{Deserialize, Serialize}; use alloc::borrow::Cow; use core::fmt::Write as FmtWrite; -use crate::platform::prelude::*; +use serde::{Deserialize, Serialize}; /// The Previous Segment Component is a component that shows how much time was /// saved or lost during the previous segment based on the chosen comparison. diff --git a/src/component/splits/column.rs b/src/component/splits/column.rs index 4d40a784..308edbf6 100644 --- a/src/component/splits/column.rs +++ b/src/component/splits/column.rs @@ -1,3 +1,4 @@ +use crate::platform::prelude::*; use crate::{ analysis::{self, possible_time_save, split_color}, comparison, @@ -6,7 +7,6 @@ use crate::{ GeneralLayoutSettings, Segment, TimeSpan, Timer, TimingMethod, }; use serde::{Deserialize, Serialize}; -use crate::platform::prelude::*; /// The settings of an individual column showing timing information on each /// split. diff --git a/src/component/text.rs b/src/component/text.rs index d0e8a185..626ed727 100644 --- a/src/component/text.rs +++ b/src/component/text.rs @@ -4,11 +4,11 @@ //! a situation where you have a label and a value. use super::key_value; +use crate::platform::prelude::*; use crate::settings::{Color, Field, Gradient, SettingsDescription, Value}; use alloc::borrow::Cow; use core::mem::replace; use serde::{Deserialize, Serialize}; -use crate::platform::prelude::*; /// The Text Component simply visualizes any given text. This can either be a /// single centered text, or split up into a left and right text, which is diff --git a/src/component/title/mod.rs b/src/component/title/mod.rs index f968015c..d0c6554c 100644 --- a/src/component/title/mod.rs +++ b/src/component/title/mod.rs @@ -3,13 +3,13 @@ //! that is being run. Additionally, the game icon, the attempt count, and the //! total number of finished runs can be shown. +use crate::platform::prelude::*; use crate::settings::{Alignment, Color, Field, Gradient, SettingsDescription, Value}; use crate::{ settings::{CachedImageId, Image, ImageData}, Timer, TimerPhase, }; use serde::{Deserialize, Serialize}; -use crate::platform::prelude::*; #[cfg(test)] mod tests; diff --git a/src/layout/component.rs b/src/layout/component.rs index c1847908..8a498419 100644 --- a/src/layout/component.rs +++ b/src/layout/component.rs @@ -4,10 +4,10 @@ use crate::component::{ possible_time_save, previous_segment, separator, splits, sum_of_best, text, timer, title, total_playtime, }; +use crate::platform::prelude::*; use crate::settings::{SettingsDescription, Value}; use crate::Timer; use alloc::borrow::Cow; -use crate::platform::prelude::*; /// A Component provides information about a run in a way that is easy to /// visualize. This type can store any of the components provided by this crate. diff --git a/src/layout/component_settings.rs b/src/layout/component_settings.rs index e4b1a8a4..08a3c1d9 100644 --- a/src/layout/component_settings.rs +++ b/src/layout/component_settings.rs @@ -4,8 +4,8 @@ use crate::component::{ possible_time_save, previous_segment, separator, splits, sum_of_best, text, timer, title, total_playtime, }; -use serde::{Deserialize, Serialize}; use crate::platform::prelude::*; +use serde::{Deserialize, Serialize}; /// The settings for one of the components available. #[derive(Clone, Serialize, Deserialize)] diff --git a/src/layout/component_state.rs b/src/layout/component_state.rs index 43d535ab..89befd81 100644 --- a/src/layout/component_state.rs +++ b/src/layout/component_state.rs @@ -1,8 +1,8 @@ use crate::component::{ blank_space, detailed_timer, graph, key_value, separator, splits, text, timer, title, }; -use serde::{Deserialize, Serialize}; use crate::platform::prelude::*; +use serde::{Deserialize, Serialize}; /// The state object for one of the components available. #[derive(Serialize, Deserialize)] diff --git a/src/layout/editor/state.rs b/src/layout/editor/state.rs index f68dbf3b..301b175d 100644 --- a/src/layout/editor/state.rs +++ b/src/layout/editor/state.rs @@ -1,7 +1,7 @@ use super::Editor; +use crate::platform::prelude::*; use crate::settings::SettingsDescription; use serde::{Deserialize, Serialize}; -use crate::platform::prelude::*; /// Represents the current state of the Layout Editor in order to visualize it /// properly. diff --git a/src/layout/general_settings.rs b/src/layout/general_settings.rs index 8cc0cacd..4af97712 100644 --- a/src/layout/general_settings.rs +++ b/src/layout/general_settings.rs @@ -1,7 +1,7 @@ use super::LayoutDirection; +use crate::platform::prelude::*; use crate::settings::{Color, Field, Gradient, SettingsDescription, Value}; use serde::{Deserialize, Serialize}; -use crate::platform::prelude::*; /// The general settings of the layout that apply to all components. #[derive(Clone, Serialize, Deserialize)] diff --git a/src/layout/layout_settings.rs b/src/layout/layout_settings.rs index 174e4443..08a39c84 100644 --- a/src/layout/layout_settings.rs +++ b/src/layout/layout_settings.rs @@ -1,6 +1,6 @@ use super::{ComponentSettings, GeneralSettings}; -use serde::{Deserialize, Serialize}; use crate::platform::prelude::*; +use serde::{Deserialize, Serialize}; /// Describes a whole layout by its settings in a way that can easily be /// serialized and deserialized. diff --git a/src/layout/layout_state.rs b/src/layout/layout_state.rs index 9874a443..ebf4daec 100644 --- a/src/layout/layout_state.rs +++ b/src/layout/layout_state.rs @@ -1,7 +1,7 @@ use super::{ComponentState, LayoutDirection}; +use crate::platform::prelude::*; use crate::settings::{Color, Gradient}; use serde::{Deserialize, Serialize}; -use crate::platform::prelude::*; /// The state object describes the information to visualize for the layout. #[derive(Serialize, Deserialize)] diff --git a/src/layout/mod.rs b/src/layout/mod.rs index a1da8439..873e7750 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -23,8 +23,8 @@ pub use self::layout_settings::LayoutSettings; pub use self::layout_state::LayoutState; use crate::component::{previous_segment, splits, timer, title}; -use crate::timing::Timer; use crate::platform::prelude::*; +use crate::timing::Timer; /// A Layout allows you to combine multiple components together to visualize a /// variety of information the runner is interested in. diff --git a/src/lib.rs b/src/lib.rs index 6b851f35..588994b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,9 @@ #![warn( - missing_docs, + clippy::complexity, clippy::correctness, clippy::perf, clippy::style, - clippy::complexity, + missing_docs, rust_2018_idioms )] // Clippy false positives @@ -51,9 +51,6 @@ extern crate alloc; mod platform; -#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] -pub use crate::platform::*; - macro_rules! catch { ($($code:tt)*) => { (|| { Some({ $($code)* }) })() diff --git a/src/platform/wasm/mod.rs b/src/platform/wasm/mod.rs index b028711f..af33eba4 100644 --- a/src/platform/wasm/mod.rs +++ b/src/platform/wasm/mod.rs @@ -1,50 +1,12 @@ -#![allow(missing_docs)] - -mod time; - -pub use self::time::*; -pub use chrono::{DateTime, Duration, Local, Utc}; -pub use indexmap; - -use core::mem; - -#[no_mangle] -pub extern "C" fn alloc(size: usize) -> *mut u8 { - let mut buf = Vec::with_capacity(size); - let ptr = buf.as_mut_ptr(); - mem::forget(buf); - ptr -} - -#[no_mangle] -pub extern "C" fn dealloc(ptr: *mut u8, cap: usize) { - unsafe { - let _buf = Vec::from_raw_parts(ptr, 0, cap); - } -} - -use chrono::NaiveDateTime; -use std::mem::MaybeUninit; - -#[repr(C)] -#[derive(Copy, Clone, PartialEq, Debug, Default)] -struct FFIDateTime { - secs: i64, - nsecs: u32, -} - -extern "C" { - fn Date_now(data: *mut FFIDateTime); -} - -pub fn utc_now() -> DateTime { - unsafe { - let mut date_time = MaybeUninit::uninit(); - Date_now(date_time.as_mut_ptr()); - let date_time = date_time.assume_init(); - DateTime::from_utc( - NaiveDateTime::from_timestamp(date_time.secs, date_time.nsecs), - Utc, - ) +cfg_if::cfg_if! { + if #[cfg(target_os = "wasi")] { + mod wasi; + pub use self::wasi::*; + } else if #[cfg(feature = "wasm-web")] { + mod web; + pub use self::web::*; + } else { + mod unknown; + pub use self::unknown::*; } } diff --git a/src/platform/wasm/unknown/mod.rs b/src/platform/wasm/unknown/mod.rs new file mode 100644 index 00000000..61870a4a --- /dev/null +++ b/src/platform/wasm/unknown/mod.rs @@ -0,0 +1,32 @@ +#![allow(missing_docs)] + +mod time; + +pub use self::time::*; +pub use chrono::{DateTime, Duration, Local, Utc}; +pub use indexmap; + +use chrono::NaiveDateTime; +use std::mem::MaybeUninit; + +#[repr(C)] +struct FFIDateTime { + secs: i64, + nsecs: u32, +} + +extern "C" { + fn Date_now(data: *mut FFIDateTime); +} + +pub fn utc_now() -> DateTime { + unsafe { + let mut date_time = MaybeUninit::uninit(); + Date_now(date_time.as_mut_ptr()); + let date_time = date_time.assume_init(); + DateTime::from_utc( + NaiveDateTime::from_timestamp(date_time.secs, date_time.nsecs), + Utc, + ) + } +} diff --git a/src/platform/wasm/time.rs b/src/platform/wasm/unknown/time.rs similarity index 100% rename from src/platform/wasm/time.rs rename to src/platform/wasm/unknown/time.rs diff --git a/src/platform/wasm/wasi/mod.rs b/src/platform/wasm/wasi/mod.rs new file mode 100644 index 00000000..baa9c503 --- /dev/null +++ b/src/platform/wasm/wasi/mod.rs @@ -0,0 +1,17 @@ +#![allow(missing_docs)] + +pub use chrono::{DateTime, Duration, Local, Utc}; +pub use indexmap; +pub use std::time::{Instant, SystemTime}; + +use chrono::NaiveDateTime; + +pub fn utc_now() -> DateTime { + let unix = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap(); + DateTime::from_utc( + NaiveDateTime::from_timestamp(unix.as_secs() as _, unix.subsec_nanos()), + Utc, + ) +} diff --git a/src/platform/wasm/web/mod.rs b/src/platform/wasm/web/mod.rs new file mode 100644 index 00000000..67384e6f --- /dev/null +++ b/src/platform/wasm/web/mod.rs @@ -0,0 +1,11 @@ +#![allow(missing_docs)] + +mod time; + +pub use self::time::*; +pub use chrono::{DateTime, Duration, Local, Utc}; +pub use indexmap; + +pub fn utc_now() -> DateTime { + Utc::now() +} diff --git a/src/platform/wasm/web/time.rs b/src/platform/wasm/web/time.rs new file mode 100644 index 00000000..9009622f --- /dev/null +++ b/src/platform/wasm/web/time.rs @@ -0,0 +1,29 @@ +use ordered_float::OrderedFloat; +use std::ops::Sub; +use std::time::Duration; +use web_sys::window; + +#[derive(Copy, Clone, PartialOrd, PartialEq, Ord, Eq, Debug)] +pub struct Instant(OrderedFloat); + +impl Instant { + pub fn now() -> Self { + let seconds = window() + .and_then(|w| w.performance()) + .expect("Can't measure time without a performance object") + .now() + / 1000.0; + Instant(OrderedFloat(seconds)) + } +} + +impl Sub for Instant { + type Output = Duration; + + fn sub(self, rhs: Instant) -> Duration { + let secs = (self.0).0 - (rhs.0).0; + let nanos = ((secs % 1.0) * 1_000_000_000.0) as _; + let secs = secs as _; + Duration::new(secs, nanos) + } +} diff --git a/src/run/editor/cleaning.rs b/src/run/editor/cleaning.rs index cb0cc078..17d6484a 100644 --- a/src/run/editor/cleaning.rs +++ b/src/run/editor/cleaning.rs @@ -7,12 +7,12 @@ //! them seem wrong. use crate::analysis::sum_of_segments::{best, track_branch, Prediction}; +use crate::platform::prelude::*; +use crate::platform::Local; use crate::timing::formatter::{Short, TimeFormatter}; use crate::{Attempt, Run, Segment, TimeSpan, TimingMethod}; -use crate::platform::Local; use core::fmt; use core::mem::replace; -use crate::platform::prelude::*; /// A Sum of Best Cleaner allows you to interactively remove potential issues in /// the Segment History that lead to an inaccurate Sum of Best. If you skip a diff --git a/src/run/editor/fuzzy_list.rs b/src/run/editor/fuzzy_list.rs index 31aebf55..ce4e5eb6 100644 --- a/src/run/editor/fuzzy_list.rs +++ b/src/run/editor/fuzzy_list.rs @@ -1,6 +1,6 @@ +use crate::platform::prelude::*; use alloc::collections::BinaryHeap; use core::usize; -use crate::platform::prelude::*; /// With a Fuzzy List, you can implement a fuzzy searching algorithm. The list /// stores all the items that can be searched for. With the `search` method you diff --git a/src/run/editor/state.rs b/src/run/editor/state.rs index fb36ddd1..674ee538 100644 --- a/src/run/editor/state.rs +++ b/src/run/editor/state.rs @@ -1,11 +1,11 @@ use super::{Editor, SegmentRow, TimingMethod}; use crate::comparison::personal_best; +use crate::platform::prelude::*; use crate::run::RunMetadata; use crate::settings::{CachedImageId, ImageData}; use crate::timing::formatter::none_wrapper::EmptyWrapper; use crate::timing::formatter::{Accuracy, Short, TimeFormatter}; use serde::{Deserialize, Serialize}; -use crate::platform::prelude::*; /// Represents the current state of the Run Editor in order to visualize it /// properly. diff --git a/src/run/mod.rs b/src/run/mod.rs index ca287609..bccb6a35 100644 --- a/src/run/mod.rs +++ b/src/run/mod.rs @@ -35,16 +35,16 @@ pub use segment::Segment; pub use segment_history::SegmentHistory; use crate::comparison::{default_generators, personal_best, ComparisonGenerator}; +use crate::platform::prelude::*; use crate::{settings::Image, AtomicDateTime, Time, TimeSpan, TimingMethod}; use alloc::borrow::Cow; +#[cfg(not(feature = "std"))] +use alloc::string::String as PathBuf; use core::cmp::max; -use ordered_float::OrderedFloat; use hashbrown::HashSet; +use ordered_float::OrderedFloat; #[cfg(feature = "std")] use std::path::PathBuf; -#[cfg(not(feature = "std"))] -use alloc::string::String as PathBuf; -use crate::platform::prelude::*; /// A Run stores the split times for a specific game and category of a runner. /// diff --git a/src/run/parser/llanfair.rs b/src/run/parser/llanfair.rs index fbabe67b..56fbaa54 100644 --- a/src/run/parser/llanfair.rs +++ b/src/run/parser/llanfair.rs @@ -2,11 +2,11 @@ use crate::{settings::Image, RealTime, Run, Segment, Time, TimeSpan}; use byteorder::{ReadBytesExt, BE}; +use core::result::Result as StdResult; +use core::str::{from_utf8, Utf8Error}; use image::{png, ColorType, ImageBuffer, Rgba}; use snafu::{OptionExt, ResultExt}; use std::io::{self, Read, Seek, SeekFrom}; -use core::result::Result as StdResult; -use core::str::{from_utf8, Utf8Error}; /// The Error type for splits files that couldn't be parsed by the Llanfair /// Parser. diff --git a/src/run/parser/llanfair2.rs b/src/run/parser/llanfair2.rs index db335563..7a4b2771 100644 --- a/src/run/parser/llanfair2.rs +++ b/src/run/parser/llanfair2.rs @@ -5,9 +5,9 @@ use crate::xml_util::{ }; use crate::{RealTime, Run, Segment, Time, TimeSpan}; use byteorder::{ByteOrder, BE}; +use core::cmp::min; use image::{png, ColorType, ImageBuffer, Rgba}; use quick_xml::Reader; -use core::cmp::min; use std::io::BufRead; use crate::xml_util::Error as XmlError; diff --git a/src/run/run_metadata.rs b/src/run/run_metadata.rs index 1589935d..2e3e26f5 100644 --- a/src/run/run_metadata.rs +++ b/src/run/run_metadata.rs @@ -1,6 +1,6 @@ use crate::indexmap::map::{IndexMap, Iter}; -use serde::{Deserialize, Serialize}; use crate::platform::prelude::*; +use serde::{Deserialize, Serialize}; /// The Run Metadata stores additional information about a run, like the /// platform and region of the game. All of this information is optional. diff --git a/src/run/segment.rs b/src/run/segment.rs index b3bf2425..97a46acd 100644 --- a/src/run/segment.rs +++ b/src/run/segment.rs @@ -1,7 +1,7 @@ use crate::comparison::personal_best; +use crate::platform::prelude::*; use crate::{settings::Image, SegmentHistory, Time, TimeSpan, TimingMethod}; use hashbrown::HashMap; -use crate::platform::prelude::*; /// A Segment describes a point in a speedrun that is suitable for storing a /// split time. This stores the name of that segment, an icon, the split times diff --git a/src/run/segment_history.rs b/src/run/segment_history.rs index 61145a19..9261ce31 100644 --- a/src/run/segment_history.rs +++ b/src/run/segment_history.rs @@ -1,7 +1,7 @@ +use crate::platform::prelude::*; use crate::Time; use core::cmp::min; use core::slice::{Iter, IterMut}; -use crate::platform::prelude::*; /// Stores the segment times achieved for a certain segment. Each segment is /// tagged with an index. Only segment times with an index larger than 0 are diff --git a/src/settings/field.rs b/src/settings/field.rs index b6f87ff3..b5fa19d1 100644 --- a/src/settings/field.rs +++ b/src/settings/field.rs @@ -1,6 +1,6 @@ use super::Value; -use serde::{Deserialize, Serialize}; use crate::platform::prelude::*; +use serde::{Deserialize, Serialize}; /// A Field describes a single setting by its name and its current value. #[derive(Serialize, Deserialize)] diff --git a/src/settings/image/shrinking.rs b/src/settings/image/shrinking.rs index fb8b45cd..3d928375 100644 --- a/src/settings/image/shrinking.rs +++ b/src/settings/image/shrinking.rs @@ -1,8 +1,8 @@ +use alloc::borrow::Cow; use image::{ bmp, gif, guess_format, hdr, ico, jpeg, load_from_memory_with_format, png, pnm, tiff, webp, DynamicImage, ImageDecoder, ImageError, ImageFormat, }; -use alloc::borrow::Cow; use std::io::Cursor; fn shrink_inner(data: &[u8], max_dim: u32) -> Result, ImageError> { diff --git a/src/settings/settings_description.rs b/src/settings/settings_description.rs index 20e9581b..12fc8783 100644 --- a/src/settings/settings_description.rs +++ b/src/settings/settings_description.rs @@ -1,6 +1,6 @@ use super::Field; -use serde::{Deserialize, Serialize}; use crate::platform::prelude::*; +use serde::{Deserialize, Serialize}; /// A generic description of the settings available and their current values. #[derive(Default, Serialize, Deserialize)] diff --git a/src/settings/value.rs b/src/settings/value.rs index 37c55d22..08186238 100644 --- a/src/settings/value.rs +++ b/src/settings/value.rs @@ -1,3 +1,4 @@ +use crate::platform::prelude::*; use crate::{ component::splits::{ColumnStartWith, ColumnUpdateTrigger, ColumnUpdateWith}, hotkey::KeyCode, @@ -8,7 +9,6 @@ use crate::{ }; use core::result::Result as StdResult; use serde::{Deserialize, Serialize}; -use crate::platform::prelude::*; /// Describes a setting's value. Such a value can be of a variety of different /// types. diff --git a/src/timing/atomic_date_time.rs b/src/timing/atomic_date_time.rs index 56866ae9..a6b6d961 100644 --- a/src/timing/atomic_date_time.rs +++ b/src/timing/atomic_date_time.rs @@ -1,6 +1,6 @@ use crate::platform::utc_now; -use crate::TimeSpan; use crate::platform::{DateTime, Utc}; +use crate::TimeSpan; use core::ops::Sub; /// An Atomic Date Time represents a UTC Date Time that tries to be as close to diff --git a/src/timing/time_span.rs b/src/timing/time_span.rs index 9b9287ef..c70a365d 100644 --- a/src/timing/time_span.rs +++ b/src/timing/time_span.rs @@ -1,10 +1,10 @@ +use crate::platform::prelude::*; use crate::platform::Duration; use core::num::ParseFloatError; use core::ops::{AddAssign, SubAssign}; use core::str::FromStr; use derive_more::{Add, From, Neg, Sub}; use snafu::ResultExt; -use crate::platform::prelude::*; /// A Time Span represents a certain span of time. #[derive(From, Add, Sub, Neg, Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]