pact_plugin_driver/
utils.rs1use std::collections::{BTreeMap, HashMap};
4use anyhow::bail;
5use itertools::Itertools;
6use os_info::Type;
7
8use prost_types::{ListValue, Struct};
9use prost_types::value::Kind;
10use semver::{Version, VersionReq};
11use serde_json::{json, Value};
12use tracing::debug;
13
14pub fn to_proto_struct(values: &HashMap<String, Value>) -> Struct {
16 Struct {
17 fields: values.iter().map(|(k, v)| (k.clone(), to_proto_value(v))).collect()
18 }
19}
20
21pub fn to_proto_value(val: &Value) -> prost_types::Value {
23 match val {
24 Value::Null => prost_types::Value { kind: Some(prost_types::value::Kind::NullValue(0)) },
25 Value::Bool(b) => prost_types::Value { kind: Some(prost_types::value::Kind::BoolValue(*b)) },
26 Value::Number(n) => if let Some(n) = n.as_u64() {
27 prost_types::Value { kind: Some(prost_types::value::Kind::NumberValue(n as f64)) }
28 } else if let Some(n) = n.as_i64() {
29 prost_types::Value { kind: Some(prost_types::value::Kind::NumberValue(n as f64)) }
30 } else {
31 prost_types::Value { kind: Some(prost_types::value::Kind::NumberValue(n.as_f64().unwrap_or_default())) }
32 }
33 Value::String(s) => prost_types::Value { kind: Some(prost_types::value::Kind::StringValue(s.clone())) },
34 Value::Array(a) => prost_types::Value { kind: Some(prost_types::value::Kind::ListValue(ListValue {
35 values: a.iter().map(|v| to_proto_value(v)).collect()
36 }))},
37 Value::Object(o) => prost_types::Value { kind: Some(prost_types::value::Kind::StructValue(Struct {
38 fields: o.iter().map(|(k, v)| (k.clone(), to_proto_value(v))).collect()
39 }))}
40 }
41}
42
43pub fn proto_struct_to_json(val: &prost_types::Struct) -> Value {
45 Value::Object(val.fields.iter()
46 .map(|(k, v)| (k.clone(), proto_value_to_json(v))).collect())
47}
48
49pub fn proto_value_to_json(val: &prost_types::Value) -> Value {
51 match &val.kind {
52 Some(kind) => match kind {
53 Kind::NullValue(_) => Value::Null,
54 Kind::NumberValue(n) => json!(n),
55 Kind::StringValue(s) => Value::String(s.clone()),
56 Kind::BoolValue(b) => Value::Bool(*b),
57 Kind::StructValue(s) => proto_struct_to_json(s),
58 Kind::ListValue(l) => Value::Array(l.values.iter()
59 .map(|v| proto_value_to_json(v)).collect())
60 }
61 None => Value::Null
62 }
63}
64
65pub fn proto_struct_to_map(val: &prost_types::Struct) -> BTreeMap<String, Value> {
67 val.fields.iter()
68 .sorted_by(|(k1, _), (k2, _)| Ord::cmp(k1, k2))
69 .map(|(k, v)| (k.clone(), proto_value_to_json(v)))
70 .collect()
71}
72
73pub fn proto_struct_to_hashmap(val: &prost_types::Struct) -> HashMap<String, Value> {
75 val.fields.iter()
76 .map(|(k, v)| (k.clone(), proto_value_to_json(v)))
77 .collect()
78}
79
80pub fn proto_value_to_string(val: &prost_types::Value) -> Option<String> {
82 match &val.kind {
83 Some(kind) => match kind {
84 Kind::NullValue(_) => None,
85 Kind::NumberValue(n) => Some(n.to_string()),
86 Kind::StringValue(s) => Some(s.clone()),
87 Kind::BoolValue(b) => Some(b.to_string()),
88 Kind::StructValue(s) => Some(proto_struct_to_json(s).to_string()),
89 Kind::ListValue(l) => Some(Value::Array(l.values.iter()
90 .map(|v| proto_value_to_json(v)).collect()).to_string())
91 }
92 None => None
93 }
94}
95
96pub fn versions_compatible(version: &str, required: &Option<String>) -> bool {
98 match required {
99 None => true,
100 Some(required) => {
101 if required == version {
102 true
103 } else if let Ok(version) = Version::parse(version) {
104 if let Ok(req) = VersionReq::parse(format!(">={}", required).as_str()) {
105 req.matches(&version)
106 } else {
107 false
108 }
109 } else {
110 false
111 }
112 }
113 }
114}
115
116pub fn optional_string<S: Into<String>>(string: S) -> Option<String> {
117 let string = string.into();
118 if string.is_empty() {
119 None
120 } else {
121 Some(string)
122 }
123}
124
125pub fn os_and_arch() -> anyhow::Result<(&'static str, &'static str)> {
127 let os_info = os_info::get();
128 debug!("Detected OS: {}", os_info);
129
130 let os = match os_info.os_type() {
131 Type::Alpine | Type::Amazon| Type::Android| Type::Arch| Type::CentOS| Type::Debian |
132 Type::EndeavourOS | Type::Fedora | Type::Gentoo | Type::Linux | Type::Manjaro | Type::Mariner |
133 Type::Mint | Type::NixOS | Type::openSUSE | Type::OracleLinux | Type::Redhat |
134 Type::RedHatEnterprise | Type::Pop | Type::Raspbian | Type::Solus | Type::SUSE |
135 Type::Ubuntu => "linux",
136 Type::Macos => "osx",
137 Type::Windows => "windows",
138 _ => bail!("{} is not a supported operating system", os_info)
139 };
140
141 Ok((os, std::env::consts::ARCH))
142}
143
144#[cfg(test)]
145mod tests {
146 use expectest::prelude::*;
147
148 use super::versions_compatible;
149
150 #[test]
151 fn versions_compatible_test() {
152 expect!(versions_compatible("1.0.0", &None)).to(be_true());
153 expect!(versions_compatible("1.0.0", &Some("1.0.0".to_string()))).to(be_true());
154 expect!(versions_compatible("1.0.0", &Some("1.0.1".to_string()))).to(be_false());
155 expect!(versions_compatible("1.0.4", &Some("1.0.3".to_string()))).to(be_true());
156 expect!(versions_compatible("1.1.0", &Some("1.0.3".to_string()))).to(be_true());
157 expect!(versions_compatible("2.0.1", &Some("1.1.0".to_string()))).to(be_true());
158 expect!(versions_compatible("1.0.1", &Some("2.1.0".to_string()))).to(be_false());
159 expect!(versions_compatible("0.1.0", &Some("0.0.3".to_string()))).to(be_true());
160 }
161}