mindustry logic execution, map- and schematic- parsing and rendering
Implement Display for error printing
KosmosPrime 2023-01-06
parent b2c1b99 · commit 4c84186
-rw-r--r--src/data/base64.rs27
-rw-r--r--src/data/schematic.rs95
-rw-r--r--src/exe/args.rs60
-rw-r--r--src/exe/mod.rs2
-rw-r--r--src/exe/print.rs62
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}");
},
}
}