mindustry logic execution, map- and schematic- parsing and rendering
Implement Display for error printing
| -rw-r--r-- | src/data/base64.rs | 27 | ||||
| -rw-r--r-- | src/data/schematic.rs | 95 | ||||
| -rw-r--r-- | src/exe/args.rs | 60 | ||||
| -rw-r--r-- | src/exe/mod.rs | 2 | ||||
| -rw-r--r-- | src/exe/print.rs | 62 |
5 files changed, 183 insertions, 63 deletions
diff --git a/src/data/base64.rs b/src/data/base64.rs index b3d7834..457d773 100644 --- a/src/data/base64.rs +++ b/src/data/base64.rs @@ -1,3 +1,5 @@ +use std::fmt; + const CHARS: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const PADDING: u8 = b'='; @@ -169,12 +171,37 @@ pub enum DecodeError TrailingData{at: usize}, } +impl fmt::Display for DecodeError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self + { + DecodeError::Malformed{at, value} => write!(f, "Malformed base64 character {value:?} (at {at})"), + DecodeError::Overflow{need, have} => write!(f, "Decoder overflow (need {need}, but only have {have})"), + DecodeError::Truncated => write!(f, "Truncated base64 input stream"), + DecodeError::TrailingData{at} => write!(f, "Trailing data in base64 stream (at {at})"), + } + } +} + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum EncodeError { Overflow{need: usize, have: usize} } +impl fmt::Display for EncodeError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self + { + EncodeError::Overflow{need, have} => write!(f, "Encoder overflow (need {need}, but only have {have})"), + } + } +} + #[cfg(test)] mod test { diff --git a/src/data/schematic.rs b/src/data/schematic.rs index d1e1c9a..eeae352 100644 --- a/src/data/schematic.rs +++ b/src/data/schematic.rs @@ -404,6 +404,18 @@ pub enum NewError Height(u16), } +impl fmt::Display for NewError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self + { + NewError::Width(w) => write!(f, "Invalid schematic width ({w})"), + NewError::Height(h) => write!(f, "Invalid schematic height ({h})"), + } + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct PosError { @@ -413,6 +425,14 @@ pub struct PosError pub h: u16, } +impl fmt::Display for PosError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "Position {x} / {y} out of bounds {w} / {h}", x = self.x, y = self.y, w = self.w, h = self.h) + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum PlaceError { @@ -420,6 +440,18 @@ pub enum PlaceError Overlap{x: u16, y: u16}, } +impl fmt::Display for PlaceError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self + { + PlaceError::Bounds{x, y, sz, w, h} => write!(f, "Block placement {x} / {y} (size {sz}) within {w} / {h}"), + PlaceError::Overlap{x, y} => write!(f, "Overlapping an existing block at {x} / {y}"), + } + } +} + impl fmt::Display for Schematic { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result @@ -872,6 +904,28 @@ impl From<PlaceError> for ReadError } } +impl fmt::Display for ReadError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self + { + ReadError::Read(..) => write!(f, "Failed to read data from buffer"), + ReadError::Header(hdr) => write!(f, "Incorrect header ({hdr:08X})"), + ReadError::Version(ver) => write!(f, "Unsupported version ({ver})"), + ReadError::Decompress(e) => e.fmt(f), + ReadError::DecompressStall => write!(f, "Decompressor stalled before completion"), + ReadError::Dimensions(w, h) => write!(f, "Invalid schematic dimensions ({w} * {h})"), + ReadError::TableSize(cnt) => write!(f, "Invalid block table size ({cnt})"), + ReadError::NoSuchBlock(name) => write!(f, "Unknown block {name:?}"), + ReadError::BlockCount(cnt) => write!(f, "Invalid total block count ({cnt})"), + ReadError::BlockIndex(idx, cnt) => write!(f, "Invalid block index ({idx} / {cnt})"), + ReadError::BlockState(..) => write!(f, "Failed to read block state"), + ReadError::Placement(e) => e.fmt(f), + } + } +} + #[derive(Debug)] pub enum WriteError { @@ -908,6 +962,23 @@ impl From<dynamic::WriteError> for WriteError } } +impl fmt::Display for WriteError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self + { + WriteError::Write(..) => write!(f, "Failed to write data to buffer"), + WriteError::TagCount(cnt) => write!(f, "Invalid tag count ({cnt})"), + WriteError::TableSize(cnt) => write!(f, "Invalid block table size ({cnt})"), + WriteError::BlockState(..) => write!(f, "Failed to write block state"), + WriteError::Compress(e) => e.fmt(f), + WriteError::CompressEof(remain) => write!(f, "Compression overflow with {remain} bytes of input remaining"), + WriteError::CompressStall => write!(f, "Compressor stalled before completion"), + } + } +} + impl<'l> SchematicSerializer<'l> { pub fn deserialize_base64(&mut self, data: &str) -> Result<Schematic, R64Error> @@ -958,6 +1029,18 @@ impl From<ReadError> for R64Error } } +impl fmt::Display for R64Error +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self + { + R64Error::Base64(e) => e.fmt(f), + R64Error::Content(e) => e.fmt(f), + } + } +} + #[derive(Debug)] pub enum W64Error { @@ -981,6 +1064,18 @@ impl From<WriteError> for W64Error } } +impl fmt::Display for W64Error +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self + { + W64Error::Base64(e) => e.fmt(f), + W64Error::Content(e) => e.fmt(f), + } + } +} + pub struct PosIter { x: u16, diff --git a/src/exe/args.rs b/src/exe/args.rs index 9ea30aa..afd9e72 100644 --- a/src/exe/args.rs +++ b/src/exe/args.rs @@ -1,5 +1,6 @@ use std::borrow::Cow; use std::collections::HashMap; +use std::fmt; use std::slice::from_ref; pub trait ArgHandler @@ -20,7 +21,19 @@ pub enum Error<E> EmptyName{pos: usize}, } -pub fn parse<I: Iterator, H: ArgHandler>(args: &mut I, handler: &mut H) -> Result<bool, Error<H::Error>> +impl<E: fmt::Display> fmt::Display for Error<E> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self + { + Error::Handler{pos, val} => write!(f, "{val} (at #{pos})"), + Error::EmptyName{pos} => write!(f, "Malformed argument (at #{pos})"), + } + } +} + +pub fn parse<I: Iterator, H: ArgHandler>(args: &mut I, handler: &mut H, arg_off: usize) -> Result<bool, Error<H::Error>> where I::Item: AsRef<str> { for (pos, arg) in args.enumerate() @@ -38,10 +51,10 @@ pub fn parse<I: Iterator, H: ArgHandler>(args: &mut I, handler: &mut H) -> Resul None => (&arg[2..], None), Some((i, _)) => (&arg[2..i], Some(&arg[i + 1..])), }; - if name.is_empty() {return Err(Error::EmptyName{pos});} + if name.is_empty() {return Err(Error::EmptyName{pos: arg_off + pos});} if let Err(val) = handler.on_long(name, value) { - return Err(Error::Handler{pos, val}); + return Err(Error::Handler{pos: arg_off + pos, val}); } } else @@ -57,18 +70,18 @@ pub fn parse<I: Iterator, H: ArgHandler>(args: &mut I, handler: &mut H) -> Resul { if let Err(val) = handler.on_short(c, value) { - return Err(Error::Handler{pos, val}); + return Err(Error::Handler{pos: arg_off + pos, val}); } } } - else {return Err(Error::EmptyName{pos});} + else {return Err(Error::EmptyName{pos: arg_off + pos});} } } else { if let Err(val) = handler.on_literal(arg) { - return Err(Error::Handler{pos, val}); + return Err(Error::Handler{pos: arg_off + pos, val}); } } } @@ -78,7 +91,7 @@ pub fn parse<I: Iterator, H: ArgHandler>(args: &mut I, handler: &mut H) -> Resul pub fn parse_args<H: ArgHandler>(handler: &mut H) -> Result<(), Error<H::Error>> { - parse(&mut std::env::args(), handler)?; + parse(&mut std::env::args(), handler, 0)?; Ok(()) } @@ -167,6 +180,20 @@ impl ArgOption } } +impl fmt::Display for ArgOption +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match (self.get_short(), self.get_long()) + { + (None, None) => unreachable!("unnamed ArgOption"), + (None, Some(long)) => write!(f, "\"--{long}\""), + (Some(short), None) => write!(f, "\"-{short}\""), + (Some(short), Some(long)) => write!(f, "\"--{long}\" / \"-{short}\""), + } + } +} + #[derive(Clone, Debug, Eq, PartialEq)] pub enum OptionValue { @@ -434,3 +461,22 @@ pub enum OptionError ValueRequired(ArgOption), TooMany(ArgOption), } + +impl fmt::Display for OptionError +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + match self + { + OptionError::NoSuchShort(short) => write!(f, "Invalid argument \"-{short}\""), + OptionError::NoSuchLong(long) => write!(f, "Invalid argument \"--{long}\""), + OptionError::ValueForbidden(opt) => write!(f, "Argument {opt} has no value"), + OptionError::ValueRequired(opt) => write!(f, "Argument {opt} requires a value"), + OptionError::TooMany(opt) => + { + if let Some(max) = opt.count.get_max_count() {write!(f, "Too many {opt} (max {max})")} + else {write!(f, "Duplicate argument {opt}")} + }, + } + } +} diff --git a/src/exe/mod.rs b/src/exe/mod.rs index 53e74dc..e5f6760 100644 --- a/src/exe/mod.rs +++ b/src/exe/mod.rs @@ -8,7 +8,7 @@ pub fn main(mut args: Args) match args.next() { None => panic!("not enough arguments"), - Some(s) if s == "print" => print::main(args), + Some(s) if s == "print" => print::main(args, 1), Some(s) => panic!("unknown argument {s}"), } } diff --git a/src/exe/print.rs b/src/exe/print.rs index 7cb3479..1fb4801 100644 --- a/src/exe/print.rs +++ b/src/exe/print.rs @@ -6,65 +6,17 @@ use std::fs; use crate::block::build_registry; use crate::data::{DataRead, Serializer}; use crate::data::schematic::{Schematic, SchematicSerializer}; -use crate::exe::args::{self, ArgCount, ArgOption, OptionError, OptionHandler}; +use crate::exe::args::{self, ArgCount, ArgOption, OptionHandler}; -pub fn main(mut args: Args) +pub fn main(mut args: Args, arg_off: usize) { let mut handler = OptionHandler::new(); let opt_file = handler.add(ArgOption::new(Some('f'), Some(Cow::Borrowed("file")), ArgCount::Required(usize::MAX))).unwrap(); let opt_interact = handler.add(ArgOption::new(Some('i'), Some(Cow::Borrowed("interactive")), ArgCount::Forbidden)).unwrap(); - match args::parse(&mut args, &mut handler) + if let Err(e) = args::parse(&mut args, &mut handler, arg_off) { - Err(args::Error::Handler{pos, val: OptionError::NoSuchShort(short)}) => - { - println!("Invalid argument \"-{short}\" (at #{})).", pos + 1); - return; - }, - Err(args::Error::Handler{pos, val: OptionError::NoSuchLong(long)}) => - { - println!("Invalid argument \"--{long}\" (at #{})).", pos + 1); - return; - }, - Err(args::Error::Handler{pos, val: OptionError::ValueForbidden(opt)}) => - { - match (opt.get_short(), opt.get_long()) - { - (None, None) => unreachable!("unnamed ArgOption (at #{}))", pos + 1), - (None, Some(long)) => println!("Illegal valued argument \"--{long}\" (at #{})).", pos + 1), - (Some(short), None) => println!("Illegal valued argument \"-{short}\" (at #{})).", pos + 1), - (Some(short), Some(long)) => println!("Illegal valued argument \"--{long}\" (\"-{short}\", at #{})).", pos + 1), - } - return; - }, - Err(args::Error::Handler{pos, val: OptionError::ValueRequired(opt)}) => - { - match (opt.get_short(), opt.get_long()) - { - (None, None) => unreachable!("unnamed ArgOption (at #{}))", pos + 1), - (None, Some(long)) => println!("Missing value to argument \"--{long}\" (at #{})).", pos + 1), - (Some(short), None) => println!("Missing value to argument \"-{short}\" (at #{})).", pos + 1), - (Some(short), Some(long)) => println!("Missing value to argument \"--{long}\" (\"-{short}\", at #{})).", pos + 1), - } - return; - }, - Err(args::Error::Handler{pos, val: OptionError::TooMany(opt)}) => - { - let max = opt.get_count().get_max_count().unwrap(); - match (opt.get_short(), opt.get_long()) - { - (None, None) => unreachable!("unnamed ArgOption (at #{}))", pos + 1), - (None, Some(long)) => println!("Duplicate argument \"--{long}\" (more than {max} at #{})).", pos + 1), - (Some(short), None) => println!("Duplicate argument \"-{short}\" (more than {max} at #{})).", pos + 1), - (Some(short), Some(long)) => println!("Duplicate argument \"--{long}\" (\"-{short}\", more than {max} at #{})).", pos + 1), - } - return; - }, - Err(args::Error::EmptyName{pos}) => - { - println!("Invalid empty argument (at #{}).", pos + 1); - return; - }, - _ => (), + println!("Command error: {e}"); + return; } let reg = build_registry(); @@ -99,7 +51,7 @@ pub fn main(mut args: Args) if need_space {println!();} first = false; need_space = false; - println!("Could not read schematic: {e:?}"); + println!("Could not read schematic: {e}"); }, } }, @@ -135,7 +87,7 @@ pub fn main(mut args: Args) if need_space {println!();} first = false; need_space = false; - println!("Could not read schematic: {e:?}"); + println!("Could not read schematic: {e}"); }, } } |