codump/
config.rs

1//! Configuration for the tool
2//!
3//! Including both internal config data structure and CLI args
4
5#[cfg(feature = "cli")]
6use clap::Parser;
7
8use crate::presets::Preset;
9use crate::process::CommentPattern;
10use crate::Format;
11use regex::Regex;
12
13/// Command line interface
14#[derive(Debug)]
15#[cfg_attr(feature = "cli", derive(Parser))]
16#[cfg_attr(
17    feature = "cli",
18    command(
19        bin_name = "codump",
20        about,
21        version,
22        author,
23        arg_required_else_help = true
24    )
25)]
26pub struct CliArgs {
27    /// The input file to parse
28    #[cfg_attr(feature = "cli", arg(required = true))]
29    pub file: String,
30
31    /// The component search path
32    ///
33    /// Each path is a case-sensitive substring used to search for components at that level.
34    /// The first line of the code after the doc comments is searched for the substring.
35    #[cfg_attr(feature = "cli", arg(required = true))]
36    pub search_path: Vec<String>,
37
38    /// Outer single line comment regex
39    #[cfg_attr(feature = "cli", arg(long))]
40    outer: Option<String>,
41
42    /// Outer multi line comment start regex
43    #[cfg_attr(feature = "cli", arg(long))]
44    outer_start: Option<String>,
45
46    /// Outer multi line comment end regex
47    #[cfg_attr(feature = "cli", arg(long))]
48    outer_end: Option<String>,
49
50    /// Inner single line comment regex
51    #[cfg_attr(feature = "cli", arg(long))]
52    inner: Option<String>,
53
54    /// Inner multi line comment start regex
55    #[cfg_attr(feature = "cli", arg(long))]
56    inner_start: Option<String>,
57
58    /// Inner multi line comment end regex
59    #[cfg_attr(feature = "cli", arg(long))]
60    inner_end: Option<String>,
61
62    /// Pattern for lines that should be ignored
63    #[cfg_attr(feature = "cli", arg(long, short))]
64    ignore: Vec<String>,
65
66    /// Format for the output
67    #[cfg_attr(feature = "cli", arg(long, short, default_value = "summary"))]
68    format: Format,
69
70    /// Use a preset configuration
71    ///
72    /// If both presets and individual options are set,
73    /// the individual options will override the corresponding part of the preset.
74    /// The rest of the preset will still be used.
75    #[cfg_attr(feature = "cli", arg(long, short))]
76    preset: Option<Preset>,
77
78    /// Print context
79    ///
80    /// Context is the parent components of the found component
81    #[cfg_attr(feature = "cli", arg(long, short))]
82    context: bool,
83
84    /// Print context comments
85    ///
86    /// Print the comments of the parents along with the context (implies --context)
87    #[cfg_attr(feature = "cli", arg(long, short = 'C'))]
88    context_comments: bool,
89}
90
91/// Internal config data structure
92#[derive(Debug, Clone)]
93pub struct Config {
94    /// Pattern for matching outer comments
95    pub outer_comments: CommentPattern,
96    /// Pattern for matching inner comments
97    pub inner_comments: CommentPattern,
98    /// Pattern of lines to ignore
99    pub ignore_lines: Vec<Regex>,
100    /// If context should be included
101    pub include_context: bool,
102    /// If context should include comments
103    pub context_include_comments: bool,
104    /// Format of the output
105    pub format: Format,
106}
107
108impl TryFrom<CliArgs> for Config {
109    type Error = String;
110
111    fn try_from(args: CliArgs) -> Result<Self, Self::Error> {
112        let (outer_comments, inner_comments) = match args.preset {
113            Some(preset) => {
114                let (mut outer, mut inner) = preset.get_patterns();
115                if let Some(v) = args.outer {
116                    outer.single_line = parse_regex(&v)?;
117                }
118                if let Some(v) = args.outer_start {
119                    outer.multi_start = Some(parse_regex(&v)?);
120                }
121                if let Some(v) = args.outer_end {
122                    outer.multi_end = parse_regex(&v)?;
123                }
124                if let Some(v) = args.inner {
125                    inner.single_line = parse_regex(&v)?;
126                }
127                if let Some(v) = args.inner_start {
128                    inner.multi_start = Some(parse_regex(&v)?);
129                }
130                if let Some(v) = args.inner_end {
131                    inner.multi_end = parse_regex(&v)?;
132                }
133                (outer, inner)
134            }
135            None => parse_comment_pattern_from_args(&args)?,
136        };
137        let mut ignore_lines = vec![];
138        for line in args.ignore {
139            ignore_lines.push(parse_regex(&line)?);
140        }
141
142        Ok(Config {
143            outer_comments,
144            inner_comments,
145            ignore_lines,
146            include_context: args.context || args.context_comments,
147            context_include_comments: args.context_comments,
148            format: args.format,
149        })
150    }
151}
152
153fn parse_comment_pattern_from_args(
154    args: &CliArgs,
155) -> Result<(CommentPattern, CommentPattern), String> {
156    Ok((
157        CommentPattern {
158            single_line: parse_comment_pattern(args.outer.as_ref())?,
159            multi_start: Some(parse_comment_pattern(args.outer_start.as_ref())?),
160            multi_end: parse_comment_pattern(args.outer_end.as_ref())?,
161        },
162        CommentPattern {
163            single_line: parse_comment_pattern(args.inner.as_ref())?,
164            multi_start: Some(parse_comment_pattern(args.inner_start.as_ref())?),
165            multi_end: parse_comment_pattern(args.inner_end.as_ref())?,
166        },
167    ))
168}
169
170fn parse_comment_pattern(pattern: Option<&String>) -> Result<Regex, String> {
171    match pattern {
172        Some(s) => parse_regex(s),
173        None => Err("Comment pattern missing. Either use a --preset or specify all the --outer* and --inner* arguments. See --help for more.".to_string()),
174    }
175}
176
177fn parse_regex(s: &str) -> Result<Regex, String> {
178    Regex::new(s).map_err(|_| format!("Invalid regex \"{}\". See --help for more.", s))
179}