use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use atty::Stream;
use clap::{crate_authors, crate_description, crate_name, crate_version, App, Arg};
use colored_json::prelude::*;
use decart::{from_file, OctoCart};
use syntect::easy::HighlightLines;
use syntect::highlighting::{Style, ThemeSet};
use syntect::parsing::{SyntaxDefinition, SyntaxSetBuilder};
use syntect::util::{as_24_bit_terminal_escaped, LinesWithEndings};
pub fn main() {
let matches = App::new(crate_name!())
.version(crate_version!())
.author(crate_authors!())
.about(crate_description!())
.subcommand(
App::new("decode")
.about("Decode an Octocart. If no options are supplied, the full JSON payload will be printed to stdout.")
.arg(Arg::with_name("print program")
.short('p')
.long("print-program")
.help("instead of printing the entire JSON payload to stdout, print just the Octo program source code.")
)
.arg(Arg::with_name("write files")
.short('w')
.long("write-files")
.takes_value(true)
.value_name("output file stem")
.help("Instead of printing to stdout, create two files with the contents from the Octocart: \
An .8o file with the program source code, and an .octo.rc file with the runtime options. \
If you supply a value here, it will be used as the filenames (plus extensions); \
if not, the filename of the Octocart will be used.")
.min_values(0)
.max_values(1)
)
.arg(
Arg::with_name("OCTOCART")
.help("Octo cartridge file (GIF)")
.required(true)
.value_name("OCTOCART")
)
)
.subcommand(
App::new("encode")
.about("Encode an Octocart")
)
.get_matches();
if let Some(matches) = matches.subcommand_matches("decode") {
let filename = Path::new(matches.value_of("OCTOCART").unwrap());
let cart: OctoCart = from_file(filename).expect("Failed to read Octocart file");
if matches.is_present("write files") {
let path = filename.parent().unwrap();
let stem = filename.file_stem().unwrap();
let default_base = format!("{}/{}", path.display(), stem.to_str().unwrap());
let base = matches.value_of("write files").unwrap_or(&default_base);
let octo_file_path = format!("{}.{}", base, "8o");
let mut octo_file = File::create(Path::new(&octo_file_path))
.unwrap_or_else(|_| panic!("Failed to create {}", octo_file_path));
octo_file
.write_all(cart.program.as_bytes())
.unwrap_or_else(|_| panic!("Failed to write program to {}", octo_file_path));
let rc_file_path = format!("{}.{}", base, "octo.rc");
let mut rc_file = File::create(Path::new(&rc_file_path))
.unwrap_or_else(|_| panic!("Failed to create {}", rc_file_path));
rc_file
.write_all(octopt::Options::to_ini(cart.options).as_bytes())
.unwrap_or_else(|_| panic!("Failed to write options to {}", rc_file_path));
println!(
"Wrote files:\nCode: {}\nOptions: {}",
octo_file_path, rc_file_path
);
} else if matches.is_present("print program") {
if atty::is(Stream::Stdout) {
let mut ssb = SyntaxSetBuilder::new();
ssb.add(
SyntaxDefinition::load_from_str(
include_str!("octo-sublime/Octo.sublime-syntax"),
false,
None,
)
.unwrap(),
);
let ps = ssb.build();
let syntax = ps.find_syntax_by_extension("8o").unwrap();
let mut theme_cursor = std::io::Cursor::new(include_bytes!("Monokai.tmTheme"));
let theme = ThemeSet::load_from_reader(&mut theme_cursor).unwrap();
let mut h = HighlightLines::new(syntax, &theme);
let mut s = Vec::new();
for line in LinesWithEndings::from(&cart.program) {
let ranges: Vec<(Style, &str)> =
h.highlight_line(line, &ps).expect("Highlighting failed");
let escaped = as_24_bit_terminal_escaped(&ranges[..], false);
s.push(escaped);
}
println!("{}", s.join(""));
} else {
println!("{}", &cart.program);
}
} else {
println!(
"{}",
cart.to_string()
.to_colored_json_auto()
.expect("Failed to print Octocart JSON payload")
);
}
} else if let Some(_matches) = matches.subcommand_matches("encode") {
todo!();
}
}