Beatsaber Rust Utilities: A Beatsaber V3 parsing library.
beatsaber beatmap
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Updated to `loose_bool` v0.1.0-beta.2

This re-adds `LooseBool` as an internal type.

+134 -37
+3 -2
Cargo.lock
··· 384 384 385 385 [[package]] 386 386 name = "loose_enum" 387 - version = "0.1.0-beta.1" 387 + version = "0.1.0-beta.2" 388 + source = "registry+https://github.com/rust-lang/crates.io-index" 389 + checksum = "243534cb7234245d5c56c85bdcd1fcebc690688a2098b9b5935555160c0e471f" 388 390 dependencies = [ 389 - "num-traits", 390 391 "serde_core", 391 392 ] 392 393
+1 -1
Cargo.toml
··· 19 19 ] } 20 20 bevy_reflect = { version = "0.17.0", optional = true, default-features = false } 21 21 indexmap = "2.10.0" 22 - loose_enum = { path = "../loose_enum", features = ["serde"] } 22 + loose_enum = { version = "0.1.0-beta.2", features = ["serde"] } 23 23 ordered-float = "5.0.0" 24 24 serde = { version = "1.0.219", features = ["derive"] } 25 25 serde_json = "1.0.140"
+2 -2
src/difficulty/lightshow.rs
··· 127 127 fraction * modified_value 128 128 } 129 129 DistributionType::Step => dist_value * filtered_id, 130 - DistributionType::Unknown(_) => 0.0, 130 + DistributionType::Undefined(_) => 0.0, 131 131 } 132 132 } 133 133 ··· 213 213 mod tests { 214 214 use super::*; 215 215 use crate::difficulty::lightshow::filter::FilterType; 216 - use loose_enum::LooseBool; 216 + use crate::loose_bool::LooseBool; 217 217 218 218 #[test] 219 219 fn wave() {
+2 -2
src/difficulty/lightshow/easing.rs
··· 60 60 /// Applies the relevant easing function. 61 61 /// 62 62 /// The Beatsaber specific easing use the standard equivalent instead. 63 - /// If the easing is [`None`](Self::None) or [`Unknown`](Self::Unknown), then the result will be zero. 63 + /// If the easing is [`None`](Self::None) or [`Undefined`](Self::Undefined), then the result will be zero. 64 64 pub fn ease(&self, num: f32) -> f32 { 65 65 match self { 66 66 Easing::None => 0.0, ··· 100 100 Easing::BeatSaberInOutBack => back_in_out(num), 101 101 Easing::BeatSaberInOutElastic => elastic_in_out(num), 102 102 Easing::BeatSaberInOutBounce => bounce_in_out(num), 103 - Easing::Unknown(_) => 0.0, 103 + Easing::Undefined(_) => 0.0, 104 104 } 105 105 } 106 106 }
+14 -13
src/difficulty/lightshow/filter.rs
··· 1 1 //! Controls which light IDs are affected by an event. 2 2 3 - use loose_enum::{LooseBool, loose_enum}; 3 + use crate::loose_bool::LooseBool; 4 + use loose_enum::loose_enum; 4 5 use serde::{Deserialize, Serialize}; 5 6 6 7 /// Controls which light IDs are affected by an event. ··· 23 24 pub parameter2: i32, 24 25 /// If true, the filter will start at the end of a group and work backwards. 25 26 #[serde(rename = "r")] 26 - pub reverse: LooseBool<i32>, 27 + pub reverse: LooseBool, 27 28 // V3.1: 28 29 /// > Only present in difficulty file V3.1 or higher. 29 30 /// ··· 71 72 72 73 impl Filter { 73 74 /// Returns true if the light ID is in the filter. 74 - /// # Unknown 75 - /// If the [`FilterType`] is `Unknown` then the result will be `true`. 75 + /// # Undefined 76 + /// If the [`FilterType`] is `Undefined` then the result will be `true`. 76 77 /// # Panics 77 78 /// Will panic if the light ID is greater than or equal to the group size. 78 79 #[must_use] ··· 110 111 let offset_light_id = light_id - self.parameter1; 111 112 offset_light_id % self.parameter2.max(1) == 0 && offset_light_id >= 0 112 113 } 113 - FilterType::Unknown(_) => true, 114 + FilterType::Undefined(_) => true, 114 115 } 115 116 } 116 117 ··· 119 120 /// This is required for distribution calculations. 120 121 /// 121 122 /// Also see [`count_filtered`](Self::count_filtered). 122 - /// # Unknown 123 - /// If the [`FilterType`] is `Unknown` then the result will be the same as `group_size`. 123 + /// # Undefined 124 + /// If the [`FilterType`] is `Undefined` then the result will be the same as `group_size`. 124 125 #[must_use] 125 126 #[inline] 126 127 #[deprecated(note = "Experimental. Does not consider random in calculations.")] ··· 141 142 FilterType::StepAndOffset => { 142 143 group_size / self.parameter2.max(1) - self.parameter1 / self.parameter2.max(1) 143 144 } 144 - FilterType::Unknown(_) => group_size, 145 + FilterType::Undefined(_) => group_size, 145 146 } 146 147 } 147 148 ··· 149 150 /// Returns the number of light chunks effected by the filter. 150 151 /// 151 152 /// Also see [`count_filtered_without_limit`](Self::count_filtered_without_limit). 152 - /// # Unknown 153 - /// If the [`FilterType`] is `Unknown` then the result will be the same as `group_size`. 153 + /// # Undefined 154 + /// If the [`FilterType`] is `Undefined` then the result will be the same as `group_size`. 154 155 #[must_use] 155 156 #[inline] 156 157 #[deprecated(note = "Experimental. Does not consider random in calculations.")] ··· 167 168 168 169 #[allow(deprecated)] 169 170 /// Returns the light chunk ID relative to the [filtered count](Self::count_filtered). 170 - /// # Unknown 171 - /// If the [`FilterType`] is `Unknown` then the result will be the same as `light_id`. 171 + /// # Undefined 172 + /// If the [`FilterType`] is `Undefined` then the result will be the same as `light_id`. 172 173 /// # Panics 173 174 /// Will panic if the light ID is greater than or equal to the group size. 174 175 // Todo what is the behaviour when the light ID is not in the filter? ··· 199 200 let offset_light_id = light_id - self.parameter1; 200 201 offset_light_id / self.parameter2.max(1) 201 202 } 202 - FilterType::Unknown(_) => group_size, 203 + FilterType::Undefined(_) => group_size, 203 204 } 204 205 } 205 206 }
+4 -3
src/difficulty/lightshow/group/color.rs
··· 3 3 use crate::difficulty::lightshow::DistributionType; 4 4 use crate::difficulty::lightshow::easing::Easing; 5 5 use crate::difficulty::lightshow::filter::Filter; 6 + use crate::loose_bool::LooseBool; 6 7 use crate::{impl_event_box, impl_event_data, impl_event_group, impl_timed}; 7 - use loose_enum::{LooseBool, loose_enum}; 8 + use loose_enum::loose_enum; 8 9 use serde::{Deserialize, Serialize}; 9 10 10 11 /// A collection of [`ColorEventGroup`]s that share the same group ID and beat. ··· 65 66 pub bright_dist_value: f32, 66 67 /// Whether the first [`ColorEventData`] of the group will be effected by brightness distribution. 67 68 #[serde(rename = "b")] 68 - pub bright_dist_effect_first: LooseBool<i32>, 69 + pub bright_dist_effect_first: LooseBool, 69 70 /// > Only present in difficulty file V3.2 or higher. 70 71 #[serde(rename = "i")] 71 72 pub bright_dist_easing: Option<Easing>, ··· 140 141 /// 141 142 /// Whether to fade between strobe states or not. 142 143 #[serde(rename = "sf")] 143 - pub strobe_fade: Option<LooseBool<i32>>, 144 + pub strobe_fade: Option<LooseBool>, 144 145 } 145 146 146 147 impl Default for ColorEventData {
+4 -4
src/difficulty/lightshow/group/fx.rs
··· 7 7 use crate::difficulty::lightshow::DistributionType; 8 8 use crate::difficulty::lightshow::easing::Easing; 9 9 use crate::difficulty::lightshow::filter::Filter; 10 + use crate::loose_bool::LooseBool; 10 11 use crate::{TransitionType, impl_event_box, impl_event_data, impl_event_group, impl_timed}; 11 12 use indexmap::IndexSet; 12 - use loose_enum::LooseBool; 13 13 use ordered_float::OrderedFloat; 14 14 use serde::ser::SerializeStruct; 15 15 use serde::{Deserialize, Deserializer, Serialize, Serializer}; ··· 237 237 /// A value of zero will have no effect. 238 238 pub fx_dist_value: f32, 239 239 /// Whether the first [`FxEventData`] of the group will be effected by brightness distribution. 240 - pub fx_dist_effect_first: LooseBool<i32>, 240 + pub fx_dist_effect_first: LooseBool, 241 241 pub fx_dist_easing: Option<Easing>, 242 242 /// In the actual JSON structure, this is a list of indexes to a separate list of event data. 243 243 /// For consistency, this is merged during parsing. ··· 258 258 #[serde(rename = "s")] 259 259 fx_dist_value: f32, 260 260 #[serde(rename = "b")] 261 - fx_dist_effect_first: LooseBool<i32>, 261 + fx_dist_effect_first: LooseBool, 262 262 #[serde(rename = "i")] 263 263 fx_dist_easing: Option<Easing>, 264 264 #[serde(rename = "l")] ··· 367 367 #[cfg(test)] 368 368 mod tests { 369 369 use super::*; 370 - use loose_enum::LooseBool; 370 + use crate::loose_bool::LooseBool; 371 371 use serde_json::{Value, json}; 372 372 373 373 fn get_test_container() -> FxEventContainer {
+4 -3
src/difficulty/lightshow/group/rotation.rs
··· 4 4 use crate::difficulty::lightshow::filter::Filter; 5 5 use crate::difficulty::lightshow::group::EventData; 6 6 use crate::difficulty::lightshow::{DistributionType, EventAxis, TransitionType}; 7 + use crate::loose_bool::LooseBool; 7 8 use crate::{impl_event_box, impl_event_group, impl_timed}; 8 - use loose_enum::{LooseBool, loose_enum}; 9 + use loose_enum::loose_enum; 9 10 use serde::{Deserialize, Serialize}; 10 11 11 12 /// A collection of [`RotationEventGroup`]s that share the same group ID and beat. ··· 65 66 pub rotation_dist_value: f32, 66 67 /// Whether the first [`RotationEventData`] of the group will be effected by rotation distribution. 67 68 #[serde(rename = "b")] 68 - pub rotation_dist_effect_first: LooseBool<i32>, 69 + pub rotation_dist_effect_first: LooseBool, 69 70 /// > Only present in difficulty file V3.2 or higher. 70 71 #[serde(rename = "i")] 71 72 pub rotation_dist_easing: Option<Easing>, ··· 73 74 pub axis: EventAxis, 74 75 /// If true, the rotation will be mirrored. 75 76 #[serde(rename = "r")] 76 - pub invert_axis: LooseBool<i32>, 77 + pub invert_axis: LooseBool, 77 78 #[serde(rename = "l")] 78 79 pub data: Vec<RotationEventData>, 79 80 }
+3 -3
src/difficulty/lightshow/group/translation.rs
··· 4 4 use crate::difficulty::lightshow::filter::Filter; 5 5 use crate::difficulty::lightshow::group::EventData; 6 6 use crate::difficulty::lightshow::{DistributionType, EventAxis, TransitionType}; 7 + use crate::loose_bool::LooseBool; 7 8 use crate::{impl_event_box, impl_event_group, impl_timed}; 8 - use loose_enum::LooseBool; 9 9 use serde::{Deserialize, Serialize}; 10 10 11 11 /// A collection of [`TranslationEventGroup`]s that share the same group ID and beat. ··· 69 69 pub translation_dist_value: f32, 70 70 /// Whether the first [`TranslationEventData`] of the group will be effected by translation distribution. 71 71 #[serde(rename = "b")] 72 - pub translation_dist_effect_first: LooseBool<i32>, 72 + pub translation_dist_effect_first: LooseBool, 73 73 #[serde(rename = "i")] 74 74 pub translation_dist_easing: Easing, 75 75 #[serde(rename = "a")] 76 76 pub axis: EventAxis, 77 77 /// If true, the translation will be mirrored. 78 78 #[serde(rename = "r")] 79 - pub invert_axis: LooseBool<i32>, 79 + pub invert_axis: LooseBool, 80 80 #[serde(rename = "l")] 81 81 pub data: Vec<TranslationEventData>, 82 82 }
+2 -2
src/difficulty/playfield.rs
··· 78 78 impl CutDirection { 79 79 /// Returns the number of degrees a note is rotated, with zero degrees being a downward note. 80 80 /// 81 - /// Returns zero if the cut direction is unknown/any. 81 + /// Returns zero if the cut direction is undefined/any. 82 82 pub fn get_degrees(&self) -> f32 { 83 83 match self { 84 84 CutDirection::Up => 180.0, ··· 90 90 CutDirection::DownLeft => -45.0, 91 91 CutDirection::DownRight => 45.0, 92 92 CutDirection::Any => 0.0, 93 - CutDirection::Unknown(_) => 0.0, 93 + CutDirection::Undefined(_) => 0.0, 94 94 } 95 95 } 96 96 }
+2 -2
src/info/color_scheme/presets.rs
··· 69 69 /// White light colors are not currently supported, and are therefor ignored. 70 70 pub fn get_color_scheme(&self) -> ColorScheme { 71 71 match self { 72 - Environment::Unknown(_) 72 + Environment::Undefined(_) 73 73 | Environment::TheFirst 74 74 | Environment::Triangle 75 75 | Environment::Nice ··· 423 423 rgb!(0.32222217, 0.6111111, 0.75), 424 424 rgb!(0.03844783, 0.62239975, 0.90566039) 425 425 ), 426 - AllDirectionEnvironment::Unknown(_) => ColorScheme::default(), 426 + AllDirectionEnvironment::Undefined(_) => ColorScheme::default(), 427 427 } 428 428 } 429 429 }
+1
src/lib.rs
··· 2 2 3 3 pub mod difficulty; 4 4 pub mod info; 5 + mod loose_bool; 5 6 pub mod timing_traits; 6 7 7 8 #[doc(hidden)]
+92
src/loose_bool.rs
··· 1 + //! Taken from [this example](https://github.com/AlephCubed/loose_enum/blob/main/examples/loose_bool.rs). 2 + 3 + use core::error::Error; 4 + use core::fmt::{Display, Formatter}; 5 + use loose_enum::loose_enum; 6 + 7 + loose_enum! { 8 + /// An integer repr bool, with 0 being false and 1 being true. Any other value will be saved as Undefined. 9 + #[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Hash)] 10 + #[cfg_attr( 11 + feature = "bevy_reflect", 12 + derive(bevy_reflect::Reflect), 13 + reflect(Debug, Clone, PartialEq) 14 + )] 15 + pub enum LooseBool: i32 { 16 + /// A falsy value of zero. 17 + #[default] 18 + False = 0, 19 + /// A truthy value of one. 20 + True = 1, 21 + } 22 + } 23 + 24 + impl LooseBool { 25 + /// Returns true if the value is [`True`](Self::True). 26 + pub fn is_true(&self) -> bool { 27 + matches!(self, Self::True) 28 + } 29 + 30 + /// Returns true if the value is [`False`](Self::False). 31 + pub fn is_false(&self) -> bool { 32 + matches!(self, Self::False) 33 + } 34 + } 35 + 36 + impl From<bool> for LooseBool { 37 + fn from(value: bool) -> Self { 38 + match value { 39 + true => Self::True, 40 + false => Self::False, 41 + } 42 + } 43 + } 44 + 45 + impl TryFrom<LooseBool> for bool { 46 + type Error = UndefinedBoolError; 47 + 48 + fn try_from(value: LooseBool) -> Result<Self, Self::Error> { 49 + match value { 50 + LooseBool::False => Ok(false), 51 + LooseBool::True => Ok(true), 52 + LooseBool::Undefined(_) => Err(UndefinedBoolError), 53 + } 54 + } 55 + } 56 + 57 + /// Error returned when attempting to convert a [`LooseBool::Undefined`] into a `bool`. 58 + #[derive(Debug, Eq, PartialEq, Hash)] 59 + pub struct UndefinedBoolError; 60 + 61 + impl Display for UndefinedBoolError { 62 + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { 63 + write!(f, "Cannot convert `LooseBool::Undefined` into `bool`.") 64 + } 65 + } 66 + 67 + impl Error for UndefinedBoolError {} 68 + 69 + #[cfg(test)] 70 + mod tests { 71 + use super::*; 72 + 73 + #[test] 74 + fn loose_to_bool() { 75 + assert_eq!(bool::try_from(LooseBool::True), Ok(true)); 76 + assert_eq!(bool::try_from(LooseBool::False), Ok(false)); 77 + 78 + for i in 2..256 { 79 + assert_eq!( 80 + bool::try_from(LooseBool::Undefined(i)), 81 + Err(UndefinedBoolError), 82 + "Failed for i={i}" 83 + ); 84 + } 85 + } 86 + 87 + #[test] 88 + fn bool_to_loose() { 89 + assert_eq!(LooseBool::from(false), LooseBool::False); 90 + assert_eq!(LooseBool::from(true), LooseBool::True); 91 + } 92 + }