|
10 | 10 | //! This is a core functionality that is not considered part of the stable API. |
11 | 11 | //! If you use it, note that it may include breaking changes in the future. |
12 | 12 |
|
13 | | -use std::{collections::HashMap, path::PathBuf}; |
| 13 | +use std::{collections::HashMap, fmt, fs::read_to_string, path::PathBuf}; |
14 | 14 |
|
15 | | -use serde::Deserialize; |
| 15 | +use serde::{ |
| 16 | + de::{Deserializer, Error as DeError, Visitor}, |
| 17 | + Deserialize, |
| 18 | +}; |
16 | 19 | use serde_json::Value as JsonValue; |
17 | 20 | use url::Url; |
18 | 21 |
|
@@ -479,16 +482,68 @@ impl Default for BuildConfig { |
479 | 482 | } |
480 | 483 | } |
481 | 484 |
|
| 485 | +#[derive(Debug, PartialEq)] |
| 486 | +struct PackageVersion(String); |
| 487 | + |
| 488 | +impl<'d> serde::Deserialize<'d> for PackageVersion { |
| 489 | + fn deserialize<D: Deserializer<'d>>(deserializer: D) -> Result<PackageVersion, D::Error> { |
| 490 | + struct PackageVersionVisitor; |
| 491 | + |
| 492 | + impl<'d> Visitor<'d> for PackageVersionVisitor { |
| 493 | + type Value = PackageVersion; |
| 494 | + |
| 495 | + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 496 | + write!( |
| 497 | + formatter, |
| 498 | + "a semver string or a path to a package.json file" |
| 499 | + ) |
| 500 | + } |
| 501 | + |
| 502 | + fn visit_str<E: DeError>(self, value: &str) -> Result<PackageVersion, E> { |
| 503 | + let path = PathBuf::from(value); |
| 504 | + if path.exists() { |
| 505 | + let json_str = read_to_string(&path) |
| 506 | + .map_err(|e| DeError::custom(format!("failed to read version JSON file: {}", e)))?; |
| 507 | + let package_json: serde_json::Value = serde_json::from_str(&json_str) |
| 508 | + .map_err(|e| DeError::custom(format!("failed to read version JSON file: {}", e)))?; |
| 509 | + if let Some(obj) = package_json.as_object() { |
| 510 | + let version = obj |
| 511 | + .get("version") |
| 512 | + .ok_or_else(|| DeError::custom("JSON must contain a `version` field"))? |
| 513 | + .as_str() |
| 514 | + .ok_or_else(|| DeError::custom("`version` must be a string"))?; |
| 515 | + Ok(PackageVersion(version.into())) |
| 516 | + } else { |
| 517 | + Err(DeError::custom("value is not a path to a JSON object")) |
| 518 | + } |
| 519 | + } else { |
| 520 | + Ok(PackageVersion(value.into())) |
| 521 | + } |
| 522 | + } |
| 523 | + } |
| 524 | + |
| 525 | + deserializer.deserialize_string(PackageVersionVisitor {}) |
| 526 | + } |
| 527 | +} |
| 528 | + |
482 | 529 | /// The package configuration. |
483 | 530 | #[derive(Debug, Default, PartialEq, Deserialize)] |
484 | 531 | #[serde(rename_all = "camelCase")] |
485 | 532 | pub struct PackageConfig { |
486 | 533 | /// App name. |
487 | 534 | pub product_name: Option<String>, |
488 | | - /// App version. |
| 535 | + /// App version. It is a semver version number or a path to a `package.json` file contaning the `version` field. |
| 536 | + #[serde(deserialize_with = "version_deserializer", default)] |
489 | 537 | pub version: Option<String>, |
490 | 538 | } |
491 | 539 |
|
| 540 | +fn version_deserializer<'de, D>(deserializer: D) -> Result<Option<String>, D::Error> |
| 541 | +where |
| 542 | + D: Deserializer<'de>, |
| 543 | +{ |
| 544 | + Option::<PackageVersion>::deserialize(deserializer).map(|v| v.map(|v| v.0)) |
| 545 | +} |
| 546 | + |
492 | 547 | /// The config type mapped to `tauri.conf.json`. |
493 | 548 | #[derive(Debug, Default, PartialEq, Deserialize)] |
494 | 549 | #[serde(rename_all = "camelCase")] |
|
0 commit comments