Skip to content

Commit 3e23763

Browse files
Extract and rename Fix and Command (#188)
These internal models can be reused for #160 (fixes for known errors), so they will no longer be specific to ScopeDoctorGroup I also wrapped a test in `#[cfg(test)]` so it no longer gets compiled into the production binary.
1 parent 19bea10 commit 3e23763

File tree

5 files changed

+183
-137
lines changed

5 files changed

+183
-137
lines changed

scope/src/doctor/check.rs

+13-13
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use std::collections::BTreeMap;
77
use crate::models::HelpMetadata;
88
use crate::prelude::{ActionReport, ActionReportBuilder, ActionTaskReport};
99
use crate::shared::prelude::{
10-
CaptureError, CaptureOpts, DoctorGroup, DoctorGroupAction, DoctorGroupActionCommand,
11-
DoctorGroupCachePath, ExecutionProvider, OutputDestination,
10+
CaptureError, CaptureOpts, DoctorCommand, DoctorGroup, DoctorGroupAction, DoctorGroupCachePath,
11+
ExecutionProvider, OutputDestination,
1212
};
1313
use async_trait::async_trait;
1414
use derive_builder::Builder;
@@ -460,7 +460,7 @@ impl DefaultDoctorActionRun {
460460

461461
async fn run_check_command(
462462
&self,
463-
action_command: &DoctorGroupActionCommand,
463+
action_command: &DoctorCommand,
464464
) -> Result<CacheResults, RuntimeError> {
465465
info!("Evaluating {:?}", action_command);
466466
let mut action_reports = Vec::new();
@@ -663,13 +663,13 @@ pub(crate) mod tests {
663663
.check(
664664
DoctorGroupActionCheckBuilder::default()
665665
.files(None)
666-
.command(Some(DoctorGroupActionCommand::from(vec!["check"])))
666+
.command(Some(DoctorCommand::from(vec!["check"])))
667667
.build()
668668
.unwrap(),
669669
)
670670
.fix(
671-
DoctorGroupActionFixBuilder::default()
672-
.command(Some(DoctorGroupActionCommand::from(vec!["fix"])))
671+
DoctorFixBuilder::default()
672+
.command(Some(DoctorCommand::from(vec!["fix"])))
673673
.build()
674674
.unwrap(),
675675
)
@@ -685,14 +685,14 @@ pub(crate) mod tests {
685685
.check(
686686
DoctorGroupActionCheckBuilder::default()
687687
.files(None)
688-
.command(Some(DoctorGroupActionCommand::from(vec!["check"])))
688+
.command(Some(DoctorCommand::from(vec!["check"])))
689689
.build()
690690
.unwrap(),
691691
)
692692
.fix(
693-
DoctorGroupActionFixBuilder::default()
694-
.command(Some(DoctorGroupActionCommand::from(vec!["fix"])))
695-
.prompt(Some(DoctorGroupActionFixPrompt {
693+
DoctorFixBuilder::default()
694+
.command(Some(DoctorCommand::from(vec!["fix"])))
695+
.prompt(Some(DoctorFixPrompt {
696696
text: "do you want to continue?".to_string(),
697697
extra_context: Some("additional context here".to_string()),
698698
}))
@@ -716,8 +716,8 @@ pub(crate) mod tests {
716716
.unwrap(),
717717
)
718718
.fix(
719-
DoctorGroupActionFixBuilder::default()
720-
.command(Some(DoctorGroupActionCommand::from(vec!["fix"])))
719+
DoctorFixBuilder::default()
720+
.command(Some(DoctorCommand::from(vec!["fix"])))
721721
.build()
722722
.unwrap(),
723723
)
@@ -1201,7 +1201,7 @@ pub(crate) mod tests {
12011201
MockGlobWalker::new(),
12021202
);
12031203

1204-
let action_commands = DoctorGroupActionCommandBuilder::default()
1204+
let action_commands = DoctorCommandBuilder::default()
12051205
.commands(vec!["test -f ~/.somefile".to_string()])
12061206
.build()
12071207
.unwrap();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use std::path::Path;
2+
3+
use derive_builder::Builder;
4+
5+
use super::extract_command_path;
6+
7+
#[derive(Debug, PartialEq, Clone, Builder)]
8+
#[builder(setter(into))]
9+
pub struct DoctorCommand {
10+
pub commands: Vec<String>,
11+
}
12+
13+
impl<T> From<(&Path, Vec<T>)> for DoctorCommand
14+
where
15+
String: for<'a> From<&'a T>,
16+
{
17+
fn from((base_path, command_strings): (&Path, Vec<T>)) -> Self {
18+
let commands = command_strings
19+
.iter()
20+
.map(|s| {
21+
let exec: String = s.into();
22+
extract_command_path(base_path, &exec)
23+
})
24+
.collect();
25+
26+
DoctorCommand { commands }
27+
}
28+
}
29+
30+
#[cfg(test)]
31+
impl From<Vec<&str>> for DoctorCommand {
32+
/// This is only used by some tests and should NOT be used in production code
33+
/// because it does not properly pre-pend the command with a base path.
34+
fn from(value: Vec<&str>) -> Self {
35+
let commands = value.iter().map(|x| x.to_string()).collect();
36+
Self { commands }
37+
}
38+
}
39+
40+
#[cfg(test)]
41+
mod tests {
42+
use super::*;
43+
44+
#[test]
45+
fn from_vec_str() {
46+
let input = vec!["echo 'foo'", "false"];
47+
48+
let actual = DoctorCommand::from(input.clone());
49+
50+
assert_eq!(
51+
DoctorCommand {
52+
commands: vec![input[0].to_string(), input[1].to_string(),]
53+
},
54+
actual
55+
)
56+
}
57+
58+
#[test]
59+
fn from_path_and_vec_string() {
60+
let base_path = Path::new("/foo/bar");
61+
let input = vec!["echo 'foo'", "baz/qux", "./qux"];
62+
63+
let actual =
64+
DoctorCommand::from((base_path, input.iter().map(|cmd| cmd.to_string()).collect()));
65+
66+
assert_eq!(
67+
DoctorCommand {
68+
commands: vec![
69+
"echo 'foo'".to_string(),
70+
"baz/qux".to_string(),
71+
"/foo/bar/qux".to_string(),
72+
]
73+
},
74+
actual
75+
)
76+
}
77+
}

scope/src/shared/models/internal/doctor_group.rs

+16-110
Original file line numberDiff line numberDiff line change
@@ -8,70 +8,18 @@ use minijinja::{context, Environment};
88
use crate::models::prelude::{ModelMetadata, V1AlphaDoctorGroup};
99
use crate::models::HelpMetadata;
1010
use crate::prelude::{DoctorGroupActionSpec, DoctorInclude};
11-
use crate::shared::models::internal::extract_command_path;
11+
use crate::shared::models::internal::{DoctorCommand, DoctorFix, DoctorFixPrompt};
1212

1313
#[derive(Debug, PartialEq, Clone, Builder)]
1414
#[builder(setter(into))]
1515
pub struct DoctorGroupAction {
1616
pub name: String,
1717
pub description: String,
18-
pub fix: DoctorGroupActionFix,
18+
pub fix: DoctorFix,
1919
pub check: DoctorGroupActionCheck,
2020
pub required: bool,
2121
}
2222

23-
#[derive(Debug, PartialEq, Clone, Builder)]
24-
#[builder(setter(into))]
25-
pub struct DoctorGroupActionFix {
26-
#[builder(default)]
27-
pub command: Option<DoctorGroupActionCommand>,
28-
#[builder(default)]
29-
pub help_text: Option<String>,
30-
#[builder(default)]
31-
pub help_url: Option<String>,
32-
#[builder(default)]
33-
pub prompt: Option<DoctorGroupActionFixPrompt>,
34-
}
35-
36-
#[derive(Debug, PartialEq, Clone, Builder)]
37-
#[builder(setter(into))]
38-
pub struct DoctorGroupActionFixPrompt {
39-
#[builder(default)]
40-
pub text: String,
41-
#[builder(default)]
42-
pub extra_context: Option<String>,
43-
}
44-
45-
impl DoctorGroupAction {
46-
pub fn make_from(
47-
name: &str,
48-
description: &str,
49-
prompt: Option<DoctorGroupActionFixPrompt>,
50-
fix_command: Option<Vec<&str>>,
51-
check_path: Option<(&str, Vec<&str>)>,
52-
check_command: Option<Vec<&str>>,
53-
) -> Self {
54-
Self {
55-
required: true,
56-
name: name.to_string(),
57-
description: description.to_string(),
58-
fix: DoctorGroupActionFix {
59-
command: fix_command.map(DoctorGroupActionCommand::from),
60-
help_text: None,
61-
help_url: None,
62-
prompt,
63-
},
64-
check: DoctorGroupActionCheck {
65-
command: check_command.map(DoctorGroupActionCommand::from),
66-
files: check_path.map(|(base, paths)| DoctorGroupCachePath {
67-
base_path: PathBuf::from(base),
68-
paths: crate::shared::convert_to_string(paths),
69-
}),
70-
},
71-
}
72-
}
73-
}
74-
7523
#[derive(Debug, PartialEq, Clone, Builder)]
7624
#[builder(setter(into))]
7725
pub struct DoctorGroupCachePath {
@@ -94,40 +42,10 @@ impl From<(&str, Vec<&str>)> for DoctorGroupCachePath {
9442
#[derive(Debug, PartialEq, Clone, Builder)]
9543
#[builder(setter(into))]
9644
pub struct DoctorGroupActionCheck {
97-
pub command: Option<DoctorGroupActionCommand>,
45+
pub command: Option<DoctorCommand>,
9846
pub files: Option<DoctorGroupCachePath>,
9947
}
10048

101-
#[derive(Debug, PartialEq, Clone, Builder)]
102-
#[builder(setter(into))]
103-
pub struct DoctorGroupActionCommand {
104-
pub commands: Vec<String>,
105-
}
106-
107-
impl From<Vec<&str>> for DoctorGroupActionCommand {
108-
fn from(value: Vec<&str>) -> Self {
109-
let commands = value.iter().map(|x| x.to_string()).collect();
110-
Self { commands }
111-
}
112-
}
113-
114-
impl<T> From<(&Path, Vec<T>)> for DoctorGroupActionCommand
115-
where
116-
String: for<'a> From<&'a T>,
117-
{
118-
fn from((base_path, command_strings): (&Path, Vec<T>)) -> Self {
119-
let commands = command_strings
120-
.iter()
121-
.map(|s| {
122-
let exec: String = s.into();
123-
extract_command_path(base_path, &exec)
124-
})
125-
.collect();
126-
127-
DoctorGroupActionCommand { commands }
128-
}
129-
}
130-
13149
#[derive(Debug, PartialEq, Clone, Builder)]
13250
#[builder(setter(into))]
13351
pub struct DoctorGroup {
@@ -204,10 +122,7 @@ fn parse_action(
204122
for command in &fix.commands {
205123
templated_commands.push(substitute_templates(&working_dir, command)?);
206124
}
207-
Some(DoctorGroupActionCommand::from((
208-
containing_dir,
209-
templated_commands,
210-
)))
125+
Some(DoctorCommand::from((containing_dir, templated_commands)))
211126
} else {
212127
None
213128
};
@@ -217,10 +132,7 @@ fn parse_action(
217132
for command in check {
218133
templated_commands.push(substitute_templates(&working_dir, command)?);
219134
}
220-
Some(DoctorGroupActionCommand::from((
221-
containing_dir,
222-
templated_commands,
223-
)))
135+
Some(DoctorCommand::from((containing_dir, templated_commands)))
224136
} else {
225137
None
226138
};
@@ -231,14 +143,12 @@ fn parse_action(
231143
description: spec_action
232144
.description
233145
.unwrap_or_else(|| "default".to_string()),
234-
fix: DoctorGroupActionFix {
146+
fix: DoctorFix {
235147
command: fix_command,
236-
prompt: spec_action.fix.and_then(|fix| fix.prompt).map(|p| {
237-
DoctorGroupActionFixPrompt {
238-
text: p.text,
239-
extra_context: p.extra_context,
240-
}
241-
}),
148+
prompt: spec_action
149+
.fix
150+
.and_then(|fix| fix.prompt)
151+
.map(DoctorFixPrompt::from),
242152
help_text,
243153
help_url,
244154
},
@@ -261,7 +171,7 @@ mod tests {
261171

262172
use crate::shared::models::parse_models_from_string;
263173
use crate::shared::models::prelude::{
264-
DoctorGroupAction, DoctorGroupActionCheck, DoctorGroupActionCommand, DoctorGroupActionFix,
174+
DoctorCommand, DoctorFix, DoctorGroupAction, DoctorGroupActionCheck,
265175
};
266176
use crate::shared::prelude::DoctorGroupCachePath;
267177

@@ -290,18 +200,14 @@ mod tests {
290200
name: "1".to_string(),
291201
required: false,
292202
description: "foo1".to_string(),
293-
fix: DoctorGroupActionFix {
294-
command: Some(DoctorGroupActionCommand::from(vec![
295-
"/foo/bar/.scope/fix1.sh"
296-
])),
203+
fix: DoctorFix {
204+
command: Some(DoctorCommand::from(vec!["/foo/bar/.scope/fix1.sh"])),
297205
prompt: None,
298206
help_text: Some("There is a good way to fix this, maybe...".to_string()),
299207
help_url: Some("https://go.example.com/fixit".to_string()),
300208
},
301209
check: DoctorGroupActionCheck {
302-
command: Some(DoctorGroupActionCommand::from(vec![
303-
"/foo/bar/.scope/foo1.sh"
304-
])),
210+
command: Some(DoctorCommand::from(vec!["/foo/bar/.scope/foo1.sh"])),
305211
files: Some(DoctorGroupCachePath::from((
306212
"/foo/bar",
307213
vec!["flig/bar/**/*"]
@@ -315,14 +221,14 @@ mod tests {
315221
name: "2".to_string(),
316222
required: true,
317223
description: "foo2".to_string(),
318-
fix: DoctorGroupActionFix {
224+
fix: DoctorFix {
319225
command: None,
320226
help_text: None,
321227
help_url: None,
322228
prompt: None,
323229
},
324230
check: DoctorGroupActionCheck {
325-
command: Some(DoctorGroupActionCommand::from(vec!["sleep infinity"])),
231+
command: Some(DoctorCommand::from(vec!["sleep infinity"])),
326232
files: Some(DoctorGroupCachePath::from(("/foo/bar", vec!["*/*.txt"])))
327233
}
328234
}

0 commit comments

Comments
 (0)