From d9969a9f4f76e394b0d5b283c0f329669c051834 Mon Sep 17 00:00:00 2001 From: "a.r" <887320+twwn@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:35:06 +0200 Subject: [PATCH] =?UTF-8?q?lazy=5Fstatic=20=E2=86=92=20once=5Fcell=20?= =?UTF-8?q?=E2=86=92=20stabilized=20versions=20(#3447)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Anki: Replace lazy_static with once_cell Unify to once_cell, lazy_static's replacement. The latter in unmaintained. * Anki: Replace once_cell with stabilized LazyCell / LazyLock as far as possible Since 1.80: https://github.com/rust-lang/rust/issues/109736 and https://github.com/rust-lang/rust/pull/98165 Non-Thread-Safe Lazy → std::cell::LazyCell https://doc.rust-lang.org/nightly/std/cell/struct.LazyCell.html Thread-safe SyncLazy → std::sync::LazyLock https://doc.rust-lang.org/nightly/std/sync/struct.LazyLock.html The compiler accepted LazyCell only in minilints. The final use in rslib/src/log.rs couldn't be replaced since get_or_try_init has not yet been standardized: https://github.com/rust-lang/rust/issues/109737 * Declare correct MSRV (dae) Some of our deps require newer Rust versions, so this was misleading. Updating the MSRV also allows us to use .inspect() on Option now --- Cargo.lock | 6 -- Cargo.toml | 2 +- build/ninja_gen/Cargo.toml | 1 - build/ninja_gen/src/input.rs | 5 +- ftl/Cargo.toml | 1 - ftl/src/garbage_collection.rs | 18 ++-- rslib/Cargo.toml | 1 - rslib/linkchecker/Cargo.toml | 1 - rslib/linkchecker/tests/links.rs | 7 +- rslib/proto_gen/Cargo.toml | 1 - rslib/proto_gen/src/lib.rs | 6 +- rslib/src/ankidroid/db.rs | 15 ++- rslib/src/ankihub/login.rs | 11 +-- rslib/src/backend/mod.rs | 6 +- rslib/src/cloze.rs | 12 +-- rslib/src/import_export/text/csv/export.rs | 17 ++-- rslib/src/latex.rs | 20 ++-- rslib/src/lib.rs | 8 +- rslib/src/media/check.rs | 4 +- rslib/src/media/files.rs | 28 +++--- rslib/src/notetype/checks.rs | 6 +- rslib/src/notetype/mod.rs | 17 ++-- rslib/src/scheduler/queue/learning.rs | 3 +- rslib/src/scheduler/queue/main.rs | 3 +- rslib/src/scheduler/reviews.rs | 12 +-- rslib/src/search/parser.rs | 31 +++--- rslib/src/search/writer.rs | 7 +- rslib/src/storage/deck/mod.rs | 3 +- rslib/src/sync/collection/tests.rs | 6 +- rslib/src/sync/http_server/user.rs | 3 +- .../sync/media/database/server/meta/mod.rs | 3 +- rslib/src/sync/request/mod.rs | 8 +- rslib/src/template.rs | 12 +-- rslib/src/template_filters.rs | 6 +- rslib/src/text.rs | 98 ++++++++++--------- rslib/src/typeanswer.rs | 4 +- rslib/src/version.rs | 19 ++-- tools/minilints/Cargo.toml | 1 - tools/minilints/src/main.rs | 8 +- 39 files changed, 200 insertions(+), 220 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9643c9bff..b0a42b53a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -134,7 +134,6 @@ dependencies = [ "id_tree", "inflections", "itertools 0.13.0", - "lazy_static", "nom", "num_cpus", "num_enum", @@ -249,7 +248,6 @@ dependencies = [ "camino", "inflections", "itertools 0.13.0", - "once_cell", "prost-reflect", "prost-types", "regex", @@ -1891,7 +1889,6 @@ dependencies = [ "clap", "fluent-syntax", "itertools 0.13.0", - "lazy_static", "regex", "serde_json", "snafu", @@ -3167,7 +3164,6 @@ dependencies = [ "anki", "futures", "itertools 0.13.0", - "lazy_static", "linkcheck", "regex", "reqwest 0.12.7", @@ -3445,7 +3441,6 @@ dependencies = [ "anki_process", "anyhow", "camino", - "once_cell", "walkdir", "which", ] @@ -3609,7 +3604,6 @@ dependencies = [ "dunce", "globset", "itertools 0.13.0", - "lazy_static", "maplit", "num_cpus", "walkdir", diff --git a/Cargo.toml b/Cargo.toml index 513340be0..44a7c46fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ version = "0.0.0" authors = ["Ankitects Pty Ltd and contributors "] edition = "2021" license = "AGPL-3.0-or-later" -rust-version = "1.65" +rust-version = "1.80" [workspace] members = [ diff --git a/build/ninja_gen/Cargo.toml b/build/ninja_gen/Cargo.toml index 408b187ab..7757116c6 100644 --- a/build/ninja_gen/Cargo.toml +++ b/build/ninja_gen/Cargo.toml @@ -14,7 +14,6 @@ camino.workspace = true dunce.workspace = true globset.workspace = true itertools.workspace = true -lazy_static.workspace = true maplit.workspace = true num_cpus.workspace = true walkdir.workspace = true diff --git a/build/ninja_gen/src/input.rs b/build/ninja_gen/src/input.rs index 00ae60483..e43093773 100644 --- a/build/ninja_gen/src/input.rs +++ b/build/ninja_gen/src/input.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use std::fmt::Display; +use std::sync::LazyLock; use camino::Utf8PathBuf; @@ -118,9 +119,7 @@ pub struct Glob { pub exclude: Option, } -lazy_static::lazy_static! { - static ref CACHED_FILES: Vec = cache_files(); -} +static CACHED_FILES: LazyLock> = LazyLock::new(cache_files); /// Walking the source tree once instead of for each glob yields ~4x speed /// improvements. diff --git a/ftl/Cargo.toml b/ftl/Cargo.toml index 2bd9ff5e1..55252e60d 100644 --- a/ftl/Cargo.toml +++ b/ftl/Cargo.toml @@ -16,7 +16,6 @@ camino.workspace = true clap.workspace = true fluent-syntax.workspace = true itertools.workspace = true -lazy_static.workspace = true regex.workspace = true serde_json.workspace = true snafu.workspace = true diff --git a/ftl/src/garbage_collection.rs b/ftl/src/garbage_collection.rs index 3cbfbb4fa..17d47e430 100644 --- a/ftl/src/garbage_collection.rs +++ b/ftl/src/garbage_collection.rs @@ -6,6 +6,7 @@ use std::fs; use std::io::BufReader; use std::iter::FromIterator; use std::path::PathBuf; +use std::sync::LazyLock; use anki_io::create_file; use anyhow::Context; @@ -14,7 +15,6 @@ use clap::Args; use fluent_syntax::ast; use fluent_syntax::ast::Resource; use fluent_syntax::parser; -use lazy_static::lazy_static; use regex::Regex; use walkdir::DirEntry; use walkdir::WalkDir; @@ -144,9 +144,8 @@ fn extract_nested_messages_and_terms( ftl_roots: &[impl AsRef], used_ftls: &mut HashSet, ) { - lazy_static! { - static ref REFERENCE: Regex = Regex::new(r"\{\s*-?([-0-9a-z]+)\s*\}").unwrap(); - } + static REFERENCE: LazyLock = + LazyLock::new(|| Regex::new(r"\{\s*-?([-0-9a-z]+)\s*\}").unwrap()); for_files_with_ending(ftl_roots, ".ftl", |entry| { let source = fs::read_to_string(entry.path()).expect("file not readable"); for caps in REFERENCE.captures_iter(&source) { @@ -198,11 +197,12 @@ fn entry_use_check(used_ftls: &HashSet) -> impl Fn(&ast::Entry<&str>) -> } fn extract_references_from_file(refs: &mut HashSet, entry: &DirEntry) { - lazy_static! { - static ref SNAKECASE_TR: Regex = Regex::new(r"\Wtr\s*\.([0-9a-z_]+)\W").unwrap(); - static ref CAMELCASE_TR: Regex = Regex::new(r"\Wtr2?\.([0-9A-Za-z_]+)\W").unwrap(); - static ref DESIGNER_STYLE_TR: Regex = Regex::new(r"([0-9a-z_]+)").unwrap(); - } + static SNAKECASE_TR: LazyLock = + LazyLock::new(|| Regex::new(r"\Wtr\s*\.([0-9a-z_]+)\W").unwrap()); + static CAMELCASE_TR: LazyLock = + LazyLock::new(|| Regex::new(r"\Wtr2?\.([0-9A-Za-z_]+)\W").unwrap()); + static DESIGNER_STYLE_TR: LazyLock = + LazyLock::new(|| Regex::new(r"([0-9a-z_]+)").unwrap()); let file_name = entry.file_name().to_str().expect("non-unicode filename"); diff --git a/rslib/Cargo.toml b/rslib/Cargo.toml index 39f8393f7..fb14826b4 100644 --- a/rslib/Cargo.toml +++ b/rslib/Cargo.toml @@ -69,7 +69,6 @@ htmlescape.workspace = true hyper.workspace = true id_tree.workspace = true itertools.workspace = true -lazy_static.workspace = true nom.workspace = true num_cpus.workspace = true num_enum.workspace = true diff --git a/rslib/linkchecker/Cargo.toml b/rslib/linkchecker/Cargo.toml index c9a5a9ebb..70ff22043 100644 --- a/rslib/linkchecker/Cargo.toml +++ b/rslib/linkchecker/Cargo.toml @@ -11,7 +11,6 @@ rust-version.workspace = true anki.workspace = true futures.workspace = true itertools.workspace = true -lazy_static.workspace = true linkcheck.workspace = true regex.workspace = true reqwest.workspace = true diff --git a/rslib/linkchecker/tests/links.rs b/rslib/linkchecker/tests/links.rs index 04482c99a..48656ace4 100644 --- a/rslib/linkchecker/tests/links.rs +++ b/rslib/linkchecker/tests/links.rs @@ -6,6 +6,7 @@ use std::borrow::Cow; use std::env; use std::iter; +use std::sync::LazyLock; use std::time::Duration; use anki::links::help_page_link_suffix; @@ -13,7 +14,6 @@ use anki::links::help_page_to_link; use anki::links::HelpPage; use futures::StreamExt; use itertools::Itertools; -use lazy_static::lazy_static; use linkcheck::validation::check_web; use linkcheck::validation::Context; use linkcheck::validation::Reason; @@ -70,9 +70,8 @@ impl From<&'static str> for CheckableUrl { } fn ts_help_pages() -> impl Iterator { - lazy_static! { - static ref QUOTED_URL: Regex = Regex::new("\"(http.+)\"").unwrap(); - } + static QUOTED_URL: LazyLock = LazyLock::new(|| Regex::new("\"(http.+)\"").unwrap()); + QUOTED_URL .captures_iter(include_str!("../../../ts/lib/tslib/help-page.ts")) .map(|caps| caps.get(1).unwrap().as_str()) diff --git a/rslib/proto_gen/Cargo.toml b/rslib/proto_gen/Cargo.toml index 552de7e14..5f1ee9817 100644 --- a/rslib/proto_gen/Cargo.toml +++ b/rslib/proto_gen/Cargo.toml @@ -15,7 +15,6 @@ anyhow.workspace = true camino.workspace = true inflections.workspace = true itertools.workspace = true -once_cell.workspace = true prost-reflect.workspace = true prost-types.workspace = true regex.workspace = true diff --git a/rslib/proto_gen/src/lib.rs b/rslib/proto_gen/src/lib.rs index f32505236..83bbc5f10 100644 --- a/rslib/proto_gen/src/lib.rs +++ b/rslib/proto_gen/src/lib.rs @@ -7,6 +7,7 @@ use std::collections::HashMap; use std::env; use std::path::PathBuf; +use std::sync::LazyLock; use anki_io::read_to_string; use anki_io::write_file_if_changed; @@ -16,7 +17,6 @@ use camino::Utf8Path; use inflections::Inflect; use itertools::Either; use itertools::Itertools; -use once_cell::sync::Lazy; use prost_reflect::DescriptorPool; use prost_reflect::MessageDescriptor; use prost_reflect::MethodDescriptor; @@ -238,8 +238,8 @@ pub fn add_must_use_annotations_to_file(path: &Utf8Path, is_empty: E) -> Resu where E: Fn(&Utf8Path, &str) -> bool, { - static MESSAGE_OR_ENUM_RE: Lazy = - Lazy::new(|| Regex::new(r"pub (struct|enum) ([[:alnum:]]+?)\s").unwrap()); + static MESSAGE_OR_ENUM_RE: LazyLock = + LazyLock::new(|| Regex::new(r"pub (struct|enum) ([[:alnum:]]+?)\s").unwrap()); let contents = read_to_string(path)?; let contents = MESSAGE_OR_ENUM_RE.replace_all(&contents, |caps: &Captures| { let is_enum = caps.get(1).unwrap().as_str() == "enum"; diff --git a/rslib/src/ankidroid/db.rs b/rslib/src/ankidroid/db.rs index 32b069ef8..e14355074 100644 --- a/rslib/src/ankidroid/db.rs +++ b/rslib/src/ankidroid/db.rs @@ -5,6 +5,7 @@ use std::collections::HashMap; use std::mem::size_of; use std::sync::atomic::AtomicI32; use std::sync::atomic::Ordering; +use std::sync::LazyLock; use std::sync::Mutex; use anki_proto::ankidroid::sql_value::Data; @@ -16,7 +17,6 @@ use itertools::FoldWhile; use itertools::FoldWhile::Continue; use itertools::FoldWhile::Done; use itertools::Itertools; -use lazy_static::lazy_static; use rusqlite::ToSql; use serde::Deserialize; @@ -110,10 +110,8 @@ fn select_slice_of_size<'a>( type SequenceNumber = i32; -lazy_static! { - static ref HASHMAP: Mutex>> = - Mutex::new(HashMap::new()); -} +static HASHMAP: LazyLock>>> = + LazyLock::new(|| Mutex::new(HashMap::new())); pub(crate) fn flush_single_result(col: &Collection, sequence_number: i32) { HASHMAP @@ -244,10 +242,9 @@ pub(crate) fn next_sequence_number() -> i32 { SEQUENCE_NUMBER.fetch_add(1, Ordering::SeqCst) } -lazy_static! { - // same as we get from io.requery.android.database.CursorWindow.sCursorWindowSize - static ref DB_COMMAND_PAGE_SIZE: Mutex = Mutex::new(1024 * 1024 * 2); -} +// same as we get from +// io.requery.android.database.CursorWindow.sCursorWindowSize +static DB_COMMAND_PAGE_SIZE: LazyLock> = LazyLock::new(|| Mutex::new(1024 * 1024 * 2)); pub(crate) fn set_max_page_size(size: usize) { let mut state = DB_COMMAND_PAGE_SIZE.lock().expect("Could not lock mutex"); diff --git a/rslib/src/ankihub/login.rs b/rslib/src/ankihub/login.rs index 42aacec59..972d00f09 100644 --- a/rslib/src/ankihub/login.rs +++ b/rslib/src/ankihub/login.rs @@ -1,7 +1,8 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -use lazy_static::lazy_static; +use std::sync::LazyLock; + use regex::Regex; use reqwest::Client; use serde; @@ -31,11 +32,9 @@ pub async fn ankihub_login>( client: Client, ) -> Result { let client = HttpAnkiHubClient::new("", client); - lazy_static! { - static ref EMAIL_RE: Regex = - Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$") - .unwrap(); - } + static EMAIL_RE: LazyLock = LazyLock::new(|| { + Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap() + }); let mut request = LoginRequest { username: None, email: None, diff --git a/rslib/src/backend/mod.rs b/rslib/src/backend/mod.rs index cff86d7e4..42260b150 100644 --- a/rslib/src/backend/mod.rs +++ b/rslib/src/backend/mod.rs @@ -19,10 +19,10 @@ use std::ops::Deref; use std::result; use std::sync::Arc; use std::sync::Mutex; +use std::sync::OnceLock; use std::thread::JoinHandle; use futures::future::AbortHandle; -use once_cell::sync::OnceCell; use prost::Message; use reqwest::Client; use tokio::runtime; @@ -53,7 +53,7 @@ pub struct BackendInner { server: bool, sync_abort: Mutex>, progress_state: Arc>, - runtime: OnceCell, + runtime: OnceLock, state: Mutex, backup_task: Mutex>>>, media_sync_task: Mutex>>>, @@ -88,7 +88,7 @@ impl Backend { want_abort: false, last_progress: None, })), - runtime: OnceCell::new(), + runtime: OnceLock::new(), state: Mutex::new(BackendState::default()), backup_task: Mutex::new(None), media_sync_task: Mutex::new(None), diff --git a/rslib/src/cloze.rs b/rslib/src/cloze.rs index 1fb97ed85..8bcf5765f 100644 --- a/rslib/src/cloze.rs +++ b/rslib/src/cloze.rs @@ -5,11 +5,11 @@ use std::borrow::Cow; use std::collections::HashMap; use std::collections::HashSet; use std::fmt::Write; +use std::sync::LazyLock; use anki_proto::image_occlusion::get_image_occlusion_note_response::ImageOcclusion; use anki_proto::image_occlusion::get_image_occlusion_note_response::ImageOcclusionShape; use htmlescape::encode_attribute; -use lazy_static::lazy_static; use nom::branch::alt; use nom::bytes::complete::tag; use nom::bytes::complete::take_while; @@ -24,16 +24,16 @@ use crate::latex::contains_latex; use crate::template::RenderContext; use crate::text::strip_html_preserving_entities; -lazy_static! { - static ref MATHJAX: Regex = Regex::new( +static MATHJAX: LazyLock = LazyLock::new(|| { + Regex::new( r"(?xsi) (\\[(\[]) # 1 = mathjax opening tag (.*?) # 2 = inner content (\\[])]) # 3 = mathjax closing tag - " + ", ) - .unwrap(); -} + .unwrap() +}); mod mathjax_caps { pub const OPENING_TAG: usize = 1; diff --git a/rslib/src/import_export/text/csv/export.rs b/rslib/src/import_export/text/csv/export.rs index 0f1e712e8..edf3d3ca1 100644 --- a/rslib/src/import_export/text/csv/export.rs +++ b/rslib/src/import_export/text/csv/export.rs @@ -6,10 +6,10 @@ use std::collections::HashMap; use std::fs::File; use std::io::Write; use std::sync::Arc; +use std::sync::LazyLock; use anki_proto::import_export::ExportNoteCsvRequest; use itertools::Itertools; -use lazy_static::lazy_static; use regex::Regex; use super::metadata::Delimiter; @@ -156,23 +156,22 @@ fn field_to_record_field(field: &str, with_html: bool) -> Cow { } fn strip_redundant_sections(text: &str) -> Cow { - lazy_static! { - static ref RE: Regex = Regex::new( + static RE: LazyLock = LazyLock::new(|| { + Regex::new( r"(?isx) # style elements | \[\[type:[^]]+\]\] # type replacements - " + ", ) - .unwrap(); - } + .unwrap() + }); RE.replace_all(text.as_ref(), "") } fn strip_answer_side_question(text: &str) -> Cow { - lazy_static! { - static ref RE: Regex = Regex::new(r"(?is)^.*
\n*").unwrap(); - } + static RE: LazyLock = + LazyLock::new(|| Regex::new(r"(?is)^.*
\n*").unwrap()); RE.replace_all(text.as_ref(), "") } diff --git a/rslib/src/latex.rs b/rslib/src/latex.rs index f6a03c107..3ebeebf8a 100644 --- a/rslib/src/latex.rs +++ b/rslib/src/latex.rs @@ -2,8 +2,8 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use std::borrow::Cow; +use std::sync::LazyLock; -use lazy_static::lazy_static; use regex::Captures; use regex::Regex; @@ -11,26 +11,28 @@ use crate::cloze::expand_clozes_to_reveal_latex; use crate::media::files::sha1_of_data; use crate::text::strip_html; -lazy_static! { - pub(crate) static ref LATEX: Regex = Regex::new( +pub(crate) static LATEX: LazyLock = LazyLock::new(|| { + Regex::new( r"(?xsi) \[latex\](.+?)\[/latex\] # 1 - standard latex | \[\$\](.+?)\[/\$\] # 2 - inline math | \[\$\$\](.+?)\[/\$\$\] # 3 - math environment - " + ", ) - .unwrap(); - static ref LATEX_NEWLINES: Regex = Regex::new( + .unwrap() +}); +static LATEX_NEWLINES: LazyLock = LazyLock::new(|| { + Regex::new( r#"(?xi) |
- "# + "#, ) - .unwrap(); -} + .unwrap() +}); pub(crate) fn contains_latex(text: &str) -> bool { LATEX.is_match(text) diff --git a/rslib/src/lib.rs b/rslib/src/lib.rs index 8d3251f49..2258c3592 100644 --- a/rslib/src/lib.rs +++ b/rslib/src/lib.rs @@ -52,9 +52,7 @@ pub mod undo; pub mod version; use std::env; +use std::sync::LazyLock; -use lazy_static::lazy_static; - -lazy_static! { - pub(crate) static ref PYTHON_UNIT_TESTS: bool = env::var("ANKI_TEST_MODE").is_ok(); -} +pub(crate) static PYTHON_UNIT_TESTS: LazyLock = + LazyLock::new(|| env::var("ANKI_TEST_MODE").is_ok()); diff --git a/rslib/src/media/check.rs b/rslib/src/media/check.rs index 0c23918fe..c87fadb91 100644 --- a/rslib/src/media/check.rs +++ b/rslib/src/media/check.rs @@ -6,11 +6,11 @@ use std::collections::HashMap; use std::collections::HashSet; use std::fs; use std::io; +use std::sync::LazyLock; use anki_i18n::without_unicode_isolation; use anki_io::write_file; use data_encoding::BASE64; -use once_cell::sync::Lazy; use regex::Regex; use tracing::debug; use tracing::info; @@ -459,7 +459,7 @@ impl MediaChecker<'_> { } fn maybe_extract_inline_image<'a>(&mut self, fname_decoded: &'a str) -> Result> { - static BASE64_IMG: Lazy = Lazy::new(|| { + static BASE64_IMG: LazyLock = LazyLock::new(|| { Regex::new("(?i)^data:image/(jpg|jpeg|png|gif|webp|avif);base64,(.+)$").unwrap() }); diff --git a/rslib/src/media/files.rs b/rslib/src/media/files.rs index d60e70791..2cbef1a89 100644 --- a/rslib/src/media/files.rs +++ b/rslib/src/media/files.rs @@ -7,6 +7,7 @@ use std::io; use std::io::Read; use std::path::Path; use std::path::PathBuf; +use std::sync::LazyLock; use std::time; use anki_io::create_dir; @@ -15,7 +16,6 @@ use anki_io::write_file; use anki_io::FileIoError; use anki_io::FileIoSnafu; use anki_io::FileOp; -use lazy_static::lazy_static; use regex::Regex; use sha1::Digest; use sha1::Sha1; @@ -27,8 +27,8 @@ use unicode_normalization::UnicodeNormalization; use crate::prelude::*; use crate::sync::media::MAX_MEDIA_FILENAME_LENGTH; -lazy_static! { - static ref WINDOWS_DEVICE_NAME: Regex = Regex::new( +static WINDOWS_DEVICE_NAME: LazyLock = LazyLock::new(|| { + Regex::new( r"(?xi) # starting with one of the following names ^ @@ -39,30 +39,34 @@ lazy_static! { ( \. | $ ) - " + ", ) - .unwrap(); - static ref WINDOWS_TRAILING_CHAR: Regex = Regex::new( + .unwrap() +}); +static WINDOWS_TRAILING_CHAR: LazyLock = LazyLock::new(|| { + Regex::new( r"(?x) # filenames can't end with a space or period ( \x20 | \. ) $ - " + ", ) - .unwrap(); - pub(crate) static ref NONSYNCABLE_FILENAME: Regex = Regex::new( + .unwrap() +}); +pub(crate) static NONSYNCABLE_FILENAME: LazyLock = LazyLock::new(|| { + Regex::new( r#"(?xi) ^ (:? thumbs.db | .ds_store ) $ - "# + "#, ) - .unwrap(); -} + .unwrap() +}); /// True if character may cause problems on one or more platforms. fn disallowed_char(char: char) -> bool { diff --git a/rslib/src/notetype/checks.rs b/rslib/src/notetype/checks.rs index 1cd6320dc..fd3f20d09 100644 --- a/rslib/src/notetype/checks.rs +++ b/rslib/src/notetype/checks.rs @@ -4,9 +4,9 @@ use std::borrow::Cow; use std::fmt::Write; use std::ops::Deref; +use std::sync::LazyLock; use anki_i18n::without_unicode_isolation; -use lazy_static::lazy_static; use regex::Captures; use regex::Match; use regex::Regex; @@ -24,9 +24,7 @@ struct Template<'a> { front: bool, } -lazy_static! { - static ref FIELD_REPLACEMENT: Regex = Regex::new(r"\{\{.+\}\}").unwrap(); -} +static FIELD_REPLACEMENT: LazyLock = LazyLock::new(|| Regex::new(r"\{\{.+\}\}").unwrap()); impl Collection { pub fn report_media_field_referencing_templates(&mut self, buf: &mut String) -> Result<()> { diff --git a/rslib/src/notetype/mod.rs b/rslib/src/notetype/mod.rs index 52489e618..fc713c798 100644 --- a/rslib/src/notetype/mod.rs +++ b/rslib/src/notetype/mod.rs @@ -20,6 +20,7 @@ use std::collections::HashMap; use std::collections::HashSet; use std::iter::FromIterator; use std::sync::Arc; +use std::sync::LazyLock; pub use anki_proto::notetypes::notetype::config::card_requirement::Kind as CardRequirementKind; pub use anki_proto::notetypes::notetype::config::CardRequirement; @@ -33,7 +34,6 @@ pub use anki_proto::notetypes::Notetype as NotetypeProto; pub(crate) use cardgen::AlreadyGeneratedCardInfo; pub(crate) use cardgen::CardGenContext; pub use fields::NoteField; -use lazy_static::lazy_static; pub use notetypechange::ChangeNotetypeInput; pub use notetypechange::NotetypeChangeInfo; use regex::Regex; @@ -67,9 +67,9 @@ pub(crate) const DEFAULT_CSS: &str = include_str!("styling.css"); pub(crate) const DEFAULT_CLOZE_CSS: &str = include_str!("cloze_styling.css"); pub(crate) const DEFAULT_LATEX_HEADER: &str = include_str!("header.tex"); pub(crate) const DEFAULT_LATEX_FOOTER: &str = r"\end{document}"; -lazy_static! { - /// New entries must be handled in render.rs/add_special_fields(). - static ref SPECIAL_FIELDS: HashSet<&'static str> = HashSet::from_iter(vec![ +/// New entries must be handled in render.rs/add_special_fields(). +static SPECIAL_FIELDS: LazyLock> = LazyLock::new(|| { + HashSet::from_iter(vec![ "FrontSide", "Card", "CardFlag", @@ -77,8 +77,8 @@ lazy_static! { "Subdeck", "Tags", "Type", - ]); -} + ]) +}); #[derive(Debug, PartialEq, Clone)] pub struct Notetype { @@ -365,9 +365,8 @@ impl Notetype { } fn ensure_template_fronts_unique(&self) -> Result<(), CardTypeError> { - lazy_static! { - static ref CARD_TAG: Regex = Regex::new(r"\{\{\s*Card\s*\}\}").unwrap(); - } + static CARD_TAG: LazyLock = + LazyLock::new(|| Regex::new(r"\{\{\s*Card\s*\}\}").unwrap()); let mut map = HashMap::new(); for (index, card) in self.templates.iter().enumerate() { diff --git a/rslib/src/scheduler/queue/learning.rs b/rslib/src/scheduler/queue/learning.rs index 12c09d27e..79d0304c9 100644 --- a/rslib/src/scheduler/queue/learning.rs +++ b/rslib/src/scheduler/queue/learning.rs @@ -118,12 +118,11 @@ impl CardQueues { /// Remove the head of the intraday learning queue, and update counts. pub(super) fn pop_intraday_learning(&mut self) -> Option { - self.intraday_learning.pop_front().map(|head| { + self.intraday_learning.pop_front().inspect(|_head| { // FIXME: // under normal circumstances this should not go below 0, but currently // the Python unit tests answer learning cards before they're due self.counts.learning = self.counts.learning.saturating_sub(1); - head }) } diff --git a/rslib/src/scheduler/queue/main.rs b/rslib/src/scheduler/queue/main.rs index da8b30330..39129fe7e 100644 --- a/rslib/src/scheduler/queue/main.rs +++ b/rslib/src/scheduler/queue/main.rs @@ -21,7 +21,7 @@ pub(crate) enum MainQueueEntryKind { impl CardQueues { /// Remove the head of the main queue, and update counts. pub(super) fn pop_main(&mut self) -> Option { - self.main.pop_front().map(|head| { + self.main.pop_front().inspect(|head| { match head.kind { MainQueueEntryKind::New => self.counts.new -= 1, MainQueueEntryKind::Review => self.counts.review -= 1, @@ -32,7 +32,6 @@ impl CardQueues { self.counts.learning = self.counts.learning.saturating_sub(1) } }; - head }) } diff --git a/rslib/src/scheduler/reviews.rs b/rslib/src/scheduler/reviews.rs index 2fac6eab5..ea2f3cc43 100644 --- a/rslib/src/scheduler/reviews.rs +++ b/rslib/src/scheduler/reviews.rs @@ -2,8 +2,8 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use std::collections::HashMap; +use std::sync::LazyLock; -use lazy_static::lazy_static; use rand::distributions::Distribution; use rand::distributions::Uniform; use regex::Regex; @@ -65,8 +65,8 @@ pub struct DueDateSpecifier { } pub fn parse_due_date_str(s: &str) -> Result { - lazy_static! { - static ref RE: Regex = Regex::new( + static RE: LazyLock = LazyLock::new(|| { + Regex::new( r"(?x)^ # a number (?P\d+) @@ -78,10 +78,10 @@ pub fn parse_due_date_str(s: &str) -> Result { # optional exclamation mark (?P!)? $ - " + ", ) - .unwrap(); - } + .unwrap() + }); let caps = RE.captures(s).or_invalid(s)?; let min: u32 = caps.name("min").unwrap().as_str().parse()?; let max = if let Some(max) = caps.name("max") { diff --git a/rslib/src/search/parser.rs b/rslib/src/search/parser.rs index 8af7991b3..492c88d93 100644 --- a/rslib/src/search/parser.rs +++ b/rslib/src/search/parser.rs @@ -1,7 +1,8 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -use lazy_static::lazy_static; +use std::sync::LazyLock; + use nom::branch::alt; use nom::bytes::complete::escaped; use nom::bytes::complete::is_not; @@ -621,9 +622,7 @@ fn parse_mid(s: &str) -> ParseResult { /// ensure a list of ids contains only numbers and commas, returning unchanged /// if true used by nid: and cid: fn check_id_list<'a>(s: &'a str, context: &str) -> ParseResult<'a, &'a str> { - lazy_static! { - static ref RE: Regex = Regex::new(r"^(\d+,)*\d+$").unwrap(); - } + static RE: LazyLock = LazyLock::new(|| Regex::new(r"^(\d+,)*\d+$").unwrap()); if RE.is_match(s) { Ok(s) } else { @@ -700,9 +699,7 @@ fn unescape(txt: &str) -> ParseResult { )) } else { Ok(if is_parser_escape(txt) { - lazy_static! { - static ref RE: Regex = Regex::new(r#"\\[\\":()-]"#).unwrap(); - } + static RE: LazyLock = LazyLock::new(|| Regex::new(r#"\\[\\":()-]"#).unwrap()); RE.replace_all(txt, |caps: &Captures| match &caps[0] { r"\\" => r"\\", "\\\"" => "\"", @@ -722,17 +719,17 @@ fn unescape(txt: &str) -> ParseResult { /// Return invalid escape sequence if any. fn invalid_escape_sequence(txt: &str) -> Option { // odd number of \s not followed by an escapable character - lazy_static! { - static ref RE: Regex = Regex::new( + static RE: LazyLock = LazyLock::new(|| { + Regex::new( r#"(?x) (?:^|[^\\]) # not a backslash (?:\\\\)* # even number of backslashes (\\ # single backslash (?:[^\\":*_()-]|$)) # anything but an escapable char - "# + "#, ) - .unwrap(); - } + .unwrap() + }); let caps = RE.captures(txt)?; Some(caps[1].to_string()) @@ -741,17 +738,17 @@ fn invalid_escape_sequence(txt: &str) -> Option { /// Check string for escape sequences handled by the parser: ":()- fn is_parser_escape(txt: &str) -> bool { // odd number of \s followed by a char with special meaning to the parser - lazy_static! { - static ref RE: Regex = Regex::new( + static RE: LazyLock = LazyLock::new(|| { + Regex::new( r#"(?x) (?:^|[^\\]) # not a backslash (?:\\\\)* # even number of backslashes \\ # single backslash [":()-] # parser escape - "# + "#, ) - .unwrap(); - } + .unwrap() + }); RE.is_match(txt) } diff --git a/rslib/src/search/writer.rs b/rslib/src/search/writer.rs index 181ecf9d9..600a18fd6 100644 --- a/rslib/src/search/writer.rs +++ b/rslib/src/search/writer.rs @@ -2,8 +2,8 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use std::mem; +use std::sync::LazyLock; -use lazy_static::lazy_static; use regex::Regex; use crate::notetype::NotetypeId as NotetypeIdType; @@ -109,9 +109,8 @@ fn maybe_quote(txt: &str) -> String { /// Checks for the reserved keywords "and" and "or", a prepended hyphen, /// whitespace and brackets. fn needs_quotation(txt: &str) -> bool { - lazy_static! { - static ref RE: Regex = Regex::new("(?i)^and$|^or$|^-.| |\u{3000}|\\(|\\)").unwrap(); - } + static RE: LazyLock = + LazyLock::new(|| Regex::new("(?i)^and$|^or$|^-.| |\u{3000}|\\(|\\)").unwrap()); RE.is_match(txt) } diff --git a/rslib/src/storage/deck/mod.rs b/rslib/src/storage/deck/mod.rs index 7e8d229f4..e6db323b6 100644 --- a/rslib/src/storage/deck/mod.rs +++ b/rslib/src/storage/deck/mod.rs @@ -161,10 +161,9 @@ impl SqliteStorage { .prepare(include_str!("alloc_id.sql"))? .query_row([TimestampMillis::now()], |r| r.get(0))?; self.add_or_update_deck_with_existing_id(deck) - .map_err(|err| { + .inspect_err(|_err| { // restore id of 0 deck.id.0 = 0; - err }) } diff --git a/rslib/src/sync/collection/tests.rs b/rslib/src/sync/collection/tests.rs index 991b895ac..abf82262f 100644 --- a/rslib/src/sync/collection/tests.rs +++ b/rslib/src/sync/collection/tests.rs @@ -4,9 +4,9 @@ #![cfg(test)] use std::future::Future; +use std::sync::LazyLock; use axum::http::StatusCode; -use once_cell::sync::Lazy; use reqwest::Client; use reqwest::Url; use serde_json::json; @@ -57,7 +57,7 @@ struct TestAuth { host_key: String, } -static AUTH: Lazy = Lazy::new(|| { +static AUTH: LazyLock = LazyLock::new(|| { if let Ok(auth) = std::env::var("TEST_AUTH") { let mut auth = auth.split(':'); TestAuth { @@ -93,7 +93,7 @@ where .unwrap(); tokio::spawn(server_fut.instrument(Span::current())); // when not using ephemeral servers, tests need to be serialized - static LOCK: Lazy> = Lazy::new(|| Mutex::new(())); + static LOCK: LazyLock> = LazyLock::new(|| Mutex::new(())); let _lock: MutexGuard<()>; // setup client to connect to it let endpoint = if let Ok(endpoint) = std::env::var("TEST_ENDPOINT") { diff --git a/rslib/src/sync/http_server/user.rs b/rslib/src/sync/http_server/user.rs index 72dcc7197..bfbacbce3 100644 --- a/rslib/src/sync/http_server/user.rs +++ b/rslib/src/sync/http_server/user.rs @@ -57,10 +57,9 @@ impl User { // Returning HTTP 400 will inform the client that a DB check+full sync // is required to fix the issue. op(col, state) - .map_err(|e| { + .inspect_err(|_e| { self.col = None; self.sync_state = None; - e }) .or_bad_request("op failed in sync_state") } diff --git a/rslib/src/sync/media/database/server/meta/mod.rs b/rslib/src/sync/media/database/server/meta/mod.rs index 0f2e9f973..68ce1cba2 100644 --- a/rslib/src/sync/media/database/server/meta/mod.rs +++ b/rslib/src/sync/media/database/server/meta/mod.rs @@ -77,9 +77,8 @@ impl ServerMediaDatabase { self.db.execute("commit", [])?; Ok(meta.last_usn) }) - .map_err(|e| { + .inspect_err(|_e| { let _ = self.db.execute("rollback", []); - e }) } diff --git a/rslib/src/sync/request/mod.rs b/rslib/src/sync/request/mod.rs index 3e876ab5b..4678cef9b 100644 --- a/rslib/src/sync/request/mod.rs +++ b/rslib/src/sync/request/mod.rs @@ -8,6 +8,7 @@ use std::any::Any; use std::env; use std::marker::PhantomData; use std::net::IpAddr; +use std::sync::LazyLock; use async_trait::async_trait; use axum::body::Body; @@ -19,7 +20,6 @@ use axum::RequestPartsExt; use axum_client_ip::SecureClientIp; use axum_extra::TypedHeader; use header_and_stream::SyncHeader; -use once_cell::sync::Lazy; use serde::de::DeserializeOwned; use serde::Serialize; use serde_json::Error; @@ -179,7 +179,7 @@ where } } -pub static MAXIMUM_SYNC_PAYLOAD_BYTES: Lazy = Lazy::new(|| { +pub static MAXIMUM_SYNC_PAYLOAD_BYTES: LazyLock = LazyLock::new(|| { env::var("MAX_SYNC_PAYLOAD_MEGS") .map(|v| v.parse().expect("invalid upload limit")) .unwrap_or(100) @@ -189,5 +189,5 @@ pub static MAXIMUM_SYNC_PAYLOAD_BYTES: Lazy = Lazy::new(|| { /// Client ignores this when a non-AnkiWeb endpoint is configured. Controls the /// maximum size of a payload after decompression, which effectively limits the /// how large a collection file can be uploaded. -pub static MAXIMUM_SYNC_PAYLOAD_BYTES_UNCOMPRESSED: Lazy = - Lazy::new(|| (*MAXIMUM_SYNC_PAYLOAD_BYTES * 3) as u64); +pub static MAXIMUM_SYNC_PAYLOAD_BYTES_UNCOMPRESSED: LazyLock = + LazyLock::new(|| (*MAXIMUM_SYNC_PAYLOAD_BYTES * 3) as u64); diff --git a/rslib/src/template.rs b/rslib/src/template.rs index 3e09af6ad..4956b09b9 100644 --- a/rslib/src/template.rs +++ b/rslib/src/template.rs @@ -6,9 +6,9 @@ use std::collections::HashMap; use std::collections::HashSet; use std::fmt::Write; use std::iter; +use std::sync::LazyLock; use anki_i18n::I18n; -use lazy_static::lazy_static; use nom::branch::alt; use nom::bytes::complete::tag; use nom::bytes::complete::take_until; @@ -546,18 +546,18 @@ fn append_str_to_nodes(nodes: &mut Vec, text: &str) { /// True if provided text contains only whitespace and/or empty BR/DIV tags. pub(crate) fn field_is_empty(text: &str) -> bool { - lazy_static! { - static ref RE: Regex = Regex::new( + static RE: LazyLock = LazyLock::new(|| { + Regex::new( r"(?xsi) ^(?: [[:space:]] | )*$ - " + ", ) - .unwrap(); - } + .unwrap() + }); RE.is_match(text) } diff --git a/rslib/src/template_filters.rs b/rslib/src/template_filters.rs index f55d45862..23b66421d 100644 --- a/rslib/src/template_filters.rs +++ b/rslib/src/template_filters.rs @@ -2,9 +2,9 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use std::borrow::Cow; +use std::sync::LazyLock; use blake3::Hasher; -use lazy_static::lazy_static; use regex::Captures; use regex::Regex; @@ -110,9 +110,7 @@ fn apply_filter( // Ruby filters //---------------------------------------- -lazy_static! { - static ref FURIGANA: Regex = Regex::new(r" ?([^ >]+?)\[(.+?)\]").unwrap(); -} +static FURIGANA: LazyLock = LazyLock::new(|| Regex::new(r" ?([^ >]+?)\[(.+?)\]").unwrap()); /// Did furigana regex match a sound tag? fn captured_sound(caps: &Captures) -> bool { diff --git a/rslib/src/text.rs b/rslib/src/text.rs index 7f741540c..9c8ddb349 100644 --- a/rslib/src/text.rs +++ b/rslib/src/text.rs @@ -2,8 +2,8 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use std::borrow::Cow; +use std::sync::LazyLock; -use lazy_static::lazy_static; use percent_encoding_iri::percent_decode_str; use percent_encoding_iri::utf8_percent_encode; use percent_encoding_iri::AsciiSet; @@ -79,17 +79,18 @@ pub enum AvTag { }, } -lazy_static! { - static ref HTML: Regex = Regex::new(concat!( +static HTML: LazyLock = LazyLock::new(|| { + Regex::new(concat!( "(?si)", // wrapped text r"()|(.*?)|(.*?)", // html tags r"|(<.*?>)", )) - .unwrap(); - - static ref HTML_LINEBREAK_TAGS: Regex = Regex::new( + .unwrap() +}); +static HTML_LINEBREAK_TAGS: LazyLock = LazyLock::new(|| { + Regex::new( r#"(?xsi) - "# - ).unwrap(); + "#, + ) + .unwrap() +}); - pub static ref HTML_MEDIA_TAGS: Regex = Regex::new( +pub static HTML_MEDIA_TAGS: LazyLock = LazyLock::new(|| { + Regex::new( r#"(?xsi) # the start of the image, audio, or object tag <\b(?:img|audio|video|object)\b @@ -141,11 +145,14 @@ lazy_static! { > ) ) - "# - ).unwrap(); + "#, + ) + .unwrap() +}); - // videos are also in sound tags - static ref AV_TAGS: Regex = Regex::new( +// videos are also in sound tags +static AV_TAGS: LazyLock = LazyLock::new(|| { + Regex::new( r"(?xs) \[sound:(.+?)\] # 1 - the filename in a sound tag | @@ -153,15 +160,21 @@ lazy_static! { \[(.*?)\] # 2 - arguments to tts call (.*?) # 3 - field text \[/anki:tts\] - ").unwrap(); + ", + ) + .unwrap() +}); - static ref PERSISTENT_HTML_SPACERS: Regex = Regex::new(r"(?i)|
|\n").unwrap(); +static PERSISTENT_HTML_SPACERS: LazyLock = + LazyLock::new(|| Regex::new(r"(?i)|
|\n").unwrap()); - static ref TYPE_TAG: Regex = Regex::new(r"\[\[type:[^]]+\]\]").unwrap(); - pub(crate) static ref SOUND_TAG: Regex = Regex::new(r"\[sound:([^]]+)\]").unwrap(); +static TYPE_TAG: LazyLock = LazyLock::new(|| Regex::new(r"\[\[type:[^]]+\]\]").unwrap()); +pub(crate) static SOUND_TAG: LazyLock = + LazyLock::new(|| Regex::new(r"\[sound:([^]]+)\]").unwrap()); - /// Files included in CSS with a leading underscore. - static ref UNDERSCORED_CSS_IMPORTS: Regex = Regex::new( +/// Files included in CSS with a leading underscore. +static UNDERSCORED_CSS_IMPORTS: LazyLock = LazyLock::new(|| { + Regex::new( r#"(?xi) (?:@import\s+ # import statement with a bare "(_[^"]*.css)" # double quoted @@ -176,10 +189,14 @@ lazy_static! { | # or (_.+) # unquoted filename \s*\)) - "#).unwrap(); + "#, + ) + .unwrap() +}); - /// Strings, src and data attributes with a leading underscore. - static ref UNDERSCORED_REFERENCES: Regex = Regex::new( +/// Strings, src and data attributes with a leading underscore. +static UNDERSCORED_REFERENCES: LazyLock = LazyLock::new(|| { + Regex::new( r#"(?x) \[sound:(_[^]]+)\] # a filename in an Anki sound tag | # or @@ -190,8 +207,10 @@ lazy_static! { \b(?:src|data) # a 'src' or 'data' attribute = # followed by (_[^ >]+) # an unquoted value - "#).unwrap(); -} + "#, + ) + .unwrap() +}); pub fn is_html(text: impl AsRef) -> bool { HTML.is_match(text.as_ref()) @@ -446,16 +465,16 @@ pub(crate) fn without_combining(s: &str) -> Cow { /// Check if string contains an unescaped wildcard. pub(crate) fn is_glob(txt: &str) -> bool { // even number of \s followed by a wildcard - lazy_static! { - static ref RE: Regex = Regex::new( + static RE: LazyLock = LazyLock::new(|| { + Regex::new( r"(?x) (?:^|[^\\]) # not a backslash (?:\\\\)* # even number of backslashes [*_] # wildcard - " + ", ) - .unwrap(); - } + .unwrap() + }); RE.is_match(txt) } @@ -467,9 +486,7 @@ pub(crate) fn to_re(txt: &str) -> Cow { /// Convert Anki style to RegEx using the provided wildcard. pub(crate) fn to_custom_re<'a>(txt: &'a str, wildcard: &str) -> Cow<'a, str> { - lazy_static! { - static ref RE: Regex = Regex::new(r"\\?.").unwrap(); - } + static RE: LazyLock = LazyLock::new(|| Regex::new(r"\\?.").unwrap()); RE.replace_all(txt, |caps: &Captures| { let s = &caps[0]; match s { @@ -485,9 +502,7 @@ pub(crate) fn to_custom_re<'a>(txt: &'a str, wildcard: &str) -> Cow<'a, str> { /// Convert to SQL respecting Anki wildcards. pub(crate) fn to_sql(txt: &str) -> Cow { // escape sequences and unescaped special characters which need conversion - lazy_static! { - static ref RE: Regex = Regex::new(r"\\[\\*]|[*%]").unwrap(); - } + static RE: LazyLock = LazyLock::new(|| Regex::new(r"\\[\\*]|[*%]").unwrap()); RE.replace_all(txt, |caps: &Captures| { let s = &caps[0]; match s { @@ -502,17 +517,13 @@ pub(crate) fn to_sql(txt: &str) -> Cow { /// Unescape everything. pub(crate) fn to_text(txt: &str) -> Cow { - lazy_static! { - static ref RE: Regex = Regex::new(r"\\(.)").unwrap(); - } + static RE: LazyLock = LazyLock::new(|| Regex::new(r"\\(.)").unwrap()); RE.replace_all(txt, "$1") } /// Escape Anki wildcards and the backslash for escaping them: \*_ pub(crate) fn escape_anki_wildcards(txt: &str) -> String { - lazy_static! { - static ref RE: Regex = Regex::new(r"[\\*_]").unwrap(); - } + static RE: LazyLock = LazyLock::new(|| Regex::new(r"[\\*_]").unwrap()); RE.replace_all(txt, r"\$0").into() } @@ -545,9 +556,8 @@ pub(crate) fn glob_matcher(search: &str) -> impl Fn(&str) -> bool + '_ { } } -lazy_static! { - pub(crate) static ref REMOTE_FILENAME: Regex = Regex::new("(?i)^https?://").unwrap(); -} +pub(crate) static REMOTE_FILENAME: LazyLock = + LazyLock::new(|| Regex::new("(?i)^https?://").unwrap()); /// https://url.spec.whatwg.org/#fragment-percent-encode-set const FRAGMENT_QUERY_UNION: &AsciiSet = &CONTROLS diff --git a/rslib/src/typeanswer.rs b/rslib/src/typeanswer.rs index c1d6547fb..d1975dbdb 100644 --- a/rslib/src/typeanswer.rs +++ b/rslib/src/typeanswer.rs @@ -2,9 +2,9 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use std::borrow::Cow; +use std::sync::LazyLock; use difflib::sequencematcher::SequenceMatcher; -use once_cell::sync::Lazy; use regex::Regex; use unic_ucd_category::GeneralCategory; @@ -12,7 +12,7 @@ use crate::card_rendering::strip_av_tags; use crate::text::normalize_to_nfkd; use crate::text::strip_html; -static LINEBREAKS: Lazy = Lazy::new(|| { +static LINEBREAKS: LazyLock = LazyLock::new(|| { Regex::new( r"(?six) ( diff --git a/rslib/src/version.rs b/rslib/src/version.rs index dfe7cab4f..29f840304 100644 --- a/rslib/src/version.rs +++ b/rslib/src/version.rs @@ -2,8 +2,7 @@ // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use std::env; - -use lazy_static::lazy_static; +use std::sync::LazyLock; pub fn version() -> &'static str { include_str!("../../.version").trim() @@ -14,25 +13,25 @@ pub fn buildhash() -> &'static str { } pub(crate) fn sync_client_version() -> &'static str { - lazy_static! { - static ref VER: String = format!( + static VER: LazyLock = LazyLock::new(|| { + format!( "anki,{version} ({buildhash}),{platform}", version = version(), buildhash = buildhash(), platform = env::var("PLATFORM").unwrap_or_else(|_| env::consts::OS.to_string()) - ); - } + ) + }); &VER } pub(crate) fn sync_client_version_short() -> &'static str { - lazy_static! { - static ref VER: String = format!( + static VER: LazyLock = LazyLock::new(|| { + format!( "{version},{buildhash},{platform}", version = version(), buildhash = buildhash(), platform = env::consts::OS - ); - } + ) + }); &VER } diff --git a/tools/minilints/Cargo.toml b/tools/minilints/Cargo.toml index 3c284ada5..bb6629f38 100644 --- a/tools/minilints/Cargo.toml +++ b/tools/minilints/Cargo.toml @@ -12,6 +12,5 @@ anki_io.workspace = true anki_process.workspace = true anyhow.workspace = true camino.workspace = true -once_cell.workspace = true walkdir.workspace = true which.workspace = true diff --git a/tools/minilints/src/main.rs b/tools/minilints/src/main.rs index d3b384256..811f37b2b 100644 --- a/tools/minilints/src/main.rs +++ b/tools/minilints/src/main.rs @@ -1,6 +1,7 @@ // Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +use std::cell::LazyCell; use std::collections::HashSet; use std::env; use std::fs; @@ -16,7 +17,6 @@ use anki_process::CommandExt; use anyhow::Context; use anyhow::Result; use camino::Utf8Path; -use once_cell::unsync::Lazy; use walkdir::WalkDir; const NONSTANDARD_HEADER: &[&str] = &[ @@ -63,7 +63,7 @@ fn main() -> Result<()> { struct LintContext { want_fix: bool, - unstaged_changes: Lazy<()>, + unstaged_changes: LazyCell<()>, found_problems: bool, nonstandard_headers: HashSet<&'static Utf8Path>, } @@ -72,7 +72,7 @@ impl LintContext { pub fn new(want_fix: bool) -> Self { Self { want_fix, - unstaged_changes: Lazy::new(check_for_unstaged_changes), + unstaged_changes: LazyCell::new(check_for_unstaged_changes), found_problems: false, nonstandard_headers: NONSTANDARD_HEADER.iter().map(Utf8Path::new).collect(), } @@ -113,7 +113,7 @@ impl LintContext { let missing = !head.contains("Ankitects Pty Ltd and contributors"); if missing { if self.want_fix { - Lazy::force(&self.unstaged_changes); + LazyCell::force(&self.unstaged_changes); fix_copyright(path)?; } else { println!("missing standard copyright header: {:?}", path);