datasources-postgres 0.1.24

Postgres datasource provider
use base64::{engine::general_purpose::STANDARD, Engine};
use datasources::{Result, TypeConverter, Value};

pub struct BinaryConverter;

fn decode(text: &str) -> Option<Vec<u8>> {
    let text = text.trim();
    (text.len() >= 4
        && text.len() % 4 == 0
        && text.contains('=')
        && text
            .bytes()
            .all(|b| b.is_ascii_alphanumeric() || b == b'+' || b == b'/' || b == b'=')
        && STANDARD.decode(text).is_ok())
    .then(|| STANDARD.decode(text).ok())
    .flatten()
}

impl TypeConverter<Value, Value> for BinaryConverter {
    fn convert(&self, from: &Value) -> Result<Value> {
        Ok(match from {
            Value::Text(text) => decode(text)
                .map(Value::Binary)
                .unwrap_or_else(|| from.clone()),
            other => other.clone(),
        })
    }

    fn reverse_convert(&self, to: &Value) -> Result<Value> {
        self.convert(to)
    }
}

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

    #[test]
    fn maps_binary() {
        let converter = BinaryConverter;
        let raw = Value::Binary(vec![1, 2, 3, 4]);
        assert_eq!(converter.convert(&raw).unwrap(), raw);
        assert_eq!(converter.reverse_convert(&raw).unwrap(), raw);
        assert_eq!(
            converter.reverse_convert(&Value::from("AQIDBA==")).unwrap(),
            raw
        );
        assert_eq!(converter.convert(&Value::from("x")).unwrap(), Value::from("x"));
        assert!(matches!(
            converter.convert(&Value::from("AA==")).unwrap(),
            Value::Binary(_)
        ));
    }
}