use base64::{engine::general_purpose::STANDARD as base64_engine, Engine};
use crate::error::YdkeError;
use crate::deck::Deck;
#[cfg(target_endian = "little")]
fn bytes_to_u32(bytes: &[u8]) -> u32 {
u32::from_le_bytes(bytes.try_into().unwrap())
}
#[cfg(target_endian = "big")]
fn bytes_to_u32(bytes: &[u8]) -> u32 {
let mut arr = [0u8; 4];
arr.copy_from_slice(bytes);
arr.reverse();
u32::from_be_bytes(arr)
}
#[cfg(target_endian = "little")]
fn u32_to_bytes(value: u32) -> [u8; 4] {
value.to_le_bytes()
}
#[cfg(target_endian = "big")]
fn u32_to_bytes(value: u32) -> [u8; 4] {
let mut bytes = value.to_be_bytes();
bytes.reverse();
bytes
}
fn base64_to_passcodes(base64: &str) -> Result<Vec<u32>, YdkeError> {
let bytes = base64_engine.decode(base64)?;
if bytes.len() % 4 != 0 {
return Ok(Vec::new());
}
let passcodes: Vec<u32> = bytes
.chunks_exact(4)
.map(bytes_to_u32)
.collect();
Ok(passcodes)
}
fn passcodes_to_base64(passcodes: &[u32]) -> String {
let bytes: Vec<u8> = passcodes
.iter()
.flat_map(|&code| u32_to_bytes(code).to_vec())
.collect();
base64_engine.encode(bytes)
}
pub fn parse_url(ydke: &str) -> Result<Deck, YdkeError> {
if !ydke.starts_with("ydke://") {
return Err(YdkeError::NotYdkeUrl);
}
let components: Vec<&str> = ydke["ydke://".len()..]
.split('!')
.collect();
if components.len() < 3 {
return Err(YdkeError::MissingDelimiters);
}
Ok(Deck {
main: base64_to_passcodes(components[0])?,
extra: base64_to_passcodes(components[1])?,
side: base64_to_passcodes(components[2])?,
})
}
pub fn parse_urls(urls: &[&str]) -> Result<Vec<Deck>, YdkeError> {
urls.iter()
.map(|url| parse_url(url))
.collect()
}
pub fn to_url(deck: &Deck) -> String {
format!(
"ydke://{}!{}!{}!",
passcodes_to_base64(&deck.main),
passcodes_to_base64(&deck.extra),
passcodes_to_base64(&deck.side)
)
}
pub fn to_urls(decks: &[Deck]) -> Vec<String> {
decks.iter()
.map(to_url)
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_base64_conversion() {
let passcodes = vec![26077387, 37351133];
let base64 = passcodes_to_base64(&passcodes);
let decoded = base64_to_passcodes(&base64).unwrap();
assert_eq!(passcodes, decoded);
}
}