mindustry logic execution, map- and schematic- parsing and rendering
Implement CLI schematic printing
| -rw-r--r-- | Cargo.toml | 2 | ||||
| -rw-r--r-- | src/data/schematic.rs | 3 | ||||
| -rw-r--r-- | src/main.rs | 163 |
3 files changed, 165 insertions, 3 deletions
@@ -1,6 +1,6 @@ [package] name = "plandustry" -version = "0.0.1" +version = "0.1.0" edition = "2021" publish = false diff --git a/src/data/schematic.rs b/src/data/schematic.rs index 10252aa..d1e1c9a 100644 --- a/src/data/schematic.rs +++ b/src/data/schematic.rs @@ -602,11 +602,10 @@ impl fmt::Display for Schematic writeln!(f)?; } // print the letters assigned to blocks - writeln!(f)?; for (k, _) in name_cnt { let v = *types.get(k).unwrap(); - writeln!(f, "({v}) {k}")?; + write!(f, "\n({v}) {k}")?; } } else {write!(f, "<empty {} * {}>", self.width, self.height)?;} diff --git a/src/main.rs b/src/main.rs index 90d8d48..e9f7522 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,12 @@ +use std::borrow::Cow; +use std::env::Args; +use std::io::{self, Write}; +use std::fs; + +use crate::args::{ArgOption, OptionError, OptionHandler, OptionValue}; +use crate::data::{DataRead, Serializer}; +use crate::data::schematic::{Schematic, SchematicSerializer}; + pub mod access; pub mod args; pub mod block; @@ -6,4 +15,158 @@ pub mod logic; fn main() { + let mut args = std::env::args(); + args.next().unwrap(); // path to executable + match args.next() + { + None => panic!("not enough arguments"), + Some(s) if s == "print" => main_print(args), + Some(s) => panic!("unknown argument {s}"), + } +} + +fn main_print(mut args: Args) +{ + const FILE: ArgOption = ArgOption::new(Some('f'), Some(Cow::Borrowed("file"))); + const INTERACTIVE: ArgOption = ArgOption::new(Some('i'), Some(Cow::Borrowed("interactive"))); + let mut handler = OptionHandler::new(); + handler.add(FILE).unwrap(); + handler.add(INTERACTIVE).unwrap(); + match args::parse(&mut args, &mut handler) + { + Err(args::Error::Handler{pos, val: OptionError::NoSuchShort(short)}) => + { + println!("Invalid argument \"-{short}\" (at #{pos}))."); + return; + }, + Err(args::Error::Handler{pos, val: OptionError::NoSuchLong(long)}) => + { + println!("Invalid argument \"--{long}\" (at #{pos}))."); + return; + }, + Err(args::Error::Handler{pos, val: OptionError::Duplicate(opt)}) => + { + match (opt.get_short(), opt.get_long()) + { + (None, None) => unreachable!("unnamed ArgOption (at #{pos}))"), + (None, Some(long)) => println!("Duplicate argument \"--{long}\" (at #{pos}))."), + (Some(short), None) => println!("Duplicate argument \"-{short}\" (at #{pos}))."), + (Some(short), Some(long)) => println!("Duplicate argument \"--{long}\" (\"-{short}\", at #{pos}))."), + } + return; + }, + Err(args::Error::EmptyName{pos}) => + { + println!("Invalid empty argument (at #{pos})."); + return; + }, + _ => (), + } + + let reg = block::build_registry(); + let mut ss = SchematicSerializer(®); + let mut first = true; + // process the file if any + let file = match handler.get_long("file").unwrap().1 + { + OptionValue::Absent => false, + OptionValue::Present => + { + return; + }, + OptionValue::Value(ref path) => + { + match fs::read(path) + { + Ok(data) => + { + match ss.deserialize(&mut DataRead::new(&data)) + { + Ok(s) => + { + if first {first = false;} + else {println!();} + println!("Schematic: @{path}"); + print_schematic(&s); + }, + // continue processing literals & maybe interactive mode + Err(e) => println!("Could not read schematic: {e:?}"), + } + }, + // continue processing literals & maybe interactive mode + Err(e) => 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 {first = false;} + else {println!();} + println!("Schematic: {curr}"); + print_schematic(&s); + }, + // continue processing literals & maybe interactive mode + Err(e) => println!("Could not read schematic: {e:?}"), + } + } + // if --interactive or no schematics: continue parsing from console + let interactive = if let OptionValue::Absent = handler.get_long("interactive").unwrap().1 {false} else {true}; + if interactive || (!file && handler.get_literals().is_empty()) + { + println!("\nEntering interactive mode, paste schematic to print details.\n"); + let mut buff = String::new(); + let stdin = io::stdin(); + loop + { + buff.clear(); + 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) => print_schematic(&s), + // continue interactive mode, typos are especially likely here + Err(e) => println!("Could not read schematic: {e:?}"), + } + }, + Err(e) => + { + 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}"); } |