diff --git a/src/parse/parse_utils.rs b/src/parse/parse_utils.rs index d548a0c..7379f41 100644 --- a/src/parse/parse_utils.rs +++ b/src/parse/parse_utils.rs @@ -76,6 +76,203 @@ pub fn ensure_close(events: &mut DataTreeReader) -> PsyResult<()> } } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum Range { + Full, + From(usize), + To(usize), + Range(usize, usize), +} + +impl Range { + pub fn contains(self, n: usize) -> bool { + match self { + Range::Full => true, + Range::From(start) => n >= start, + Range::To(end) => n < end, + Range::Range(start, end) => n >= start && n < end, + } + } + + /// Checks if the value is within the upper bound of the range. + /// Ignores any lower bound. + pub fn contains_upper(self, n: usize) -> bool { + match self { + Range::Full | Range::From(_) => true, + Range::To(end) => n < end, + Range::Range(_, end) => n < end, + } + } + + pub fn lower(self) -> usize { + match self { + Range::Full => 0, + Range::From(start) => start, + Range::To(_) => 0, + Range::Range(start, _) => start, + } + } + + pub fn upper(self) -> usize { + match self { + Range::Full => std::usize::MAX, + Range::From(_) => std::usize::MAX, + Range::To(end) => end, + Range::Range(_, end) => end, + } + } +} + +impl std::convert::From for Range { + fn from(_r: std::ops::RangeFull) -> Self { + Range::Full + } +} + +impl std::convert::From> for Range { + fn from(r: std::ops::RangeFrom) -> Self { + Range::From(r.start) + } +} + +impl std::convert::From> for Range { + fn from(r: std::ops::RangeTo) -> Self { + Range::To(r.end) + } +} + +impl std::convert::From> for Range { + fn from(r: std::ops::Range) -> Self { + Range::Range(r.start, r.end) + } +} + +impl std::convert::From for Range { + fn from(r: usize) -> Self { + Range::Range(r, r + 1) + } +} + +/// Acts as an intermediary for parsing, ensuring that the right number of the +/// right subsections are encountered. It loops over subsections, passing +/// through the `events` object untouched, so the passed closure needs to call +/// `next_event`. +/// +/// Tracks a maximum of 64 different subsections. +pub fn ensure_subsections( + events: &mut DataTreeReader, + subsections: &[(&str, bool, Range)], // (type name, is leaf, valid count range) + mut f: F, +) -> PsyResult<()> +where + F: FnMut(&mut DataTreeReader) -> PsyResult<()>, +{ + let mut counts = [0usize; 64]; + + // Loop through our events! + loop { + match events.peek_event()? { + Event::Leaf { + type_name, + byte_offset, + .. + } => { + if let Some(idx) = subsections + .iter() + .position(|(n, l, _)| *l == true && n == &type_name) + { + // Increment count and make sure we're within the valid count + // range for this sub-sections. + counts[idx] += 1; + if !subsections[idx].2.contains_upper(counts[idx]) { + return Err(PsyError::WrongNodeCount( + byte_offset, + format!( + "Expected at most {} '{}' leaf nodes but found \ + at least {}.", + subsections[idx].2.upper() - 1, + subsections[idx].0, + counts[idx], + ), + )); + } + + // Call handler. + f(events)? + } else { + break; + } + } + Event::InnerOpen { + type_name, + byte_offset, + .. + } => { + if let Some(idx) = subsections + .iter() + .position(|(n, l, _)| *l == false && n == &type_name) + { + // Increment count and make sure we're within the valid count + // range for this sub-sections. + counts[idx] += 1; + if !subsections[idx].2.contains_upper(counts[idx]) { + return Err(PsyError::WrongNodeCount( + byte_offset, + format!( + "Expected at most {} internal '{}' node(s) but \ + found at least {}.", + subsections[idx].2.upper() - 1, + subsections[idx].0, + counts[idx], + ), + )); + } + + // Call handler. + f(events)? + } else { + break; + } + } + Event::InnerClose { .. } => { + break; + } + Event::EOF => { + break; + } + } + } + + // Validation. + for i in 0..subsections.len() { + if !subsections[i].2.contains(counts[i]) { + if subsections[i].1 { + return Err(PsyError::WrongNodeCount( + events.byte_offset(), + format!( + "Expected at least {} '{}' leaf node(s) but only found {}.", + subsections[i].2.lower(), + subsections[i].0, + counts[i], + ), + )); + } else { + return Err(PsyError::WrongNodeCount( + events.byte_offset(), + format!( + "Expected at least {} internal '{}' node(s) but only found {}.", + subsections[i].2.lower(), + subsections[i].0, + counts[i], + ), + )); + } + } + } + + Ok(()) +} + // ======================================================== #[cfg(test)] diff --git a/src/parse/psy.rs b/src/parse/psy.rs index a0368f6..c631255 100644 --- a/src/parse/psy.rs +++ b/src/parse/psy.rs @@ -19,7 +19,7 @@ use crate::{ }; use super::{ - parse_utils::{ensure_close, ws_f32, ws_u32}, + parse_utils::{ensure_close, ensure_subsections, ws_f32, ws_u32}, psy_assembly::parse_assembly, psy_light::parse_distant_disk_light, psy_surface_shader::parse_surface_shader, @@ -127,76 +127,76 @@ pub fn parse_scene<'a>( events: &mut DataTreeReader, _ident: Option<&str>, ) -> PsyResult> { - // Get output info. - let _output_info = if let Event::InnerOpen { - type_name: "Output", - .. - } = events.next_event()? - { - parse_output_info(events)? - } else { - todo!(); // Return error. - }; + // Get everything except the root assembly (which comes last). + let mut output_info = None; + let mut render_settings = None; + let mut shaders = None; + let mut world = None; + let mut camera = None; + let valid_subsections = &[ + ("Output", false, (1).into()), + ("RenderSettings", false, (1).into()), + ("Shaders", false, (1).into()), + ("World", false, (1).into()), + ("Camera", false, (1).into()), + ]; + ensure_subsections(events, valid_subsections, |events| { + match events.next_event()? { + Event::InnerOpen { + type_name: "Output", + .. + } => { + output_info = Some(parse_output_info(events)?); + } - // Get render settings. - let _render_settings = if let Event::InnerOpen { - type_name: "RenderSettings", - .. - } = events.next_event()? - { - parse_render_settings(events)? - } else { - todo!(); // Return error. - }; + Event::InnerOpen { + type_name: "RenderSettings", + .. + } => { + render_settings = Some(parse_render_settings(events)?); + } - // Get shaders. - let shaders = if let Event::InnerOpen { - type_name: "Shaders", - .. - } = events.next_event()? - { - parse_shaders(arena, events)? - } else { - todo!(); // Return error. - }; + Event::InnerOpen { + type_name: "Shaders", + .. + } => { + shaders = Some(parse_shaders(arena, events)?); + } - // Get world. - let world = if let Event::InnerOpen { - type_name: "World", .. - } = events.next_event()? - { - parse_world(arena, events)? - } else { - todo!(); // Return error. - }; + Event::InnerOpen { + type_name: "World", .. + } => { + world = Some(parse_world(arena, events)?); + } - // Get camera. - let camera = if let Event::InnerOpen { - type_name: "Camera", - .. - } = events.next_event()? - { - parse_camera(arena, events)? - } else { - todo!(); // Return error. - }; + Event::InnerOpen { + type_name: "Camera", + .. + } => { + camera = Some(parse_camera(arena, events)?); + } + + _ => unreachable!(), + } + Ok(()) + })?; // Get the root assembly. - let root_assembly = if let Event::InnerOpen { - type_name: "Assembly", - .. - } = events.next_event()? - { - parse_assembly(arena, events)? - } else { - todo!(); // Return error. - }; + let mut root_assembly = None; + let valid_subsections = &[("Assembly", false, (1).into())]; + ensure_subsections(events, valid_subsections, |events| { + match events.next_event()? { + Event::InnerOpen { + type_name: "Assembly", + .. + } => root_assembly = Some(parse_assembly(arena, events)?), + _ => unreachable!(), + } + Ok(()) + })?; // Make sure we're closed out properly. - if let Event::InnerClose { .. } = events.next_event()? { - } else { - todo!(); // Return error. - } + ensure_close(events)?; // Put scene together // let scene_name = if let Some(name) = ident { @@ -205,10 +205,10 @@ pub fn parse_scene<'a>( // None // }; let scene = Scene { - camera: camera, - world: world, - shaders: shaders, - root_assembly: root_assembly, + camera: camera.unwrap(), + world: world.unwrap(), + shaders: shaders.unwrap(), + root_assembly: root_assembly.unwrap(), }; // // Put renderer together @@ -227,9 +227,10 @@ pub fn parse_scene<'a>( } fn parse_output_info(events: &mut DataTreeReader) -> PsyResult { - let mut found_path = false; let mut path = String::new(); - loop { + + let valid_subsections = &[("Path", true, (1).into())]; + ensure_subsections(events, valid_subsections, |events| { match events.next_event()? { Event::Leaf { type_name: "Path", @@ -253,42 +254,36 @@ fn parse_output_info(events: &mut DataTreeReader) -> PsyResult { - break; - } - _ => { - todo!(); // Return error. + unreachable!(); } } - } + Ok(()) + })?; - if found_path { - return Ok(path); - } else { - // return Err(PsyError::MissingNode( - // tree.byte_offset(), - // "Output section must contain a Path.", - // )); - todo!(); // Return error. - } + ensure_close(events)?; + + Ok(path) } fn parse_render_settings( events: &mut DataTreeReader, ) -> PsyResult<((u32, u32), u32, u32)> { - let mut found_res = false; - let mut found_spp = false; let mut res = (0, 0); let mut spp = 0; let mut seed = 0; - loop { + + // Parse + let valid_subsections = &[ + ("Resolution", true, (1).into()), + ("SamplesPerPixel", true, (1).into()), + ("Seed", true, (..).into()), + ]; + ensure_subsections(events, valid_subsections, |events| { match events.next_event()? { // Resolution Event::Leaf { @@ -298,7 +293,6 @@ fn parse_render_settings( } => { if let IResult::Ok((_, (w, h))) = all_consuming(tuple((ws_u32, ws_u32)))(&contents) { - found_res = true; res = (w, h); } else { // Found Resolution, but its contents is not in the right format @@ -318,7 +312,6 @@ fn parse_render_settings( byte_offset, } => { if let IResult::Ok((_, n)) = all_consuming(ws_u32)(&contents) { - found_spp = true; spp = n; } else { // Found SamplesPerPixel, but its contents is not in the right format @@ -350,26 +343,17 @@ fn parse_render_settings( } } - Event::InnerClose { .. } => { - break; - } - _ => { - todo!(); // Return error. + unreachable!(); } } - } - if found_res && found_spp { - return Ok((res, spp, seed)); - } else { - // return Err(PsyError::MissingNode( - // tree.byte_offset(), - // "RenderSettings must have both Resolution and \ - // SamplesPerPixel specified.", - // )); - todo!(); // Return error. - } + Ok(()) + })?; + + ensure_close(events)?; + + Ok((res, spp, seed)) } fn parse_camera<'a>( @@ -382,7 +366,13 @@ fn parse_camera<'a>( let mut aperture_radii = Vec::new(); // Parse - loop { + let valid_subsections = &[ + ("Fov", true, (1..).into()), + ("FocalDistance", true, (1..).into()), + ("ApertureRadius", true, (1..).into()), + ("Transform", true, (1..).into()), + ]; + ensure_subsections(events, valid_subsections, |events| { match events.next_event()? { // Fov Event::Leaf { @@ -455,15 +445,15 @@ fn parse_camera<'a>( } } - Event::InnerClose { .. } => { - break; - } - _ => { - todo!(); // Return error. + unreachable!(); } } - } + + Ok(()) + })?; + + ensure_close(events)?; return Ok(Camera::new( arena, @@ -481,7 +471,11 @@ fn parse_world<'a>( let mut background_color = None; let mut lights: Vec<&dyn WorldLightSource> = Vec::new(); - loop { + let valid_subsections = &[ + ("BackgroundShader", false, (1).into()), + ("DistantDiskLight", false, (..).into()), + ]; + ensure_subsections(events, valid_subsections, |events| { match events.next_event()? { // Parse background shader Event::InnerOpen { @@ -496,7 +490,7 @@ fn parse_world<'a>( { contents.to_string() } else { - todo!(); // Return error. + panic!("No type specified for the BackgroundShader"); // Return error. }; match bgs_type.as_ref() { @@ -504,12 +498,12 @@ fn parse_world<'a>( if let Event::Leaf { type_name: "Color", contents, - .. + byte_offset, } = events.next_event()? { - background_color = Some(parse_color(contents)?); + background_color = Some(parse_color(byte_offset, contents)?); } else { - todo!( + panic!( "BackgroundShader's Type is Color, \ but no Color is specified." ); // Return error. @@ -517,7 +511,7 @@ fn parse_world<'a>( } _ => { - todo!( + panic!( "The specified BackgroundShader Type \ isn't a recognized type.", ); // Return an error. @@ -541,16 +535,12 @@ fn parse_world<'a>( ident.as_deref(), )?)); } - - Event::InnerClose { .. } => { - break; - } - - _ => { - todo!(); // Return error. - } + _ => unreachable!(), } - } + Ok(()) + })?; + + ensure_close(events)?; if background_color == None { todo!(); // Return error. @@ -568,12 +558,13 @@ fn parse_shaders<'a>( events: &mut DataTreeReader, ) -> PsyResult>> { let mut shaders = HashMap::new(); - loop { + let valid_subsections = &[("SurfaceShader", false, (..).into())]; + ensure_subsections(events, valid_subsections, |events| { match events.next_event()? { Event::InnerOpen { type_name: "SurfaceShader", ident, - .. + byte_offset, } => { if let Some(name) = ident { let name = name.to_string(); @@ -582,19 +573,19 @@ fn parse_shaders<'a>( parse_surface_shader(arena, events, Some(&name))?, ); } else { - todo!("Shader has no name."); // Return error. + return Err(PsyError::ExpectedIdent( + byte_offset, + "SurfaceShader has no name.".into(), + )); } } - Event::InnerClose { .. } => { - break; - } - - _ => { - todo!(); // Return error. - } + _ => unreachable!(), } - } + Ok(()) + })?; + + ensure_close(events)?; // Return the list of shaders. return Ok(shaders); @@ -626,39 +617,44 @@ pub fn make_transform_format_error(byte_offset: usize) -> PsyError { ) } -pub fn parse_color(contents: &str) -> PsyResult { +pub fn parse_color(byte_offset: usize, contents: &str) -> PsyResult { let items: Vec<_> = contents.split(',').map(|s| s.trim()).collect(); if items.len() != 2 { - todo!(); // Return an error. + return Err(PsyError::IncorrectLeafData( + byte_offset, + "Color has invalid format.".into(), + )); } match items[0] { "rec709" => { if let IResult::Ok((_, color)) = tuple((ws_f32, ws_f32, ws_f32))(items[1]) { return Ok(Color::new_xyz(rec709_e_to_xyz(color))); - } else { - todo!(); // Return an error. } } "blackbody" => { if let IResult::Ok((_, (temperature, factor))) = tuple((ws_f32, ws_f32))(items[1]) { return Ok(Color::new_blackbody(temperature, factor)); - } else { - todo!(); // Return an error. } } "color_temperature" => { if let IResult::Ok((_, (temperature, factor))) = tuple((ws_f32, ws_f32))(items[1]) { return Ok(Color::new_temperature(temperature, factor)); - } else { - todo!(); // Return an error. } } - _ => { - todo!(); // Return an error. + s => { + return Err(PsyError::IncorrectLeafData( + byte_offset, + format!("Unrecognized color type '{}.", s), + )); } } + + Err(PsyError::IncorrectLeafData( + byte_offset, + "Color has invalid format.".into(), + )) } diff --git a/src/parse/psy_assembly.rs b/src/parse/psy_assembly.rs index e97a38f..a2a0c70 100644 --- a/src/parse/psy_assembly.rs +++ b/src/parse/psy_assembly.rs @@ -9,7 +9,7 @@ use data_tree::{DataTreeReader, Event}; use crate::scene::{Assembly, Object, ObjectData}; use super::{ - parse_utils::ensure_close, + parse_utils::{ensure_close, ensure_subsections}, psy::{parse_matrix, PsyError, PsyResult}, psy_light::{parse_rectangle_light, parse_sphere_light}, psy_mesh_surface::parse_mesh_surface, @@ -20,115 +20,112 @@ pub fn parse_assembly<'a>( events: &mut DataTreeReader, ) -> PsyResult> { let mut assembly = Assembly::new(); - loop { - match events.next_event()? { - Event::InnerOpen { - type_name: "Object", - ident, - byte_offset, - } => { - // Get object identifier. - let object_ident = if let Some(id) = ident { - id.to_string() - } else { - return Err(PsyError::ExpectedIdent( - byte_offset, - "\'Object\' types must have an identifier, but the identifier is missing." - .into(), - )); - }; + ensure_subsections(events, &[("Object", false, (1..).into())], |events| { + if let Event::InnerOpen { + type_name: "Object", + ident, + byte_offset, + } = events.next_event()? + { + // Get object identifier. + let object_ident = if let Some(id) = ident { + id.to_string() + } else { + return Err(PsyError::ExpectedIdent( + byte_offset, + "\'Object\' types must have an identifier, but the identifier is missing." + .into(), + )); + }; - // Collect instances. - let mut instance_xform_idxs = Vec::new(); - while let Event::InnerOpen { - type_name: "Instance", - .. - } = events.peek_event()? - { - events.next_event(); - let xforms_start_idx = assembly.xforms.len(); - loop { - match events.next_event()? { - Event::Leaf { - type_name: "Transform", - contents, - .. - } => { - assembly.xforms.push(parse_matrix(contents)?); - } + // Collect instances. + let mut instance_xform_idxs = Vec::new(); + while let Event::InnerOpen { + type_name: "Instance", + .. + } = events.peek_event()? + { + events.next_event()?; + let xforms_start_idx = assembly.xforms.len(); + loop { + match events.next_event()? { + Event::Leaf { + type_name: "Transform", + contents, + .. + } => { + assembly.xforms.push(parse_matrix(contents)?); + } - Event::InnerClose { .. } => { - break; - } + Event::InnerClose { .. } => { + break; + } - _ => { - todo!("Instances can only contain Transforms."); - // Return an error. - } + _ => { + todo!("Instances can only contain Transforms."); + // Return an error. } } - - instance_xform_idxs.push(xforms_start_idx..assembly.xforms.len()); } - // Get object data. - let object_data = match events.next_event()? { - Event::InnerOpen { - type_name: "Assembly", - .. - } => ObjectData::Assembly(Box::new(parse_assembly(arena, events)?)), - - Event::InnerOpen { - type_name: "MeshSurface", - .. - } => ObjectData::Surface(Box::new(parse_mesh_surface(arena, events)?)), - - Event::InnerOpen { - type_name: "SphereLight", - .. - } => ObjectData::Light(Box::new(parse_sphere_light(arena, events)?)), - - Event::InnerOpen { - type_name: "RectangleLight", - .. - } => ObjectData::Light(Box::new(parse_rectangle_light(arena, events)?)), - - Event::InnerClose { byte_offset } => { - return Err(PsyError::MissingNode( - byte_offset, - "Object contains no object data.".into(), - )); - } - - _ => { - return Err(PsyError::UnknownVariant( - byte_offset, - "Unknown data type for Object.".into(), - )); - } - }; - - // Close object node. - ensure_close(events)?; - - assembly.objects.insert( - object_ident, - Object { - data: object_data, - instance_xform_idxs: instance_xform_idxs, - }, - ); + instance_xform_idxs.push(xforms_start_idx..assembly.xforms.len()); } - Event::InnerClose { .. } => { - break; - } + // Get object data. + let object_data = match events.next_event()? { + Event::InnerOpen { + type_name: "Assembly", + .. + } => ObjectData::Assembly(Box::new(parse_assembly(arena, events)?)), - _ => { - todo!(); // Return an error. - } + Event::InnerOpen { + type_name: "MeshSurface", + .. + } => ObjectData::Surface(Box::new(parse_mesh_surface(arena, events)?)), + + Event::InnerOpen { + type_name: "SphereLight", + .. + } => ObjectData::Light(Box::new(parse_sphere_light(arena, events)?)), + + Event::InnerOpen { + type_name: "RectangleLight", + .. + } => ObjectData::Light(Box::new(parse_rectangle_light(arena, events)?)), + + Event::InnerClose { byte_offset } => { + return Err(PsyError::MissingNode( + byte_offset, + "Object contains no object data.".into(), + )); + } + + _ => { + return Err(PsyError::UnknownVariant( + byte_offset, + "Unknown data type for Object.".into(), + )); + } + }; + + // Close object node. + ensure_close(events)?; + + assembly.objects.insert( + object_ident, + Object { + data: object_data, + instance_xform_idxs: instance_xform_idxs, + }, + ); + } else { + unreachable!() } - } + + Ok(()) + })?; + + ensure_close(events)?; // if !tree.is_internal() { // return Err(PsyError::UnknownError(tree.byte_offset())); diff --git a/src/parse/psy_light.rs b/src/parse/psy_light.rs index 689e233..4a91bca 100644 --- a/src/parse/psy_light.rs +++ b/src/parse/psy_light.rs @@ -65,12 +65,7 @@ pub fn parse_distant_disk_light<'a>( contents, byte_offset, } => { - if let Ok(color) = parse_color(&contents) { - colors.push(color); - } else { - // Found color, but its contents is not in the right format - return Err(PsyError::UnknownError(byte_offset)); - } + colors.push(parse_color(byte_offset, &contents)?); } Event::InnerClose { .. } => { @@ -116,12 +111,7 @@ pub fn parse_sphere_light<'a>( contents, byte_offset, } => { - if let Ok(color) = parse_color(&contents) { - colors.push(color); - } else { - // Found color, but its contents is not in the right format - return Err(PsyError::UnknownError(byte_offset)); - } + colors.push(parse_color(byte_offset, &contents)?); } Event::InnerClose { .. } => { @@ -168,12 +158,7 @@ pub fn parse_rectangle_light<'a>( contents, byte_offset, } => { - if let Ok(color) = parse_color(&contents) { - colors.push(color); - } else { - // Found color, but its contents is not in the right format - return Err(PsyError::UnknownError(byte_offset)); - } + colors.push(parse_color(byte_offset, &contents)?); } Event::InnerClose { .. } => { diff --git a/src/parse/psy_surface_shader.rs b/src/parse/psy_surface_shader.rs index e2db366..4d74c01 100644 --- a/src/parse/psy_surface_shader.rs +++ b/src/parse/psy_surface_shader.rs @@ -40,12 +40,7 @@ pub fn parse_surface_shader( byte_offset, } = events.next_event()? { - if let Ok(color) = parse_color(contents) { - color - } else { - // Found color, but its contents is not in the right format - return Err(PsyError::UnknownError(byte_offset)); - } + parse_color(byte_offset, contents)? } else { return Err(PsyError::MissingNode( events.byte_offset(), @@ -76,13 +71,7 @@ pub fn parse_surface_shader( contents, byte_offset, } => { - if let Ok(col) = parse_color(contents) { - color = Some(col); - } else { - // Found color, but its contents is not in the right - // format. - return Err(PsyError::UnknownError(byte_offset)); - } + color = Some(parse_color(byte_offset, contents)?); } // Roughness @@ -147,12 +136,7 @@ pub fn parse_surface_shader( byte_offset, } = events.next_event()? { - if let Ok(color) = parse_color(contents) { - color - } else { - // Found color, but its contents is not in the right format - return Err(PsyError::UnknownError(byte_offset)); - } + parse_color(byte_offset, contents)? } else { return Err(PsyError::MissingNode( events.byte_offset(),