omega/src/server/short_link.rs
2026-08-20 17:05:37 +02:00

77 lines
1.8 KiB
Rust

use crate::db::short_link_repo;
use rand::RngExt;
const CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNPRSTUVWXYZ1234567890";
pub async fn add_short_link(long: &str) -> Result<String, ()> {
loop {
let raw = generate_short_link().await;
if short_link_repo::insert(&raw, long).await.map_err(|_| ())? {
return Ok(format!(
"https://omega.tensamin.net/direct/{}",
format_with_dashes(&raw)
));
}
}
}
pub async fn generate_short_link() -> String {
let len = short_length().await;
let mut rng = rand::rng();
(0..len)
.map(|_| {
let idx = rng.random_range(0..CHARSET.len());
CHARSET[idx] as char
})
.collect()
}
pub async fn get_short_link(short: &str) -> Result<String, ()> {
let key = if short.contains("/") {
short.split("/").nth(1).unwrap_or_default()
} else {
short
};
let frag = short.replace(key, "");
let normalized = normalize_short(key);
let target = short_link_repo::get(&normalized)
.await
.map_err(|_| ())?
.ok_or(())?;
Ok(format!("{}{}", target, frag))
}
/* ---------------- helpers ---------------- */
async fn short_length() -> usize {
let count = short_link_repo::count().await.unwrap_or(0);
match count {
0..=1_999 => 4,
2_000..=999_999 => 8,
_ => 12,
}
}
fn format_with_dashes(s: &str) -> String {
s.chars()
.collect::<Vec<_>>()
.chunks(4)
.map(|c| c.iter().collect::<String>())
.collect::<Vec<_>>()
.join("-")
}
fn normalize_short(input: &str) -> String {
input
.chars()
.filter(|c| *c != '-')
.map(|c| match c {
'Q' | 'O' => '0',
'I' => 'l',
_ => c,
})
.collect()
}