1use std::collections::BTreeMap;
14
15pub const MAX_LABELS: usize = 16;
17
18pub const MAX_LABEL_KEY_LEN: usize = 64;
20
21pub const MAX_LABEL_VALUE_LEN: usize = 256;
23
24pub fn validate_labels(labels: &BTreeMap<String, String>) -> Result<(), String> {
32 if labels.len() > MAX_LABELS {
33 return Err(format!(
34 "a run may carry at most {MAX_LABELS} labels, got {}",
35 labels.len()
36 ));
37 }
38 for (key, value) in labels {
39 if key.len() > MAX_LABEL_KEY_LEN {
40 return Err(format!(
41 "label key `{key}` is {} bytes, over the {MAX_LABEL_KEY_LEN}-byte cap",
42 key.len()
43 ));
44 }
45 if value.len() > MAX_LABEL_VALUE_LEN {
46 return Err(format!(
47 "label value for key `{key}` is {} bytes, over the {MAX_LABEL_VALUE_LEN}-byte cap",
48 value.len()
49 ));
50 }
51 }
52 Ok(())
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 fn map(pairs: &[(&str, &str)]) -> BTreeMap<String, String> {
60 pairs
61 .iter()
62 .map(|(k, v)| ((*k).to_owned(), (*v).to_owned()))
63 .collect()
64 }
65
66 #[test]
67 fn empty_and_small_label_sets_are_valid() {
68 assert!(validate_labels(&BTreeMap::new()).is_ok());
69 assert!(validate_labels(&map(&[("build", "42"), ("env", "prod")])).is_ok());
70 }
71
72 #[test]
73 fn more_than_the_cap_is_rejected() {
74 let labels: BTreeMap<String, String> = (0..=MAX_LABELS)
75 .map(|i| (format!("k{i}"), "v".to_owned()))
76 .collect();
77 let err = validate_labels(&labels).expect_err("over the cap must be rejected");
78 assert!(err.contains("at most 16 labels"), "{err}");
79 }
80
81 #[test]
82 fn exactly_the_cap_is_valid() {
83 let labels: BTreeMap<String, String> = (0..MAX_LABELS)
84 .map(|i| (format!("k{i}"), "v".to_owned()))
85 .collect();
86 assert!(validate_labels(&labels).is_ok());
87 }
88
89 #[test]
90 fn an_oversized_key_is_rejected() {
91 let long_key = "k".repeat(MAX_LABEL_KEY_LEN + 1);
92 let err =
93 validate_labels(&map(&[(&long_key, "v")])).expect_err("a long key must be rejected");
94 assert!(err.contains("over the 64-byte cap"), "{err}");
95 }
96
97 #[test]
98 fn an_oversized_value_is_rejected() {
99 let long_value = "v".repeat(MAX_LABEL_VALUE_LEN + 1);
100 let err = validate_labels(&map(&[("k", &long_value)]))
101 .expect_err("a long value must be rejected");
102 assert!(err.contains("over the 256-byte cap"), "{err}");
103 }
104
105 #[test]
106 fn keys_and_values_exactly_at_the_cap_are_valid() {
107 let key = "k".repeat(MAX_LABEL_KEY_LEN);
108 let value = "v".repeat(MAX_LABEL_VALUE_LEN);
109 assert!(validate_labels(&map(&[(&key, &value)])).is_ok());
110 }
111}