use crate::options::PreviewOptions;
pub(crate) fn build_file_id_query(file_id: u64, options: &PreviewOptions) -> String {
let mut parts = Vec::new();
parts.push(format!("fileId={file_id}"));
append_options(&mut parts, options);
format!("?{}", parts.join("&"))
}
pub(crate) fn build_path_query(path: &str, options: &PreviewOptions) -> String {
let mut parts = Vec::new();
parts.push(format!("file={}", encode_component(path)));
append_options(&mut parts, options);
format!("?{}", parts.join("&"))
}
fn append_options(parts: &mut Vec<String>, options: &PreviewOptions) {
if let Some(x) = options.width_px() {
parts.push(format!("x={x}"));
}
if let Some(y) = options.height_px() {
parts.push(format!("y={y}"));
}
if let Some(a) = options.aspect_ratio() {
let val = if a { "1" } else { "0" };
parts.push(format!("a={val}"));
}
if let Some(mode) = options.preview_mode() {
parts.push(format!("mode={}", mode.as_str()));
}
}
fn encode_component(value: &str) -> String {
let mut encoded = String::with_capacity(value.len());
for byte in value.bytes() {
match byte {
b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => {
encoded.push(char::from(byte));
}
other => {
encoded.push('%');
encoded.push(hex_digit(other >> 4));
encoded.push(hex_digit(other & 0x0f));
}
}
}
encoded
}
const fn hex_digit(nibble: u8) -> char {
match nibble {
0..=9 => (b'0' + nibble) as char,
_ => (b'A' + (nibble - 10)) as char,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mode::PreviewMode;
#[test]
fn file_id_query_minimal() {
let options = PreviewOptions::new();
assert_eq!(build_file_id_query(42, &options), "?fileId=42");
}
#[test]
fn file_id_query_all_options() {
let options = PreviewOptions::new()
.width(64)
.height(128)
.keep_aspect_ratio(true)
.mode(PreviewMode::Cover);
assert_eq!(
build_file_id_query(999, &options),
"?fileId=999&x=64&y=128&a=1&mode=cover"
);
}
#[test]
fn path_query_minimal() {
let options = PreviewOptions::new();
assert_eq!(
build_path_query("Documents/test.png", &options),
"?file=Documents%2Ftest.png"
);
}
#[test]
fn path_query_all_options() {
let options = PreviewOptions::new()
.width(200)
.height(200)
.keep_aspect_ratio(false)
.mode(PreviewMode::Fill);
assert_eq!(
build_path_query("/Photos/photo 1.jpg", &options),
"?file=%2FPhotos%2Fphoto%201.jpg&x=200&y=200&a=0&mode=fill"
);
}
#[test]
fn encode_component_escapes_special_characters() {
assert_eq!(encode_component("a b&c=d"), "a%20b%26c%3Dd");
assert_eq!(encode_component("hello/world"), "hello%2Fworld");
assert_eq!(encode_component("simple-name_1.txt~"), "simple-name_1.txt~");
}
}