1
Fork 0

Build the project on stable rust

This commit is contained in:
prescientmoon 2025-02-11 18:35:38 +01:00
parent 5bc6dfa667
commit 8d3c26b036
Signed by: prescientmoon
SSH key fingerprint: SHA256:UUF9JT2s8Xfyv76b8ZuVL7XrmimH4o49p4b+iexbVH4
11 changed files with 126 additions and 178 deletions

31
Cargo.lock generated
View file

@ -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",

View file

@ -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"] }

View file

@ -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:
```

View file

@ -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": {

View file

@ -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

View file

@ -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";

View file

@ -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(

View file

@ -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<C: MessageContext>(
ctx: &mut C,
user: &User,
embeds: &mut Vec<CreateEmbed>,
attachments: &mut Vec<CreateAttachment>,
plays: &mut Vec<Play>,
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<C: MessageContext>(
ctx: &mut C,
files: &[C::Attachment],
@ -43,76 +121,23 @@ pub async fn magic_impl<C: MessageContext>(
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<C: MessageContext>(
.send_discord_error(ctx, &image, C::filename(attachment), user_err)
.await?;
}
// }}}
}
if !embeds.is_empty() {

View file

@ -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)
}

View file

@ -17,4 +17,3 @@ pub mod recognition;
pub mod time;
pub mod transform;
pub mod user;
mod utils;

View file

@ -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
}
}