fundaia 0.55.0

Command line for the Fundaia deployment platform: projects, services, variables, deployments, logs and metrics
use crate::api::models::VariableDraft;

pub fn variables_in(text: &str, secret: bool) -> Vec<VariableDraft> {
    text.lines()
        .filter_map(pair_in)
        .map(|(key, value)| VariableDraft {
            key: key.to_owned(),
            value,
            secret,
        })
        .collect()
}

fn pair_in(line: &str) -> Option<(&str, String)> {
    let line = line.trim();

    if line.is_empty() || line.starts_with('#') {
        return None;
    }

    let line = line.strip_prefix("export ").unwrap_or(line);
    let (key, value) = line.split_once('=')?;
    let key = key.trim();

    if !is_a_key(key) {
        return None;
    }

    Some((key, unquoted(value.trim())))
}

fn is_a_key(key: &str) -> bool {
    !key.is_empty()
        && !key.starts_with(|c: char| c.is_ascii_digit())
        && key.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
}

fn unquoted(value: &str) -> String {
    for quote in ['"', '\''] {
        if let Some(inner) = value
            .strip_prefix(quote)
            .and_then(|rest| rest.strip_suffix(quote))
        {
            return inner.to_owned();
        }
    }

    match value.split_once(" #") {
        Some((before, _)) => before.trim_end().to_owned(),
        None => value.to_owned(),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    fn only(text: &str) -> VariableDraft {
        variables_in(text, false)
            .pop()
            .expect("the text holds exactly one variable")
    }

    #[test]
    fn it_should_read_a_plain_assignment() {
        assert_eq!(only("PORT=8080").key, "PORT");
    }

    #[test]
    fn it_should_read_the_value_of_a_plain_assignment() {
        assert_eq!(only("PORT=8080").value, "8080");
    }

    #[test]
    fn it_should_tolerate_the_export_people_write_in_front() {
        assert_eq!(only("export PORT=8080").key, "PORT");
    }

    #[test]
    fn it_should_keep_every_equals_sign_after_the_first() {
        assert_eq!(
            only("DATABASE_URL=postgres://u:p@host/db?a=b").value,
            "postgres://u:p@host/db?a=b",
        );
    }

    #[test]
    fn it_should_take_off_double_quotes() {
        assert_eq!(only(r#"GREETING="hola mundo""#).value, "hola mundo");
    }

    #[test]
    fn it_should_take_off_single_quotes() {
        assert_eq!(only("GREETING='hola mundo'").value, "hola mundo");
    }

    #[test]
    fn it_should_keep_a_hash_that_is_inside_quotes() {
        assert_eq!(only(r#"PASSWORD="a#b""#).value, "a#b");
    }

    #[test]
    fn it_should_drop_a_trailing_comment_from_an_unquoted_value() {
        assert_eq!(only("PORT=8080 # the http one").value, "8080");
    }

    #[test]
    fn it_should_skip_a_commented_line() {
        assert!(variables_in("# PORT=8080", false).is_empty());
    }

    #[test]
    fn it_should_skip_a_blank_line() {
        assert!(variables_in("\n   \n", false).is_empty());
    }

    #[test]
    fn it_should_skip_a_line_that_is_not_an_assignment() {
        assert!(variables_in("just some prose", false).is_empty());
    }

    #[test]
    fn it_should_refuse_a_key_a_shell_would_not_export() {
        assert!(variables_in("not-a-key=1", false).is_empty());
    }

    #[test]
    fn it_should_refuse_a_key_that_starts_with_a_digit() {
        assert!(variables_in("1PORT=8080", false).is_empty());
    }

    #[test]
    fn it_should_read_every_variable_in_a_file() {
        assert_eq!(variables_in("A=1\n\n# comment\nB=2\n", false).len(), 2);
    }

    #[test]
    fn it_should_mark_them_all_secret_when_asked_to() {
        assert!(variables_in("TOKEN=abc", true)[0].secret);
    }

    #[test]
    fn it_should_leave_them_plain_when_not_asked_to() {
        assert!(!variables_in("PORT=8080", false)[0].secret);
    }

    #[test]
    fn it_should_leave_a_platform_reference_exactly_as_written() {
        assert_eq!(
            only("DATABASE_URL=${{ Postgres.DATABASE_URL }}").value,
            "${{ Postgres.DATABASE_URL }}",
        );
    }
}