| 1 | // Parse system-deps metadata from Cargo.toml |
| 2 | |
| 3 | use std::{fmt, fs, io::Read, path::Path}; |
| 4 | |
| 5 | use toml::{map::Map, Value}; |
| 6 | |
| 7 | #[derive (Debug, PartialEq)] |
| 8 | pub(crate) struct MetaData { |
| 9 | pub(crate) deps: Vec<Dependency>, |
| 10 | } |
| 11 | |
| 12 | #[derive (Debug, Clone, PartialEq)] |
| 13 | pub(crate) struct Dependency { |
| 14 | pub(crate) key: String, |
| 15 | pub(crate) version: Option<String>, |
| 16 | pub(crate) name: Option<String>, |
| 17 | pub(crate) fallback_names: Option<Vec<String>>, |
| 18 | pub(crate) feature: Option<String>, |
| 19 | pub(crate) optional: bool, |
| 20 | pub(crate) cfg: Option<cfg_expr::Expression>, |
| 21 | pub(crate) version_overrides: Vec<VersionOverride>, |
| 22 | } |
| 23 | |
| 24 | impl Dependency { |
| 25 | fn new(name: &str) -> Self { |
| 26 | Self { |
| 27 | key: name.to_string(), |
| 28 | ..Default::default() |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | pub(crate) fn lib_name(&self) -> &str { |
| 33 | self.name.as_ref().unwrap_or(&self.key) |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | impl Default for Dependency { |
| 38 | fn default() -> Self { |
| 39 | Self { |
| 40 | key: "" .to_string(), |
| 41 | version: None, |
| 42 | name: None, |
| 43 | fallback_names: None, |
| 44 | feature: None, |
| 45 | optional: false, |
| 46 | cfg: None, |
| 47 | version_overrides: Vec::new(), |
| 48 | } |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | #[derive (Debug, PartialEq)] |
| 53 | enum VersionOverrideBuilderError { |
| 54 | MissingVersionField, |
| 55 | } |
| 56 | |
| 57 | impl fmt::Display for VersionOverrideBuilderError { |
| 58 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 59 | match *self { |
| 60 | Self::MissingVersionField => write!(f, "missing version field" ), |
| 61 | } |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | impl std::error::Error for VersionOverrideBuilderError {} |
| 66 | |
| 67 | #[derive (Debug, PartialEq)] |
| 68 | enum MetadataError { |
| 69 | MissingKey(String), |
| 70 | NotATable(String), |
| 71 | NestedCfg(String), |
| 72 | NotStringOrTable(String), |
| 73 | NotString(String), |
| 74 | CfgExpr(cfg_expr::ParseError), |
| 75 | Toml(toml::de::Error), |
| 76 | UnexpectedVersionSetting(String, String, String), |
| 77 | UnexpectedKey(String, String, String), |
| 78 | VersionOverrideBuilder(VersionOverrideBuilderError), |
| 79 | } |
| 80 | |
| 81 | impl fmt::Display for MetadataError { |
| 82 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 83 | match self { |
| 84 | Self::MissingKey(k: &String) => write!(f, "missing key ` {}`" , k), |
| 85 | Self::NotATable(k: &String) => write!(f, "` {}` is not a table" , k), |
| 86 | Self::NestedCfg(k: &String) => write!(f, "` {}`: cfg() cannot be nested" , k), |
| 87 | Self::NotString(k: &String) => write!(f, "` {}`: not a string" , k), |
| 88 | Self::NotStringOrTable(k: &String) => write!(f, "` {}`: not a string or a table" , k), |
| 89 | Self::CfgExpr(e: &ParseError) => write!(f, " {}" , e), |
| 90 | Self::Toml(e: &Error) => write!(f, "error parsing TOML: {}" , e), |
| 91 | Self::UnexpectedVersionSetting(n: &String, k: &String, t: &String) => { |
| 92 | write!( |
| 93 | f, |
| 94 | " {}: unexpected version settings key: {} type: {}" , |
| 95 | n, k, t |
| 96 | ) |
| 97 | } |
| 98 | Self::UnexpectedKey(n: &String, k: &String, t: &String) => write!(f, " {}: unexpected key {} type {}" , n, k, t), |
| 99 | Self::VersionOverrideBuilder(e: &VersionOverrideBuilderError) => write!(f, " {}" , e), |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | impl std::error::Error for MetadataError { |
| 105 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { |
| 106 | match self { |
| 107 | Self::CfgExpr(e: &ParseError) => Some(e), |
| 108 | Self::Toml(e: &Error) => Some(e), |
| 109 | Self::VersionOverrideBuilder(e: &VersionOverrideBuilderError) => Some(e), |
| 110 | _ => None, |
| 111 | } |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | impl From<cfg_expr::ParseError> for MetadataError { |
| 116 | fn from(err: cfg_expr::ParseError) -> Self { |
| 117 | Self::CfgExpr(err) |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | impl From<toml::de::Error> for MetadataError { |
| 122 | fn from(err: toml::de::Error) -> Self { |
| 123 | Self::Toml(err) |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | impl From<VersionOverrideBuilderError> for MetadataError { |
| 128 | fn from(err: VersionOverrideBuilderError) -> Self { |
| 129 | Self::VersionOverrideBuilder(err) |
| 130 | } |
| 131 | } |
| 132 | |
| 133 | #[derive (Debug, Clone, PartialEq)] |
| 134 | pub(crate) struct VersionOverride { |
| 135 | pub(crate) key: String, |
| 136 | pub(crate) version: String, |
| 137 | pub(crate) name: Option<String>, |
| 138 | pub(crate) fallback_names: Option<Vec<String>>, |
| 139 | pub(crate) optional: Option<bool>, |
| 140 | } |
| 141 | |
| 142 | struct VersionOverrideBuilder { |
| 143 | version_id: String, |
| 144 | version: Option<String>, |
| 145 | full_name: Option<String>, |
| 146 | fallback_names: Option<Vec<String>>, |
| 147 | optional: Option<bool>, |
| 148 | } |
| 149 | |
| 150 | impl VersionOverrideBuilder { |
| 151 | fn new(version_id: &str) -> Self { |
| 152 | Self { |
| 153 | version_id: version_id.to_string(), |
| 154 | version: None, |
| 155 | full_name: None, |
| 156 | fallback_names: None, |
| 157 | optional: None, |
| 158 | } |
| 159 | } |
| 160 | |
| 161 | fn build(self) -> Result<VersionOverride, VersionOverrideBuilderError> { |
| 162 | let version = self |
| 163 | .version |
| 164 | .ok_or(VersionOverrideBuilderError::MissingVersionField)?; |
| 165 | |
| 166 | Ok(VersionOverride { |
| 167 | key: self.version_id, |
| 168 | version, |
| 169 | name: self.full_name, |
| 170 | fallback_names: self.fallback_names, |
| 171 | optional: self.optional, |
| 172 | }) |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | impl MetaData { |
| 177 | pub(crate) fn from_file(path: &Path) -> Result<Self, crate::Error> { |
| 178 | let mut manifest = fs::File::open(path).map_err(|e| { |
| 179 | crate::Error::FailToRead(format!("error opening {}" , path.display()), e) |
| 180 | })?; |
| 181 | |
| 182 | let mut manifest_str = String::new(); |
| 183 | manifest.read_to_string(&mut manifest_str).map_err(|e| { |
| 184 | crate::Error::FailToRead(format!("error reading {}" , path.display()), e) |
| 185 | })?; |
| 186 | |
| 187 | Self::from_str(manifest_str) |
| 188 | .map_err(|e| crate::Error::InvalidMetadata(format!(" {}: {}" , path.display(), e))) |
| 189 | } |
| 190 | |
| 191 | fn from_str(manifest_str: String) -> Result<Self, MetadataError> { |
| 192 | let toml = manifest_str.parse::<toml::Value>()?; |
| 193 | let key = "package.metadata.system-deps" ; |
| 194 | let meta = toml |
| 195 | .get("package" ) |
| 196 | .and_then(|v| v.get("metadata" )) |
| 197 | .and_then(|v| v.get("system-deps" )) |
| 198 | .ok_or_else(|| MetadataError::MissingKey(key.to_owned()))?; |
| 199 | |
| 200 | let deps = Self::parse_deps_table(meta, key, true)?; |
| 201 | |
| 202 | Ok(MetaData { deps }) |
| 203 | } |
| 204 | |
| 205 | fn parse_deps_table( |
| 206 | table: &Value, |
| 207 | key: &str, |
| 208 | allow_cfg: bool, |
| 209 | ) -> Result<Vec<Dependency>, MetadataError> { |
| 210 | let table = table |
| 211 | .as_table() |
| 212 | .ok_or_else(|| MetadataError::NotATable(key.to_owned()))?; |
| 213 | |
| 214 | let mut deps = Vec::new(); |
| 215 | |
| 216 | for (name, value) in table { |
| 217 | if name.starts_with("cfg(" ) { |
| 218 | if allow_cfg { |
| 219 | let cfg_exp = cfg_expr::Expression::parse(name)?; |
| 220 | |
| 221 | for mut dep in |
| 222 | Self::parse_deps_table(value, &format!(" {}. {}" , key, name), false)? |
| 223 | { |
| 224 | dep.cfg = Some(cfg_exp.clone()); |
| 225 | deps.push(dep); |
| 226 | } |
| 227 | } else { |
| 228 | return Err(MetadataError::NestedCfg(format!(" {}. {}" , key, name))); |
| 229 | } |
| 230 | } else { |
| 231 | let dep = Self::parse_dep(key, name, value)?; |
| 232 | deps.push(dep); |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | Ok(deps) |
| 237 | } |
| 238 | |
| 239 | fn parse_dep(key: &str, name: &str, value: &Value) -> Result<Dependency, MetadataError> { |
| 240 | let mut dep = Dependency::new(name); |
| 241 | |
| 242 | match value { |
| 243 | // somelib = "1.0" |
| 244 | toml::Value::String(ref s) => { |
| 245 | if !validate_version(s) { |
| 246 | return Err(MetadataError::UnexpectedVersionSetting( |
| 247 | key.into(), |
| 248 | name.into(), |
| 249 | value.type_str().to_owned(), |
| 250 | )); |
| 251 | } |
| 252 | |
| 253 | dep.version = Some(s.clone()); |
| 254 | } |
| 255 | toml::Value::Table(ref t) => { |
| 256 | Self::parse_dep_table(key, name, &mut dep, t)?; |
| 257 | } |
| 258 | _ => { |
| 259 | return Err(MetadataError::NotStringOrTable(format!(" {}. {}" , key, name))); |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | Ok(dep) |
| 264 | } |
| 265 | |
| 266 | fn parse_dep_table( |
| 267 | p_key: &str, |
| 268 | name: &str, |
| 269 | dep: &mut Dependency, |
| 270 | t: &Map<String, Value>, |
| 271 | ) -> Result<(), MetadataError> { |
| 272 | for (key, value) in t { |
| 273 | match (key.as_str(), value) { |
| 274 | ("feature" , toml::Value::String(s)) => { |
| 275 | dep.feature = Some(s.clone()); |
| 276 | } |
| 277 | ("version" , toml::Value::String(s)) => { |
| 278 | if !validate_version(s) { |
| 279 | return Err(MetadataError::UnexpectedVersionSetting( |
| 280 | format!(" {}. {}" , p_key, name), |
| 281 | key.into(), |
| 282 | value.type_str().to_owned(), |
| 283 | )); |
| 284 | } |
| 285 | |
| 286 | dep.version = Some(s.clone()); |
| 287 | } |
| 288 | ("name" , toml::Value::String(s)) => { |
| 289 | dep.name = Some(s.clone()); |
| 290 | } |
| 291 | ("fallback-names" , toml::Value::Array(values)) => { |
| 292 | let key = format!(" {}. {}" , p_key, name); |
| 293 | dep.fallback_names = Some(Self::parse_name_list(&key, values)?); |
| 294 | } |
| 295 | ("optional" , &toml::Value::Boolean(optional)) => { |
| 296 | dep.optional = optional; |
| 297 | } |
| 298 | (version_feature, toml::Value::Table(version_settings)) |
| 299 | if version_feature.starts_with('v' ) => |
| 300 | { |
| 301 | let mut builder = VersionOverrideBuilder::new(version_feature); |
| 302 | |
| 303 | for (k, v) in version_settings { |
| 304 | match (k.as_str(), v) { |
| 305 | ("version" , toml::Value::String(feat_vers)) => { |
| 306 | if !validate_version(feat_vers) { |
| 307 | return Err(MetadataError::UnexpectedVersionSetting( |
| 308 | format!(" {}. {}" , p_key, name), |
| 309 | k.into(), |
| 310 | v.type_str().to_owned(), |
| 311 | )); |
| 312 | } |
| 313 | |
| 314 | builder.version = Some(feat_vers.into()); |
| 315 | } |
| 316 | ("name" , toml::Value::String(feat_name)) => { |
| 317 | builder.full_name = Some(feat_name.into()); |
| 318 | } |
| 319 | ("fallback-names" , toml::Value::Array(values)) => { |
| 320 | let key = format!(" {}. {}. {}" , p_key, name, version_feature); |
| 321 | builder.fallback_names = Some(Self::parse_name_list(&key, values)?); |
| 322 | } |
| 323 | ("optional" , &toml::Value::Boolean(optional)) => { |
| 324 | builder.optional = Some(optional); |
| 325 | } |
| 326 | _ => { |
| 327 | return Err(MetadataError::UnexpectedVersionSetting( |
| 328 | format!(" {}. {}" , p_key, name), |
| 329 | k.to_owned(), |
| 330 | v.type_str().to_owned(), |
| 331 | )); |
| 332 | } |
| 333 | } |
| 334 | } |
| 335 | |
| 336 | dep.version_overrides.push(builder.build()?); |
| 337 | } |
| 338 | _ => { |
| 339 | return Err(MetadataError::UnexpectedKey( |
| 340 | format!(" {}. {}" , p_key, name), |
| 341 | key.to_owned(), |
| 342 | value.type_str().to_owned(), |
| 343 | )); |
| 344 | } |
| 345 | } |
| 346 | } |
| 347 | Ok(()) |
| 348 | } |
| 349 | |
| 350 | fn parse_name_list(key: &str, values: &[Value]) -> Result<Vec<String>, MetadataError> { |
| 351 | values |
| 352 | .iter() |
| 353 | .enumerate() |
| 354 | .map(|(i, value)| { |
| 355 | value |
| 356 | .as_str() |
| 357 | .map(|x| x.to_owned()) |
| 358 | .ok_or_else(|| MetadataError::NotString(format!(" {}[ {}]" , key, i))) |
| 359 | }) |
| 360 | .collect() |
| 361 | } |
| 362 | } |
| 363 | |
| 364 | fn validate_version(version: &str) -> bool { |
| 365 | if let Some((min: &str, max: &str)) = version.split_once(delimiter:',' ) { |
| 366 | if !min.trim_start().starts_with(">=" ) || !max.trim_start().starts_with('<' ) { |
| 367 | return false; |
| 368 | } |
| 369 | |
| 370 | true |
| 371 | } else { |
| 372 | true |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | #[derive (Debug, Clone)] |
| 377 | pub(crate) enum VersionRange<'a> { |
| 378 | Range(std::ops::Range<&'a str>), |
| 379 | RangeFrom(std::ops::RangeFrom<&'a str>), |
| 380 | } |
| 381 | |
| 382 | impl<'a> std::ops::RangeBounds<&'a str> for VersionRange<'a> { |
| 383 | fn start_bound(&self) -> std::ops::Bound<&&'a str> { |
| 384 | match self { |
| 385 | VersionRange::Range(r: &Range<&str>) => r.start_bound(), |
| 386 | VersionRange::RangeFrom(r: &RangeFrom<&str>) => r.start_bound(), |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | fn end_bound(&self) -> std::ops::Bound<&&'a str> { |
| 391 | match self { |
| 392 | VersionRange::Range(r: &Range<&str>) => r.end_bound(), |
| 393 | VersionRange::RangeFrom(r: &RangeFrom<&str>) => r.end_bound(), |
| 394 | } |
| 395 | } |
| 396 | } |
| 397 | |
| 398 | pub(crate) fn parse_version(version: &str) -> VersionRange { |
| 399 | if let Some((min: &str, max: &str)) = version.split_once(delimiter:',' ) { |
| 400 | // Format checked when parsing |
| 401 | let min: &str = min.trim_start().strip_prefix(">=" ).unwrap().trim(); |
| 402 | let max: &str = max.trim_start().strip_prefix('<' ).unwrap().trim(); |
| 403 | VersionRange::Range(min..max) |
| 404 | } else if let Some(min: &str) = version.trim_start().strip_prefix(">=" ) { |
| 405 | VersionRange::RangeFrom(min..) |
| 406 | } else { |
| 407 | VersionRange::RangeFrom(version..) |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | #[cfg (test)] |
| 412 | mod tests { |
| 413 | use super::*; |
| 414 | use assert_matches::assert_matches; |
| 415 | use cfg_expr::Expression; |
| 416 | use std::{env, path::PathBuf}; |
| 417 | |
| 418 | fn parse_file(dir: &str) -> Result<MetaData, crate::Error> { |
| 419 | let manifest_dir = env::var("CARGO_MANIFEST_DIR" ).unwrap(); |
| 420 | let mut p: PathBuf = manifest_dir.into(); |
| 421 | p.push("src" ); |
| 422 | p.push("tests" ); |
| 423 | p.push(dir); |
| 424 | p.push("Cargo.toml" ); |
| 425 | assert!(p.exists()); |
| 426 | |
| 427 | MetaData::from_file(&p) |
| 428 | } |
| 429 | |
| 430 | #[test ] |
| 431 | fn parse_good() { |
| 432 | let m = parse_file("toml-good" ).unwrap(); |
| 433 | |
| 434 | assert_eq!( |
| 435 | m, |
| 436 | MetaData { |
| 437 | deps: vec![ |
| 438 | Dependency { |
| 439 | key: "testdata" .into(), |
| 440 | version: Some("4" .into()), |
| 441 | ..Default::default() |
| 442 | }, |
| 443 | Dependency { |
| 444 | key: "testlib" .into(), |
| 445 | version: Some("1" .into()), |
| 446 | feature: Some("test-feature" .into()), |
| 447 | ..Default::default() |
| 448 | }, |
| 449 | Dependency { |
| 450 | key: "testmore" .into(), |
| 451 | version: Some("2" .into()), |
| 452 | feature: Some("another-test-feature" .into()), |
| 453 | ..Default::default() |
| 454 | } |
| 455 | ] |
| 456 | } |
| 457 | ) |
| 458 | } |
| 459 | |
| 460 | #[test ] |
| 461 | fn parse_feature_not_string() { |
| 462 | assert_matches!( |
| 463 | parse_file("toml-feature-not-string" ), |
| 464 | Err(crate::Error::InvalidMetadata(_)) |
| 465 | ); |
| 466 | } |
| 467 | |
| 468 | #[test ] |
| 469 | fn parse_override_name() { |
| 470 | let m = parse_file("toml-override-name" ).unwrap(); |
| 471 | |
| 472 | assert_eq!( |
| 473 | m, |
| 474 | MetaData { |
| 475 | deps: vec![Dependency { |
| 476 | key: "test_lib" .into(), |
| 477 | version: Some("1.0" .into()), |
| 478 | name: Some("testlib" .into()), |
| 479 | version_overrides: vec![VersionOverride { |
| 480 | key: "v1_2" .into(), |
| 481 | version: "1.2" .into(), |
| 482 | name: None, |
| 483 | fallback_names: None, |
| 484 | optional: None, |
| 485 | }], |
| 486 | ..Default::default() |
| 487 | },] |
| 488 | } |
| 489 | ) |
| 490 | } |
| 491 | |
| 492 | #[test ] |
| 493 | fn parse_feature_versions() { |
| 494 | let m = parse_file("toml-feature-versions" ).unwrap(); |
| 495 | |
| 496 | assert_eq!( |
| 497 | m, |
| 498 | MetaData { |
| 499 | deps: vec![Dependency { |
| 500 | key: "testdata" .into(), |
| 501 | version: Some("4" .into()), |
| 502 | version_overrides: vec![ |
| 503 | VersionOverride { |
| 504 | key: "v5" .into(), |
| 505 | version: "5" .into(), |
| 506 | name: None, |
| 507 | fallback_names: None, |
| 508 | optional: None, |
| 509 | }, |
| 510 | VersionOverride { |
| 511 | key: "v6" .into(), |
| 512 | version: "6" .into(), |
| 513 | name: None, |
| 514 | fallback_names: None, |
| 515 | optional: None, |
| 516 | }, |
| 517 | ], |
| 518 | ..Default::default() |
| 519 | },] |
| 520 | } |
| 521 | ) |
| 522 | } |
| 523 | |
| 524 | #[test ] |
| 525 | fn parse_fallback_names() { |
| 526 | let m = parse_file("toml-fallback-names" ).unwrap(); |
| 527 | |
| 528 | assert_eq!( |
| 529 | m, |
| 530 | MetaData { |
| 531 | deps: vec![Dependency { |
| 532 | key: "test_lib" .into(), |
| 533 | version: Some("1.0" .into()), |
| 534 | name: Some("nosuchlib" .into()), |
| 535 | fallback_names: Some(vec![ |
| 536 | "also-no-such-lib" .into(), |
| 537 | "testlib" .into(), |
| 538 | "should-not-get-here" .into(), |
| 539 | ]), |
| 540 | version_overrides: vec![], |
| 541 | ..Default::default() |
| 542 | }] |
| 543 | } |
| 544 | ) |
| 545 | } |
| 546 | |
| 547 | #[test ] |
| 548 | fn parse_version_fallback_names() { |
| 549 | let m = parse_file("toml-version-fallback-names" ).unwrap(); |
| 550 | |
| 551 | assert_eq!( |
| 552 | m, |
| 553 | MetaData { |
| 554 | deps: vec![Dependency { |
| 555 | key: "test_lib" .into(), |
| 556 | version: Some("0.1" .into()), |
| 557 | name: Some("nosuchlib" .into()), |
| 558 | fallback_names: Some(vec![ |
| 559 | "also-no-such-lib" .into(), |
| 560 | "testlib" .into(), |
| 561 | "should-not-get-here" .into(), |
| 562 | ]), |
| 563 | version_overrides: vec![ |
| 564 | VersionOverride { |
| 565 | key: "v1" .into(), |
| 566 | version: "1.0" .into(), |
| 567 | name: None, |
| 568 | fallback_names: None, |
| 569 | optional: None, |
| 570 | }, |
| 571 | VersionOverride { |
| 572 | key: "v2" .into(), |
| 573 | version: "2.0" .into(), |
| 574 | name: None, |
| 575 | fallback_names: Some(vec!["testlib-2.0" .into()]), |
| 576 | optional: None, |
| 577 | }, |
| 578 | VersionOverride { |
| 579 | key: "v99" .into(), |
| 580 | version: "99.0" .into(), |
| 581 | name: None, |
| 582 | fallback_names: Some(vec![]), |
| 583 | optional: None, |
| 584 | }, |
| 585 | ], |
| 586 | ..Default::default() |
| 587 | }] |
| 588 | } |
| 589 | ) |
| 590 | } |
| 591 | |
| 592 | #[test ] |
| 593 | fn parse_optional() { |
| 594 | let m = parse_file("toml-optional" ).unwrap(); |
| 595 | |
| 596 | assert_eq!( |
| 597 | m, |
| 598 | MetaData { |
| 599 | deps: vec![ |
| 600 | Dependency { |
| 601 | key: "testbadger" .into(), |
| 602 | version: Some("1" .into()), |
| 603 | optional: true, |
| 604 | ..Default::default() |
| 605 | }, |
| 606 | Dependency { |
| 607 | key: "testlib" .into(), |
| 608 | version: Some("1.0" .into()), |
| 609 | optional: true, |
| 610 | version_overrides: vec![VersionOverride { |
| 611 | key: "v5" .into(), |
| 612 | version: "5.0" .into(), |
| 613 | name: Some("testlib-5.0" .into()), |
| 614 | fallback_names: None, |
| 615 | optional: Some(false), |
| 616 | },], |
| 617 | ..Default::default() |
| 618 | }, |
| 619 | Dependency { |
| 620 | key: "testmore" .into(), |
| 621 | version: Some("2" .into()), |
| 622 | version_overrides: vec![VersionOverride { |
| 623 | key: "v3" .into(), |
| 624 | version: "3.0" .into(), |
| 625 | name: None, |
| 626 | fallback_names: None, |
| 627 | optional: Some(true), |
| 628 | },], |
| 629 | ..Default::default() |
| 630 | }, |
| 631 | ] |
| 632 | } |
| 633 | ) |
| 634 | } |
| 635 | |
| 636 | #[test ] |
| 637 | fn parse_os_specific() { |
| 638 | let m = parse_file("toml-os-specific" ).unwrap(); |
| 639 | |
| 640 | assert_eq!( |
| 641 | m, |
| 642 | MetaData { |
| 643 | deps: vec![ |
| 644 | Dependency { |
| 645 | key: "testlib" .into(), |
| 646 | version: Some("1" .into()), |
| 647 | cfg: Some(Expression::parse("not(target_os = \"macos \")" ).unwrap()), |
| 648 | ..Default::default() |
| 649 | }, |
| 650 | Dependency { |
| 651 | key: "testdata" .into(), |
| 652 | version: Some("1" .into()), |
| 653 | cfg: Some(Expression::parse("target_os = \"linux \"" ).unwrap()), |
| 654 | ..Default::default() |
| 655 | }, |
| 656 | Dependency { |
| 657 | key: "testanotherlib" .into(), |
| 658 | version: Some("1" .into()), |
| 659 | cfg: Some(Expression::parse("unix" ).unwrap()), |
| 660 | optional: true, |
| 661 | ..Default::default() |
| 662 | }, |
| 663 | ] |
| 664 | } |
| 665 | ) |
| 666 | } |
| 667 | } |
| 668 | |