mylang_cli_ext/
with_output.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use std::{
    fs::File,
    io::{BufWriter, Stdout, Write},
    marker::PhantomData,
};

use clap::{Arg, ArgGroup, ArgMatches, Command, Parser};

use crate::{CommandFromParser, MatchesFromParser};

pub struct MatchesWithOutput<P>
where
    P: Parser,
{
    matches: ArgMatches,
    parser: PhantomData<P>,
}

impl<P> MatchesWithOutput<P>
where
    P: Parser,
{
    pub fn output<'a>(&self, stdout: &'a Stdout) -> anyhow::Result<Box<dyn Write + 'a>> {
        let stdout_flag = self.matches.contains_id("stdout");
        let file_path = self.matches.get_one::<String>("output");
        match (stdout_flag, file_path) {
            (true, Some(_)) => panic!("Cannot specify both --stdout and --output"),

            (true, None) => Ok(Box::new(stdout.lock())),

            (false, Some(path)) => Ok(Box::new(BufWriter::new(File::create(path)?))),

            (false, None) => {
                panic!("No output specified. You can specify either --stdout or --output")
            }
        }
    }
}

impl<P> MatchesFromParser<P> for MatchesWithOutput<P>
where
    P: Parser,
{
    fn parse(&self) -> P {
        P::from_arg_matches(&self.matches).unwrap()
    }
}

pub struct ParserWithOutput<P>
where
    P: Parser,
{
    cmd: Command,
    parser: PhantomData<P>,
}

impl<P> ParserWithOutput<P>
where
    P: Parser,
{
    pub fn new(cmd_from_parser: CommandFromParser<P>) -> Self {
        Self {
            cmd: cmd_from_parser
                .cmd
                .group(ArgGroup::new("output_group").required(true))
                .arg(Arg::new("stdout").long("stdout").group("output_group"))
                .arg(
                    Arg::new("output")
                        .long("output")
                        .short('o')
                        .group("output_group"),
                ),
            parser: PhantomData,
        }
    }

    pub fn get_matches(self) -> MatchesWithOutput<P> {
        MatchesWithOutput {
            matches: self.cmd.get_matches(),
            parser: PhantomData,
        }
    }
}

pub trait WithOutputExt<P>
where
    P: Parser,
{
    fn with_output(self) -> ParserWithOutput<P>;
}

impl<P> WithOutputExt<P> for CommandFromParser<P>
where
    P: Parser,
{
    fn with_output(self) -> ParserWithOutput<P> {
        ParserWithOutput::new(self)
    }
}