mindustry logic execution, map- and schematic- parsing and rendering
Diffstat (limited to 'src/exe/print.rs')
| -rw-r--r-- | src/exe/print.rs | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/src/exe/print.rs b/src/exe/print.rs new file mode 100644 index 0000000..7cb3479 --- /dev/null +++ b/src/exe/print.rs @@ -0,0 +1,210 @@ +use std::borrow::Cow; +use std::env::Args; +use std::io::{self, Write}; +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}; + +pub fn main(mut args: Args) +{ + 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) + { + 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; + }, + _ => (), + } + + let reg = build_registry(); + let mut ss = SchematicSerializer(®); + let mut first = true; + let mut need_space = false; + // process the files if any + let file = match handler.get_value(opt_file).get_values() + { + None => false, + Some(paths) => + { + for path in paths + { + match fs::read(path) + { + Ok(data) => + { + match ss.deserialize(&mut DataRead::new(&data)) + { + Ok(s) => + { + if !first || need_space {println!();} + first = false; + need_space = true; + println!("Schematic: @{path}"); + print_schematic(&s); + }, + // continue processing files, literals & maybe interactive mode + Err(e) => + { + if need_space {println!();} + first = false; + need_space = false; + println!("Could not read schematic: {e:?}"); + }, + } + }, + // continue processing files, literals & maybe interactive mode + Err(e) => + { + if need_space {println!();} + first = false; + need_space = false; + println!("Could not read file {path:?}: {e}"); + }, + } + } + true + }, + }; + // process schematics from command line + for curr in handler.get_literals() + { + match ss.deserialize_base64(curr) + { + Ok(s) => + { + if !first || need_space {println!();} + first = false; + need_space = true; + println!("Schematic: {curr}"); + print_schematic(&s); + }, + // continue processing literals & maybe interactive mode + Err(e) => + { + if need_space {println!();} + first = false; + need_space = false; + println!("Could not read schematic: {e:?}"); + }, + } + } + // if --interactive or no schematics: continue parsing from console + if handler.get_value(opt_interact).is_present() || (!file && handler.get_literals().is_empty()) + { + if need_space {println!();} + need_space = false; + println!("Entering interactive mode, paste schematic to print details."); + let mut buff = String::new(); + let stdin = io::stdin(); + loop + { + buff.clear(); + if need_space {println!();} + need_space = false; + print!("> "); + if let Err(e) = io::stdout().flush() + { + // what the print & println macros would do + panic!("failed printing to stdout: {e}"); + } + match stdin.read_line(&mut buff) + { + Ok(..) => + { + let data = buff.trim(); + if data.is_empty() {break;} + match ss.deserialize_base64(data) + { + Ok(s) => + { + println!(); + need_space = true; + print_schematic(&s) + }, + // continue interactive mode, typos are especially likely here + Err(e) => + { + if need_space {println!();} + need_space = false; + println!("Could not read schematic: {e:?}") + }, + } + }, + Err(e) => + { + if need_space {println!();} + println!("Failed to read next line: {e}"); + break; + }, + } + } + } +} + +fn print_schematic(s: &Schematic) +{ + if let Some(name) = s.get_tags().get("name") + { + if !name.is_empty() {println!("Name: {name}");} + } + if let Some(desc) = s.get_tags().get("description") + { + if !desc.is_empty() {println!("Desc: {desc}");} + } + if let Some(labels) = s.get_tags().get("labels") + { + if !labels.is_empty() && labels != "[]" {println!("Tags: {:?}", labels);} + } + println!("\n{s}"); +} |