AnyRange: Add AnyRange for taking any range in error
This commit is contained in:
		
							
								
								
									
										17
									
								
								src/cpu.rs
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								src/cpu.rs
									
									
									
									
									
								
							| @@ -476,22 +476,27 @@ impl CPU { | ||||
|             return Ok(self); | ||||
|         } | ||||
|         self.cycle += 1; | ||||
|         let opchunk = self | ||||
|             .screen | ||||
|             .get(self.pc as usize..) | ||||
|             .ok_or(Error::InvalidAddressRange { | ||||
|                 range: (self.pc as usize..).into(), | ||||
|             })?; | ||||
|         // fetch opcode | ||||
|         let opcode: &[u8; 2] = if let Some(slice) = bus.get(self.pc as usize..self.pc as usize + 2) | ||||
|         { | ||||
|         let opcode: &[u8; 2] = | ||||
|             if let Some(slice) = self.screen.get(self.pc as usize..self.pc as usize + 2) { | ||||
|                 slice | ||||
|                     .try_into() | ||||
|                 .expect("`slice` should be exactly 2 bytes.") | ||||
|                     .expect("`slice` should be exactly 4 bytes.") | ||||
|             } else { | ||||
|                 return Err(Error::InvalidAddressRange { | ||||
|                 range: self.pc as usize..self.pc as usize + 2, | ||||
|                     range: (self.pc as usize..self.pc as usize + 4).into(), | ||||
|                 }); | ||||
|             }; | ||||
|  | ||||
|         // Print opcode disassembly: | ||||
|         if self.flags.debug { | ||||
|             self.timers.insn = Instant::now(); | ||||
|             std::print!( | ||||
|             std::println!( | ||||
|                 "{:3} {:03x}: {:<36}", | ||||
|                 self.cycle.bright_black(), | ||||
|                 self.pc, | ||||
|   | ||||
							
								
								
									
										23
									
								
								src/error.rs
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								src/error.rs
									
									
									
									
									
								
							| @@ -3,7 +3,8 @@ | ||||
|  | ||||
| //! Error type for Chirp | ||||
|  | ||||
| use std::ops::Range; | ||||
| pub mod any_range; | ||||
| use any_range::AnyRange; | ||||
|  | ||||
| use crate::cpu::bus::Region; | ||||
| use thiserror::Error; | ||||
| @@ -15,7 +16,7 @@ pub type Result<T> = std::result::Result<T, Error>; | ||||
| #[derive(Debug, Error)] | ||||
| pub enum Error { | ||||
|     /// Represents a breakpoint being hit | ||||
|     #[error("Breakpoint hit: {addr:03x} ({next:04x})")] | ||||
|     #[error("breakpoint hit: {addr:03x} ({next:04x})")] | ||||
|     BreakpointHit { | ||||
|         /// The address of the breakpoint | ||||
|         addr: u16, | ||||
| @@ -23,37 +24,37 @@ pub enum Error { | ||||
|         next: u16, | ||||
|     }, | ||||
|     /// Represents an unimplemented operation | ||||
|     #[error("Unrecognized opcode: {word:04x}")] | ||||
|     #[error("opcode {word:04x} not recognized")] | ||||
|     UnimplementedInstruction { | ||||
|         /// The offending word | ||||
|         word: u16, | ||||
|     }, | ||||
|     /// The region you asked for was not defined | ||||
|     #[error("No {region} found on bus")] | ||||
|     #[error("region {region} is not present on bus")] | ||||
|     MissingRegion { | ||||
|         /// The offending [Region] | ||||
|         region: Region, | ||||
|     }, | ||||
|     /// Tried to fetch [Range] from bus, received nothing | ||||
|     #[error("Invalid range {range:04x?} for bus")] | ||||
|     /// Tried to fetch data at [AnyRange] from bus, received nothing | ||||
|     #[error("range {range:04x?} is not present on bus")] | ||||
|     InvalidAddressRange { | ||||
|         /// The offending [Range] | ||||
|         range: Range<usize>, | ||||
|         /// The offending [AnyRange] | ||||
|         range: AnyRange<usize>, | ||||
|     }, | ||||
|     /// Tried to press a key that doesn't exist | ||||
|     #[error("Invalid key: {key:X}")] | ||||
|     #[error("tried to press key {key:X} which does not exist")] | ||||
|     InvalidKey { | ||||
|         /// The offending key | ||||
|         key: usize, | ||||
|     }, | ||||
|     /// Tried to get/set an out-of-bounds register | ||||
|     #[error("Invalid register: v{reg:X}")] | ||||
|     #[error("tried to access register v{reg:X} which does not exist")] | ||||
|     InvalidRegister { | ||||
|         /// The offending register | ||||
|         reg: usize, | ||||
|     }, | ||||
|     /// Tried to convert string into mode, but it did not match. | ||||
|     #[error("Invalid mode: {mode}")] | ||||
|     #[error("no suitable conversion of \"{mode}\" into Mode")] | ||||
|     InvalidMode { | ||||
|         /// The string which failed to become a mode | ||||
|         mode: String, | ||||
|   | ||||
							
								
								
									
										69
									
								
								src/error/any_range.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								src/error/any_range.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| // (c) 2023 John A. Breaux | ||||
| // This code is licensed under MIT license (see LICENSE for details) | ||||
|  | ||||
| //! Holder for any [Range] | ||||
|  | ||||
| // This is super over-engineered, considering it was originally meant to help with only one LOC | ||||
| use std::ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive}; | ||||
| use thiserror::Error; | ||||
|  | ||||
| #[macro_use] | ||||
| mod macros; | ||||
|  | ||||
| #[derive(Clone, Debug, Error, PartialEq, Eq, Hash)] | ||||
| #[error("Failed to convert variant {0} back into range.")] | ||||
| /// Emitted when conversion back into a [std::ops]::Range\* fails. | ||||
| pub struct AnyRangeError<Idx>(AnyRange<Idx>); | ||||
|  | ||||
| /// Holder for any [Range] | ||||
| #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||||
| pub enum AnyRange<Idx> { | ||||
|     /// Bounded exclusive [Range] i.e. `0..10` | ||||
|     Range(Range<Idx>), | ||||
|     /// Unbounded [RangeFrom], i.e. `0..` | ||||
|     RangeFrom(RangeFrom<Idx>), | ||||
|     /// Unbounded [RangeFull], i.e. `..` | ||||
|     RangeFull(RangeFull), | ||||
|     /// Bounded inclusive [RangeInclusive], i.e. `0..=10` | ||||
|     RangeInclusive(RangeInclusive<Idx>), | ||||
|     /// Unbounded [RangeTo], i.e. `..10` | ||||
|     RangeTo(RangeTo<Idx>), | ||||
|     /// Unbounded inclusive [RangeToInclusive], i.e. `..=10` | ||||
|     RangeToInclusive(RangeToInclusive<Idx>), | ||||
| } | ||||
|  | ||||
| variant_from! { | ||||
|     match impl<Idx> (From, TryInto) for AnyRange<Idx> { | ||||
|         type Error = AnyRangeError<Idx>; | ||||
|         Range<Idx> => AnyRange::Range, | ||||
|         RangeFrom<Idx> => AnyRange::RangeFrom, | ||||
|         RangeFull => AnyRange::RangeFull, | ||||
|         RangeInclusive<Idx> => AnyRange::RangeInclusive, | ||||
|         RangeTo<Idx> => AnyRange::RangeTo, | ||||
|         RangeToInclusive<Idx> => AnyRange::RangeToInclusive, | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Convenient conversion functions from [AnyRange] to the inner type | ||||
| impl<Idx> AnyRange<Idx> { | ||||
|     try_into_fn! { | ||||
|         /// Converts from [AnyRange::Range] into a [Range], else [None] | ||||
|         pub fn range(self) -> Option<Range<Idx>>; | ||||
|         /// Converts from [AnyRange::RangeFrom] into a [RangeFrom], else [None] | ||||
|         pub fn range_from(self) -> Option<RangeFrom<Idx>>; | ||||
|         /// Converts from [AnyRange::RangeFull] into a [RangeFull], else [None] | ||||
|         pub fn range_full(self) -> Option<RangeFull>; | ||||
|         /// Converts from [AnyRange::RangeInclusive] into a [RangeInclusive], else [None] | ||||
|         pub fn range_inclusive(self) -> Option<RangeInclusive<Idx>>; | ||||
|         /// Converts from [AnyRange::RangeTo] into a [RangeTo], else [None] | ||||
|         pub fn range_to(self) -> Option<RangeTo<Idx>>; | ||||
|         /// Converts from [AnyRange::RangeToInclusive] into a [RangeToInclusive], else [None] | ||||
|         pub fn range_to_inclusive(self) -> Option<RangeToInclusive<Idx>>; | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl<Idx> From<AnyRange<Idx>> for AnyRangeError<Idx> { | ||||
|     fn from(value: AnyRange<Idx>) -> Self { | ||||
|         AnyRangeError(value) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										33
									
								
								src/error/any_range/macros.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/error/any_range/macros.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| //! Macros for AnyRange | ||||
|  | ||||
| /// Generate From and TryFrom impls for each variant | ||||
| macro_rules! variant_from {( | ||||
|     match impl<$T:ident> $_:tt for $enum:path { | ||||
|         type Error = $error:path; | ||||
|         $($src:path => $variant:path),+$(,)? | ||||
|     } | ||||
| ) => { | ||||
|     // The forward direction is infallible | ||||
|     $(impl<$T> From<$src> for $enum { | ||||
|         fn from(value: $src) -> Self { $variant(value) } | ||||
|     })+ | ||||
|     // The reverse direction could fail if the $variant doesn't hold $src | ||||
|     $(impl<$T> TryFrom<$enum> for $src { | ||||
|         type Error = $error; | ||||
|         fn try_from(value: $enum) -> Result<Self, Self::Error> { | ||||
|             if let $variant(r) = value { Ok(r) } else { Err(value.into()) } | ||||
|         } | ||||
|     })+ | ||||
| }} | ||||
|  | ||||
| /// Turns a list of function prototypes into functions which use [TryInto], returning [Option] | ||||
| /// | ||||
| /// # Examples | ||||
| /// ```rust,ignore | ||||
| /// try_into_fn! { fn range(self) -> Option<Other>; } | ||||
| /// ``` | ||||
| macro_rules! try_into_fn { | ||||
|     ($($(#[$doc:meta])? $pub:vis fn $name:ident $args:tt -> $ret:ty);+ $(;)?) => { | ||||
|         $($(#[$doc])? $pub fn $name $args -> $ret { $args.try_into().ok() })+ | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user