firebase_rs_sdk/util/
base64.rs1use base64::engine::general_purpose::URL_SAFE;
2use base64::engine::Engine as _;
3use std::fmt;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct DecodeBase64Error;
7
8impl fmt::Display for DecodeBase64Error {
9 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
10 write!(f, "failed to decode base64 string")
11 }
12}
13
14impl std::error::Error for DecodeBase64Error {}
15
16pub fn base64_encode(input: &str) -> String {
18 base64_url_encode(input)
19}
20
21pub fn base64_url_encode(input: &str) -> String {
23 base64_url_encode_bytes(input.as_bytes())
24}
25
26pub fn base64_url_encode_bytes(bytes: &[u8]) -> String {
28 let encoded = URL_SAFE.encode(bytes);
29 encoded.replace('=', ".")
30}
31
32pub fn base64_url_encode_trimmed(input: &str) -> String {
34 base64_encode(input).trim_end_matches('.').to_owned()
35}
36
37pub fn base64_decode(input: &str) -> Result<String, DecodeBase64Error> {
39 let bytes = base64_decode_bytes(input)?;
40 String::from_utf8(bytes).map_err(|_err| DecodeBase64Error)
41}
42
43pub fn base64_decode_bytes(input: &str) -> Result<Vec<u8>, DecodeBase64Error> {
45 let mut normalized = input.replace('.', "=");
46 let remainder = normalized.len() % 4;
48 if remainder != 0 {
49 normalized.extend("====".chars().take(4 - remainder));
50 }
51 URL_SAFE
52 .decode(normalized.as_bytes())
53 .map_err(|_err| DecodeBase64Error)
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn encode_and_decode_roundtrip() {
62 let original = "hello firebase";
63 let encoded = base64_encode(original);
64 let decoded = base64_decode(&encoded).unwrap();
65 assert_eq!(decoded, original);
66 }
67
68 #[test]
69 fn encode_trimmed_removes_padding() {
70 let trimmed = base64_url_encode_trimmed("test");
71 assert!(!trimmed.ends_with('.'));
72 }
73
74 #[test]
75 fn decode_tolerates_missing_padding() {
76 let encoded = base64_encode("data");
77 let without_padding = encoded.trim_end_matches('.');
78 let decoded = base64_decode(without_padding).unwrap();
79 assert_eq!(decoded, "data");
80 }
81
82 #[test]
83 fn decode_invalid_returns_error() {
84 assert!(base64_decode("@@invalid@@").is_err());
85 }
86}