1use crate::util;
2use std::{
3 collections::BTreeMap,
4 fmt::{self, Debug, Display},
5 process::ExitStatus,
6};
7
8pub trait TargetTrait<'a>: Debug + Sized {
9 const DEFAULT_KEY: &'static str;
10
11 fn all() -> &'a BTreeMap<&'a str, Self>;
12
13 fn name_list() -> Vec<&'a str>;
14
15 fn default_ref() -> &'a Self {
16 Self::all()
17 .get(Self::DEFAULT_KEY)
18 .expect("developer error: no target matched `DEFAULT_KEY`")
19 }
20
21 fn for_name(name: &str) -> Option<&'a Self> {
22 Self::all().get(name)
23 }
24
25 fn for_arch(arch: &str) -> Option<&'a Self> {
26 Self::all().values().find(|target| target.arch() == arch)
27 }
28
29 fn triple(&'a self) -> &'a str;
30
31 fn arch(&'a self) -> &'a str;
32
33 fn install(&'a self) -> Result<ExitStatus, std::io::Error> {
34 util::rustup_add(self.triple())
35 }
36
37 fn install_all() -> Result<(), std::io::Error>
38 where
39 Self: 'a,
40 {
41 for target in Self::all().values() {
42 target.install()?;
43 }
44 Ok(())
45 }
46}
47
48#[derive(Debug)]
49pub struct TargetInvalid {
50 pub(crate) name: String,
51 pub(crate) possible: Vec<String>,
52}
53
54impl Display for TargetInvalid {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 write!(
57 f,
58 "Target {:?} is invalid; the possible targets are {:?}",
59 self.name, self.possible,
60 )
61 }
62}
63
64#[allow(clippy::type_complexity)]
65pub fn get_targets<'a, Iter, I, T, U>(
66 targets: Iter,
67 fallback: Option<(&'a dyn Fn(U) -> Option<&'a T>, U)>,
69) -> Result<Vec<&'a T>, TargetInvalid>
70where
71 Iter: ExactSizeIterator<Item = &'a I>,
72 I: AsRef<str> + 'a,
73 T: TargetTrait<'a>,
74{
75 let targets_empty = targets.len() == 0;
76 Ok(if !targets_empty {
77 targets
78 .map(|name| {
79 T::for_name(name.as_ref()).ok_or_else(|| TargetInvalid {
80 name: name.as_ref().to_owned(),
81 possible: T::all().keys().map(|key| key.to_string()).collect(),
82 })
83 })
84 .collect::<Result<_, _>>()?
85 } else {
86 let target = fallback
87 .and_then(|(get_target, arg)| get_target(arg))
88 .unwrap_or_else(|| {
89 log::info!("falling back on default target ({})", T::DEFAULT_KEY);
90 T::default_ref()
91 });
92 vec![target]
93 })
94}
95
96pub fn call_for_targets_with_fallback<'a, Iter, I, T, U, E, F>(
97 targets: Iter,
98 fallback: &'a dyn Fn(U) -> Option<&'a T>,
99 arg: U,
100 mut f: F,
101) -> Result<Result<(), E>, TargetInvalid>
102where
103 Iter: ExactSizeIterator<Item = &'a I>,
104 I: AsRef<str> + 'a,
105 T: TargetTrait<'a>,
106 F: FnMut(&T) -> Result<(), E>,
107{
108 get_targets(targets, Some((fallback, arg))).map(|targets| {
109 for target in targets {
110 f(target)?;
111 }
112 Ok(())
113 })
114}
115
116pub fn call_for_targets<'a, Iter, I, T, E, F>(
117 targets: Iter,
118 f: F,
119) -> Result<Result<(), E>, TargetInvalid>
120where
121 Iter: ExactSizeIterator<Item = &'a I>,
122 I: AsRef<str> + 'a,
123 T: TargetTrait<'a> + 'a,
124 F: Fn(&T) -> Result<(), E>,
125{
126 get_targets::<_, _, _, ()>(targets, None).map(|targets| {
127 for target in targets {
128 f(target)?;
129 }
130 Ok(())
131 })
132}