• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tamada / sibling / 21286006216

23 Jan 2026 12:18PM UTC coverage: 64.325% (+2.3%) from 61.989%
21286006216

push

github

tamada
feat: enhance CLI options with format support and refactor path handling.

upgrading with enhanced clippy check (cargo clippy -- -W clippy::pedantic)

57 of 109 new or added lines in 5 files covered. (52.29%)

4 existing lines in 3 files now uncovered.

348 of 541 relevant lines covered (64.33%)

4.72 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.21
/cli/src/main.rs
1
use std::vec;
2

3
use crate::cli::{CliOpts, PrintingOpts};
4
use clap::Parser;
5
use sibling::{Dirs, Error, Nexter, Result};
6

7
mod cli;
8
mod gencomp;
9
mod init;
10
mod printer;
11

12
#[derive(clap::ValueEnum, Clone, Debug)]
13
pub enum LogLevel {
14
    Error,
15
    Warn,
16
    Info,
17
    Debug,
18
    Trace,
19
}
20

21
fn init_log(level: &LogLevel) {
×
22
    use LogLevel::{Error, Warn, Info, Debug, Trace};
23
    if std::env::var_os("RUST_LOG").is_none() {
×
24
        match level {
×
25
            Error => std::env::set_var("RUST_LOG", "error"),
×
26
            Warn => std::env::set_var("RUST_LOG", "warn"),
×
27
            Info => std::env::set_var("RUST_LOG", "info"),
×
28
            Debug => std::env::set_var("RUST_LOG", "debug"),
×
29
            Trace => std::env::set_var("RUST_LOG", "trace"),
×
30
        }
31
    }
×
32
    env_logger::init();
×
33
    log::info!("Log level set to {level:?}");
×
34
}
×
35

36
fn perform_impl(
1✔
37
    dirs: &Dirs,
1✔
38
    nexter: &dyn Nexter,
1✔
39
    step: usize,
1✔
40
    opts: &PrintingOpts,
1✔
41
) -> String {
1✔
42
    let next = dirs.next_with(nexter, step);
1✔
43
    printer::result_string(dirs, next, opts)
1✔
44
}
1✔
45

46
fn perform_from_file(opts: CliOpts) -> Vec<Result<String>> {
1✔
47
    let nexter = sibling::NexterFactory::build(&opts.nexter);
1✔
48
    let r = match opts.input {
1✔
49
        None => Err(Error::Fatal("input is not specified".into())),
×
50
        Some(file) => match Dirs::new_from_file(file) {
1✔
51
            Err(e) => Err(e),
1✔
NEW
52
            Ok(dirs) => Ok(perform_impl(&dirs, nexter.as_ref(), opts.step, &opts.p_opts)),
×
53
        },
54
    };
55
    vec![r]
1✔
56
}
1✔
57

58
fn perform_each(
1✔
59
    dir: std::path::PathBuf,
1✔
60
    nexter: &dyn Nexter,
1✔
61
    step: usize,
1✔
62
    opts: &PrintingOpts,
1✔
63
) -> Result<String> {
1✔
64
    match Dirs::new(dir) {
1✔
65
        Err(e) => Err(e),
×
66
        Ok(dirs) => Ok(perform_impl(&dirs, nexter, step, opts)),
1✔
67
    }
68
}
1✔
69

70
fn perform_sibling(opts: CliOpts) -> Vec<Result<String>> {
1✔
71
    let nexter = sibling::NexterFactory::build(&opts.nexter);
1✔
72
    let target_dirs = if opts.dirs.is_empty() {
1✔
73
        vec![std::env::current_dir().unwrap()]
×
74
    } else {
75
        opts.dirs
1✔
76
    };
77
    let mut result = vec![];
1✔
78
    for dir in target_dirs {
1✔
79
        let dir = if dir == std::path::Path::new(".") {
1✔
80
            std::env::current_dir().unwrap()
1✔
81
        } else {
82
            dir
×
83
        };
84
        let r = perform_each(dir, nexter.as_ref(), opts.step, &opts.p_opts);
1✔
85
        result.push(r);
1✔
86
    }
87
    result
1✔
88
}
1✔
89

90
fn perform(opts: CliOpts) -> Vec<Result<String>> {
2✔
91
    if let Some(shell) = opts.init {
2✔
NEW
92
        vec![init::generate_init_script(&shell)]
×
93
    } else if opts.input.is_some() {
2✔
94
        perform_from_file(opts)
1✔
95
    } else {
96
        perform_sibling(opts)
1✔
97
    }
98
}
2✔
99

100
fn main() {
×
101
    let mut args = std::env::args();
×
102
    let args = if args.len() == 1 {
×
103
        vec![args.next().unwrap(), ".".into()]
×
104
    } else {
105
        args.collect()
×
106
    };
107
    let opts = cli::CliOpts::parse_from(args);
×
108
    init_log(&opts.log);
×
109
    if cfg!(debug_assertions) {
×
110
        #[cfg(debug_assertions)]
111
        if opts.compopts.completion {
×
NEW
112
            return gencomp::generate(&opts.compopts.dest);
×
113
        }
×
114
    }
×
115
    for item in perform(opts) {
×
116
        match item {
×
117
            Ok(result) => println!("{result}"),
×
118
            Err(e) => eprintln!("{e}"),
×
119
        }
120
    }
121
}
×
122

123
#[cfg(test)]
124
mod tests {
125
    use super::*;
126

127
    #[test]
128
    fn test_nexter_example() {
1✔
129
        let opts_r = cli::CliOpts::try_parse_from(vec!["sibling", "."]);
1✔
130

131
        if let Err(e) = &opts_r {
1✔
132
            eprintln!("{e}");
×
133
        }
1✔
134
        assert!(opts_r.is_ok());
1✔
135
        let r = perform(opts_r.unwrap());
1✔
136
        assert_eq!(r.len(), 1);
1✔
137
        match r.first().unwrap() {
1✔
138
            Err(e) => eprintln!("{e}"),
×
139
            Ok(result) => println!("{result}"),
1✔
140
        }
141
    }
1✔
142

143
    #[test]
144
    fn test_from_file() {
1✔
145
        let opts_r = cli::CliOpts::try_parse_from(vec![
1✔
146
            "sibling",
147
            "--input",
1✔
148
            "testdata/basic/dirlist.txt",
1✔
149
            "--type",
1✔
150
            "previous",
1✔
151
        ]);
152

153
        if let Err(e) = &opts_r {
1✔
154
            eprintln!("{e}");
×
155
        }
1✔
156
        assert!(opts_r.is_ok());
1✔
157
        let r = perform(opts_r.unwrap());
1✔
158
        assert_eq!(r.len(), 1);
1✔
159
        match r.first().unwrap() {
1✔
160
            Err(e) => eprintln!("{e}"),
1✔
161
            Ok(result) => assert_eq!(result, "testdata/basic/a"),
×
162
        }
163
    }
1✔
164
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc