gitforge 0.2.0

uniform access to git forges (gitlab and github)
Documentation
// Copyright 2021 Citrix
// SPDX-License-Identifier: MIT OR Apache-2.0
// There is NO WARRANTY.

use std::io::{self, BufWriter, Write};
use std::path::PathBuf;

use anyhow::anyhow;
use anyhow::Context as _;
use fehler::{throw, throws};
use structopt::StructOpt;

use gitforge::forge;
use forge::TokenConfig;

use anyhow::Error as AE;
pub fn default<T>() -> T where T: Default { Default::default() }

#[derive(StructOpt)]
struct Opts {
  #[structopt(
    long="ron",
    about="operation is 1 argument in RON (Rusty Object Notation) \
           (this option currently mandatory)"
  )]
  format_ron: bool,

  #[structopt(
    long="log",
    short="L",
    about="default log level (verbosity)",
  )]
  log_level: Option<log::LevelFilter>,

  // structopt stops us making these DTRT, whatever
  #[structopt(long="token-file")] token_file: Option<PathBuf>,
  #[structopt(long="no-token")] no_token: bool,

  #[structopt(name="KIND", about="forge kind, eg github or gitlab")]
  kind: forge::Kind,

  #[structopt(name="HOST", about="forge (host) domain name")]
  host: String,

  #[structopt(name="OPERATION", about="operation (in RON syntax")]
  op: Vec<String>,
}

#[throws(AE)]
fn main(){
  let opts = Opts::from_args();

  let mut log_builder = env_logger::Builder::from_env("GITFORGE_LOG");
  if let Some(filter) = opts.log_level { log_builder.filter_level(filter); }
  log_builder.init();

  let req = match opts.format_ron {
    true => match opts.op.as_slice() {
      [op] => ron::de::from_str(op).context("parse request argument")?,
     _ => throw!(anyhow!("OPERATION must be exactly argument")),
    },
    false => {
      throw!(anyhow!("--ron required, no other format supported right now"));
    },
  };

  let mut config = forge::Config {
    kind: Some(opts.kind),
    host: opts.host,
    ..default()
  };

  config.token = match &opts.token_file {
    Some(token_file)      => Some(TokenConfig::Path(token_file.clone())),
    None if opts.no_token => Some(TokenConfig::Anonymous),
    None                  => None,
  };

  let mut fo = config.forge().context("construct forge")?;

  let resp = fo.request(&req).context("make request")?;

  let mut bw = BufWriter::new(io::stdout());
  ron::ser::to_writer(&mut bw, &resp).context("print response")?;
  writeln!(bw, "").context("write newline")?;
  bw.flush().context("flush stdout")?;
}