treescript-interpreter 0.1.1

TreeScript interpreter, with some built-in libraries and API to create more
Documentation
#![feature(refcell_map_split, generators, generator_trait)]

extern crate clap;
#[macro_use]
mod debug;
mod parse;
mod print;
mod program;
mod reduce;
mod session;
mod util;
mod value;
use crate::program::Program;
use crate::util::AtomicFileCommit;
use clap::{App, AppSettings, Arg};
use std::env;
use std::env::Args;
use std::fs::File;
use std::io;
use std::io::{Read, Write};
use std::path::{Path, PathBuf};

fn main() {
  let mut args: Args = env::args();
  args.next().expect("missing path to treescript-interpreter");
  let prog_path_str = args
    .next()
    .expect("missing a compiled '.tprg' as first argument to treescript-interpreter");
  let prog_path = Path::new(&prog_path_str);
  let prog_name = prog_path
    .file_stem()
    .expect("provided compiled '.tprg' must be a file")
    .to_str()
    .expect("provided compiled '.tprg' must have a non-UTF8 name");
  let prog_file = File::open(prog_path).expect("provided compiled '.tprg' doesn't exist");
  let prog = Program::from(prog_file);
  let mut session = prog.new_session();
  let matches = App::new(prog_name)
    .version("1.0")
    .arg(Arg::with_name("INPUT")
          .help("The input file (or directory if recursive)")
          .index(1)
          .required_unless("stdin"))
    .arg(Arg::with_name("output")
          .short("o")
          .takes_value(true)
          .value_name("OUTPUT")
          .help("The output file (or directory). Defaults to same as input file, but possibly with different extension")
          .required_unless_one(&["INPUT", "stdout"]))
    .arg(Arg::with_name("recurse")
          .short("r")
          .help("Runs on all files in a directory"))
    .arg(Arg::with_name("stdin")
          .long("stdin")
          .help("reads input as AST data from STDIN. Useful for piping/shell scripts")
          .conflicts_with("recurse"))
    .arg(Arg::with_name("stdout")
          .long("stdout")
          .help("prints input as AST data to STDOUT. Useful for piping/shell scripts")
          .conflicts_with("output")
          .conflicts_with("recurse"))
    .setting(AppSettings::NoBinaryName)
    .get_matches_from(args);
  if matches.is_present("recurse") {
    panic!("TODO");
  } else {
    let in_path: Option<PathBuf> = matches
      .value_of("INPUT")
      .map(|in_path| PathBuf::from(in_path));
    let mut input: Box<Read>;
    if matches.is_present("stdin") {
      input = Box::new(io::stdin());
    } else {
      input = session.read_ast(in_path.as_ref().unwrap());
    }
    let mut raw_output_commit: Option<AtomicFileCommit> = None;
    let mut output: Box<Write>;
    if matches.is_present("stdout") {
      output = Box::new(io::stdout());
    } else {
      let output_path = matches.value_of("OUTPUT").map_or_else(
        || {
          let mut path = PathBuf::from(matches.value_of("INPUT").unwrap());
          let inferred_lang = prog
            .inferred_lang()
            .expect("can't infer output language - you must specify an output file explicitly");
          let output_lang = session.lang_with_name(&inferred_lang).expect(
            format!(
              "program has unsupported inferred language: {}",
              inferred_lang
            )
            .as_str(),
          );
          path.set_extension(&output_lang.extension);
          return path;
        },
        |output_path| PathBuf::from(output_path),
      );
      let (output_, raw_output_commit_) = session.write_ast(output_path);
      output = output_;
      raw_output_commit = Some(raw_output_commit_);
    }
    prog.run(
      &mut session,
      &in_path,
      &mut input.as_mut(),
      &mut output.as_mut(),
    );
    if let Some(raw_output) = raw_output_commit {
      raw_output.run().expect("can't finish writing output");
    }
  }
}

//fn main() {}