added support for use of a custom names file

This commit is contained in:
Schrottkatze 2023-02-17 23:27:39 +01:00
parent 7d5c70ddd6
commit 7abb3c5d11
10 changed files with 120 additions and 69 deletions

View file

@ -3,6 +3,7 @@ use lazy_static::lazy_static;
use std::convert::Infallible; use std::convert::Infallible;
use std::fmt; use std::fmt;
use std::net::IpAddr; use std::net::IpAddr;
use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
lazy_static! { lazy_static! {
@ -124,6 +125,11 @@ pub struct Args {
#[clap(long, env = "MICROBIN_CUSTOM_CSS")] #[clap(long, env = "MICROBIN_CUSTOM_CSS")]
pub custom_css: Option<String>, pub custom_css: Option<String>,
/// Replace built-in animal names file with custom names file for pasta links.
/// The file must be newline seperated.
#[clap(long, env = "MICROBIN_CUSTOM_NAMES")]
pub custom_names: Option<PathBuf>,
/// Enable the use of Hash IDs for shorter URLs instead of animal names. /// Enable the use of Hash IDs for shorter URLs instead of animal names.
#[clap(long, env = "MICROBIN_HASH_IDS")] #[clap(long, env = "MICROBIN_HASH_IDS")]
pub hash_ids: bool, pub hash_ids: bool,

View file

@ -1,8 +1,8 @@
use crate::dbio::save_to_file; use crate::dbio::save_to_file;
use crate::pasta::PastaFile; use crate::pasta::PastaFile;
use crate::util::animalnumbers::to_animal_names;
use crate::util::hashids::to_hashids; use crate::util::hashids::to_hashids;
use crate::util::misc::is_valid_url; use crate::util::misc::is_valid_url;
use crate::util::pasta_id_converter::CONVERTER;
use crate::{AppState, Pasta, ARGS}; use crate::{AppState, Pasta, ARGS};
use actix_multipart::Multipart; use actix_multipart::Multipart;
use actix_web::{get, web, Error, HttpResponse, Responder}; use actix_web::{get, web, Error, HttpResponse, Responder};
@ -194,7 +194,7 @@ pub async fn create(
let slug = if ARGS.hash_ids { let slug = if ARGS.hash_ids {
to_hashids(id) to_hashids(id)
} else { } else {
to_animal_names(id) CONVERTER.to_names(id)
}; };
Ok(HttpResponse::Found() Ok(HttpResponse::Found()
.append_header(("Location", format!("{}/pasta/{}", ARGS.public_path, slug))) .append_header(("Location", format!("{}/pasta/{}", ARGS.public_path, slug)))

View file

@ -1,9 +1,9 @@
use crate::args::Args; use crate::args::Args;
use crate::dbio::save_to_file; use crate::dbio::save_to_file;
use crate::endpoints::errors::ErrorTemplate; use crate::endpoints::errors::ErrorTemplate;
use crate::util::animalnumbers::to_u64;
use crate::util::hashids::to_u64 as hashid_to_u64; use crate::util::hashids::to_u64 as hashid_to_u64;
use crate::util::misc::remove_expired; use crate::util::misc::remove_expired;
use crate::util::pasta_id_converter::CONVERTER;
use crate::{AppState, Pasta, ARGS}; use crate::{AppState, Pasta, ARGS};
use actix_multipart::Multipart; use actix_multipart::Multipart;
use actix_web::{get, post, web, Error, HttpResponse}; use actix_web::{get, post, web, Error, HttpResponse};
@ -24,7 +24,7 @@ pub async fn get_edit(data: web::Data<AppState>, id: web::Path<String>) -> HttpR
let id = if ARGS.hash_ids { let id = if ARGS.hash_ids {
hashid_to_u64(&id).unwrap_or(0) hashid_to_u64(&id).unwrap_or(0)
} else { } else {
to_u64(&id.into_inner()).unwrap_or(0) CONVERTER.to_u64(&id.into_inner()).unwrap_or(0)
}; };
remove_expired(&mut pastas); remove_expired(&mut pastas);
@ -62,7 +62,7 @@ pub async fn post_edit(
let id = if ARGS.hash_ids { let id = if ARGS.hash_ids {
hashid_to_u64(&id).unwrap_or(0) hashid_to_u64(&id).unwrap_or(0)
} else { } else {
to_u64(&id.into_inner()).unwrap_or(0) CONVERTER.to_u64(&id.into_inner()).unwrap_or(0)
}; };
let mut pastas = data.pastas.lock().await; let mut pastas = data.pastas.lock().await;

View file

@ -2,10 +2,10 @@ use crate::args::{Args, ARGS};
use crate::dbio::save_to_file; use crate::dbio::save_to_file;
use crate::endpoints::errors::ErrorTemplate; use crate::endpoints::errors::ErrorTemplate;
use crate::pasta::Pasta; use crate::pasta::Pasta;
use crate::util::animalnumbers::to_u64;
use crate::util::hashids::to_u64 as hashid_to_u64; use crate::util::hashids::to_u64 as hashid_to_u64;
use crate::util::misc::remove_expired; use crate::util::misc::remove_expired;
use crate::AppState; use crate::AppState;
use crate::util::pasta_id_converter::CONVERTER;
use actix_web::{get, web, HttpResponse}; use actix_web::{get, web, HttpResponse};
use askama::Template; use askama::Template;
@ -27,7 +27,7 @@ pub async fn getpasta(data: web::Data<AppState>, id: web::Path<String>) -> HttpR
let id = if ARGS.hash_ids { let id = if ARGS.hash_ids {
hashid_to_u64(&id).unwrap_or(0) hashid_to_u64(&id).unwrap_or(0)
} else { } else {
to_u64(&id.into_inner()).unwrap_or(0) CONVERTER.to_u64(&id.into_inner()).unwrap_or(0)
}; };
// remove expired pastas (including this one if needed) // remove expired pastas (including this one if needed)
@ -92,7 +92,7 @@ pub async fn redirecturl(data: web::Data<AppState>, id: web::Path<String>) -> Ht
let id = if ARGS.hash_ids { let id = if ARGS.hash_ids {
hashid_to_u64(&id).unwrap_or(0) hashid_to_u64(&id).unwrap_or(0)
} else { } else {
to_u64(&id.into_inner()).unwrap_or(0) CONVERTER.to_u64(&id.into_inner()).unwrap_or(0)
}; };
// remove expired pastas (including this one if needed) // remove expired pastas (including this one if needed)
@ -160,7 +160,7 @@ pub async fn getrawpasta(data: web::Data<AppState>, id: web::Path<String>) -> St
let id = if ARGS.hash_ids { let id = if ARGS.hash_ids {
hashid_to_u64(&id).unwrap_or(0) hashid_to_u64(&id).unwrap_or(0)
} else { } else {
to_u64(&id.into_inner()).unwrap_or(0) CONVERTER.to_u64(&id.into_inner()).unwrap_or(0)
}; };
// remove expired pastas (including this one if needed) // remove expired pastas (including this one if needed)

View file

@ -1,10 +1,10 @@
use crate::args::{Args, ARGS}; use crate::args::{Args, ARGS};
use crate::endpoints::errors::ErrorTemplate; use crate::endpoints::errors::ErrorTemplate;
use crate::pasta::Pasta; use crate::pasta::Pasta;
use crate::util::animalnumbers::to_u64;
use crate::util::hashids::to_u64 as hashid_to_u64; use crate::util::hashids::to_u64 as hashid_to_u64;
use crate::util::misc::{self, remove_expired}; use crate::util::misc::{self, remove_expired};
use crate::AppState; use crate::AppState;
use crate::util::pasta_id_converter::CONVERTER;
use actix_web::{get, web, HttpResponse}; use actix_web::{get, web, HttpResponse};
use askama::Template; use askama::Template;
@ -25,7 +25,7 @@ pub async fn getqr(data: web::Data<AppState>, id: web::Path<String>) -> HttpResp
let u64_id = if ARGS.hash_ids { let u64_id = if ARGS.hash_ids {
hashid_to_u64(&id).unwrap_or(0) hashid_to_u64(&id).unwrap_or(0)
} else { } else {
to_u64(&id).unwrap_or(0) CONVERTER.to_u64(&id).unwrap_or(0)
}; };
// remove expired pastas (including this one if needed) // remove expired pastas (including this one if needed)

View file

@ -3,10 +3,10 @@ use actix_web::{get, web, HttpResponse};
use crate::args::ARGS; use crate::args::ARGS;
use crate::endpoints::errors::ErrorTemplate; use crate::endpoints::errors::ErrorTemplate;
use crate::pasta::PastaFile; use crate::pasta::PastaFile;
use crate::util::animalnumbers::to_u64;
use crate::util::hashids::to_u64 as hashid_to_u64; use crate::util::hashids::to_u64 as hashid_to_u64;
use crate::util::misc::remove_expired; use crate::util::misc::remove_expired;
use crate::AppState; use crate::AppState;
use crate::util::pasta_id_converter::CONVERTER;
use askama::Template; use askama::Template;
use std::fs; use std::fs;
@ -24,7 +24,7 @@ pub async fn remove(data: web::Data<AppState>, id: web::Path<String>) -> HttpRes
let id = if ARGS.hash_ids { let id = if ARGS.hash_ids {
hashid_to_u64(&id).unwrap_or(0) hashid_to_u64(&id).unwrap_or(0)
} else { } else {
to_u64(&id.into_inner()).unwrap_or(0) CONVERTER.to_u64(&id.into_inner()).unwrap_or(0)
}; };
for (i, pasta) in pastas.iter().enumerate() { for (i, pasta) in pastas.iter().enumerate() {

View file

@ -20,7 +20,7 @@ pub mod args;
pub mod pasta; pub mod pasta;
pub mod util { pub mod util {
pub mod animalnumbers; pub mod pasta_id_converter;
pub mod auth; pub mod auth;
pub mod dbio; pub mod dbio;
pub mod hashids; pub mod hashids;

View file

@ -6,8 +6,8 @@ use std::path::Path;
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
use crate::args::ARGS; use crate::args::ARGS;
use crate::util::animalnumbers::to_animal_names;
use crate::util::hashids::to_hashids; use crate::util::hashids::to_hashids;
use crate::util::pasta_id_converter::CONVERTER;
use crate::util::syntaxhighlighter::html_highlight; use crate::util::syntaxhighlighter::html_highlight;
#[derive(Serialize, Deserialize, PartialEq, Eq)] #[derive(Serialize, Deserialize, PartialEq, Eq)]
@ -55,7 +55,7 @@ impl Pasta {
if ARGS.hash_ids { if ARGS.hash_ids {
to_hashids(self.id) to_hashids(self.id)
} else { } else {
to_animal_names(self.id) CONVERTER.to_names(self.id)
} }
} }

View file

@ -1,53 +0,0 @@
const ANIMAL_NAMES: &[&str] = &[
"ant", "eel", "mole", "sloth", "ape", "emu", "monkey", "snail", "bat", "falcon", "mouse",
"snake", "bear", "fish", "otter", "spider", "bee", "fly", "parrot", "squid", "bird", "fox",
"panda", "swan", "bison", "frog", "pig", "tiger", "camel", "gecko", "pigeon", "toad", "cat",
"goat", "pony", "turkey", "cobra", "goose", "pug", "turtle", "crow", "hawk", "rabbit", "viper",
"deer", "horse", "rat", "wasp", "dog", "jaguar", "raven", "whale", "dove", "koala", "seal",
"wolf", "duck", "lion", "shark", "worm", "eagle", "lizard", "sheep", "zebra",
];
pub fn to_animal_names(mut number: u64) -> String {
let mut result: Vec<&str> = Vec::new();
if number == 0 {
return ANIMAL_NAMES[0].parse().unwrap();
}
let mut power = 6;
loop {
let digit = number / ANIMAL_NAMES.len().pow(power) as u64;
if !(result.is_empty() && digit == 0) {
result.push(ANIMAL_NAMES[digit as usize]);
}
number -= digit * ANIMAL_NAMES.len().pow(power) as u64;
if power > 0 {
power -= 1;
} else if power == 0 || number == 0 {
break;
}
}
result.join("-")
}
pub fn to_u64(animal_names: &str) -> Result<u64, &str> {
let mut result: u64 = 0;
let animals: Vec<&str> = animal_names.split('-').collect();
let mut pow = animals.len();
for animal in animals {
pow -= 1;
let animal_index = ANIMAL_NAMES.iter().position(|&r| r == animal);
match animal_index {
None => return Err("Failed to convert animal name to u64!"),
Some(_) => {
result += (animal_index.unwrap() * ANIMAL_NAMES.len().pow(pow as u32)) as u64
}
}
}
Ok(result)
}

View file

@ -0,0 +1,98 @@
use std::fs;
use lazy_static::lazy_static;
use crate::args::ARGS;
const ANIMAL_NAMES: &[&str] = &[
"ant", "eel", "mole", "sloth", "ape", "emu", "monkey", "snail", "bat", "falcon", "mouse",
"snake", "bear", "fish", "otter", "spider", "bee", "fly", "parrot", "squid", "bird", "fox",
"panda", "swan", "bison", "frog", "pig", "tiger", "camel", "gecko", "pigeon", "toad", "cat",
"goat", "pony", "turkey", "cobra", "goose", "pug", "turtle", "crow", "hawk", "rabbit", "viper",
"deer", "horse", "rat", "wasp", "dog", "jaguar", "raven", "whale", "dove", "koala", "seal",
"wolf", "duck", "lion", "shark", "worm", "eagle", "lizard", "sheep", "zebra",
];
lazy_static!{
pub static ref CONVERTER: PastaIdConverter = PastaIdConverter::new();
}
/// Convert pasta IDs to names and vice versa
pub struct PastaIdConverter {
names: Vec<String>
}
impl PastaIdConverter {
pub fn new() -> Self {
let names;
if let Some(names_path) = &ARGS.custom_names {
let names_data = fs::read_to_string(names_path)
.expect("path for the names file should contain a names file");
names = names_data
.split('\n')
.map(ToOwned::to_owned)
.collect::<Vec<String>>();
} else {
names = ANIMAL_NAMES
.iter()
.copied()
.map(ToOwned::to_owned)
.collect();
}
Self { names }
}
pub fn to_names(&self, mut number: u64) -> String {
let mut result: Vec<&str> = Vec::new();
if number == 0 {
return self.names[0].parse().unwrap();
}
let mut power = 6;
loop {
let digit = number / self.names.len().pow(power) as u64;
if !(result.is_empty() && digit == 0) {
result.push(&self.names[digit as usize]);
}
number -= digit * self.names.len().pow(power) as u64;
if power > 0 {
power -= 1;
} else if power == 0 || number == 0 {
break;
}
}
result.join("-")
}
pub fn to_u64(&self, pasta_id: &str) -> Result<u64, &str> {
let mut result: u64 = 0;
let names: Vec<&str> = pasta_id.split('-').collect();
let mut pow = names.len();
for name in names {
pow -= 1;
let name_index = self.names.iter().position(|r| r == name);
match name_index {
None => return Err("Failed to convert animal name to u64!"),
Some(_) => {
result += (name_index.unwrap() * self.names.len().pow(pow as u32)) as u64
}
}
}
Ok(result)
}
}
impl Default for PastaIdConverter {
fn default() -> Self {
Self::new()
}
}