perty 0.0.1

A implementation of the Programme Evaluation and Review Technique (PERT).
Documentation
#![doc = include_str!("../README.md")]
use std::{
    fs::File,
    io::{BufReader, BufWriter},
    path::PathBuf,
};

use clap::{Parser, Subcommand};
use perty::{Pert, Task};

#[derive(Debug, Parser)]
#[command(version, about, long_about = None)]
struct Cli {
    #[command(subcommand)]
    command: Option<Command>,
}

#[derive(Debug, Subcommand)]
enum Command {
    Pert {
        inp: PathBuf,
        out: PathBuf,
        max_iter: Option<usize>,
    },
}

fn main() {
    let cli = Cli::parse();
    let cmd = cli.command.expect("Subcommand not found.");
    // Moved to after parsing the cmd so it does not print out
    // when someone calls the help command.
    println!("[START] PERTy");

    let Command::Pert { inp, out, max_iter } = cmd;
    let Some(tasks) = parse_tasks(inp) else {
        eprintln!("[ERROR] Task parsing error");
        return;
    };
    let max_iter = max_iter.unwrap_or(1_000);
    match tasks.solve_pert(max_iter) {
        Ok(solution) => {
            let file = File::create(out).unwrap();
            let writer = BufWriter::new(file);
            serde_json::to_writer_pretty(writer, &solution).unwrap();
        }
        Err(e) => eprintln!("{:?}", e),
    }
    println!("[END  ] PERTy")
}

fn parse_tasks(inp: PathBuf) -> Option<Vec<Task>> {
    let ext = inp
        .extension()
        .expect("No file extension found.")
        .to_str()
        .expect("ext string error.");
    match ext {
        "json" => {
            let file = File::open(inp).expect("Expected file to open.");
            let reader = BufReader::new(file);
            let tasks: Vec<Task> = serde_json::from_reader(reader).expect("Invalid json format.");
            Some(tasks)
        }
        _ => {
            eprintln!("[ERROR] Unsupported input file type. Supported types are .json.");
            None
        }
    }
}