Do you really need to see this?

   +----------+    +----------+
   | cli opts | -> | project  | -+->
   |  (done)  |    |  config  |  |
   +----------+    +----------+  |
                                 |
                                 +->
                                 |
                                 |
                                 |
                                 +->

















We may not be doing this the canonical way, but its the fun way

We are going to do this a bit weird, but I think its fantastic. It will really show you the power of the trait system.

















Lets build the Projector object

src/projector/config.go

LETS BUILD!!!

















The code!

use std::path::PathBuf;

use crate::opts::CLIOptions;
use anyhow::{Result, anyhow, Context};

pub enum Operation {
    Print(Option<String>),
    Add((String, String)),
    Remove(String),
}

pub struct ProjectorConfig {
    pub operation: Operation,
    pub config: PathBuf,
    pub pwd: PathBuf,
}

impl TryFrom<Vec<String>> for Operation {
    type Error = anyhow::Error;

    fn try_from(mut value: Vec<String>) -> Result<Self, Self::Error> {
        if value.len() == 0 {
            return Ok(Operation::Print(None));
        }

        let term = value.get(0).unwrap();

        if term == "add" {
            if value.len() != 3 {
                return Err(anyhow!("expected 2 arguments but you provided {}", value.len() - 1));
            }
            let mut drain = value.drain(1..=2);
            return Ok(Operation::Add((drain.next().unwrap(), drain.next().unwrap())));
        }

        if term == "rm" {
            if value.len() != 2 {
                return Err(anyhow!("expected 1 arguments but you provided {}", value.len() - 1));
            }
            let mut drain = value.drain(1..2);
            return Ok(Operation::Remove(drain.next().unwrap()));
        }

        if value.len() != 1 {
            return Err(anyhow!("expected 0 or 1 arguments but you provided {}", value.len()));
        }

        return Ok(Operation::Print(Some(term.clone())));
    }
}

fn get_config(config: Option<PathBuf>) -> Result<PathBuf> {
    if let Some(c) = config {
        return Ok(c);
    }

    if let Ok(home) = std::env::var("XDG_CONFIG_HOME") {

        let mut home = PathBuf::from(home);
        home.push("projector");
        home.push("projector.json");
        return Ok(home);
    }

    if let Ok(home) = std::env::var("HOME") {
        let mut home = PathBuf::from(home);
        home.push("projector");
        home.push("projector.json");
        return Ok(home);
    }

    return Err(anyhow!("unable to find config location"));
}

fn get_pwd(pwd: Option<PathBuf>) -> Result<PathBuf> {
    if let Some(p) = pwd {
        return Ok(p);
    }

    return std::env::current_dir().context("unable to get std::env::current_dir");
}

pub fn get_projector_config(opts: CLIOptions) -> Result<ProjectorConfig> {
    return Ok(ProjectorConfig {
        operation: opts.arguments.try_into()?,
        config: get_config(opts.config)?,
        pwd: get_pwd(opts.pwd)?,
    });
}