local-ssl 0.1.0

Local HTTPS certificate generation for development — pair with local-dns
Documentation
use base64::Engine;

pub fn pem_decode(pem: &str) -> Result<Vec<u8>, String> {
    let mut b64 = String::new();
    for line in pem.lines() {
        if line.starts_with("-----") {
            continue;
        }
        b64.push_str(line.trim());
    }
    let engine = base64::engine::general_purpose::STANDARD;
    engine
        .decode(b64.as_bytes())
        .map_err(|e| format!("Base64 decode error: {e}"))
}

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

    #[test]
    fn test_pem_decode_valid() {
        let data = "SGVsbG8gV29ybGQ=";
        let pem = format!("-----BEGIN CERTIFICATE-----\n{data}\n-----END CERTIFICATE-----");
        let result = pem_decode(&pem).unwrap();
        assert_eq!(result, b"Hello World");
    }

    #[test]
    fn test_pem_decode_no_headers() {
        let result = pem_decode("SGVsbG8gV29ybGQ=").unwrap();
        assert_eq!(result, b"Hello World");
    }

    #[test]
    fn test_pem_decode_invalid_base64() {
        let pem = "\
-----BEGIN CERTIFICATE-----\n\
!!!invalid base64!!!\n\
-----END CERTIFICATE-----";
        let result = pem_decode(pem);
        assert!(result.is_err());
        assert!(result.unwrap_err().contains("Base64 decode error"));
    }

    #[test]
    fn test_pem_decode_empty() {
        let result = pem_decode("");
        assert!(result.is_ok());
        assert!(result.unwrap().is_empty());
    }

    #[test]
    fn test_pem_decode_only_headers() {
        let pem = "\
-----BEGIN CERTIFICATE-----\n\
-----END CERTIFICATE-----";
        let result = pem_decode(pem).unwrap();
        assert!(result.is_empty());
    }
}