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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
use anstyle;
use clap::{Args, Command, CommandFactory, Parser, Subcommand};
use clap_complete::{generate, Generator, Shell};
use std::{fmt::Debug, io};

// Reference the modules for the subcommands
mod center_opts;
mod clear_kinetics_opts;
mod ddda_to_m6a_opts;
mod decorator_opts;
mod extract_opts;
mod fire_opts;
mod footprint_opts;
mod nucleosome_opts;
mod pileup_opts;
mod predict_opts;
mod qc_opts;
mod strip_basemods_opts;

// include the subcommand modules as top level functions and structs in the cli module
pub use center_opts::*;
pub use clear_kinetics_opts::*;
pub use ddda_to_m6a_opts::*;
pub use decorator_opts::*;
pub use extract_opts::*;
pub use fire_opts::*;
pub use footprint_opts::*;
pub use nucleosome_opts::*;
pub use pileup_opts::*;
pub use predict_opts::*;
pub use qc_opts::*;
pub use strip_basemods_opts::*;

//
// The main CLI structure
//
#[derive(Parser)]
#[clap(
    author,
    version,
    about,
    propagate_version = true,
    subcommand_required = true,
    infer_subcommands = true,
    arg_required_else_help = true
)]
#[command(version = super::LONG_VERSION)]
#[command(styles=get_styles())]
pub struct Cli {
    #[clap(flatten)]
    pub global: GlobalOpts,
    /// Subcommands for fibertools-rs
    #[clap(subcommand)]
    pub command: Option<Commands>,
}

//
// Global options available to all subcommands
//
#[derive(Debug, Args, PartialEq, Eq)]
pub struct GlobalOpts {
    /// Threads
    #[clap(
        global = true,
        short,
        long,
        default_value_t = 8,
        help_heading = "Global-Options"
    )]
    pub threads: usize,

    /// Logging level [-v: Info, -vv: Debug, -vvv: Trace]
    #[clap(
        global = true,
        short,
        long,
        action = clap::ArgAction::Count,
        help_heading = "Debug-Options"
    )]
    pub verbose: u8,
    /// Turn off all logging
    #[clap(global = true, long, help_heading = "Debug-Options")]
    pub quiet: bool,
}

impl std::default::Default for GlobalOpts {
    fn default() -> Self {
        Self {
            threads: 8,
            verbose: 0,
            quiet: false,
        }
    }
}

//
// This structure contains all the subcommands and their help descriptions.
//
#[derive(Subcommand, Debug)]
pub enum Commands {
    /// Predict m6A positions using HiFi kinetics data and encode the results in the MM and ML bam tags. Also adds nucleosome (nl, ns) and MTase sensitive patches (al, as).
    #[clap(visible_aliases = &["m6A", "m6a"])]
    PredictM6A(PredictM6AOptions),
    /// Add nucleosomes to a bam file with m6a predictions
    AddNucleosomes(AddNucleosomeOptions),
    /// Add FIREs (Fiber-seq Inferred Regulatory Elements) to a bam file with m6a predictions
    Fire(FireOptions),
    /// Extract fiberseq data into plain text files.
    ///
    /// See https://fiberseq.github.io/fibertools/extracting/extract.html for a description of the outputs.
    #[clap(visible_aliases = &["ex", "e"])]
    Extract(ExtractOptions),
    /// This command centers fiberseq data around given reference positions. This is useful for making aggregate m6A and CpG observations, as well as visualization of SVs.
    ///
    ///  See https://fiberseq.github.io/fibertools/extracting/center.html for a description of the output.
    #[clap(visible_aliases = &["c", "ct"])]
    Center(CenterOptions),
    /// Infer footprints from fiberseq data
    Footprint(FootprintOptions),
    /// Collect QC metrics from a fiberseq bam file
    Qc(QcOpts),
    /// Make decorated bed files for fiberseq data
    TrackDecorators(DecoratorOptions),
    /// Make a pileup track of Fiber-seq features from a FIRE bam
    Pileup(PileupOptions),
    /// Remove HiFi kinetics tags from the input bam file
    ClearKinetics(ClearKineticsOptions),
    /// Strip out select base modifications
    StripBasemods(StripBasemodsOptions),
    /// Convert a DddA BAM file to pseudo m6A BAM file
    DddaToM6a(DddaToM6aOptions),
    /// Make command line completions
    #[clap(hide = true)]
    Completions(CompletionOptions),
    /// Make a man page for fibertools-rs
    ///
    /// Writes file for `man` to stdout.
    #[clap(hide = true)]
    Man {},
}

//
// CLI utility functions
//

/// This function is used to generate the styles for the CLI help messages.
fn get_styles() -> clap::builder::Styles {
    let cmd_color = anstyle::AnsiColor::Magenta;
    let header_color = anstyle::AnsiColor::BrightGreen;
    let placeholder_color = anstyle::AnsiColor::Cyan;
    let header_style = anstyle::Style::new()
        .bold()
        .underline()
        .fg_color(Some(anstyle::Color::Ansi(header_color)));
    let cmd_style = anstyle::Style::new()
        .bold()
        .fg_color(Some(anstyle::Color::Ansi(cmd_color)));
    let placeholder_style = anstyle::Style::new()
        //.bold()
        .fg_color(Some(anstyle::Color::Ansi(placeholder_color)));
    clap::builder::Styles::styled()
        .header(header_style)
        .literal(cmd_style)
        .usage(header_style)
        .placeholder(placeholder_style)
}

pub fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
    generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
}

pub fn make_cli_parse() -> Cli {
    Cli::parse()
}

pub fn make_cli_app() -> Command {
    Cli::command()
}

#[derive(Args, Debug, PartialEq, Eq)]
pub struct CompletionOptions {
    /// If provided, outputs the completion file for given shell
    #[arg(value_enum)]
    pub shell: Shell,
}