diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index ab083d0c12..5ed0e2363a 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -16,7 +16,7 @@ concurrency: cancel-in-progress: true env: - RUST_MSRV_VERSION: '1.77' + RUST_MSRV_VERSION: '1.85' jobs: build: diff --git a/Cargo.lock b/Cargo.lock index 308f54f4b4..758a0b3e99 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -72,7 +72,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cfg-if 1.0.0", + "cfg-if", "libc", "miniz_oxide", "object", @@ -101,12 +101,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "boolinator" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9" - [[package]] name = "bumpalo" version = "3.16.0" @@ -140,12 +134,6 @@ dependencies = [ "shlex", ] -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - [[package]] name = "cfg-if" version = "1.0.0" @@ -173,7 +161,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen", ] @@ -198,26 +186,7 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "crossbeam-channel" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" -dependencies = [ - "cfg-if 0.1.10", - "lazy_static", + "cfg-if", ] [[package]] @@ -346,12 +315,6 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -[[package]] -name = "enclose" -version = "1.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1056f553da426e9c025a662efa48b52e62e0a3a7648aa2d15aeaaf7f0d329357" - [[package]] name = "equivalent" version = "1.0.1" @@ -503,7 +466,7 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "libc", "wasi", @@ -1054,7 +1017,7 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest", ] @@ -1065,7 +1028,7 @@ version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "cpufeatures", "digest", ] @@ -1107,13 +1070,11 @@ dependencies = [ "anyhow", "assert_matches", "base64 0.22.1", - "boolinator", "chrono", "data-encoding", "derivative", "derive_more", "either", - "enclose", "flate2", "futures", "hex", @@ -1124,7 +1085,6 @@ dependencies = [ "lz-str", "magnet-url", "num", - "once_cell", "percent-encoding", "pretty_assertions", "regex", @@ -1145,7 +1105,6 @@ dependencies = [ "strum", "thiserror", "tokio", - "tokio-current-thread", "tracing", "url", ] @@ -1154,11 +1113,9 @@ dependencies = [ name = "stremio-core-web" version = "0.51.1" dependencies = [ - "boolinator", "chrono", "console_error_panic_hook", "either", - "enclose", "futures", "getrandom", "gloo-utils", @@ -1166,7 +1123,6 @@ dependencies = [ "http", "itertools", "js-sys", - "once_cell", "regex", "semver", "serde", @@ -1301,7 +1257,7 @@ version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "once_cell", ] @@ -1362,22 +1318,6 @@ dependencies = [ "tokio-macros", ] -[[package]] -name = "tokio-current-thread" -version = "0.2.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ed30983efe64aa01758777622d70f45054802d959f02b7895b7245883699487" -dependencies = [ - "crossbeam-channel", - "tokio-executor", -] - -[[package]] -name = "tokio-executor" -version = "0.2.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0937fcedb52baa1424b7483977ec1e387a75413c12abc232c3c092ed35f68d" - [[package]] name = "tokio-macros" version = "2.4.0" @@ -1522,7 +1462,7 @@ version = "0.2.78" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "serde", "serde_json", "wasm-bindgen-macro", @@ -1549,7 +1489,7 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "js-sys", "wasm-bindgen", "web-sys", diff --git a/Cargo.toml b/Cargo.toml index 7b267ac971..0f6b3952ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,8 +4,8 @@ version = "0.1.0" authors = ["Smart Code OOD"] edition = "2021" -# When upgrading MSRV make sure to update the msrv.yaml workflow -rust-version = "1.77" +# When upgrading MSRV make sure to update the msrv.yml workflow +rust-version = "1.85" [profile.release] lto = true @@ -69,15 +69,12 @@ sha1 = "0.10" sha2 = "0.10" either = "1.6" -enclose = "1.1" derivative = "2.2" derive_more = { version = "2", features = ["from", "into", "into_iterator", "try_into", "deref"] } -boolinator = "2.4" strum = { version = "0.27", features = ["derive"] } lazysort = "0.2" -once_cell = "1" itertools = "0.14" magnet-url = "3.0" @@ -103,7 +100,6 @@ lz-str = "0.2" [dev-dependencies] tokio = { version = "1.12", features = ["rt", "macros"] } -tokio-current-thread = "=0.2.0-alpha.1" serde_test = "1.0" assert_matches = "1.5" pretty_assertions = "1" diff --git a/src/addon_transport/http_transport/http_transport.rs b/src/addon_transport/http_transport/http_transport.rs index 04b6d7f50e..ea77ec5053 100644 --- a/src/addon_transport/http_transport/http_transport.rs +++ b/src/addon_transport/http_transport/http_transport.rs @@ -2,8 +2,8 @@ use std::marker::PhantomData; use futures::future; use http::Request; -use once_cell::sync::Lazy; use percent_encoding::utf8_percent_encode; +use std::sync::LazyLock; use url::Url; use crate::addon_transport::http_transport::legacy::AddonLegacyTransport; @@ -67,7 +67,7 @@ impl AddonTransport for AddonHTTPTransport { .as_str() .replace(ADDON_MANIFEST_PATH, &path); - static CINEMETA_ADDONS_CATALOG_URL: Lazy = Lazy::new(|| { + static CINEMETA_ADDONS_CATALOG_URL: LazyLock = LazyLock::new(|| { CINEMETA_URL .as_str() .replace(ADDON_MANIFEST_PATH, "/addon_catalog/all/community.json") diff --git a/src/analytics.rs b/src/analytics.rs index 0fdadf28a5..0d629663d1 100644 --- a/src/analytics.rs +++ b/src/analytics.rs @@ -5,7 +5,6 @@ use std::{ }; use derivative::Derivative; -use enclose::enclose; use futures::{ future::{self, Either}, Future, FutureExt, @@ -120,19 +119,22 @@ impl Analytics { Err(EnvError::Fetch(_)) | Err(EnvError::Serde(_)) => Err(()), _ => Ok(()), }) - .then(enclose!((self.state => state) move |result| async move { - let mut state = state.lock().expect("analytics state lock failed"); - if state.pending == Some(batch) { - match result { - Ok(_) => { - state.pending = None; - } - Err(_) => { - state.revert_pending(); - } + .then({ + let state = self.state.clone(); + move |result| async move { + let mut state = state.lock().expect("analytics state lock failed"); + if state.pending == Some(batch) { + match result { + Ok(_) => { + state.pending = None; + } + Err(_) => { + state.revert_pending(); + } + }; }; - }; - })), + } + }), ); }; }; diff --git a/src/constants.rs b/src/constants.rs index fa29343ad7..c79d9705b4 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; -use once_cell::sync::Lazy; use percent_encoding::{AsciiSet, NON_ALPHANUMERIC}; +use std::sync::LazyLock; use tracing::trace; use url::Url; @@ -72,25 +72,26 @@ pub const PLAYER_IGNORE_SEEK_AFTER: u64 = 600_000; pub static BASE64: base64::engine::general_purpose::GeneralPurpose = base64::engine::general_purpose::STANDARD; -pub static CINEMETA_CATALOGS_URL: Lazy = Lazy::new(|| { +pub static CINEMETA_CATALOGS_URL: LazyLock = LazyLock::new(|| { Url::parse("https://cinemeta-catalogs.strem.io").expect("CINEMETA_URL parse failed") }); /// Manifest URL for Cinemeta V3 -pub static CINEMETA_URL: Lazy = Lazy::new(|| { +pub static CINEMETA_URL: LazyLock = LazyLock::new(|| { Url::parse("https://v3-cinemeta.strem.io/manifest.json").expect("CINEMETA_URL parse failed") }); -pub static USER_LIKES_API_URL: Lazy = - Lazy::new(|| Url::parse("https://likes.stremio.com").expect("API_URL parse failed")); -pub static API_URL: Lazy = - Lazy::new(|| Url::parse("https://api.strem.io").expect("API_URL parse failed")); -pub static LINK_API_URL: Lazy = - Lazy::new(|| Url::parse("https://link.stremio.com").expect("LINK_API_URL parse failed")); -pub static STREAMING_SERVER_URL: Lazy = - Lazy::new(|| Url::parse("http://127.0.0.1:11470").expect("STREAMING_SERVER_URL parse failed")); -pub static IMDB_URL: Lazy = - Lazy::new(|| Url::parse("https://imdb.com").expect("IMDB_URL parse failed")); -pub static OFFICIAL_ADDONS: Lazy> = Lazy::new(|| { +pub static USER_LIKES_API_URL: LazyLock = + LazyLock::new(|| Url::parse("https://likes.stremio.com").expect("API_URL parse failed")); +pub static API_URL: LazyLock = + LazyLock::new(|| Url::parse("https://api.strem.io").expect("API_URL parse failed")); +pub static LINK_API_URL: LazyLock = + LazyLock::new(|| Url::parse("https://link.stremio.com").expect("LINK_API_URL parse failed")); +pub static STREAMING_SERVER_URL: LazyLock = LazyLock::new(|| { + Url::parse("http://127.0.0.1:11470").expect("STREAMING_SERVER_URL parse failed") +}); +pub static IMDB_URL: LazyLock = + LazyLock::new(|| Url::parse("https://imdb.com").expect("IMDB_URL parse failed")); +pub static OFFICIAL_ADDONS: LazyLock> = LazyLock::new(|| { if std::env::var("EMPTY_OFFICIAL_ADDONS").unwrap_or("0".to_string()) == "1" { trace!("Official addons are disabled"); return serde_json::from_str("[]").expect("OFFICIAL_ADDONS parse failed"); @@ -99,43 +100,43 @@ pub static OFFICIAL_ADDONS: Lazy> = Lazy::new(|| { return serde_json::from_slice(&addons_str).expect("OFFICIAL_ADDONS parse failed"); } }); -pub static SKIP_EXTRA_PROP: Lazy = Lazy::new(|| ExtraProp { +pub static SKIP_EXTRA_PROP: LazyLock = LazyLock::new(|| ExtraProp { name: "skip".to_owned(), is_required: false, options: vec![], options_limit: OptionsLimit::default(), }); -pub static VIDEO_HASH_EXTRA_PROP: Lazy = Lazy::new(|| ExtraProp { +pub static VIDEO_HASH_EXTRA_PROP: LazyLock = LazyLock::new(|| ExtraProp { name: "videoHash".to_owned(), is_required: false, options: vec![], options_limit: OptionsLimit::default(), }); -pub static VIDEO_SIZE_EXTRA_PROP: Lazy = Lazy::new(|| ExtraProp { +pub static VIDEO_SIZE_EXTRA_PROP: LazyLock = LazyLock::new(|| ExtraProp { name: "videoSize".to_owned(), is_required: false, options: vec![], options_limit: OptionsLimit::default(), }); -pub static VIDEO_FILENAME_EXTRA_PROP: Lazy = Lazy::new(|| ExtraProp { +pub static VIDEO_FILENAME_EXTRA_PROP: LazyLock = LazyLock::new(|| ExtraProp { name: "filename".to_owned(), is_required: false, options: vec![], options_limit: OptionsLimit::default(), }); -pub static LAST_VIDEOS_IDS_EXTRA_PROP: Lazy = Lazy::new(|| ExtraProp { +pub static LAST_VIDEOS_IDS_EXTRA_PROP: LazyLock = LazyLock::new(|| ExtraProp { name: "lastVideosIds".to_owned(), is_required: false, options: vec![], options_limit: OptionsLimit(1), }); -pub static CALENDAR_IDS_EXTRA_PROP: Lazy = Lazy::new(|| ExtraProp { +pub static CALENDAR_IDS_EXTRA_PROP: LazyLock = LazyLock::new(|| ExtraProp { name: "calendarVideosIds".to_owned(), is_required: false, options: vec![], options_limit: OptionsLimit(1), }); -pub static TYPE_PRIORITIES: Lazy> = Lazy::new(|| { +pub static TYPE_PRIORITIES: LazyLock> = LazyLock::new(|| { vec![ ("all", 5), ("movie", 4), diff --git a/src/models/catalog_with_filters.rs b/src/models/catalog_with_filters.rs index 8a252786cc..f7627946c8 100644 --- a/src/models/catalog_with_filters.rs +++ b/src/models/catalog_with_filters.rs @@ -12,7 +12,6 @@ use crate::types::addon::{ }; use crate::types::profile::Profile; use crate::types::resource::MetaItemPreview; -use boolinator::Boolinator; use derivative::Derivative; use itertools::Itertools; use serde::{Deserialize, Serialize}; @@ -417,32 +416,29 @@ fn selectable_update( extra_prop.name != SKIP_EXTRA_PROP.name && !extra_prop.options.is_empty() }) .map(|extra_prop| { - let none_option = - (!extra_prop.is_required) - .as_option() - .map(|_| SelectableExtraOption { - value: None, - selected: selected + let none_option = (!extra_prop.is_required).then(|| SelectableExtraOption { + value: None, + selected: selected + .request + .path + .extra + .iter() + .all(|extra_value| extra_value.name != extra_prop.name), + request: ResourceRequest { + base: selected.request.base.to_owned(), + path: ResourcePath { + id: manifest_catalog.id.to_owned(), + r#type: manifest_catalog.r#type.to_owned(), + resource: selected.request.path.resource.to_owned(), + extra: selected .request .path .extra - .iter() - .all(|extra_value| extra_value.name != extra_prop.name), - request: ResourceRequest { - base: selected.request.base.to_owned(), - path: ResourcePath { - id: manifest_catalog.id.to_owned(), - r#type: manifest_catalog.r#type.to_owned(), - resource: selected.request.path.resource.to_owned(), - extra: selected - .request - .path - .extra - .to_owned() - .extend_one(&extra_prop, None), - }, - }, - }); + .to_owned() + .extend_one(&extra_prop, None), + }, + }, + }); let options = extra_prop .options .iter() diff --git a/src/models/common/resource_loadable.rs b/src/models/common/resource_loadable.rs index 34721ec4be..cb94ade7dd 100644 --- a/src/models/common/resource_loadable.rs +++ b/src/models/common/resource_loadable.rs @@ -4,8 +4,6 @@ use crate::models::common::{eq_update, Loadable}; use crate::runtime::msg::{Internal, Msg}; use crate::runtime::{EffectFuture, Effects, Env, EnvError, EnvFutureExt}; use crate::types::addon::{AggrRequest, Descriptor, ResourceRequest, ResourceResponse}; - -use enclose::enclose; use futures::FutureExt; use serde::Serialize; @@ -111,9 +109,15 @@ where Effects::future(EffectFuture::Concurrent( E::addon_transport(&request.base) .resource(&request.path) - .map(enclose!((request) move |result| { - Msg::Internal(Internal::ResourceRequestResult(request, Box::new(result))) - })) + .map({ + let request = request.clone(); + move |result| { + Msg::Internal(Internal::ResourceRequestResult( + request, + Box::new(result), + )) + } + }) .boxed_env(), )) } diff --git a/src/models/ctx/ctx.rs b/src/models/ctx/ctx.rs index 8820fe575b..f120b5f3a4 100644 --- a/src/models/ctx/ctx.rs +++ b/src/models/ctx/ctx.rs @@ -30,7 +30,6 @@ use crate::{ #[cfg(test)] use derivative::Derivative; -use enclose::enclose; use futures::{future, FutureExt, TryFutureExt}; use serde::Serialize; @@ -382,9 +381,10 @@ fn authenticate(auth_request: &AuthRequest) -> Effect { library_items_result: datastore_library_result, }) } - .map(enclose!((auth_request) move |result| { - Msg::Internal(Internal::CtxAuthResult(auth_request, result)) - })) + .map({ + let auth_request = auth_request.clone(); + move |result| Msg::Internal(Internal::CtxAuthResult(auth_request, result)) + }) .boxed_env(), ) .into() @@ -406,13 +406,16 @@ fn delete_session(auth_key: &AuthKey) -> Effect { APIResult::Ok(result) => future::ok(result), APIResult::Err(error) => future::err(CtxError::from(error)), }) - .map(enclose!((auth_key) move |result| match result { - Ok(_) => Msg::Event(Event::SessionDeleted { auth_key }), - Err(error) => Msg::Event(Event::Error { - error, - source: Box::new(Event::SessionDeleted { auth_key }), - }), - })) + .map({ + let auth_key = auth_key.clone(); + move |result| match result { + Ok(_) => Msg::Event(Event::SessionDeleted { auth_key }), + Err(error) => Msg::Event(Event::Error { + error, + source: Box::new(Event::SessionDeleted { auth_key }), + }), + } + }) .boxed_env(), ) .into() diff --git a/src/models/ctx/update_events.rs b/src/models/ctx/update_events.rs index 1c46ecac1a..522ebab066 100644 --- a/src/models/ctx/update_events.rs +++ b/src/models/ctx/update_events.rs @@ -1,5 +1,4 @@ use chrono::DateTime; -use enclose::enclose; use futures::{future, FutureExt, TryFutureExt}; use crate::constants::DISMISSED_EVENTS_STORAGE_KEY; @@ -140,15 +139,16 @@ fn push_dismissed_events_to_storage( ) -> Effect { EffectFuture::Sequential( E::set_storage(DISMISSED_EVENTS_STORAGE_KEY, Some(&dismissed_events)) - .map( - enclose!((dismissed_events.uid => uid) move |result| match result { + .map({ + let uid = dismissed_events.uid.clone(); + move |result| match result { Ok(_) => Msg::Event(Event::DismissedEventsPushedToStorage { uid }), Err(error) => Msg::Event(Event::Error { error: CtxError::from(error), source: Box::new(Event::DismissedEventsPushedToStorage { uid }), - }) - }), - ) + }), + } + }) .boxed_env(), ) .into() diff --git a/src/models/ctx/update_notifications.rs b/src/models/ctx/update_notifications.rs index ff7bc88401..bf278ef66c 100644 --- a/src/models/ctx/update_notifications.rs +++ b/src/models/ctx/update_notifications.rs @@ -3,7 +3,7 @@ use std::collections::{hash_map::Entry, HashMap}; use chrono::{DateTime, Duration, Utc}; use futures::FutureExt; use lazysort::SortedBy; -use once_cell::sync::Lazy; +use std::sync::LazyLock; use tracing::trace; use crate::{ @@ -28,7 +28,7 @@ use crate::{ }, }; -static REQUEST_LAST_VIDEOS_EVERY: Lazy = Lazy::new(|| Duration::hours(6)); +static REQUEST_LAST_VIDEOS_EVERY: LazyLock = LazyLock::new(|| Duration::hours(6)); pub fn update_notifications( notifications: &mut NotificationsBucket, diff --git a/src/models/ctx/update_profile.rs b/src/models/ctx/update_profile.rs index dccf37d7f4..b2eaaddc8d 100644 --- a/src/models/ctx/update_profile.rs +++ b/src/models/ctx/update_profile.rs @@ -1,7 +1,5 @@ -use std::collections::HashSet; - -use enclose::enclose; use futures::{future, FutureExt, TryFutureExt}; +use std::collections::HashSet; use crate::constants::{OFFICIAL_ADDONS, PROFILE_STORAGE_KEY}; use crate::models::ctx::{CtxError, CtxStatus, OtherError}; @@ -525,13 +523,16 @@ fn pull_addons_from_api(auth_key: &AuthKey) -> Effect { fn push_profile_to_storage(profile: &Profile) -> Effect { EffectFuture::Sequential( E::set_storage(PROFILE_STORAGE_KEY, Some(profile)) - .map(enclose!((profile.uid() => uid) move |result| match result { - Ok(_) => Msg::Event(Event::ProfilePushedToStorage { uid }), - Err(error) => Msg::Event(Event::Error { - error: CtxError::from(error), - source: Box::new(Event::ProfilePushedToStorage { uid }), - }) - })) + .map({ + let uid = profile.uid().clone(); + move |result| match result { + Ok(_) => Msg::Event(Event::ProfilePushedToStorage { uid }), + Err(error) => Msg::Event(Event::Error { + error: CtxError::from(error), + source: Box::new(Event::ProfilePushedToStorage { uid }), + }), + } + }) .boxed_env(), ) .into() diff --git a/src/models/ctx/update_search_history.rs b/src/models/ctx/update_search_history.rs index ee91295afc..7d93118453 100644 --- a/src/models/ctx/update_search_history.rs +++ b/src/models/ctx/update_search_history.rs @@ -1,4 +1,3 @@ -use enclose::enclose; use futures::FutureExt; use crate::constants::SEARCH_HISTORY_STORAGE_KEY; @@ -48,15 +47,16 @@ fn push_search_history_to_storage( ) -> Effect { EffectFuture::Sequential( E::set_storage(SEARCH_HISTORY_STORAGE_KEY, Some(&search_history)) - .map( - enclose!((search_history.uid => uid) move |result| match result { + .map({ + let uid = search_history.uid.clone(); + move |result| match result { Ok(_) => Msg::Event(Event::SearchHistoryPushedToStorage { uid }), Err(error) => Msg::Event(Event::Error { error: CtxError::from(error), source: Box::new(Event::SearchHistoryPushedToStorage { uid }), - }) - }), - ) + }), + } + }) .boxed_env(), ) .into() diff --git a/src/models/ctx/update_streams.rs b/src/models/ctx/update_streams.rs index 3f3206cbe5..06e73d5e7f 100644 --- a/src/models/ctx/update_streams.rs +++ b/src/models/ctx/update_streams.rs @@ -1,4 +1,3 @@ -use enclose::enclose; use futures::FutureExt; use std::collections::hash_map::Entry; @@ -105,13 +104,16 @@ pub fn update_streams( fn push_streams_to_storage(streams: &StreamsBucket) -> Effect { EffectFuture::Sequential( E::set_storage(STREAMS_STORAGE_KEY, Some(&streams)) - .map(enclose!((streams.uid => uid) move |result| match result { - Ok(_) => Msg::Event(Event::StreamsPushedToStorage { uid }), - Err(error) => Msg::Event(Event::Error { - error: CtxError::from(error), - source: Box::new(Event::StreamsPushedToStorage { uid }), - }) - })) + .map({ + let uid = streams.uid.clone(); + move |result| match result { + Ok(_) => Msg::Event(Event::StreamsPushedToStorage { uid }), + Err(error) => Msg::Event(Event::Error { + error: CtxError::from(error), + source: Box::new(Event::StreamsPushedToStorage { uid }), + }), + } + }) .boxed_env(), ) .into() diff --git a/src/models/link.rs b/src/models/link.rs index f61d0393ad..2f6d2df562 100644 --- a/src/models/link.rs +++ b/src/models/link.rs @@ -7,7 +7,6 @@ use crate::types::api::{ }; use derivative::Derivative; use derive_more::From; -use enclose::enclose; use futures::{future, FutureExt, TryFutureExt}; use serde::Serialize; use std::fmt; @@ -135,9 +134,10 @@ fn read_data(code: &str) -> Effect { APIResult::Ok(result) => future::ok(result), APIResult::Err(error) => future::err(LinkError::from(error)), }) - .map(enclose!((code.to_owned() => code) move |result| { - Msg::Internal(Internal::LinkDataResult(code, result)) - })) + .map({ + let code = code.to_owned(); + move |result| Msg::Internal(Internal::LinkDataResult(code, result)) + }) .boxed_env(), ) .into() diff --git a/src/models/local_search.rs b/src/models/local_search.rs index ae56966d1e..77d057dd53 100644 --- a/src/models/local_search.rs +++ b/src/models/local_search.rs @@ -1,6 +1,4 @@ //! Local autocompletion search - -use enclose::enclose; use futures::FutureExt; use http::Request; use magnet_url::Magnet; @@ -106,13 +104,14 @@ impl LocalSearch { EffectFuture::Concurrent( E::fetch::<_, SearchableItemsResponse>(request) - .map(enclose!((url) move |response| { - let result = response.map(|response| response.0); + .map({ + let url = url.clone(); + move |response| { + let result = response.map(|response| response.0); - Msg::Internal(Internal::LoadLocalSearchResult( - url, result, - )) - })) + Msg::Internal(Internal::LoadLocalSearchResult(url, result)) + } + }) .boxed_env(), ) .into() diff --git a/src/models/meta_details.rs b/src/models/meta_details.rs index a5cec33723..9c26fd7376 100644 --- a/src/models/meta_details.rs +++ b/src/models/meta_details.rs @@ -1,8 +1,6 @@ -use std::{borrow::Cow, marker::PhantomData}; - -use enclose::enclose; use futures::FutureExt; use serde::{Deserialize, Serialize}; +use std::{borrow::Cow, marker::PhantomData}; use stremio_watched_bitfield::WatchedBitField; @@ -526,11 +524,10 @@ fn get_rating(auth_key: AuthKey, meta_path: &ResourcePath) -> EffectFuture::Concurrent( E::fetch::<_, RatingGetStatusResponse>(request.into()) - .map(enclose!((meta_id) move |result| { - Msg::Internal(Internal::RatingGetStatusResult( - meta_id, result, - )) - })) + .map({ + let meta_id = meta_id.clone(); + move |result| Msg::Internal(Internal::RatingGetStatusResult(meta_id, result)) + }) .boxed_env(), ) .into() @@ -552,11 +549,10 @@ fn send_rating( EffectFuture::Concurrent( E::fetch::<_, RatingSendResponse>(request.into()) - .map(enclose!((meta_id) move |result| { - Msg::Internal(Internal::RatingSendResult( - meta_id, result, - )) - })) + .map({ + let meta_id = meta_id.clone(); + move |result| Msg::Internal(Internal::RatingSendResult(meta_id, result)) + }) .boxed_env(), ) .into() diff --git a/src/models/player.rs b/src/models/player.rs index 9d018c28e1..fe99bcc604 100644 --- a/src/models/player.rs +++ b/src/models/player.rs @@ -42,10 +42,10 @@ use derivative::Derivative; use itertools::Itertools; use serde::{Deserialize, Serialize}; -use once_cell::sync::Lazy; +use std::sync::LazyLock; /// The duration that must have passed in order for a library item to be updated. -pub static PUSH_TO_LIBRARY_EVERY: Lazy = Lazy::new(|| Duration::seconds(90)); +pub static PUSH_TO_LIBRARY_EVERY: LazyLock = LazyLock::new(|| Duration::seconds(90)); #[derive(Clone, Default, PartialEq, Eq, Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] @@ -1608,11 +1608,10 @@ fn send_watched(auth_key: AuthKey, meta_path: &ResourcePath) - EffectFuture::Concurrent( E::fetch::<_, RatingSendResponse>(request.into()) - .map(enclose::enclose!((meta_id) move |result| { - Msg::Internal(Internal::WatchedSendResult( - meta_id, result, - )) - })) + .map({ + let meta_id = meta_id.clone(); + move |result| Msg::Internal(Internal::WatchedSendResult(meta_id, result)) + }) .boxed_env(), ) .into() diff --git a/src/models/streaming_server.rs b/src/models/streaming_server.rs index b7ab6f5a1f..027ca7d6e3 100644 --- a/src/models/streaming_server.rs +++ b/src/models/streaming_server.rs @@ -1,4 +1,3 @@ -use enclose::enclose; use futures::{FutureExt, TryFutureExt}; use http::request::Request; use magnet_url::{Magnet, MagnetError}; @@ -414,11 +413,10 @@ fn get_settings(url: &Url) -> Effect { .expect("request builder failed"); EffectFuture::Concurrent( E::fetch::<_, SettingsResponse>(request) - .map(enclose!((url) move |result| { - Msg::Internal(Internal::StreamingServerSettingsResult( - url, result, - )) - })) + .map({ + let url = url.clone(); + move |result| Msg::Internal(Internal::StreamingServerSettingsResult(url, result)) + }) .boxed_env(), ) .into() @@ -432,9 +430,12 @@ fn get_playback_devices(url: &Url) -> Effect { EffectFuture::Concurrent( E::fetch::<_, Vec>(request) .map_ok(|resp| resp) - .map(enclose!((url) move |result| - Msg::Internal(Internal::StreamingServerPlaybackDevicesResult(url, result)) - )) + .map({ + let url = url.clone(); + move |result| { + Msg::Internal(Internal::StreamingServerPlaybackDevicesResult(url, result)) + } + }) .boxed_env(), ) .into() @@ -448,9 +449,10 @@ fn get_network_info(url: &Url) -> Effect { EffectFuture::Concurrent( E::fetch::<_, NetworkInfo>(request) .map_ok(|resp| resp) - .map(enclose!((url) move |result| - Msg::Internal(Internal::StreamingServerNetworkInfoResult(url, result)) - )) + .map({ + let url = url.clone(); + move |result| Msg::Internal(Internal::StreamingServerNetworkInfoResult(url, result)) + }) .boxed_env(), ) .into() @@ -464,9 +466,10 @@ fn get_device_info(url: &Url) -> Effect { EffectFuture::Concurrent( E::fetch::<_, DeviceInfo>(request) .map_ok(|resp| resp) - .map(enclose!((url) move |result| - Msg::Internal(Internal::StreamingServerDeviceInfoResult(url, result)) - )) + .map({ + let url = url.clone(); + move |result| Msg::Internal(Internal::StreamingServerDeviceInfoResult(url, result)) + }) .boxed_env(), ) .into() @@ -508,11 +511,12 @@ fn set_settings(url: &Url, settings: &Settings) -> Effect { EffectFuture::Concurrent( E::fetch::<_, SuccessResponse>(request) .map_ok(|_| ()) - .map(enclose!((url) move |result| { - Msg::Internal(Internal::StreamingServerUpdateSettingsResult( - url, result, - )) - })) + .map({ + let url = url.clone(); + move |result| { + Msg::Internal(Internal::StreamingServerUpdateSettingsResult(url, result)) + } + }) .boxed_env(), ) .into() @@ -536,11 +540,14 @@ fn create_magnet(url: &Url, info_hash: InfoHash, announce: &[S EffectFuture::Concurrent( create_magnet_request::(url.to_owned(), info_hash, announce.to_vec()) .map_ok(|_response| ()) - .map(enclose!((info_hash) move |result| { - Msg::Internal(Internal::StreamingServerCreateTorrentResult( - info_hash, result, - )) - })) + .map({ + let info_hash = info_hash.clone(); + move |result| { + Msg::Internal(Internal::StreamingServerCreateTorrentResult( + info_hash, result, + )) + } + }) .boxed_env(), ) .into() @@ -559,11 +566,14 @@ pub fn create_torrent_request( EffectFuture::Concurrent( E::fetch::<_, serde_json::Value>(request.into()) .map_ok(|_| ()) - .map(enclose!((info_hash) move |result| { - Msg::Internal(Internal::StreamingServerCreateTorrentResult( - info_hash, result, - )) - })) + .map({ + let info_hash = info_hash.clone(); + move |result| { + Msg::Internal(Internal::StreamingServerCreateTorrentResult( + info_hash, result, + )) + } + }) .boxed_env(), ) .into() @@ -647,16 +657,20 @@ fn parse_torrent(torrent: &[u8]) -> Result<(InfoHash, Vec), serde_bencod } fn get_torrent_statistics(url: &Url, request: &StatisticsRequest) -> Effect { - let fetch_fut = enclose!((url, request) async move { - let request = TorrentStatisticsRequest { - server_url: url, - request, - }; - - let statistics: Option = E::fetch(request.into()).await?; - - Ok(statistics) - }); + let fetch_fut = { + let url = url.clone(); + let request = request.clone(); + async move { + let request = TorrentStatisticsRequest { + server_url: url, + request, + }; + + let statistics: Option = E::fetch(request.into()).await?; + + Ok(statistics) + } + }; // let statistics_request = request.to_owned(); // It's happening when the engine is destroyed for inactivity: @@ -664,9 +678,16 @@ fn get_torrent_statistics(url: &Url, request: &StatisticsReque // it will create a new engine and return the correct stats EffectFuture::Concurrent( fetch_fut - .map(enclose!((url, request) move |result| - Msg::Internal(Internal::StreamingServerStatisticsResult((url, request), result)) - )) + .map({ + let url = url.clone(); + let request = request.clone(); + move |result| { + Msg::Internal(Internal::StreamingServerStatisticsResult( + (url, request), + result, + )) + } + }) .boxed_env(), ) .into() @@ -694,9 +715,9 @@ fn play_on_device(url: &Url, args: &PlayOnDeviceArgs) -> Effec EffectFuture::Concurrent( E::fetch::<_, serde_json::Value>(request) .map_ok(|_| ()) - .map(enclose!(() move |result| + .map(move |result| { Msg::Internal(Internal::StreamingServerPlayOnDeviceResult(device, result)) - )) + }) .boxed_env(), ) .into() @@ -719,9 +740,10 @@ fn get_https_endpoint( .expect("request builder failed"); EffectFuture::Concurrent( E::fetch::<_, GetHTTPSResponse>(request) - .map(enclose!((url) move |result| - Msg::Internal(Internal::StreamingServerGetHTTPSResult(url, result)) - )) + .map({ + let url = url.clone(); + move |result| Msg::Internal(Internal::StreamingServerGetHTTPSResult(url, result)) + }) .boxed_env(), ) .into() diff --git a/src/runtime/runtime.rs b/src/runtime/runtime.rs index 62c073719c..f1c06f7424 100644 --- a/src/runtime/runtime.rs +++ b/src/runtime/runtime.rs @@ -1,7 +1,6 @@ use crate::runtime::msg::{Action, Event, Msg}; use crate::runtime::{Effect, EffectFuture, Env, Model}; use derivative::Derivative; -use enclose::enclose; use futures::channel::mpsc::{channel, Receiver, Sender}; use futures::FutureExt; #[cfg(test)] @@ -86,25 +85,30 @@ where model.to_owned(), )); }; - effects - .into_iter() - .for_each(enclose!((self.clone() => runtime) move |effect| { - match effect { - Effect::Msg(msg) => { - runtime.handle_effect_output(*msg); - } - Effect::Future(EffectFuture::Sequential(future)) => { - E::exec_sequential(future.then(enclose!((runtime) move |msg| async move { + effects.into_iter().for_each({ + let runtime = self.clone(); + move |effect| match effect { + Effect::Msg(msg) => { + runtime.handle_effect_output(*msg); + } + Effect::Future(EffectFuture::Sequential(future)) => { + E::exec_sequential(future.then({ + let runtime = runtime.clone(); + move |msg| async move { runtime.handle_effect_output(msg); - }))) - }, - Effect::Future(EffectFuture::Concurrent(future)) => { - E::exec_concurrent(future.then(enclose!((runtime) move |msg| async move { + } + })) + } + Effect::Future(EffectFuture::Concurrent(future)) => { + E::exec_concurrent(future.then({ + let runtime = runtime.clone(); + move |msg| async move { runtime.handle_effect_output(msg); - }))) - } + } + })) } - })); + } + }); } fn handle_effect_output(&self, msg: Msg) { match msg { diff --git a/src/types/resource/stream.rs b/src/types/resource/stream.rs index c3bc1e7c68..142fd3f022 100644 --- a/src/types/resource/stream.rs +++ b/src/types/resource/stream.rs @@ -4,7 +4,6 @@ use std::{collections::HashMap, io::Write}; use tracing::trace; use base64::Engine; -use boolinator::Boolinator; use flate2::{ write::{ZlibDecoder, ZlibEncoder}, Compression, @@ -115,11 +114,11 @@ where impl Stream { pub fn youtube(video_id: &str) -> Option { + // video id is in format: yt_id:YT_CHANNEL_ID:YT_VIDEO_ID video_id .starts_with(YOUTUBE_ADDON_ID_PREFIX) - .as_option() - // video id is in format: yt_id:YT_CHANNEL_ID:YT_VIDEO_ID - .and_then(|_| video_id.split(':').nth(2)) + .then(|| video_id.split(':').nth(2)) + .flatten() .map(|yt_id| Self { source: StreamSource::YouTube { yt_id: yt_id.to_owned(), diff --git a/src/unit_tests/catalog_with_filters/load_action.rs b/src/unit_tests/catalog_with_filters/load_action.rs index 3ed94d1fdb..0043ffbc33 100644 --- a/src/unit_tests/catalog_with_filters/load_action.rs +++ b/src/unit_tests/catalog_with_filters/load_action.rs @@ -16,7 +16,6 @@ use crate::unit_tests::{ default_fetch_handler, Request, TestEnv, EVENTS, FETCH_HANDLER, REQUESTS, STATES, }; use assert_matches::assert_matches; -use enclose::enclose; use futures::future; use std::any::Any; use std::sync::{Arc, RwLock}; @@ -63,17 +62,16 @@ fn default_catalog() { 1000, ); let runtime = Arc::new(RwLock::new(runtime)); - TestEnv::run_with_runtime( - rx, - runtime.clone(), - enclose!((runtime) move || { + TestEnv::run_with_runtime(rx, runtime.clone(), { + let runtime = runtime.clone(); + move || { let runtime = runtime.read().unwrap(); runtime.dispatch(RuntimeAction { field: None, action: Action::Load(ActionLoad::CatalogWithFilters(None)), }); - }), - ); + } + }); let events = EVENTS.read().unwrap(); assert_eq!(events.len(), 2); assert_matches!( @@ -179,17 +177,17 @@ fn search_catalog() { }, }, }; - TestEnv::run_with_runtime( - rx, - runtime.clone(), - enclose!((runtime, selected => selected) move || { + TestEnv::run_with_runtime(rx, runtime.clone(), { + let runtime = runtime.clone(); + let selected = selected.clone(); + move || { let runtime = runtime.read().unwrap(); runtime.dispatch(RuntimeAction { field: None, action: Action::Load(ActionLoad::CatalogWithFilters(Some(selected))), }); - }), - ); + } + }); let events = EVENTS.read().unwrap(); assert_eq!(events.len(), 2); assert_matches!( diff --git a/src/unit_tests/ctx/notifications/update_notifications.rs b/src/unit_tests/ctx/notifications/update_notifications.rs index d92ffbfbe8..be4cee0222 100644 --- a/src/unit_tests/ctx/notifications/update_notifications.rs +++ b/src/unit_tests/ctx/notifications/update_notifications.rs @@ -6,11 +6,10 @@ use std::{ use assert_matches::assert_matches; use chrono::{TimeZone, Utc}; -use enclose::enclose; use futures::future; -use once_cell::sync::Lazy; use semver::Version; use serde::Deserialize; +use std::sync::LazyLock; use url::Url; use stremio_derive::Model; @@ -68,7 +67,7 @@ fn test_pull_notifications_and_play_in_player() { } /// Addon 1 with lastVideosIds catalog - pub static ADDON_1: Lazy = Lazy::new(|| Descriptor { + pub static ADDON_1: LazyLock = LazyLock::new(|| Descriptor { manifest: Manifest { id: "addon_1".to_owned(), version: Version::new(0, 0, 1), @@ -96,7 +95,7 @@ fn test_pull_notifications_and_play_in_player() { }); /// Meta item 1 with id `tt1` and 7 episodes from season 1 - pub static META_ITEM_1: Lazy = Lazy::new(|| MetaItem { + pub static META_ITEM_1: LazyLock = LazyLock::new(|| MetaItem { preview: MetaItemPreview { id: "tt1".to_string(), name: "name".to_string(), @@ -372,13 +371,17 @@ fn test_pull_notifications_test_cases() { for test in tests { let _env_lock = TestEnv::reset().expect("Should have exclusive lock to TestEnv"); - let fetch_handler = enclose!((test.network_requests => network_requests) move |request: Request| -> TryEnvFuture> { - if let Some(result) = network_requests.get(&request.url) { - return future::ok(Box::new(result.to_owned()) as Box).boxed_env(); - } + let fetch_handler = { + let network_requests = test.network_requests.clone(); + move |request: Request| -> TryEnvFuture> { + if let Some(result) = network_requests.get(&request.url) { + return future::ok(Box::new(result.to_owned()) as Box) + .boxed_env(); + } - default_fetch_handler(request) - }); + default_fetch_handler(request) + } + }; *FETCH_HANDLER.write().unwrap() = Box::new(fetch_handler); let (runtime, _rx) = Runtime::::new( @@ -516,17 +519,16 @@ fn test_dismiss_notification() { let expected_last_watched = Utc.with_ymd_and_hms(2023, 8, 14, 0, 0, 0).unwrap(); *NOW.write().unwrap() = expected_last_watched; - TestEnv::run_with_runtime( - rx, - runtime.clone(), - enclose!((runtime) move || { + TestEnv::run_with_runtime(rx, runtime.clone(), { + let runtime = runtime.clone(); + move || { let runtime = runtime.read().unwrap(); runtime.dispatch(RuntimeAction { field: None, action: Action::Ctx(ActionCtx::DismissNotificationItem("tt1".into())), }) - }), - ); + } + }); let events = EVENTS.read().unwrap(); assert_eq!(events.len(), 5); diff --git a/src/unit_tests/ctx/sync_library_with_api.rs b/src/unit_tests/ctx/sync_library_with_api.rs index f965ac492a..54a54ad5ef 100644 --- a/src/unit_tests/ctx/sync_library_with_api.rs +++ b/src/unit_tests/ctx/sync_library_with_api.rs @@ -20,8 +20,8 @@ use crate::unit_tests::{ use chrono::prelude::TimeZone; use chrono::{Duration, Utc}; use futures::future; -use once_cell::sync::Lazy; use serde::Deserialize; +use std::sync::LazyLock; use stremio_derive::Model; #[test] @@ -61,7 +61,7 @@ fn actionctx_synclibrarywithapi_with_user() { struct TestModel { ctx: Ctx, } - static REMOTE_ONLY_ITEM: Lazy = Lazy::new(|| LibraryItem { + static REMOTE_ONLY_ITEM: LazyLock = LazyLock::new(|| LibraryItem { id: "id1".to_owned(), r#type: "type".to_owned(), name: "name".to_owned(), @@ -74,7 +74,7 @@ fn actionctx_synclibrarywithapi_with_user() { state: Default::default(), behavior_hints: Default::default(), }); - static LOCAL_NEWER_ITEM: Lazy = Lazy::new(|| LibraryItem { + static LOCAL_NEWER_ITEM: LazyLock = LazyLock::new(|| LibraryItem { id: "id2".to_owned(), r#type: "type".to_owned(), name: "name".to_owned(), @@ -87,7 +87,7 @@ fn actionctx_synclibrarywithapi_with_user() { state: Default::default(), behavior_hints: Default::default(), }); - static REMOTE_NEWER_ITEM: Lazy = Lazy::new(|| LibraryItem { + static REMOTE_NEWER_ITEM: LazyLock = LazyLock::new(|| LibraryItem { id: "id3".to_owned(), r#type: "type".to_owned(), name: "name".to_owned(), @@ -100,7 +100,7 @@ fn actionctx_synclibrarywithapi_with_user() { state: Default::default(), behavior_hints: Default::default(), }); - static LOCAL_ONLY_ITEM: Lazy = Lazy::new(|| LibraryItem { + static LOCAL_ONLY_ITEM: LazyLock = LazyLock::new(|| LibraryItem { id: "id4".to_owned(), r#type: "type".to_owned(), name: "name".to_owned(), @@ -113,7 +113,7 @@ fn actionctx_synclibrarywithapi_with_user() { state: Default::default(), behavior_hints: Default::default(), }); - static LOCAL_OLD_REMOVED_ITEM: Lazy = Lazy::new(|| LibraryItem { + static LOCAL_OLD_REMOVED_ITEM: LazyLock = LazyLock::new(|| LibraryItem { id: "id5".to_owned(), r#type: "type".to_owned(), name: "name".to_owned(), @@ -126,7 +126,7 @@ fn actionctx_synclibrarywithapi_with_user() { state: Default::default(), behavior_hints: Default::default(), }); - static LOCAL_NEW_REMOVED_ITEM: Lazy = Lazy::new(|| LibraryItem { + static LOCAL_NEW_REMOVED_ITEM: LazyLock = LazyLock::new(|| LibraryItem { id: "id6".to_owned(), r#type: "type".to_owned(), name: "name".to_owned(), @@ -139,7 +139,7 @@ fn actionctx_synclibrarywithapi_with_user() { state: Default::default(), behavior_hints: Default::default(), }); - static LOCAL_OTHER_TYPE_ITEM: Lazy = Lazy::new(|| LibraryItem { + static LOCAL_OTHER_TYPE_ITEM: LazyLock = LazyLock::new(|| LibraryItem { id: "id7".to_owned(), r#type: "other".to_owned(), name: "name".to_owned(), diff --git a/src/unit_tests/data_export.rs b/src/unit_tests/data_export.rs index db2bf75124..27ea45a67c 100644 --- a/src/unit_tests/data_export.rs +++ b/src/unit_tests/data_export.rs @@ -16,7 +16,6 @@ use crate::unit_tests::{ default_fetch_handler, Request, TestEnv, EVENTS, FETCH_HANDLER, REQUESTS, STATES, }; use assert_matches::assert_matches; -use enclose::enclose; use futures::future; use std::any::Any; use std::sync::{Arc, RwLock}; @@ -67,17 +66,16 @@ fn data_export_with_user() { let data_export = DataExport::default(); let (runtime, rx) = Runtime::::new(TestModel { ctx, data_export }, vec![], 1000); let runtime = Arc::new(RwLock::new(runtime)); - TestEnv::run_with_runtime( - rx, - runtime.clone(), - enclose!((runtime) move || { + TestEnv::run_with_runtime(rx, runtime.clone(), { + let runtime = runtime.clone(); + move || { let runtime = runtime.read().unwrap(); runtime.dispatch(RuntimeAction { field: None, action: Action::Load(ActionLoad::DataExport), }); - }), - ); + } + }); let events = EVENTS.read().unwrap(); assert_eq!(events.len(), 2); @@ -151,17 +149,16 @@ fn data_export_without_a_user() { let data_export = DataExport::default(); let (runtime, rx) = Runtime::::new(TestModel { ctx, data_export }, vec![], 1000); let runtime = Arc::new(RwLock::new(runtime)); - TestEnv::run_with_runtime( - rx, - runtime.clone(), - enclose!((runtime) move || { + TestEnv::run_with_runtime(rx, runtime.clone(), { + let runtime = runtime.clone(); + move || { let runtime = runtime.read().unwrap(); runtime.dispatch(RuntimeAction { field: None, action: Action::Load(ActionLoad::DataExport), }); - }), - ); + } + }); let events = EVENTS.read().unwrap(); assert!(events.is_empty()); diff --git a/src/unit_tests/deep_links/library_item_deep_links.rs b/src/unit_tests/deep_links/library_item_deep_links.rs index a55ac5512f..3fd411df0a 100644 --- a/src/unit_tests/deep_links/library_item_deep_links.rs +++ b/src/unit_tests/deep_links/library_item_deep_links.rs @@ -1,5 +1,5 @@ use chrono::Utc; -use once_cell::sync::Lazy; +use std::sync::LazyLock; use stremio_serde_hex::{SerHex, Strict}; use crate::constants::STREAMING_SERVER_URL; @@ -16,12 +16,12 @@ use crate::types::streams::StreamsItem; const META_DETAILS_VIDEOS: &str = "stremio:///detail/series/tt13622776"; -static INFUSE_PLAYER_SETTINGS: Lazy = Lazy::new(|| Settings { +static INFUSE_PLAYER_SETTINGS: LazyLock = LazyLock::new(|| Settings { player_type: Some("infuse".to_owned()), ..Settings::default() }); -static TORRENT_STREAMS_ITEM: Lazy = Lazy::new(|| { +static TORRENT_STREAMS_ITEM: LazyLock = LazyLock::new(|| { let stream = Stream { source: StreamSource::Torrent { info_hash: SerHex::::from_hex("df2c94aec35f97943c4e432f25081b590cd35326") diff --git a/src/unit_tests/env.rs b/src/unit_tests/env.rs index ba94961c40..85e9109087 100644 --- a/src/unit_tests/env.rs +++ b/src/unit_tests/env.rs @@ -6,25 +6,24 @@ use std::{ }; use chrono::{DateTime, Utc}; -use enclose::enclose; use futures::{channel::mpsc::Receiver, future, Future, StreamExt, TryFutureExt}; -use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; +use std::sync::LazyLock; use crate::{ models::{ctx::Ctx, streaming_server::StreamingServer}, runtime::{Env, EnvFuture, EnvFutureExt, Model, Runtime, RuntimeEvent, TryEnvFuture}, }; -pub static FETCH_HANDLER: Lazy> = - Lazy::new(|| RwLock::new(Box::new(default_fetch_handler))); -pub static REQUESTS: Lazy>> = Lazy::new(Default::default); -pub static STORAGE: Lazy>> = Lazy::new(Default::default); -pub static EVENTS: Lazy>>> = - Lazy::new(Default::default); -pub static STATES: Lazy>>> = - Lazy::new(Default::default); -pub static NOW: Lazy>> = Lazy::new(|| RwLock::new(Utc::now())); +pub static FETCH_HANDLER: LazyLock> = + LazyLock::new(|| RwLock::new(Box::new(default_fetch_handler))); +pub static REQUESTS: LazyLock>> = LazyLock::new(Default::default); +pub static STORAGE: LazyLock>> = LazyLock::new(Default::default); +pub static EVENTS: LazyLock>>> = + LazyLock::new(Default::default); +pub static STATES: LazyLock>>> = + LazyLock::new(Default::default); +pub static NOW: LazyLock>> = LazyLock::new(|| RwLock::new(Utc::now())); pub static ENV_MUTEX: Mutex<()> = Mutex::new(()); pub type FetchHandler = @@ -69,16 +68,27 @@ impl TestEnv { env_mutex } pub fn run(runnable: F) { - tokio_current_thread::block_on_all(future::lazy(|_| { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("tokio runtime"); + let local = tokio::task::LocalSet::new(); + local.block_on(&rt, async move { runnable(); - })) + }); + rt.block_on(local); } pub fn run_with_runtime + Clone + Send + Sync + 'static, F: FnOnce()>( rx: Receiver>, runtime: Arc>>, runnable: F, ) { - tokio_current_thread::block_on_all(future::lazy(|_| { + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .expect("tokio runtime"); + let phase1 = tokio::task::LocalSet::new(); + phase1.block_on(&rt, async { { let runtime = runtime.read().expect("runtime read failed"); let state = runtime.model().expect("model read failed"); @@ -86,8 +96,10 @@ impl TestEnv { states.push(Box::new(state.to_owned()) as Box); } runnable(); - })); - tokio_current_thread::block_on_all(future::lazy(|_| { + }); + rt.block_on(phase1); + let phase2 = tokio::task::LocalSet::new(); + phase2.block_on(&rt, async { TestEnv::exec_concurrent(rx.for_each(move |event| { if let RuntimeEvent::NewState(_, state) = &event { let mut states = STATES.write().expect("states write failed"); @@ -97,11 +109,15 @@ impl TestEnv { events.push(Box::new(event) as Box); future::ready(()) })); - TestEnv::exec_concurrent(enclose!((runtime) async move { - let mut runtime = runtime.write().expect("runtime read failed"); - runtime.close().await.unwrap(); - })); - })); + TestEnv::exec_concurrent({ + let runtime = runtime.clone(); + async move { + let mut runtime = runtime.write().expect("runtime read failed"); + runtime.close().await.unwrap(); + } + }); + }); + rt.block_on(phase2); } } @@ -138,10 +154,10 @@ impl Env for TestEnv { future::ok(()).boxed_env() } fn exec_concurrent + 'static>(future: F) { - tokio_current_thread::spawn(future); + tokio::task::spawn_local(future); } fn exec_sequential + 'static>(future: F) { - tokio_current_thread::spawn(future); + tokio::task::spawn_local(future); } fn now() -> DateTime { *NOW.read().unwrap() diff --git a/src/unit_tests/meta_details/override_selected.rs b/src/unit_tests/meta_details/override_selected.rs index 267c1c10e4..0cde1943d5 100644 --- a/src/unit_tests/meta_details/override_selected.rs +++ b/src/unit_tests/meta_details/override_selected.rs @@ -8,7 +8,6 @@ use crate::types::profile::Profile; use crate::types::resource::{MetaItem, MetaItemBehaviorHints, MetaItemPreview}; use crate::unit_tests::{default_fetch_handler, Request, TestEnv, FETCH_HANDLER, STATES}; use assert_matches::assert_matches; -use enclose::enclose; use futures::future; use std::any::Any; use std::sync::{Arc, RwLock}; @@ -65,10 +64,9 @@ fn override_selected_default_video_id() { 1000, ); let runtime = Arc::new(RwLock::new(runtime)); - TestEnv::run_with_runtime( - rx, - runtime.clone(), - enclose!((runtime) move || { + TestEnv::run_with_runtime(rx, runtime.clone(), { + let runtime = runtime.clone(); + move || { let runtime = runtime.read().unwrap(); runtime.dispatch(RuntimeAction { field: None, @@ -77,14 +75,14 @@ fn override_selected_default_video_id() { resource: META_RESOURCE_NAME.to_owned(), r#type: "movie".to_owned(), id: "tt1".to_owned(), - extra: vec![] + extra: vec![], }, stream_path: None, guess_stream: true, })), }); - }), - ); + } + }); let states = STATES.read().unwrap(); let states = states .iter() @@ -160,10 +158,9 @@ fn override_selected_meta_id() { 1000, ); let runtime = Arc::new(RwLock::new(runtime)); - TestEnv::run_with_runtime( - rx, - runtime.clone(), - enclose!((runtime) move || { + TestEnv::run_with_runtime(rx, runtime.clone(), { + let runtime = runtime.clone(); + move || { let runtime = runtime.read().unwrap(); runtime.dispatch(RuntimeAction { field: None, @@ -172,14 +169,14 @@ fn override_selected_meta_id() { resource: META_RESOURCE_NAME.to_owned(), r#type: "movie".to_owned(), id: "tt1".to_owned(), - extra: vec![] + extra: vec![], }, stream_path: None, guess_stream: true, })), }); - }), - ); + } + }); let states = STATES.read().unwrap(); let states = states .iter() diff --git a/stremio-core-web/Cargo.toml b/stremio-core-web/Cargo.toml index aebac989d9..a024ecabb0 100644 --- a/stremio-core-web/Cargo.toml +++ b/stremio-core-web/Cargo.toml @@ -38,16 +38,13 @@ regex = {version = "1.8", optional = true } # used for Env impl hex = { version = "0.4", optional = true } either = "1.6.*" -enclose = "1.1.*" itertools = "0.14.*" -boolinator = "2.4.*" # WASM wasm-bindgen = { version = "=0.2.78", features = ["serde-serialize"], optional = true } wasm-bindgen-futures = { version = "0.4", optional = true } gloo-utils = { version = "0.2", features = ["serde"], optional = true } -once_cell = "1" # panic hook for wasm console_error_panic_hook = { version = "0.1.*", optional = true } diff --git a/stremio-core-web/src/env.rs b/stremio-core-web/src/env.rs index 5079a4eb0f..31da274f48 100644 --- a/stremio-core-web/src/env.rs +++ b/stremio-core-web/src/env.rs @@ -4,10 +4,10 @@ use chrono::{offset::TimeZone, DateTime, Utc}; use futures::{future, Future, FutureExt, TryFutureExt}; use gloo_utils::format::JsValueSerdeExt; use http::{Method, Request}; -use once_cell::sync::Lazy; use regex::Regex; use serde::{Deserialize, Serialize}; use serde_json::json; +use std::sync::LazyLock; use tracing::trace; use url::Url; @@ -50,10 +50,10 @@ extern "C" { async fn local_storage_remove_item(key: String) -> Result<(), JsValue>; } -static INSTALLATION_ID: Lazy>> = Lazy::new(Default::default); -static VISIT_ID: Lazy = Lazy::new(|| hex::encode(WebEnv::random_buffer(10))); -static ANALYTICS: Lazy> = Lazy::new(Default::default); -static PLAYER_REGEX: Lazy = Lazy::new(|| { +static INSTALLATION_ID: LazyLock>> = LazyLock::new(Default::default); +static VISIT_ID: LazyLock = LazyLock::new(|| hex::encode(WebEnv::random_buffer(10))); +static ANALYTICS: LazyLock> = LazyLock::new(Default::default); +static PLAYER_REGEX: LazyLock = LazyLock::new(|| { Regex::new(r"^/player/([^/]*)(?:/([^/]*)/([^/]*)/([^/]*)/([^/]*)/([^/]*))?$") .expect("Player Regex failed to build") }); diff --git a/stremio-core-web/src/model/serialize_discover.rs b/stremio-core-web/src/model/serialize_discover.rs index ed42894ae7..7c8db0d216 100644 --- a/stremio-core-web/src/model/serialize_discover.rs +++ b/stremio-core-web/src/model/serialize_discover.rs @@ -1,7 +1,6 @@ #[cfg(feature = "wasm")] use { crate::model::deep_links_ext::DeepLinksExt, - boolinator::Boolinator, gloo_utils::format::JsValueSerdeExt, itertools::Itertools, stremio_core::deep_links::{DiscoverDeepLinks, MetaItemDeepLinks, StreamDeepLinks}, @@ -171,7 +170,7 @@ pub fn serialize_discover( .collect(), next_page: discover.selectable.next_page.is_some(), }, - catalog: (!discover.catalog.is_empty()).as_option().map(|_| { + catalog: (!discover.catalog.is_empty()).then(|| { let first_page = discover .catalog .first() diff --git a/stremio-core-web/src/stremio_core_web.rs b/stremio-core-web/src/stremio_core_web.rs index 175bc3a950..a83c9f7241 100644 --- a/stremio-core-web/src/stremio_core_web.rs +++ b/stremio-core-web/src/stremio_core_web.rs @@ -1,10 +1,8 @@ -use std::sync::RwLock; - -use enclose::enclose; use futures::{future, try_join, FutureExt, StreamExt}; use gloo_utils::format::JsValueSerdeExt; -use once_cell::sync::Lazy; use serde::Serialize; +use std::sync::LazyLock; +use std::sync::RwLock; use tracing::{error, info, Level}; use tracing_wasm::WASMLayerConfigBuilder; use wasm_bindgen::{prelude::wasm_bindgen, JsValue, UnwrapThrowExt}; @@ -36,8 +34,8 @@ use crate::{ }; #[allow(clippy::type_complexity)] -static RUNTIME: Lazy, EnvError>>>> = - Lazy::new(Default::default); +static RUNTIME: LazyLock, EnvError>>>> = + LazyLock::new(Default::default); #[derive(Debug, Clone, Serialize)] pub enum DispatchError { @@ -166,8 +164,9 @@ pub async fn initialize_runtime(emit_to_ui: js_sys::Function) -> Result<(), JsVa ); WebEnv::exec_concurrent(rx.for_each(move |event| { if let RuntimeEvent::CoreEvent(event) = &event { - WebEnv::exec_concurrent(WebEnv::get_location_hash().then( - enclose!((event) move |location_hash| async move { + WebEnv::exec_concurrent(WebEnv::get_location_hash().then({ + let event = event.clone(); + move |location_hash| async move { let runtime = RUNTIME.read().expect("runtime read failed"); let runtime = runtime .as_ref() @@ -175,17 +174,25 @@ pub async fn initialize_runtime(emit_to_ui: js_sys::Function) -> Result<(), JsVa .as_ref() .expect("runtime is not ready"); let model = runtime.model().expect("model read failed"); - let path = location_hash.split('#').last().map(|path| path.to_owned()).unwrap_or_default(); + let path = location_hash + .split('#') + .last() + .map(|path| path.to_owned()) + .unwrap_or_default(); WebEnv::emit_to_analytics( &WebEvent::CoreEvent(Box::new(event.to_owned())), &model, - &path + &path, ); - }), - )); + } + })); }; emit_to_ui - .call1(&JsValue::NULL, &::from_serde(&event).expect("Event handler: JsValue from Event")) + .call1( + &JsValue::NULL, + &::from_serde(&event) + .expect("Event handler: JsValue from Event"), + ) .expect("emit event failed"); future::ready(()) })); diff --git a/stremio-watched-bitfield/Cargo.toml b/stremio-watched-bitfield/Cargo.toml index 3f37075bfe..783808c6d7 100644 --- a/stremio-watched-bitfield/Cargo.toml +++ b/stremio-watched-bitfield/Cargo.toml @@ -4,8 +4,8 @@ version = "0.1.0" edition = "2021" publish = false -# because of `dep:serde` supported after 1.60 -rust-version = "1.60" +# Aligned with the workspace MSRV. +rust-version = "1.85" [lib] doctest = false