From 8d3c26b0363f7ecf435f0e1a40b3e7910d4bd8fe Mon Sep 17 00:00:00 2001 From: prescientmoon Date: Tue, 11 Feb 2025 18:35:38 +0100 Subject: [PATCH] Build the project on stable rust --- Cargo.lock | 31 ++++--- Cargo.toml | 2 +- README.md | 2 + flake.lock | 39 --------- flake.nix | 19 ++--- nix/shimmeringmoon.nix | 10 +-- src/bin/discord-bot/main.rs | 6 +- src/commands/score.rs | 162 +++++++++++++++++++++--------------- src/context/hash.rs | 3 +- src/lib.rs | 1 - src/utils.rs | 29 ------- 11 files changed, 126 insertions(+), 178 deletions(-) delete mode 100644 src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 8d628ad..95128fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,9 +173,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.82" +version = "0.1.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" +checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d" dependencies = [ "proc-macro2", "quote", @@ -219,14 +219,14 @@ dependencies = [ [[package]] name = "axum" -version = "0.7.6" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f43644eed690f5374f1af436ecd6aea01cd201f6fbdf0178adaf6907afb2cec" +checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" dependencies = [ - "async-trait", "axum-core", "axum-macros", "bytes", + "form_urlencoded", "futures-util", "http 1.1.0", "http-body 1.0.1", @@ -246,7 +246,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper 1.0.1", "tokio", - "tower 0.5.1", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", @@ -254,11 +254,10 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6b8ba012a258d63c9adfa28b9ddcf66149da6f986c5b5452e629d5ee64bf00" +checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" dependencies = [ - "async-trait", "bytes", "futures-util", "http 1.1.0", @@ -275,9 +274,9 @@ dependencies = [ [[package]] name = "axum-macros" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce" +checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c" dependencies = [ "proc-macro2", "quote", @@ -2002,9 +2001,9 @@ dependencies = [ [[package]] name = "matchit" -version = "0.7.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "matrixcompare" @@ -3924,14 +3923,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", "pin-project-lite", - "sync_wrapper 0.1.2", + "sync_wrapper 1.0.1", "tokio", "tower-layer", "tower-service", diff --git a/Cargo.toml b/Cargo.toml index c562ede..33c330e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ postcard = { version = "1.0.10", features = ["use-std"], default-features = fals anyhow = "1.0.87" sha2 = "0.10.8" base16ct = { version = "0.2.0", features = ["alloc"] } -axum = { version = "0.7.6", features = ["macros"] } +axum = { version = "0.8.1", features = ["macros"] } paste = "1.0.15" discord-rich-presence = "0.2.4" reqwest = { version = "0.12.7", features = ["json"] } diff --git a/README.md b/README.md index 3f33403..398c106 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ No neural-networks/machine-learning is used by this project. All image analysis ## Running locally +> WARNING: the instructions in this file are a bit outdated. + The programs need (sometimes a subset of) the following environment variables in order to run: ``` diff --git a/flake.lock b/flake.lock index 4cec65b..79fe3dc 100644 --- a/flake.lock +++ b/flake.lock @@ -1,26 +1,5 @@ { "nodes": { - "fenix": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ], - "rust-analyzer-src": "rust-analyzer-src" - }, - "locked": { - "lastModified": 1739169164, - "narHash": "sha256-2+/6/2rHsMjlrulkrjZbwthCQiQpeW2pukRFaaUMLy8=", - "owner": "nix-community", - "repo": "fenix", - "rev": "d7616af878cd0cb7f940b470440347920395be0c", - "type": "github" - }, - "original": { - "owner": "nix-community", - "repo": "fenix", - "type": "github" - } - }, "flake-utils": { "inputs": { "systems": "systems" @@ -57,29 +36,11 @@ }, "root": { "inputs": { - "fenix": "fenix", "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", "shimmeringdarkness": "shimmeringdarkness" } }, - "rust-analyzer-src": { - "flake": false, - "locked": { - "lastModified": 1739099919, - "narHash": "sha256-YUdM2yZzQIbakgc2LdVmkgJMYTqeTu3YdWGgFfiZiTg=", - "owner": "rust-lang", - "repo": "rust-analyzer", - "rev": "35181e167efb94d5090df588e6af9f93250421f3", - "type": "github" - }, - "original": { - "owner": "rust-lang", - "ref": "nightly", - "repo": "rust-analyzer", - "type": "github" - } - }, "shimmeringdarkness": { "flake": false, "locked": { diff --git a/flake.nix b/flake.nix index 29877c6..22b6763 100644 --- a/flake.nix +++ b/flake.nix @@ -2,8 +2,6 @@ inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; flake-utils.url = "github:numtide/flake-utils"; - fenix.url = "github:nix-community/fenix"; - fenix.inputs.nixpkgs.follows = "nixpkgs"; shimmeringdarkness.url = "git+ssh://forgejo@ssh.git.moonythm.dev/prescientmoon/shimmeringdarkness.git"; shimmeringdarkness.flake = false; }; @@ -13,15 +11,12 @@ inputs.flake-utils.lib.eachSystem (with inputs.flake-utils.lib.system; [ x86_64-linux ]) ( system: let - pkgs = inputs.nixpkgs.legacyPackages.${system}.extend inputs.fenix.overlays.default; - rust-toolchain = pkgs.fenix.complete.toolchain; + pkgs = inputs.nixpkgs.legacyPackages.${system}; spkgs = inputs.self.packages.${system}; inherit (pkgs) lib; in { packages = { - inherit rust-toolchain; - # {{{ Private config shimmeringdarkness = inputs.shimmeringdarkness.outPath; glass-bundler = pkgs.callPackage ./nix/glass-bundler.nix { }; @@ -43,23 +38,27 @@ inherit (spkgs) exo kazesawa geosans-light; }; # }}} - + # {{{ Shimmeringmoon cc-data = pkgs.callPackage ./nix/cc-data.nix { }; default = spkgs.shimmeringmoon; shimmeringmoon = pkgs.callPackage ./nix/shimmeringmoon.nix { inherit (spkgs) shimmering-fonts - rust-toolchain cc-data private-config ; }; + # }}} }; # {{{ Devshell devShell = pkgs.mkShell rec { - nativeBuildInputs = with pkgs; [ - spkgs.rust-toolchain + nativeBuildInputs = [ + pkgs.rustc + pkgs.cargo + pkgs.rustfmt + pkgs.clippy + pkgs.rust-analyzer pkgs.ruff pkgs.imagemagick diff --git a/nix/shimmeringmoon.nix b/nix/shimmeringmoon.nix index 4fda44a..0b4f5d3 100644 --- a/nix/shimmeringmoon.nix +++ b/nix/shimmeringmoon.nix @@ -1,5 +1,4 @@ { - lib, pkg-config, makeWrapper, @@ -7,19 +6,12 @@ fontconfig, openssl, sqlite, - makeRustPlatform, - rust-toolchain, + rustPlatform, shimmering-fonts, cc-data, private-config, }: -let - rustPlatform = makeRustPlatform { - cargo = rust-toolchain; - rustc = rust-toolchain; - }; -in rustPlatform.buildRustPackage rec { pname = "shimmeringmoon"; version = "unstable-2025-02-11"; diff --git a/src/bin/discord-bot/main.rs b/src/bin/discord-bot/main.rs index c03dd44..1b83403 100644 --- a/src/bin/discord-bot/main.rs +++ b/src/bin/discord-bot/main.rs @@ -32,8 +32,8 @@ async fn main() -> anyhow::Result<()> { return Ok(None); } - if let (Ok(global_prefix)) = global_prefix { - if message.content.starts_with(global_prefix) { + if let Ok(global_prefix) = global_prefix { + if message.content.starts_with(&global_prefix) { return Ok(Some(message.content.split_at(global_prefix.len()))); } } @@ -46,7 +46,7 @@ async fn main() -> anyhow::Result<()> { } } - return Ok(None); + Ok(None) }) }), edit_tracker: Some(Arc::new(poise::EditTracker::for_timespan( diff --git a/src/commands/score.rs b/src/commands/score.rs index be8a3da..8474a9a 100644 --- a/src/commands/score.rs +++ b/src/commands/score.rs @@ -4,9 +4,10 @@ use crate::arcaea::score::Score; use crate::context::{Error, ErrorKind, PoiseContext, TagError, TaggedError}; use crate::recognition::recognize::{ImageAnalyzer, ScoreKind}; use crate::user::User; -use crate::{async_try_block, get_user_error, timed}; +use crate::{get_user_error, timed}; use anyhow::anyhow; use image::DynamicImage; +use poise::serenity_prelude::{CreateAttachment, CreateEmbed}; use poise::{serenity_prelude as serenity, CreateReply}; use super::discord::{CreateReplyExtra, MessageContext}; @@ -26,6 +27,83 @@ pub async fn score(_ctx: PoiseContext<'_>) -> Result<(), Error> { // }}} // {{{ Score magic // {{{ Implementation +#[allow(clippy::too_many_arguments)] +async fn magic_detect_one( + ctx: &mut C, + user: &User, + embeds: &mut Vec, + attachments: &mut Vec, + plays: &mut Vec, + analyzer: &mut ImageAnalyzer, + attachment: &C::Attachment, + index: usize, + image: &mut DynamicImage, + grayscale_image: &mut DynamicImage, +) -> Result<(), TaggedError> { + // {{{ Detection + let kind = timed!("read_score_kind", { + analyzer.read_score_kind(ctx.data(), grayscale_image)? + }); + + let difficulty = timed!("read_difficulty", { + analyzer.read_difficulty(ctx.data(), image, grayscale_image, kind)? + }); + + let (song, chart) = timed!("read_jacket", { + analyzer.read_jacket(ctx.data(), image, kind, difficulty)? + }); + + let max_recall = match kind { + ScoreKind::ScoreScreen => { + // NOTE: are we ok with discarding errors like that? + analyzer.read_max_recall(ctx.data(), grayscale_image).ok() + } + ScoreKind::SongSelect => None, + }; + + grayscale_image.invert(); + let note_distribution = match kind { + ScoreKind::ScoreScreen => Some(analyzer.read_distribution(ctx.data(), grayscale_image)?), + ScoreKind::SongSelect => None, + }; + + let score = timed!("read_score", { + analyzer + .read_score(ctx.data(), Some(chart.note_count), grayscale_image, kind) + .map_err(|err| { + anyhow!( + "Could not read score for chart {} [{:?}]: {err}", + song.title, + chart.difficulty + ) + })? + }); + + // {{{ Build play + let maybe_fars = + Score::resolve_distibution_ambiguities(score, note_distribution, chart.note_count); + + let play = CreatePlay::new(score) + .with_attachment(C::attachment_id(attachment)) + .with_fars(maybe_fars) + .with_max_recall(max_recall) + .save(ctx.data(), user, chart) + .await?; + // }}} + // }}} + // {{{ Deliver embed + let (embed, attachment) = timed!("to embed", { + play.to_embed(ctx.data(), user, song, chart, index, None)? + }); + + plays.push(play); + embeds.push(embed); + attachments.extend(attachment); + // }}} + + Ok(()) +} + pub async fn magic_impl( ctx: &mut C, files: &[C::Attachment], @@ -43,76 +121,23 @@ pub async fn magic_impl( let mut analyzer = ImageAnalyzer::default(); for (i, (attachment, bytes)) in files.into_iter().enumerate() { - // {{{ Preapare image + // {{{ Process attachment let mut image = image::load_from_memory(&bytes)?; let mut grayscale_image = DynamicImage::ImageLuma8(image.to_luma8()); - // }}} - let result: Result<(), TaggedError> = async_try_block!({ - // {{{ Detection - - let kind = timed!("read_score_kind", { - analyzer.read_score_kind(ctx.data(), &grayscale_image)? - }); - - let difficulty = timed!("read_difficulty", { - analyzer.read_difficulty(ctx.data(), &image, &grayscale_image, kind)? - }); - - let (song, chart) = timed!("read_jacket", { - analyzer.read_jacket(ctx.data(), &mut image, kind, difficulty)? - }); - - let max_recall = match kind { - ScoreKind::ScoreScreen => { - // NOTE: are we ok with discarding errors like that? - analyzer.read_max_recall(ctx.data(), &grayscale_image).ok() - } - ScoreKind::SongSelect => None, - }; - - grayscale_image.invert(); - let note_distribution = match kind { - ScoreKind::ScoreScreen => { - Some(analyzer.read_distribution(ctx.data(), &grayscale_image)?) - } - ScoreKind::SongSelect => None, - }; - - let score = timed!("read_score", { - analyzer - .read_score(ctx.data(), Some(chart.note_count), &grayscale_image, kind) - .map_err(|err| { - anyhow!( - "Could not read score for chart {} [{:?}]: {err}", - song.title, - chart.difficulty - ) - })? - }); - - // {{{ Build play - let maybe_fars = - Score::resolve_distibution_ambiguities(score, note_distribution, chart.note_count); - - let play = CreatePlay::new(score) - .with_attachment(C::attachment_id(attachment)) - .with_fars(maybe_fars) - .with_max_recall(max_recall) - .save(ctx.data(), &user, chart) - .await?; - // }}} - // }}} - // {{{ Deliver embed - let (embed, attachment) = timed!("to embed", { - play.to_embed(ctx.data(), &user, song, chart, i, None)? - }); - - plays.push(play); - embeds.push(embed); - attachments.extend(attachment); - // }}} - }); + let result = magic_detect_one( + ctx, + &user, + &mut embeds, + &mut attachments, + &mut plays, + &mut analyzer, + attachment, + i, + &mut image, + &mut grayscale_image, + ) + .await; if let Err(err) = result { let user_err = get_user_error!(err); @@ -120,6 +145,7 @@ pub async fn magic_impl( .send_discord_error(ctx, &image, C::filename(attachment), user_err) .await?; } + // }}} } if !embeds.is_empty() { diff --git a/src/context/hash.rs b/src/context/hash.rs index af89738..84ecccf 100644 --- a/src/context/hash.rs +++ b/src/context/hash.rs @@ -27,6 +27,5 @@ pub fn hash_bytes(bytes: &[u8]) -> String { let mut hasher = Sha256::default(); hasher.update(bytes); let res = hasher.finalize(); - let string = base16ct::lower::encode_string(&res); - string + base16ct::lower::encode_string(&res) } diff --git a/src/lib.rs b/src/lib.rs index a7e5e1e..8f020de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,4 +17,3 @@ pub mod recognition; pub mod time; pub mod transform; pub mod user; -mod utils; diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 752cf6e..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,29 +0,0 @@ -/// Performs "Ok-wrapping" on the result of an expression. -/// This is compatible with [`Result`], [`Option`], [`ControlFlow`], and any type that -/// implements the unstable [`std::ops::Try`] trait. -/// -/// The destination type must be specified with a type ascription somewhere. -#[macro_export] -macro_rules! wrap_ok { - ($e:expr) => { - ::core::iter::empty().try_fold($e, |_, __x: ::core::convert::Infallible| match __x {}) - }; -} - -#[macro_export] -macro_rules! try_block { - { $($token:tt)* } => { - (|| $crate::wrap_ok!({ - $($token)* - }))() - } -} - -#[macro_export] -macro_rules! async_try_block { - { $($token:tt)* } => { - (async || $crate::wrap_ok!({ - $($token)* - }))().await - } -}