pidcat 0.2.1

An adb logcat wrapper and filters
Documentation
use crate::log::Level;
use anyhow::Result;
use clap::{value_parser, Arg, ArgAction, ArgMatches, Command};
use std::path::PathBuf;

#[derive(Debug, Clone)]
pub(crate) struct Cli {
    pub tag: String,
    pub tag_width: usize,
    pub revert: String,
    pub clear: bool,
    pub level: Level,
    pub color: String,
    pub output: Option<PathBuf>,
    pub process: Vec<String>,
    pub buffers: Vec<String>,
    pub device: String,
    pub ignore: bool,
}

pub(crate) fn cli() -> Result<Cli> {
    let matches = get_arg_matches();
    let tag = match matches.get_one::<String>("tag") {
        None => "",
        Some(s) => s,
    }
    .to_owned();

    let revert = match matches.get_one::<String>("revert") {
        None => "",
        Some(s) => s,
    }
    .to_owned();

    let tag_width = match matches.get_one::<String>("tag_width") {
        None => "20",
        Some(s) => s,
    }
    .parse::<usize>()
    .unwrap_or(20);

    let output = matches.get_one::<PathBuf>("output");
    let color = matches.get_one::<String>("color").unwrap().to_owned();
    let process = get_many(&matches, "process");
    let buffers = get_many(&matches, "buffer");
    let clear = matches.get_flag("clear");
    let level = matches.get_one::<Level>("level").unwrap().to_owned();
    let ignore = matches.get_flag("ignore");
    let device = match matches.get_one::<String>("device") {
        None => "",
        Some(s) => s,
    }
    .to_owned();

    Ok(Cli {
        tag,
        tag_width,
        revert,
        color,
        clear,
        output: output.cloned(),
        level,
        process,
        buffers,
        device,
        ignore,
    })
}

fn get_arg_matches() -> ArgMatches {
    let crate_name: &str = env!("CARGO_PKG_NAME");
    let version: &str = env!("CARGO_PKG_VERSION");
    let author: &str = env!("CARGO_PKG_AUTHORS");
    Command::new(crate_name)
        .version(version)
        .author(author)
        .about("A logcat colored command which displays only source entries for processes of a specific application package.")
        .arg(
            Arg::new("tag")
                .short('t')
                .long("tag")
                .help("The tag filter patterns")
                .conflicts_with("revert")
        )
        .arg(
            Arg::new("tag_width")
                .long("tag-width")
                .default_value("20")
                .help("Set the tag show width. must >= 10")
        )
        .arg(
            Arg::new("revert")
                .short('v')
                .long("revert-match")
                .help("Selected lines are those not matching any of the specified patterns.")
                .conflicts_with("tag")
        )
        .arg(
            Arg::new("buffer")
                .short('b')
                .default_values(["main", "system"])
                .long("buffer")
                .value_parser(["main", "system", "crash", "radio", "events", "all"])
                .help("The buffer to filter")
                .action(ArgAction::Append)
        )
        .arg(
            Arg::new("clear")
                .short('c')
                .long("clear")
                .help("Clear (flush) the entire log and exit")
                .action(ArgAction::SetTrue)
        )
        .arg(
            Arg::new("level")
                .short('l')
                .long("level")
                .help("Filter log level")
                .default_value("V")
                .value_parser(value_parser!(Level))
        )
        .arg(
            Arg::new("output")
                .short('o')
                .long("output")
                .required(false)
                .help("Writing logs to a file")
                .value_parser(value_parser!(PathBuf))
        )
        .arg(
            Arg::new("color")
                .long("color")
                .help("Display in highlighted color to match priority")
                .default_value("auto")
                .value_parser(["auto", "always", "never"])
        )
        .arg(
            Arg::new("ignore")
                .short('i')
                .long("ignore-case")
                .help("Ignore case")
                .action(ArgAction::SetTrue)
        )
        .arg(
            Arg::new("device")
                .required(false)
                .short('s')
                .help("Use device with given serial")
                .action(ArgAction::Set)
        )
        .arg(
            Arg::new("process")
                .help("Name of the process to be filtered")
                .action(ArgAction::Append)
        )
        .get_matches()
}

fn get_many<'a>(matches: &ArgMatches, arg: &str) -> Vec<String> {
    matches
        .get_many::<String>(arg)
        .unwrap_or_default()
        .into_iter()
        .map(|v| v.to_owned())
        .collect::<Vec<String>>()
}