1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
use mullvad_version::{PreStableType, Version};
use std::{env, process::exit};
const ANDROID_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/android-version-name.txt"));
fn main() {
let command = env::args().nth(1);
match command.as_deref() {
None => println!("{}", mullvad_version::VERSION),
Some("semver") => println!("{}", to_semver(mullvad_version::VERSION)),
Some("version.h") => println!("{}", to_windows_h_format(mullvad_version::VERSION)),
Some("versionName") => println!("{ANDROID_VERSION}"),
Some("versionCode") => println!("{}", to_android_version_code(ANDROID_VERSION)),
Some(command) => {
eprintln!("Unknown command: {command}");
exit(1);
}
}
}
/// Takes a version without a patch number and adds the patch (set to zero).
///
/// Converts `x.y[-z]` into `x.y.0[-z]` to make the version semver compatible.
fn to_semver(version: &str) -> String {
let mut parts = version.splitn(2, '-');
let version = parts.next().expect("Year component");
let remainder = parts.next().map(|s| format!("-{s}")).unwrap_or_default();
assert_eq!(parts.next(), None);
format!("{version}.0{remainder}")
}
/// Takes a version in the normal Mullvad VPN app version format and returns the Android
/// `versionCode` formatted version.
///
/// The format of the code is: YYVVXZZZ
/// Last two digits of the year (major)---------^^
/// Incrementing version (minor)------------------^^
/// Build type (0=alpha, 1=beta, 9=stable/dev)------^
/// Build number (000 if stable/dev)-----------------^^^
///
/// # Examples
///
/// Version: 2021.1-alpha1
/// versionCode: 21010001
///
/// Version: 2021.34-beta5
/// versionCode: 21341005
///
/// Version: 2021.34
/// versionCode: 21349000
///
/// Version: 2021.34-dev
/// versionCode: 21349000
fn to_android_version_code(version: &str) -> String {
let version = Version::parse(version);
let (build_type, build_number) = if version.dev.is_some() {
("9", "000")
} else {
match &version.pre_stable {
Some(PreStableType::Alpha(v)) => ("0", v.as_str()),
Some(PreStableType::Beta(v)) => ("1", v.as_str()),
// Stable version
None => ("9", "000"),
}
};
format!(
"{}{:0>2}{}{:0>3}",
version.year, version.incremental, build_type, build_number,
)
}
fn to_windows_h_format(version_str: &str) -> String {
let version = Version::parse(version_str);
assert!(
is_valid_windows_version(&version),
"Invalid Windows version: {version:?}"
);
let Version {
year, incremental, ..
} = version;
format!(
"#define MAJOR_VERSION 20{year}
#define MINOR_VERSION {incremental}
#define PATCH_VERSION 0
#define PRODUCT_VERSION \"{version_str}\""
)
}
/// On Windows we currently support the following versions: stable, beta and dev.
fn is_valid_windows_version(version: &Version) -> bool {
version.is_stable()
|| version.beta().is_some()
|| (version.dev.is_some() && version.alpha().is_none())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_version_code() {
assert_eq!("21349000", to_android_version_code("2021.34"));
}
#[test]
fn test_version_code_alpha() {
assert_eq!("21010001", to_android_version_code("2021.1-alpha1"));
}
#[test]
fn test_version_code_beta() {
assert_eq!("21341005", to_android_version_code("2021.34-beta5"));
}
#[test]
fn test_version_code_dev() {
assert_eq!("21349000", to_android_version_code("2021.34-dev-be846a5f0"));
}
#[test]
#[should_panic]
fn test_invalid_windows_version_code() {
to_windows_h_format("2021.34-alpha1");
}
}
|