Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
03490a1
Fixed to_url function to format a proper URL in request/level, commen…
Dec 14, 2020
8879608
Moved '?' character into the format string rather than endpoint const…
Dec 16, 2020
e021c0d
Merge branch 'master' of https://github.com/stadust/dash-rs into master
Dec 26, 2020
1c8792b
Renamed export::Formatter use clauses to be '__private::Formatter' be…
Feb 15, 2021
88c28e3
Merge branch 'stadust:master' into master
zRyder Jul 24, 2021
87c8170
Added request structs for getting comment history, added models for p…
Dec 25, 2021
b25bb3e
Resolving merge conflicts
Dec 25, 2021
2869105
Merge branch 'stadust-master' into master
Dec 25, 2021
72f75fd
Removed unneccessary tests and properly used assert statement in seri…
Dec 25, 2021
1660975
Removed CommentHistory Struct, instead opted in to make index 7 type …
Dec 26, 2021
2873e22
Removed more unneccesary testing
Dec 26, 2021
165e64d
Merge pull request #2 from zRyder/improvement/optional-index-7
zRyder Dec 26, 2021
7f31123
Account (#3)
zRyder Oct 30, 2022
4305066
Fixing merge conflicts
Oct 30, 2022
4f08a42
Merge branch 'stadust-master' into develop
Oct 30, 2022
833c5d3
Post delete comment (#5)
zRyder Oct 31, 2022
da92fbe
Added more verbose unit test cases
Oct 31, 2022
0bf2a4f
Integration testing
Nov 16, 2022
b685231
Trait implementations (#7)
zRyder Nov 25, 2022
aa31c0d
Merge branch 'develop' of github.com:zRyder/dash-rs into develop
Nov 25, 2022
3c69f8a
Level (#8)
zRyder Dec 18, 2022
c284cf6
Merge branch 'develop' of github.com:zRyder/dash-rs into develop
Jul 10, 2023
a101d2f
Level (#9)
zRyder Jul 10, 2023
dbca9e3
rand downgrade
Aug 4, 2023
58f283e
Merge branch 'develop' of github.com:zRyder/dash-rs into develop
Aug 4, 2023
25418d6
version fixes
Aug 4, 2023
1873c69
version fixes
Aug 4, 2023
b36f299
version fixes
Aug 4, 2023
db2dac3
version fixes
Aug 4, 2023
7096fed
fixes
Aug 4, 2023
c3b7ef5
Cleanup and removed unneeded tests
Nov 11, 2023
56ec8fb
Added SuggestGJStars request models and tests (#11)
zRyder Nov 23, 2023
5df3ce4
Added Platformer length to models
Jan 12, 2024
ac55ade
Added Epic, Legendary, and Mythic sends
Jan 12, 2024
16654fe
Remove login auth user calls
Oct 12, 2024
865bf7b
Merge remote-tracking branch 'upstream/master' into develop
Mar 15, 2026
747e2f6
Merge fixed
Mar 15, 2026
c1881ac
delete unnecessary test
Mar 29, 2026
89e92f9
Updated for 2.2 and added unknown indexes
Jun 9, 2026
4691ecf
Updated for 2.2 and added unknown indexes
Jun 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
target
.idea
Cargo.lock
*.iml
*.iml
*.env
*.DS_Store
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ edition = "2018"

[dependencies]
serde = {version = "1.0.118", features = ["derive"]}
sha1_smol = "1.0.0"
base64 = "0.21.5"
log = {version = "0.4.8" }
percent-encoding = "2.1.0"
Expand All @@ -33,7 +34,6 @@ pretty_assertions = "1.4.0"

[build-dependencies]
serde = {version = "1.0.104", features = ["derive"]}
serde_yaml = "0.9.29"

[[bench]]
name = "level_processing_benchmark"
Expand Down
18 changes: 13 additions & 5 deletions dash-rs-derive/src/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,22 @@ impl OneToOne {
format_ident!("index_{}", self.index())
}

fn field_tokens(&self, ty: Type) -> proc_macro2::TokenStream {
fn field_tokens(&self, ty: Type) -> TokenStream {
let serde_name = self.index();
let field_name = self.internal_name();
let passthrough = &self.passthrough;

// If the API type is Option<T>, a missing key should deserialize as None
let default_attr = if utils::is_option_type(&self.api_type) {
quote! { #[serde(default)] }
} else {
quote! {}
};
if utils::type_contains_lifetime(&ty) {
quote! {
#[serde(rename = #serde_name)]
#[serde(borrow)]
#default_attr
#(
#[serde(#passthrough)]
)*
Expand All @@ -85,6 +92,7 @@ impl OneToOne {
} else {
quote! {
#[serde(rename = #serde_name)]
#default_attr
#(
#[serde(#passthrough)]
)*
Expand All @@ -93,15 +101,15 @@ impl OneToOne {
}
}

pub fn ser_field_tokens(&self, lifetime: &Lifetime) -> proc_macro2::TokenStream {
pub fn ser_field_tokens(&self, lifetime: &Lifetime) -> TokenStream {
self.field_tokens(self.ser_type(lifetime))
}

pub fn de_field_tokens(&self) -> proc_macro2::TokenStream {
pub fn de_field_tokens(&self) -> TokenStream {
self.field_tokens(self.de_type())
}

pub fn serialize(&self) -> proc_macro2::TokenStream {
pub fn serialize(&self) -> TokenStream {
let field_name = self.internal_name();
let field = &self.field;

Expand All @@ -110,7 +118,7 @@ impl OneToOne {
}
}

pub fn deserialize(&self) -> proc_macro2::TokenStream {
pub fn deserialize(&self) -> TokenStream {
let field_name = self.internal_name();
let field = &self.field;
let api_type = &self.api_type;
Expand Down
8 changes: 8 additions & 0 deletions dash-rs-derive/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,11 @@ pub fn type_contains_lifetime(ty: &Type) -> bool {
_ => todo!(),
}
}

pub fn is_option_type(ty: &Type) -> bool {
let Type::Path(type_path) = ty else { return false };
// Match Option / std::option::Option / core::option::Option
type_path.path.segments.last()
.map(|seg| seg.ident == "Option")
.unwrap_or(false)
}
2 changes: 1 addition & 1 deletion examples/refresh_test_artifacts.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{
fs::OpenOptions,
path::{Path, PathBuf},
path::Path,
};

use dash_rs::{
Expand Down
13 changes: 10 additions & 3 deletions src/model/comment/level.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::borrow::Cow;
// use std::borrow::Cow;

use dash_rs_derive::Dash;
use serde::{Deserialize, Serialize};
Expand All @@ -18,6 +17,13 @@ pub struct LevelComment<'a> {
#[dash(no_index)]
pub user: Option<CommentUser<'a>>,

/// The id of the level that this [`CommentEntry`] was posted to
/// This value is negative if it was posted on a list
/// This value is only returned on Comment History responses
#[dash(index = 1)]
#[serde(skip_serializing_if = "Option::is_none")]
pub level_id: Option<i64>,

/// The actual content of the [`LevelComment`] made.
#[dash(index = 2)]
#[serde(borrow)]
Expand All @@ -32,7 +38,8 @@ pub struct LevelComment<'a> {
#[dash(index = 4)]
pub likes: i32,

/// The unique id of this [`LevelComment`]. Additionally, there is also no [`ProfileComment`](crate::model::comment::profile::ProfileComment)
/// The unique id of this [`LevelComment`]. Additionally,
/// there is also no [`ProfileComment`](crate::model::comment::profile::ProfileComment)
/// with this id
#[dash(index = 6)]
pub comment_id: u64,
Expand Down Expand Up @@ -73,7 +80,7 @@ impl ThunkProcessor for Color {
type Error = ProcessError;
type Output<'a> = Color;

fn from_unprocessed(unprocessed: Cow<str>) -> Result<Self::Output<'_>, Self::Error> {
fn from_unprocessed(unprocessed: Cow<'_, str>) -> Result<Self::Output<'_>, Self::Error> {
let mut split = unprocessed.split(',');

let r = split.next();
Expand Down
65 changes: 36 additions & 29 deletions src/model/level/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,16 @@ struct InternalLevel<'src> {
#[serde(rename = "39")]
index_39: Option<u8>,
#[serde(rename = "42")]
index_42: bool,
index_42: i32,
#[serde(rename = "43")]
index_43: u8,
#[serde(with = "crate::util::default_to_none")]
#[serde(rename = "45")]
index_45: Option<u32>,
#[serde(rename = "46")]
index_46: Option<&'src str>,
index_46: u32,
#[serde(rename = "47")]
index_47: Option<&'src str>,
index_47: Option<u32>,

// Only present sometimes
#[serde(skip_serializing_if = "Option::is_none")]
Expand All @@ -91,7 +91,7 @@ struct InternalLevel<'src> {
index_36: Option<&'src str>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "40")]
index_40: Option<&'src str>,
index_40: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "52")]
index_52: Option<&'src str>,
Expand All @@ -100,7 +100,7 @@ struct InternalLevel<'src> {
index_53: Option<&'src str>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(rename = "57")]
index_57: Option<&'src str>,
index_57: Option<u32>,
}

impl<'de> Dash<'de> for Level<'de, (), Option<u64>, u64> {
Expand All @@ -125,10 +125,10 @@ impl<'de> Dash<'de> for Level<'de, (), Option<u64>, u64> {
coin_amount: InternalProxy::from_deserialize_proxy(internal.index_37),
coins_verified: InternalProxy::from_deserialize_proxy(internal.index_38),
stars_requested: InternalProxy::from_deserialize_proxy(internal.index_39),
is_epic: InternalProxy::from_deserialize_proxy(internal.index_42),
epic: InternalProxy::from_deserialize_proxy(internal.index_42),
object_amount: InternalProxy::from_deserialize_proxy(internal.index_45),
index_46: InternalProxy::from_deserialize_proxy(internal.index_46),
index_47: InternalProxy::from_deserialize_proxy(internal.index_47),
editor_time: InternalProxy::from_deserialize_proxy(internal.index_46),
editor_time_copies: InternalProxy::from_deserialize_proxy(internal.index_47),

main_song: if internal.index_35.is_some() {
None
Expand Down Expand Up @@ -170,10 +170,10 @@ impl<'de> Dash<'de> for Level<'de, (), Option<u64>, u64> {
index_37: self.coin_amount.to_serialize_proxy(),
index_38: self.coins_verified.to_serialize_proxy(),
index_39: self.stars_requested.to_serialize_proxy(),
index_42: self.is_epic.to_serialize_proxy(),
index_42: self.epic.to_serialize_proxy(),
index_45: self.object_amount.to_serialize_proxy(),
index_46: self.index_46.to_serialize_proxy(),
index_47: self.index_47.to_serialize_proxy(),
index_46: self.editor_time.to_serialize_proxy(),
index_47: self.editor_time_copies.to_serialize_proxy(),

index_12: self.main_song.map(|song| song.main_song_id).unwrap_or(0),
index_25: self.difficulty == LevelRating::Auto,
Expand Down Expand Up @@ -206,17 +206,24 @@ impl<'de> Dash<'de> for Level<'de, LevelData<'de>, Option<u64>, u64> {
fn dash_deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let internal = InternalLevel::deserialize(deserializer)?;

let level_data = match (internal.index_4, internal.index_27, internal.index_28, internal.index_29) {
(Some(idx4), Some(idx27), Some(idx28), Some(idx29)) => LevelData {
let level_data = match (
internal.index_4,
internal.index_27,
internal.index_28,
internal.index_29,
internal.index_36,
internal.index_40
) {
(Some(idx4), Some(idx27), Some(idx28), Some(idx29), Some(idx36), Some(idx40)) => LevelData {
level_data: InternalProxy::from_deserialize_proxy(idx4),
password: InternalProxy::from_deserialize_proxy(idx27),
time_since_upload: InternalProxy::from_deserialize_proxy(idx28),
time_since_update: InternalProxy::from_deserialize_proxy(idx29),
index_36: InternalProxy::from_deserialize_proxy(internal.index_36.unwrap_or_default()),
index_40: InternalProxy::from_deserialize_proxy(internal.index_40.unwrap_or_default()),
index_52: InternalProxy::from_deserialize_proxy(internal.index_52.unwrap_or_default()),
index_53: InternalProxy::from_deserialize_proxy(internal.index_53.unwrap_or_default()),
index_57: InternalProxy::from_deserialize_proxy(internal.index_57.unwrap_or_default()),
extra_string: InternalProxy::from_deserialize_proxy(idx36),
low_detail_mode: InternalProxy::from_deserialize_proxy(idx40),
song_ids: InternalProxy::from_deserialize_proxy(internal.index_52.unwrap_or_default()),
sfx_ids: InternalProxy::from_deserialize_proxy(internal.index_53.unwrap_or_default()),
verification_time: InternalProxy::from_deserialize_proxy(internal.index_57.unwrap_or_default()),
},
_ => return Err(D::Error::custom("Missing indices for level data!")),
};
Expand All @@ -239,10 +246,10 @@ impl<'de> Dash<'de> for Level<'de, LevelData<'de>, Option<u64>, u64> {
coin_amount: InternalProxy::from_deserialize_proxy(internal.index_37),
coins_verified: InternalProxy::from_deserialize_proxy(internal.index_38),
stars_requested: InternalProxy::from_deserialize_proxy(internal.index_39),
is_epic: InternalProxy::from_deserialize_proxy(internal.index_42),
epic: InternalProxy::from_deserialize_proxy(internal.index_42),
object_amount: InternalProxy::from_deserialize_proxy(internal.index_45),
index_46: InternalProxy::from_deserialize_proxy(internal.index_46),
index_47: InternalProxy::from_deserialize_proxy(internal.index_47),
editor_time: InternalProxy::from_deserialize_proxy(internal.index_46),
editor_time_copies: InternalProxy::from_deserialize_proxy(internal.index_47),

main_song: if internal.index_35.is_some() {
None
Expand Down Expand Up @@ -287,10 +294,10 @@ impl<'de> Dash<'de> for Level<'de, LevelData<'de>, Option<u64>, u64> {
index_37: self.coin_amount.to_serialize_proxy(),
index_38: self.coins_verified.to_serialize_proxy(),
index_39: self.stars_requested.to_serialize_proxy(),
index_42: self.is_epic.to_serialize_proxy(),
index_42: self.epic.to_serialize_proxy(),
index_45: self.object_amount.to_serialize_proxy(),
index_46: self.index_46.to_serialize_proxy(),
index_47: self.index_47.to_serialize_proxy(),
index_46: self.editor_time.to_serialize_proxy(),
index_47: self.editor_time_copies.to_serialize_proxy(),

index_12: self.main_song.map(|song| song.main_song_id).unwrap_or(0),
index_25: self.difficulty == LevelRating::Auto,
Expand All @@ -310,11 +317,11 @@ impl<'de> Dash<'de> for Level<'de, LevelData<'de>, Option<u64>, u64> {
index_27: Some(index_27.borrow()),
index_28: Some(self.level_data.time_since_upload.to_serialize_proxy()),
index_29: Some(self.level_data.time_since_update.to_serialize_proxy()),
index_36: Some(self.level_data.index_36.to_serialize_proxy()),
index_40: Some(self.level_data.index_40.to_serialize_proxy()),
index_52: Some(self.level_data.index_52.to_serialize_proxy()),
index_53: Some(self.level_data.index_53.to_serialize_proxy()),
index_57: Some(self.level_data.index_57.to_serialize_proxy()),
index_36: Some(self.level_data.extra_string.to_serialize_proxy()),
index_40: Some(self.level_data.low_detail_mode.to_serialize_proxy()),
index_52: Some(self.level_data.song_ids.to_serialize_proxy()),
index_53: Some(self.level_data.sfx_ids.to_serialize_proxy()),
index_57: Some(self.level_data.verification_time.to_serialize_proxy()),
};
internal.serialize(serializer)
}
Expand Down
Loading
Loading