cargo_mobile2/
target.rs

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    // we use `dyn` so the type doesn't need to be known when this is `None`
68    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}