1use anyhow::{bail, Result};
2use std::{fmt, str::FromStr};
3
4macro_rules! impl_otherwise {
5 ($e:ident, $a:ident, $b:ident) => {
6 impl $e {
7 pub fn otherwise(&self) -> Self {
8 match self {
9 $e::$a => $e::$b,
10 $e::$b => $e::$a,
11 }
12 }
13 }
14 };
15}
16
17pub const VALID_CONFIGS: &[&str] = &[
18 "mkl-dynamic-ilp64-iomp",
19 "mkl-dynamic-ilp64-seq",
20 "mkl-dynamic-lp64-iomp",
21 "mkl-dynamic-lp64-seq",
22 "mkl-static-ilp64-iomp",
23 "mkl-static-ilp64-seq",
24 "mkl-static-lp64-iomp",
25 "mkl-static-lp64-seq",
26];
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum LinkType {
31 Static,
32 Dynamic,
33}
34impl_otherwise!(LinkType, Static, Dynamic);
35
36impl fmt::Display for LinkType {
37 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38 match self {
39 LinkType::Static => write!(f, "static"),
40 LinkType::Dynamic => write!(f, "dynamic"),
41 }
42 }
43}
44
45impl Default for LinkType {
46 fn default() -> Self {
47 LinkType::Static
48 }
49}
50
51impl FromStr for LinkType {
52 type Err = anyhow::Error;
53 fn from_str(input: &str) -> Result<Self> {
54 Ok(match input {
55 "static" => LinkType::Static,
56 "dynamic" => LinkType::Dynamic,
57 another => bail!("Invalid link spec: {}", another),
58 })
59 }
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum DataModel {
68 LP64,
70 ILP64,
72}
73impl_otherwise!(DataModel, LP64, ILP64);
74
75impl fmt::Display for DataModel {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 match self {
78 DataModel::LP64 => write!(f, "lp64"),
79 DataModel::ILP64 => write!(f, "ilp64"),
80 }
81 }
82}
83
84impl Default for DataModel {
85 fn default() -> Self {
86 DataModel::ILP64
87 }
88}
89
90impl FromStr for DataModel {
91 type Err = anyhow::Error;
92 fn from_str(input: &str) -> Result<Self> {
93 Ok(match input {
94 "lp64" => DataModel::LP64,
95 "ilp64" => DataModel::ILP64,
96 another => bail!("Invalid index spec: {}", another),
97 })
98 }
99}
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq)]
103pub enum Threading {
104 OpenMP,
106 Sequential,
108}
109
110impl_otherwise!(Threading, OpenMP, Sequential);
111
112impl Default for Threading {
113 fn default() -> Self {
114 Threading::Sequential
115 }
116}
117
118impl fmt::Display for Threading {
119 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
120 match self {
121 Threading::OpenMP => write!(f, "iomp"),
122 Threading::Sequential => write!(f, "seq"),
123 }
124 }
125}
126
127impl FromStr for Threading {
128 type Err = anyhow::Error;
129 fn from_str(input: &str) -> Result<Self> {
130 Ok(match input {
131 "iomp" => Threading::OpenMP,
132 "seq" => Threading::Sequential,
133 another => bail!("Invalid parallel spec: {}", another),
134 })
135 }
136}
137
138#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
142pub struct Config {
143 pub link: LinkType,
144 pub index_size: DataModel,
145 pub parallel: Threading,
146}
147
148impl fmt::Display for Config {
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 write!(f, "mkl-{}-{}-{}", self.link, self.index_size, self.parallel)
151 }
152}
153
154impl FromStr for Config {
155 type Err = anyhow::Error;
156 fn from_str(name: &str) -> Result<Self> {
157 let parts: Vec<_> = name.split('-').collect();
158 if parts.len() != 4 {
159 bail!("Invalid name: {}", name);
160 }
161 if parts[0] != "mkl" {
162 bail!("Name must start with 'mkl': {}", name);
163 }
164 Ok(Config {
165 link: LinkType::from_str(parts[1])?,
166 index_size: DataModel::from_str(parts[2])?,
167 parallel: Threading::from_str(parts[3])?,
168 })
169 }
170}
171
172impl Config {
173 pub fn possibles() -> Vec<Self> {
174 VALID_CONFIGS
175 .iter()
176 .map(|name| Self::from_str(name).unwrap())
177 .collect()
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn name_to_config() -> Result<()> {
187 let cfg = Config::from_str("mkl-static-lp64-iomp")?;
188 assert_eq!(
189 cfg,
190 Config {
191 link: LinkType::Static,
192 index_size: DataModel::LP64,
193 parallel: Threading::OpenMP
194 }
195 );
196 Ok(())
197 }
198
199 #[test]
200 fn name_to_config_to_name() -> Result<()> {
201 for name in VALID_CONFIGS {
202 let cfg = Config::from_str(name)?;
203 assert_eq!(&cfg.to_string(), name);
204 }
205 Ok(())
206 }
207
208 #[test]
209 fn invalid_names() -> Result<()> {
210 assert!(Config::from_str("").is_err());
211 assert!(Config::from_str("static-lp64-iomp").is_err());
212 assert!(Config::from_str("mkll-static-lp64-iomp").is_err());
213 assert!(Config::from_str("mkl-sttic-lp64-iomp").is_err());
214 assert!(Config::from_str("mkl-static-l64-iomp").is_err());
215 assert!(Config::from_str("mkl-static-lp64-omp").is_err());
216 Ok(())
217 }
218}