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
use crate::{get_compose_filename, ComposeYaml, Formats, Verbosity};
use colored::Colorize;
use regex::Regex;
use serde_yaml::Mapping;
use std::vec::IntoIter;
use std::{fs, process};

/// Get the tag value (or None), or exit if the filter
/// passed doesn't star with "tag=" prefix.
pub fn unwrap_filter_tag(filter: Option<&str>) -> Option<&str> {
    filter.as_ref().map(|f| {
        if let Some(val) = f.strip_prefix("tag=") {
            val
        } else {
            eprintln!(
                "{}: wrong filter '{}', only '{}' filter supported",
                "ERROR".red(),
                f.yellow(),
                "tag=".yellow()
            );
            process::exit(2);
        }
    })
}

/// Get the regex value expressed in &str, or exit if the filter
/// passed doesn't star with "regex=" or "regex!=" prefixes.
/// When expression has "=" the bool is true, when is "!="
/// the bool is false.
/// If the option passed is None, this method returns None as well.
pub fn unwrap_filter_regex(filter: Option<&str>) -> Option<(Regex, bool)> {
    filter.as_ref().map(|f| {
        if let Some(val) = f.strip_prefix("regex=") {
            let regex = Regex::new(val).unwrap_or_else(|e| {
                invalid_regex_exit(e, val);
            });
            return (regex, true);
        }
        if let Some(val) = f.strip_prefix("regex!=") {
            let regex = Regex::new(val).unwrap_or_else(|e| {
                invalid_regex_exit(e, val);
            });
            return (regex, false);
        }
        eprintln!(
            "{}: wrong filter '{}', only '{}' or '{}' filters are supported",
            "ERROR".red(),
            f.yellow(),
            "regex=".yellow(),
            "regex!=".yellow()
        );
        process::exit(2);
    })
}

fn invalid_regex_exit(e: regex::Error, val: &str) -> ! {
    eprintln!(
        "{}: invalid regex expression '{}' in filter - {}",
        "ERROR".red(),
        val.yellow(),
        e
    );
    process::exit(2);
}

pub fn print_names(iter: IntoIter<&str>, pretty: Formats) {
    match pretty {
        Formats::Full => iter.for_each(|service| println!("{}", service)),
        Formats::Oneline => println!("{}", iter.collect::<Vec<&str>>().join(" ")),
    }
}

pub fn get_service<'a>(compose: &'a ComposeYaml, service_name: &str) -> &'a Mapping {
    let service = compose.get_service(service_name);
    match service {
        None => {
            eprintln!("{}: No such service found: {}", "ERROR".red(), service_name);
            process::exit(16);
        }
        Some(serv) => serv,
    }
}

pub fn get_yml_content(filename: Option<&str>, verbosity: Verbosity) -> String {
    let filename = get_compose_filename(filename, verbosity).unwrap_or_else(|err| {
        eprintln!("{}: {}", "ERROR".red(), err);
        if err.contains("no such file or directory") {
            process::exit(14);
        }
        process::exit(10);
    });
    fs::read_to_string(filename).unwrap_or_else(|err| {
        eprintln!("{}: reading compose file: {}", "ERROR".red(), err);
        process::exit(11);
    })
}