add_determinism/linkdupes/
config.rs

1/* SPDX-License-Identifier: GPL-3.0-or-later */
2
3use clap::Parser;
4
5use anyhow::Result;
6use log::{info, LevelFilter};
7use std::ops::Add;
8use std::path::PathBuf;
9use std::time;
10
11use crate::{setup, simplelog};
12
13#[derive(Debug, Parser)]
14#[command(version, about, long_about = None)]
15struct Options {
16    /// Paths to operate on
17    #[arg(value_name = "path")]
18    pub inputs: Vec<PathBuf>,
19
20    /// Adjust behaviour as appropriate for a build root program
21    #[arg(long)]
22    pub brp: bool,
23
24    /// Turn on debugging output
25    #[arg(short, long, action = clap::ArgAction::Count)]
26    pub verbose: u8,
27
28    /// Turn on debugging output
29    #[arg(short = 'n', long)]
30    pub dry_run: bool,
31
32    /// Link even if mtimes are different
33    #[arg(long)]
34    pub ignore_mtime: bool,
35
36    /// Link even if access modes are different
37    #[arg(long)]
38    pub ignore_mode: bool,
39
40    /// Link even if owner or group are different
41    #[arg(long)]
42    pub ignore_owner: bool,
43
44    /// Disable selinux context comparisons
45    #[arg(long)]
46    pub ignore_selinux_context: bool,
47
48    /// Print selinux contexts
49    #[arg(long)]
50    pub print_selinux_contexts: bool,
51}
52
53pub struct Config {
54    #[allow(dead_code)]
55    pub root: Option<PathBuf>,  // we need this to do selinux lookups
56
57    pub inputs: Vec<PathBuf>,
58    pub fatal_errors: bool,
59    pub _verbose: u8,
60    pub dry_run: bool,
61    pub ignore_mtime: bool,
62    pub ignore_mode: bool,
63    pub ignore_owner: bool,
64    pub source_date_epoch: Option<time::SystemTime>,
65    pub print_selinux_contexts: bool,
66
67    #[cfg(feature = "selinux")]
68    pub selinux_labels: Option<selinux::label::Labeler<selinux::label::back_end::File>>,
69}
70
71impl Config {
72    pub fn make() -> Result<Self> {
73        let mut root = None;
74
75        let options = Options::parse();
76
77        // log level
78        let log_level = match options.verbose {
79            0 => LevelFilter::Warn,
80            1 => LevelFilter::Info,
81            2 => LevelFilter::Debug,
82            3.. => LevelFilter::Trace,
83        };
84
85        simplelog::init(log_level, false)?;
86
87        // $SOURCE_DATE_EPOCH
88        let source_date_epoch = setup::source_date_epoch()?.map(
89            |s| time::UNIX_EPOCH.add(time::Duration::new(s as u64, 0))
90        );
91
92        // Extra checks in case --brp was specified
93        if options.brp {
94            root = Some(setup::brp_check(None, &options.inputs)?);
95        }
96
97        #[cfg(feature = "selinux")]
98        let selinux_labels = if options.ignore_selinux_context {
99            None
100        } else {
101            Some(selinux::label::Labeler::new(&[], false)?)
102        };
103
104        // positional args
105        if options.inputs.is_empty() && !options.brp {
106            info!("No arguments specified, nothing to do. 😎");
107        }
108
109        Ok(Self {
110            root,
111            inputs: options.inputs,
112            fatal_errors: options.brp,
113            _verbose: options.verbose,
114            dry_run: options.dry_run,
115            ignore_mtime: options.ignore_mtime,
116            ignore_mode: options.ignore_mode,
117            ignore_owner: options.ignore_owner,
118            source_date_epoch,
119            print_selinux_contexts: options.print_selinux_contexts,
120
121            #[cfg(feature = "selinux")]
122            selinux_labels,
123        })
124    }
125
126    #[allow(dead_code)]
127    // FIXME: should this be marked as #[cfg(test)]? But then the tests don't compile.
128    pub const fn empty() -> Self {
129        Self {
130            root: None,
131            inputs: vec![],
132            fatal_errors: false,
133            _verbose: 0,
134            dry_run: false,
135            ignore_mtime: false,
136            ignore_mode: false,
137            ignore_owner: false,
138            source_date_epoch: None,
139            print_selinux_contexts: false,
140
141            #[cfg(feature = "selinux")]
142            selinux_labels: None,
143        }
144    }
145}