rustic_core/blob/tree/
excludes.rs1use derive_setters::Setters;
2use ignore::overrides::{Override, OverrideBuilder};
3use serde::{Deserialize, Serialize};
4
5use crate::{ErrorKind, RusticError, RusticResult};
6
7#[cfg_attr(feature = "clap", derive(clap::Parser))]
8#[cfg_attr(feature = "merge", derive(conflate::Merge))]
9#[derive(Clone, Debug, Default, PartialEq, Eq, Setters, Serialize, Deserialize)]
10#[serde(default, rename_all = "kebab-case", deny_unknown_fields)]
11#[setters(into)]
12#[non_exhaustive]
13pub struct Excludes {
15 #[cfg_attr(feature = "clap", clap(long = "glob", value_name = "GLOB"))]
17 #[cfg_attr(feature = "merge", merge(strategy = conflate::vec::overwrite_empty))]
18 pub globs: Vec<String>,
19
20 #[cfg_attr(feature = "clap", clap(long = "iglob", value_name = "GLOB"))]
22 #[cfg_attr(feature = "merge", merge(strategy = conflate::vec::overwrite_empty))]
23 pub iglobs: Vec<String>,
24
25 #[cfg_attr(feature = "clap", clap(long = "glob-file", value_name = "FILE",))]
27 #[cfg_attr(feature = "merge", merge(strategy = conflate::vec::overwrite_empty))]
28 pub glob_files: Vec<String>,
29
30 #[cfg_attr(feature = "clap", clap(long = "iglob-file", value_name = "FILE",))]
32 #[cfg_attr(feature = "merge", merge(strategy = conflate::vec::overwrite_empty))]
33 pub iglob_files: Vec<String>,
34}
35
36impl Excludes {
37 #[must_use]
38 pub fn is_empty(&self) -> bool {
40 self == &Self::default()
41 }
42
43 pub(crate) fn as_override(&self) -> RusticResult<Override> {
44 let mut override_builder = OverrideBuilder::new("");
45 for g in &self.globs {
46 _ = override_builder.add(g).map_err(|err| {
47 RusticError::with_source(
48 ErrorKind::Internal,
49 "Failed to add glob pattern `{glob}` to override builder.",
50 err,
51 )
52 .attach_context("glob", g)
53 .ask_report()
54 })?;
55 }
56
57 for file in &self.glob_files {
58 for line in std::fs::read_to_string(file)
59 .map_err(|err| {
60 RusticError::with_source(
61 ErrorKind::Internal,
62 "Failed to read string from glob file `{glob_file}` ",
63 err,
64 )
65 .attach_context("glob_file", file)
66 .ask_report()
67 })?
68 .lines()
69 {
70 _ = override_builder.add(line).map_err(|err| {
71 RusticError::with_source(
72 ErrorKind::Internal,
73 "Failed to add glob pattern line `{glob_pattern_line}` to override builder.",
74 err,
75 )
76 .attach_context("glob_pattern_line", line.to_string())
77 .ask_report()
78 })?;
79 }
80 }
81
82 _ = override_builder.case_insensitive(true).map_err(|err| {
83 RusticError::with_source(
84 ErrorKind::Internal,
85 "Failed to set case insensitivity in override builder.",
86 err,
87 )
88 .ask_report()
89 })?;
90 for g in &self.iglobs {
91 _ = override_builder.add(g).map_err(|err| {
92 RusticError::with_source(
93 ErrorKind::Internal,
94 "Failed to add iglob pattern `{iglob}` to override builder.",
95 err,
96 )
97 .attach_context("iglob", g)
98 .ask_report()
99 })?;
100 }
101
102 for file in &self.iglob_files {
103 for line in std::fs::read_to_string(file)
104 .map_err(|err| {
105 RusticError::with_source(
106 ErrorKind::Internal,
107 "Failed to read string from iglob file `{iglob_file}`",
108 err,
109 )
110 .attach_context("iglob_file", file)
111 .ask_report()
112 })?
113 .lines()
114 {
115 _ = override_builder.add(line).map_err(|err| {
116 RusticError::with_source(
117 ErrorKind::Internal,
118 "Failed to add iglob pattern line `{iglob_pattern_line}` to override builder.",
119 err,
120 )
121 .attach_context("iglob_pattern_line", line.to_string())
122 .ask_report()
123 })?;
124 }
125 }
126 let overrides = override_builder.build().map_err(|err| {
127 RusticError::with_source(
128 ErrorKind::Internal,
129 "Failed to build matcher for a set of glob overrides.",
130 err,
131 )
132 .ask_report()
133 })?;
134 Ok(overrides)
135 }
136}