fmt + clippy

This commit is contained in:
Lee-Orr
2024-02-08 17:21:38 -05:00
parent 733b3cf9f3
commit 81f6efa10c
2 changed files with 145 additions and 55 deletions
+8 -9
View File
@@ -1,4 +1,7 @@
use bevy::{pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap}, prelude::*}; use bevy::{
pbr::{CascadeShadowConfigBuilder, DirectionalLightShadowMap},
prelude::*,
};
use bevy_inspector_egui::quick::WorldInspectorPlugin; use bevy_inspector_egui::quick::WorldInspectorPlugin;
fn main() { fn main() {
@@ -11,13 +14,10 @@ fn main() {
} }
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn( commands.spawn(Camera3dBundle {
Camera3dBundle { transform: Transform::from_xyz(0.7, 0.7, 1.0).looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
transform: Transform::from_xyz(0.7, 0.7, 1.0) ..default()
.looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y), });
..default()
}
);
commands.spawn(DirectionalLightBundle { commands.spawn(DirectionalLightBundle {
directional_light: DirectionalLight { directional_light: DirectionalLight {
@@ -37,7 +37,6 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
..default() ..default()
}); });
commands.spawn(SceneBundle { commands.spawn(SceneBundle {
scene: asset_server.load("example.gltf#Scene0"), scene: asset_server.load("example.gltf#Scene0"),
..default() ..default()
+133 -42
View File
@@ -1,6 +1,6 @@
use std::{num::ParseFloatError, path::Display, str::FromStr, sync::Arc}; use std::{num::ParseFloatError, str::FromStr};
use bevy::{time::{self, Time}, utils::{default, HashMap}};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
@@ -46,27 +46,54 @@ impl FromStr for Timestamp {
return Err(TimestampParseError::EmptyTimestamp); return Err(TimestampParseError::EmptyTimestamp);
}; };
let frame = frame.parse::<f32>().map(|v| v.floor() as u8).map_err(|e| TimestampParseError::NumberParseError(e, format!("frame failed for {s}")))?; let frame = frame.parse::<f32>().map(|v| v.floor() as u8).map_err(|e| {
let second = segments.get(1).unwrap_or(&"0").parse::<f32>().map(|v| v.floor() as u8).map_err(|e| TimestampParseError::NumberParseError(e, format!("second failed for {s}")))?; TimestampParseError::NumberParseError(e, format!("frame failed for {s}"))
let minute = segments.get(2).unwrap_or(&"0").parse::<f32>().map(|v| v.floor() as u8).map_err(|e| TimestampParseError::NumberParseError(e, format!("minute failed for {s}")))?; })?;
let hour = segments.get(3).unwrap_or(&"0").parse::<f32>().map(|v| v.floor() as u8).map_err(|e| TimestampParseError::NumberParseError(e, format!("hour failed for {s}")))?; let second = segments
.get(1)
.unwrap_or(&"0")
.parse::<f32>()
.map(|v| v.floor() as u8)
.map_err(|e| {
TimestampParseError::NumberParseError(e, format!("second failed for {s}"))
})?;
let minute = segments
.get(2)
.unwrap_or(&"0")
.parse::<f32>()
.map(|v| v.floor() as u8)
.map_err(|e| {
TimestampParseError::NumberParseError(e, format!("minute failed for {s}"))
})?;
let hour = segments
.get(3)
.unwrap_or(&"0")
.parse::<f32>()
.map(|v| v.floor() as u8)
.map_err(|e| {
TimestampParseError::NumberParseError(e, format!("hour failed for {s}"))
})?;
Ok(Self { hour, minute, second, frame }) Ok(Self {
hour,
minute,
second,
frame,
})
} }
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct CSVFrame { pub struct CSVFrame {
pub timestamp: Timestamp, pub timestamp: Timestamp,
pub morphs: Vec<f32> pub morphs: Vec<f32>,
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CSVAnimation { pub struct CSVAnimation {
pub frames: Vec<CSVFrame>, pub frames: Vec<CSVFrame>,
pub morph_names: Vec<String>, pub morph_names: Vec<String>,
pub fps: u8 pub fps: u8,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -88,31 +115,45 @@ impl std::fmt::Display for CSVParseError {
impl std::error::Error for CSVParseError {} impl std::error::Error for CSVParseError {}
const SKIP_COLUMNS_BEFORE_MORPH : usize = 1; const SKIP_COLUMNS_BEFORE_MORPH: usize = 1;
impl CSVAnimation { impl CSVAnimation {
pub fn parse(csv: &str) -> Result<CSVAnimation, CSVParseError> { pub fn parse(csv: &str) -> Result<CSVAnimation, CSVParseError> {
if csv.is_empty() { if csv.is_empty() {
return Err(CSVParseError::NoData); return Err(CSVParseError::NoData);
} }
let buffer = csv.as_bytes(); let buffer = csv.as_bytes();
let mut csv_content = csv::ReaderBuilder::new().has_headers(false).from_reader(buffer); let mut csv_content = csv::ReaderBuilder::new()
.has_headers(false)
.from_reader(buffer);
let mut records = csv_content.records(); let mut records = csv_content.records();
let header_row = records.next().ok_or(CSVParseError::NoData)?.map_err(|e| CSVParseError::InvalidCSV(e))?; let header_row = records
.next()
.ok_or(CSVParseError::NoData)?
.map_err(CSVParseError::InvalidCSV)?;
let morph_names : Vec<String> = header_row.iter().enumerate().filter_map(|(i, v)| if i > SKIP_COLUMNS_BEFORE_MORPH { Some(v.trim().to_string()) } else { None }).collect(); let morph_names: Vec<String> = header_row
.iter()
.enumerate()
.filter_map(|(i, v)| {
if i > SKIP_COLUMNS_BEFORE_MORPH {
Some(v.trim().to_string())
} else {
None
}
})
.collect();
if morph_names.is_empty() { if morph_names.is_empty() {
return Err(CSVParseError::NoMorphs); return Err(CSVParseError::NoMorphs);
} }
let mut frames = Vec::new(); let mut frames = Vec::new();
let mut fps : u8 = 0; let mut fps: u8 = 0;
for result in records { for result in records {
let record = result.map_err(CSVParseError::InvalidCSV)?; let record = result.map_err(CSVParseError::InvalidCSV)?;
@@ -120,12 +161,21 @@ impl CSVAnimation {
let mut iter = record.iter(); let mut iter = record.iter();
let timestamp = iter.next().ok_or(CSVParseError::MisssingKnownHeaders)?; // This error should be impossible since the CSV parser validates that all rows have the same length, and we already confirmed there is more than 0 characters in the first row. let timestamp = iter.next().ok_or(CSVParseError::MisssingKnownHeaders)?; // This error should be impossible since the CSV parser validates that all rows have the same length, and we already confirmed there is more than 0 characters in the first row.
let timestamp : Timestamp = timestamp.parse().map_err(CSVParseError::TimecodeParseError)?; let timestamp: Timestamp = timestamp
.parse()
.map_err(CSVParseError::TimecodeParseError)?;
for _ in 0..SKIP_COLUMNS_BEFORE_MORPH { for _ in 0..SKIP_COLUMNS_BEFORE_MORPH {
let _ = iter.next().ok_or(CSVParseError::MisssingKnownHeaders)?; // similarly, we already validate that there are a single morph in the CSV, so the skipped columns shouldn't be valid CSVs. let _ = iter.next().ok_or(CSVParseError::MisssingKnownHeaders)?;
// similarly, we already validate that there are a single morph in the CSV, so the skipped columns shouldn't be valid CSVs.
} }
let morphs = iter.map(|v| v.trim().parse::<f32>().map_err(CSVParseError::MorphParseError)).collect::<Result<Vec<_>,_>>()?; let morphs = iter
.map(|v| {
v.trim()
.parse::<f32>()
.map_err(CSVParseError::MorphParseError)
})
.collect::<Result<Vec<_>, _>>()?;
if fps < timestamp.frame { if fps < timestamp.frame {
fps = timestamp.frame; fps = timestamp.frame;
@@ -141,59 +191,91 @@ impl CSVAnimation {
Ok(CSVAnimation { Ok(CSVAnimation {
frames, frames,
morph_names, morph_names,
fps: fps + 1 fps: fps + 1,
}) })
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use bevy::time::{self, Time};
use crate::{CSVAnimation, CSVParseError, Timestamp, TimestampParseError}; use crate::{CSVAnimation, CSVParseError, Timestamp, TimestampParseError};
#[test] #[test]
fn given_an_empty_timestamp_it_returns_an_empty_timestamp_error() { fn given_an_empty_timestamp_it_returns_an_empty_timestamp_error() {
let timestamp = "".parse::<Timestamp>().expect_err("succeeded against expectations"); let timestamp = ""
.parse::<Timestamp>()
.expect_err("succeeded against expectations");
assert!(matches!(timestamp, TimestampParseError::EmptyTimestamp)) assert!(matches!(timestamp, TimestampParseError::EmptyTimestamp))
} }
#[test] #[test]
fn given_a_timestamp_with_more_than_4_segments_returns_a_too_many_segments_error() { fn given_a_timestamp_with_more_than_4_segments_returns_a_too_many_segments_error() {
let timestamp = "00:00:00:00:00.1234".parse::<Timestamp>().expect_err("succeeded against expectations"); let timestamp = "00:00:00:00:00.1234"
.parse::<Timestamp>()
.expect_err("succeeded against expectations");
assert!(matches!(timestamp, TimestampParseError::TooManySegments)) assert!(matches!(timestamp, TimestampParseError::TooManySegments))
} }
#[test] #[test]
fn given_a_zero_timestamp_it_is_parsed_correctly() { fn given_a_zero_timestamp_it_is_parsed_correctly() {
let timestamp = "00:00:00:00".parse::<Timestamp>().expect("Timestamp failed to parse"); let timestamp = "00:00:00:00"
assert_eq!(timestamp, Timestamp { hour: 0, minute: 0, second: 0, frame: 0 }) .parse::<Timestamp>()
.expect("Timestamp failed to parse");
assert_eq!(
timestamp,
Timestamp {
hour: 0,
minute: 0,
second: 0,
frame: 0
}
)
} }
#[test] #[test]
fn given_a_valid_timestamp_it_is_parsed_correctly() { fn given_a_valid_timestamp_it_is_parsed_correctly() {
let timestamp = "01:02:03:04.123".parse::<Timestamp>().expect("Timestamp failed to parse"); let timestamp = "01:02:03:04.123"
assert_eq!(timestamp, Timestamp { hour: 1, minute: 2, second: 3, frame: 4 }) .parse::<Timestamp>()
.expect("Timestamp failed to parse");
assert_eq!(
timestamp,
Timestamp {
hour: 1,
minute: 2,
second: 3,
frame: 4
}
)
} }
#[test] #[test]
fn given_no_data_it_emits_a_no_data_error() { fn given_no_data_it_emits_a_no_data_error() {
assert!(matches!(CSVAnimation::parse("").err().unwrap(), CSVParseError::NoData)) assert!(matches!(
CSVAnimation::parse("").err().unwrap(),
CSVParseError::NoData
))
} }
#[test] #[test]
fn given_an_invalid_csv_it_emits_an_invalid_csv_error() { fn given_an_invalid_csv_it_emits_an_invalid_csv_error() {
let result = CSVAnimation::parse("an, invalid, csv, file let result = CSVAnimation::parse(
"an, invalid, csv, file
seems to be here... right now? seems to be here... right now?
I wonder, will it be caught"); I wonder, will it be caught",
);
if !matches!(result, Err(CSVParseError::InvalidCSV(_))) { if !matches!(result, Err(CSVParseError::InvalidCSV(_))) {
panic!( "got {result:?} instead of a CSVParseError"); panic!("got {result:?} instead of a CSVParseError");
} }
} }
#[test] #[test]
fn given_a_header_row_it_sets_correct_headers() { fn given_a_header_row_it_sets_correct_headers() {
let result = CSVAnimation::parse("timecode, blendshapecount, shape_1, shape_2 let result = CSVAnimation::parse(
00:00:00:00, 0, 0, 0").unwrap(); "timecode, blendshapecount, shape_1, shape_2
00:00:00:00, 0, 0, 0",
)
.unwrap();
assert_eq!(result.morph_names.first().unwrap(), "shape_1"); assert_eq!(result.morph_names.first().unwrap(), "shape_1");
assert_eq!(result.morph_names.get(1).unwrap(), "shape_2"); assert_eq!(result.morph_names.get(1).unwrap(), "shape_2");
} }
@@ -206,33 +288,42 @@ mod tests {
#[test] #[test]
fn given_no_frames_it_emits_a_no_frame_error() { fn given_no_frames_it_emits_a_no_frame_error() {
let result = CSVAnimation::parse("timecode, blendshapecount, shape_1, shape_2").unwrap_err(); let result =
CSVAnimation::parse("timecode, blendshapecount, shape_1, shape_2").unwrap_err();
assert!(matches!(result, CSVParseError::NoFrames)); assert!(matches!(result, CSVParseError::NoFrames));
} }
#[test] #[test]
fn given_a_single_frame_it_has_a_time_of_0() { fn given_a_single_frame_it_has_a_time_of_0() {
let result = CSVAnimation::parse("timecode, blendshapecount, shape_1, shape_2 let result = CSVAnimation::parse(
00:00:00:00.00, 0, 0, 0").unwrap(); "timecode, blendshapecount, shape_1, shape_2
00:00:00:00.00, 0, 0, 0",
)
.unwrap();
assert_eq!(result.frames.first().unwrap().timestamp.frame, 0); assert_eq!(result.frames.first().unwrap().timestamp.frame, 0);
} }
#[test] #[test]
fn given_multiple_frames_can_deduce_fps() { fn given_multiple_frames_can_deduce_fps() {
let result = CSVAnimation::parse("timecode, blendshapecount, shape_1, shape_2 let result = CSVAnimation::parse(
"timecode, blendshapecount, shape_1, shape_2
00:00:00:00.00, 0, 0, 0 00:00:00:00.00, 0, 0, 0
00:00:00:01.00, 0, 0, 0 00:00:00:01.00, 0, 0, 0
00:00:00:02.00, 0, 0, 0 00:00:00:02.00, 0, 0, 0
00:00:01:00.00, 0, 0, 0 00:00:01:00.00, 0, 0, 0
00:00:01:01.00, 0, 0, 0").unwrap(); 00:00:01:01.00, 0, 0, 0",
)
.unwrap();
assert_eq!(result.fps, 3); assert_eq!(result.fps, 3);
} }
#[test] #[test]
fn given_a_single_frame_it_parses_morphs_correctly() { fn given_a_single_frame_it_parses_morphs_correctly() {
let result = CSVAnimation::parse("timecode, blendshapecount, shape_1, shape_2 let result = CSVAnimation::parse(
00:00:00:00.00, 0, 0.1, 0.5").unwrap(); "timecode, blendshapecount, shape_1, shape_2
00:00:00:00.00, 0, 0.1, 0.5",
)
.unwrap();
assert!(&result.frames.first().unwrap().morphs[0] - 0.1 < 0.0001); assert!(&result.frames.first().unwrap().morphs[0] - 0.1 < 0.0001);
assert!(&result.frames.first().unwrap().morphs[1] - 0.5 < 0.0001); assert!(&result.frames.first().unwrap().morphs[1] - 0.5 < 0.0001);
} }