You've already forked AstralRinth
forked from didirus/AstralRinth
Merge tag 'v0.10.21' into beta
This commit is contained in:
@@ -9,10 +9,10 @@ use crate::util::fetch::REQWEST_CLIENT;
|
||||
#[tracing::instrument]
|
||||
pub async fn check_reachable() -> crate::Result<()> {
|
||||
let resp = REQWEST_CLIENT
|
||||
.get("https://api.minecraftservices.com/entitlements/mcstore")
|
||||
.get("https://sessionserver.mojang.com/session/minecraft/hasJoined")
|
||||
.send()
|
||||
.await?;
|
||||
if resp.status() == StatusCode::UNAUTHORIZED {
|
||||
if resp.status() == StatusCode::NO_CONTENT {
|
||||
return Ok(());
|
||||
}
|
||||
resp.error_for_status()?;
|
||||
|
||||
@@ -395,17 +395,36 @@ pub async fn download_libraries(
|
||||
.unwrap_or("https://libraries.minecraft.net/")
|
||||
);
|
||||
|
||||
let bytes =
|
||||
fetch(&url, None, &st.fetch_semaphore, &st.pool)
|
||||
.await?;
|
||||
|
||||
write(&path, &bytes, &st.io_semaphore).await?;
|
||||
|
||||
tracing::trace!(
|
||||
"Fetched library {} to path {:?}",
|
||||
&library.name,
|
||||
&path
|
||||
"Attempting to fetch {} from {url}",
|
||||
library.name,
|
||||
);
|
||||
|
||||
// It's OK for this fetch to fail, since the URL might not even be valid.
|
||||
// We're constructing a download URL basically out of thin air, and hoping
|
||||
// that it's valid. Since PrismLauncher ignores the library (see above), a
|
||||
// failed download here is not a fatal condition.
|
||||
//
|
||||
// See DEV-479.
|
||||
match fetch(&url, None, &st.fetch_semaphore, &st.pool).await
|
||||
{
|
||||
Ok(bytes) => {
|
||||
write(&path, &bytes, &st.io_semaphore).await?;
|
||||
|
||||
tracing::debug!(
|
||||
"Fetched library {} to path {:?}",
|
||||
&library.name,
|
||||
&path
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
tracing::debug!(
|
||||
"Failed to download library {} from {url} - \
|
||||
this is not necessarily an error: {err:#?}",
|
||||
&library.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,19 @@ actix-web = { workspace = true }
|
||||
derive_more = { workspace = true, features = ["display", "error", "from"] }
|
||||
dotenvy = { workspace = true }
|
||||
eyre = { workspace = true }
|
||||
rust_decimal = { workspace = true, features = ["macros"], optional = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
tracing = { workspace = true }
|
||||
tracing-ecs = { workspace = true }
|
||||
tracing-subscriber = { workspace = true }
|
||||
utoipa = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = { workspace = true }
|
||||
|
||||
[features]
|
||||
decimal = ["dep:rust_decimal", "utoipa?/decimal"]
|
||||
utoipa = ["dep:utoipa"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
226
packages/modrinth-util/src/decimal.rs
Normal file
226
packages/modrinth-util/src/decimal.rs
Normal file
@@ -0,0 +1,226 @@
|
||||
use std::{
|
||||
cmp,
|
||||
ops::{Add, Sub},
|
||||
};
|
||||
|
||||
use derive_more::{Deref, Display, Error};
|
||||
use rust_decimal::{Decimal, RoundingStrategy};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(
|
||||
Debug,
|
||||
Display,
|
||||
Clone,
|
||||
Copy,
|
||||
PartialEq,
|
||||
Eq,
|
||||
PartialOrd,
|
||||
Ord,
|
||||
Hash,
|
||||
Deref,
|
||||
Serialize,
|
||||
Deserialize,
|
||||
)]
|
||||
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
|
||||
#[serde(try_from = "Decimal")]
|
||||
pub struct DecimalDp<const DP: u32>(Decimal);
|
||||
|
||||
pub type Decimal2dp = DecimalDp<2>;
|
||||
|
||||
#[derive(Debug, Display, Clone, Error)]
|
||||
#[display("decimal is not rounded to {dp} decimal places")]
|
||||
pub struct NotRounded {
|
||||
pub dp: u32,
|
||||
}
|
||||
|
||||
impl<const DP: u32> DecimalDp<DP> {
|
||||
pub const ZERO: Self = Self(Decimal::ZERO);
|
||||
|
||||
pub fn rounded(v: Decimal, strategy: RoundingStrategy) -> Self {
|
||||
Self(v.round_dp_with_strategy(DP, strategy))
|
||||
}
|
||||
|
||||
pub fn new(v: Decimal) -> Result<Self, NotRounded> {
|
||||
if v.round_dp(DP) == v {
|
||||
Ok(Self(v))
|
||||
} else {
|
||||
Err(NotRounded { dp: DP })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(self) -> Decimal {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn mul_round(
|
||||
self,
|
||||
other: impl Into<Decimal>,
|
||||
strategy: RoundingStrategy,
|
||||
) -> Self {
|
||||
Self::rounded(self.0 * other.into(), strategy)
|
||||
}
|
||||
}
|
||||
|
||||
// conversion
|
||||
|
||||
impl<const DP: u32> TryFrom<Decimal> for DecimalDp<DP> {
|
||||
type Error = NotRounded;
|
||||
|
||||
fn try_from(value: Decimal) -> Result<Self, Self::Error> {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const DP: u32> From<DecimalDp<DP>> for Decimal {
|
||||
fn from(value: DecimalDp<DP>) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
// ord
|
||||
|
||||
impl<const DP: u32> PartialOrd<Decimal> for DecimalDp<DP> {
|
||||
fn partial_cmp(&self, other: &Decimal) -> Option<cmp::Ordering> {
|
||||
self.0.partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const DP: u32> PartialOrd<DecimalDp<DP>> for Decimal {
|
||||
fn partial_cmp(&self, other: &DecimalDp<DP>) -> Option<cmp::Ordering> {
|
||||
self.partial_cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
// eq
|
||||
|
||||
impl<const DP: u32> PartialEq<Decimal> for DecimalDp<DP> {
|
||||
fn eq(&self, other: &Decimal) -> bool {
|
||||
self.0.eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const DP: u32> PartialEq<DecimalDp<DP>> for Decimal {
|
||||
fn eq(&self, other: &DecimalDp<DP>) -> bool {
|
||||
self.eq(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
// add
|
||||
|
||||
impl<const DP: u32> Add for DecimalDp<DP> {
|
||||
type Output = Self;
|
||||
|
||||
fn add(self, rhs: DecimalDp<DP>) -> Self::Output {
|
||||
let v = self.0 + rhs.0;
|
||||
debug_assert!(Self::new(v).is_ok());
|
||||
Self(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const DP: u32> Add<Decimal> for DecimalDp<DP> {
|
||||
type Output = Decimal;
|
||||
|
||||
fn add(self, rhs: Decimal) -> Self::Output {
|
||||
self.0 + rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<const DP: u32> Add<DecimalDp<DP>> for Decimal {
|
||||
type Output = Decimal;
|
||||
|
||||
fn add(self, rhs: DecimalDp<DP>) -> Self::Output {
|
||||
self + rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
// sub
|
||||
|
||||
impl<const DP: u32> Sub for DecimalDp<DP> {
|
||||
type Output = Self;
|
||||
|
||||
fn sub(self, rhs: Self) -> Self::Output {
|
||||
let v = self.0 - rhs.0;
|
||||
debug_assert!(Self::new(v).is_ok());
|
||||
Self(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const DP: u32> Sub<Decimal> for DecimalDp<DP> {
|
||||
type Output = Decimal;
|
||||
|
||||
fn sub(self, rhs: Decimal) -> Self::Output {
|
||||
self.0 - rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl<const DP: u32> Sub<DecimalDp<DP>> for Decimal {
|
||||
type Output = Decimal;
|
||||
|
||||
fn sub(self, rhs: DecimalDp<DP>) -> Self::Output {
|
||||
self - rhs.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use rust_decimal::dec;
|
||||
|
||||
#[test]
|
||||
fn new() {
|
||||
Decimal2dp::new(dec!(1)).unwrap();
|
||||
Decimal2dp::new(dec!(1.0)).unwrap();
|
||||
Decimal2dp::new(dec!(1.1)).unwrap();
|
||||
Decimal2dp::new(dec!(1.01)).unwrap();
|
||||
Decimal2dp::new(dec!(1.00)).unwrap();
|
||||
Decimal2dp::new(dec!(1.000)).unwrap();
|
||||
Decimal2dp::new(dec!(1.001)).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rounded() {
|
||||
assert_eq!(
|
||||
dec!(1),
|
||||
Decimal2dp::rounded(dec!(1), RoundingStrategy::ToZero)
|
||||
);
|
||||
assert_eq!(
|
||||
dec!(1),
|
||||
Decimal2dp::rounded(dec!(1.001), RoundingStrategy::ToZero)
|
||||
);
|
||||
assert_eq!(
|
||||
dec!(1),
|
||||
Decimal2dp::rounded(dec!(1.005), RoundingStrategy::ToZero)
|
||||
);
|
||||
assert_eq!(
|
||||
dec!(1),
|
||||
Decimal2dp::rounded(dec!(1.009), RoundingStrategy::ToZero)
|
||||
);
|
||||
assert_eq!(
|
||||
dec!(1.01),
|
||||
Decimal2dp::rounded(dec!(1.010), RoundingStrategy::ToZero)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize() {
|
||||
serde_json::from_str::<Decimal2dp>("1").unwrap();
|
||||
serde_json::from_str::<Decimal2dp>("1.0").unwrap();
|
||||
serde_json::from_str::<Decimal2dp>("1.00").unwrap();
|
||||
serde_json::from_str::<Decimal2dp>("1.000").unwrap();
|
||||
serde_json::from_str::<Decimal2dp>("1.001").unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ops() {
|
||||
assert_eq!(
|
||||
Decimal2dp::new(dec!(1.23)).unwrap()
|
||||
+ Decimal2dp::new(dec!(0.27)).unwrap(),
|
||||
dec!(1.50)
|
||||
);
|
||||
assert_eq!(
|
||||
Decimal2dp::new(dec!(1.23)).unwrap()
|
||||
- Decimal2dp::new(dec!(0.23)).unwrap(),
|
||||
dec!(1.00)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@
|
||||
mod error;
|
||||
pub mod log;
|
||||
|
||||
#[cfg(feature = "decimal")]
|
||||
pub mod decimal;
|
||||
|
||||
pub use error::*;
|
||||
|
||||
use eyre::{Result, eyre};
|
||||
|
||||
@@ -10,6 +10,16 @@ export type VersionEntry = {
|
||||
}
|
||||
|
||||
const VERSIONS: VersionEntry[] = [
|
||||
{
|
||||
date: `2025-11-19T15:15:00-08:00`,
|
||||
product: 'app',
|
||||
version: '0.10.20',
|
||||
body: `## Improvements
|
||||
- Improved contrast, visibility, and consistency of UI elements, especially in light mode.
|
||||
- Fixed ads showing up in the loading screen, even when you have Modrinth+.
|
||||
- Added a warning banner when Minecraft's authentication servers are detected to be down.
|
||||
- Fixed icon when creating an instance not being saved.`,
|
||||
},
|
||||
{
|
||||
date: `2025-11-14T12:15:00-08:00`,
|
||||
product: 'servers',
|
||||
|
||||
Reference in New Issue
Block a user