fluentci_secrets/
convert.rs1use anyhow::{bail, Result};
2use serde_json::Value;
3
4pub fn as_valid_env_name(name: String) -> Result<String> {
5 let is_valid = |c: char| c.is_ascii_alphanumeric() || c == '_';
6 if !name.is_empty()
7 && name.chars().all(is_valid)
8 && name
9 .chars()
10 .next()
11 .map_or(false, |c| c.is_ascii_alphabetic())
12 {
13 Ok(name)
14 } else {
15 bail!("secret name '{}' is invalid", name)
16 }
17}
18
19#[allow(dead_code)]
20pub fn value_as_string(name: &str, v: Value) -> Result<String> {
21 match v {
22 Value::String(s) => Ok(s),
23 Value::Bool(b) => Ok(format!("{b}")),
24 Value::Number(n) => Ok(format!("{n}")),
25 Value::Null => Ok("null".to_string()),
26 _ => bail!("secret '{}' value is not convertible", name),
27 }
28}
29
30#[allow(dead_code)]
31pub fn convert_env_name(prefix: &str, name: &str) -> Result<String> {
32 let name = name[prefix.len()..].replace('-', "_");
33 as_valid_env_name(name)
34}
35
36#[allow(dead_code)]
37pub fn decode_env_from_json(name: &str, value: Value) -> Result<Vec<(String, String)>> {
38 match value {
39 Value::Object(m) => m
40 .into_iter()
41 .map(|(k, v)| Ok((as_valid_env_name(k)?, value_as_string(name, v)?)))
42 .collect(),
43 _ => bail!(
44 "top-level value for secret '{}' must be a JSON object",
45 name
46 ),
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use serde_json::json;
53 use std::collections::HashMap;
54
55 use super::*;
56
57 macro_rules! assert_invalid_secret {
58 ($a:expr) => {
59 assert!(matches!($a, Err(_)));
60 };
61 }
62
63 #[test]
64 fn as_valid_env_name_correct() {
65 macro_rules! assert_convert {
66 ($a:expr) => {
67 assert_eq!($a, as_valid_env_name($a.to_string()).unwrap());
68 };
69 }
70
71 assert_convert!("abc");
72 assert_convert!("abc123");
73 assert_convert!("ab_12_ab");
74 }
75
76 #[test]
77 fn as_valid_env_name_invalid() {
78 macro_rules! assert_fail {
79 ($a:expr) => {
80 assert_invalid_secret!(as_valid_env_name($a.to_string()));
81 };
82 }
83
84 assert_fail!("");
85 assert_fail!("123abc");
86 assert_fail!("ab!");
87 assert_fail!("ab-ab");
88 }
89
90 #[test]
91 fn value_as_string_for_normal_values() {
92 macro_rules! assert_convert {
93 ($a:expr, $b:expr) => {
94 assert_eq!($a, value_as_string("ignored", $b).unwrap());
95 };
96 }
97
98 assert_convert!("abcd", json!("abcd"));
99 assert_convert!("12", json!(12));
100 assert_convert!("12.123", json!(12.123));
101 assert_convert!("null", json!(null));
102 assert_convert!("false", json!(false));
103 assert_convert!("true", json!(true));
104 }
105
106 #[test]
107 fn value_as_string_for_arrays_and_objects() {
108 macro_rules! assert_fail {
109 ($a:expr) => {
110 assert_invalid_secret!(value_as_string("ignored", $a));
111 };
112 }
113
114 assert_fail!(json!({ "a": 123 }));
115 assert_fail!(json!([1, 2]));
116 }
117
118 #[test]
119 fn decode_env_from_json_correct_values() {
120 macro_rules! assert_decode {
121 ($a:expr, $($name:ident = $value:expr),*) => {
122 #[allow(unused_variables, unused_mut)]
123 {
124 let decoded = decode_env_from_json("ignored", $a).unwrap();
125 let len = decoded.len();
126 let mapped = decoded.into_iter().collect::<HashMap<_, _>>();
127 let mut total = 0;
128 $(
129 total += 1;
130 let name = stringify!($name);
131 let value = $value.to_string();
132 assert_eq!(Some(&value), mapped.get(&name[..]));
133 )*
134 assert_eq!(total, len);
135 }
136 };
137 }
138
139 assert_decode!(json!({}),);
140 assert_decode!(json!({"a": 1}), a = "1");
141 assert_decode!(json!({"a": 1, "b": true}), a = "1", b = "true");
142 assert_decode!(
143 json!({"a": 1, "b": true, "c": "test"}),
144 a = "1",
145 b = "true",
146 c = "test"
147 );
148 }
149
150 #[test]
151 fn decode_env_from_json_invalid() {
152 macro_rules! assert_fail {
153 ($a:expr) => {
154 assert_invalid_secret!(decode_env_from_json("ignored", $a));
155 };
156 }
157
158 assert_fail!(json!([1, 2]));
159 assert_fail!(json!("test"));
160 assert_fail!(json!(false));
161 assert_fail!(json!(true));
162 assert_fail!(json!({"a!": 1}));
163 assert_fail!(json!({"1a": 1}));
164 assert_fail!(json!({"a": {"b": 1}}));
165 }
166
167 #[test]
168 fn convert_env_name_converts_names() {
169 macro_rules! assert_convert {
170 ($a:expr, $b:expr) => {
171 assert_convert!("", $a, $b);
172 };
173 ($prefix:expr, $a:expr, $b:expr) => {
174 assert_eq!($a, convert_env_name($prefix, $b).unwrap());
175 };
176 }
177
178 assert_convert!("abc", "abc");
179 assert_convert!("abc123", "abc123");
180 assert_convert!("abc_123", "abc-123");
181 assert_convert!("abc__123", "abc--123");
182 assert_convert!("abc_123", "abc_123");
183
184 assert_convert!("zxc", "abc", "zxcabc");
185 assert_convert!("zxc", "abc", "abcabc");
186 }
187
188 #[test]
189 fn convert_env_name_invalid() {
190 macro_rules! assert_fail {
191 ($a:expr) => {
192 assert_fail!("", $a);
193 };
194 ($prefix:expr, $a:expr) => {
195 assert_invalid_secret!(convert_env_name($prefix, $a));
196 };
197 }
198
199 assert_fail!("");
200 assert_fail!("!");
201 assert_fail!("abc!");
202 assert_fail!("123abc");
203 assert_fail!("abc", "abc");
204 }
205}