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
use crate::newtypes::libcnb_newtype;
use serde::Serialize;
use std::collections::HashMap;
/// Output of a CNB exec.d program.
///
/// See [Cloud Native Buildpack specification](https://github.com/buildpacks/spec/blob/main/buildpack.md#execd)
#[derive(Serialize, Clone)]
pub struct ExecDProgramOutput(HashMap<ExecDProgramOutputKey, String>);
impl ExecDProgramOutput {
#[must_use]
pub fn new(map: HashMap<ExecDProgramOutputKey, String>) -> Self {
Self(map)
}
}
impl<K: Into<ExecDProgramOutputKey>, V: Into<String>, A: IntoIterator<Item = (K, V)>> From<A>
for ExecDProgramOutput
{
fn from(a: A) -> Self {
Self(
a.into_iter()
.map(|(key, value)| (key.into(), value.into()))
.collect(),
)
}
}
libcnb_newtype!(
exec_d,
/// Construct a [`ExecDProgramOutputKey`] value at compile time.
///
/// Passing a string that is not a valid `ExecDProgramOutputKey` value will yield a compilation
/// error.
///
/// # Examples:
/// ```
/// use libcnb_data::exec_d::ExecDProgramOutputKey;
/// use libcnb_data::exec_d_program_output_key;
///
/// let key: ExecDProgramOutputKey = exec_d_program_output_key!("PATH");
/// ```
exec_d_program_output_key,
/// A key of exec.d program output
///
/// It MUST only contain numbers, letters, and the characters `_` and `-`.
///
/// Use the [`exec_d_program_output_key`](crate::exec_d_program_output_key) macro to construct
/// a `ExecDProgramOutputKey` from a literal string. To parse a dynamic string into a
/// `ExecDProgramOutputKey`, use [`str::parse`](str::parse).
///
/// # Examples
/// ```
/// use libcnb_data::exec_d::ExecDProgramOutputKey;
/// use libcnb_data::exec_d_program_output_key;
///
/// let from_literal = exec_d_program_output_key!("ENV_VAR");
///
/// let input = "ENV_VAR";
/// let from_dynamic: ExecDProgramOutputKey = input.parse().unwrap();
/// assert_eq!(from_dynamic, from_literal);
///
/// let input = "!nv4lid";
/// let invalid: Result<ExecDProgramOutputKey, _> = input.parse();
/// assert!(invalid.is_err());
/// ```
ExecDProgramOutputKey,
ExecDProgramOutputKeyError,
r"^[A-Za-z0-9_-]+$"
);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exec_d_program_output_key_validation_valid() {
assert!("FOO".parse::<ExecDProgramOutputKey>().is_ok());
assert!("foo".parse::<ExecDProgramOutputKey>().is_ok());
assert!("FOO_BAR".parse::<ExecDProgramOutputKey>().is_ok());
assert!("foo_bar".parse::<ExecDProgramOutputKey>().is_ok());
assert!("123".parse::<ExecDProgramOutputKey>().is_ok());
assert!("FOO-bar".parse::<ExecDProgramOutputKey>().is_ok());
assert!("foo-BAR".parse::<ExecDProgramOutputKey>().is_ok());
}
#[test]
fn exec_d_program_output_key_validation_invalid() {
assert_eq!(
"FOO BAR".parse::<ExecDProgramOutputKey>(),
Err(ExecDProgramOutputKeyError::InvalidValue(String::from(
"FOO BAR"
)))
);
assert_eq!(
"FOO.BAR".parse::<ExecDProgramOutputKey>(),
Err(ExecDProgramOutputKeyError::InvalidValue(String::from(
"FOO.BAR"
)))
);
assert_eq!(
"FOO/BAR".parse::<ExecDProgramOutputKey>(),
Err(ExecDProgramOutputKeyError::InvalidValue(String::from(
"FOO/BAR"
)))
);
assert_eq!(
"FÜCHSCHEN".parse::<ExecDProgramOutputKey>(),
Err(ExecDProgramOutputKeyError::InvalidValue(String::from(
"FÜCHSCHEN"
)))
);
assert_eq!(
"🦊".parse::<ExecDProgramOutputKey>(),
Err(ExecDProgramOutputKeyError::InvalidValue(String::from("🦊")))
);
assert_eq!(
"".parse::<ExecDProgramOutputKey>(),
Err(ExecDProgramOutputKeyError::InvalidValue(String::new()))
);
}
}