diff --git a/config.toml b/config.toml index dd46551..ff43cf4 100644 --- a/config.toml +++ b/config.toml @@ -5,6 +5,6 @@ max_content_length="10MB" upload_path="./upload" [paste] -pet_names = { enabled = true, words = 2, separator = "-" } -random = { enabled = false, length = 8 } +random_url = { enabled = true, words = 2, separator = "-", type = "petname" } +#random_url = { enabled = true, length = 8, type = "alphanumeric" } default_extension = "txt" diff --git a/src/config.rs b/src/config.rs index d247661..f9b95e7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,3 +1,4 @@ +use crate::random::RandomURLConfig; use byte_unit::Byte; use config::{self, ConfigError}; use std::path::PathBuf; @@ -27,34 +28,12 @@ pub struct ServerConfig { /// Paste configuration. #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] pub struct PasteConfig { - /// Pet names configuration. - pub pet_names: PetNamesConfig, - /// Random string configuration. - pub random: RandomConfig, + /// Random URL configuration. + pub random_url: RandomURLConfig, /// Default file extension. pub default_extension: String, } -/// Pet names configuration. -#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] -pub struct PetNamesConfig { - /// Use pet names instead of original file names. - pub enabled: bool, - /// Count of words that pet name will include. - pub words: u8, - /// Separator between the words. - pub separator: String, -} - -/// Random string configuration. -#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] -pub struct RandomConfig { - /// Use random strings instead of original file names. - pub enabled: bool, - /// Length of the random string to generate. - pub length: usize, -} - impl Config { /// Parses the config file and returns the values. pub fn parse(file_name: &str) -> Result { diff --git a/src/file.rs b/src/file.rs index 7e115a8..2996bcd 100644 --- a/src/file.rs +++ b/src/file.rs @@ -1,5 +1,4 @@ use crate::config::Config; -use rand::{distributions::Alphanumeric, Rng}; use std::fs::File; use std::io::{Result as IoResult, Write}; @@ -7,12 +6,10 @@ use std::io::{Result as IoResult, Write}; /// /// - If `file_name` does not have an extension, it is replaced with [`default_extension`]. /// - If `file_name` is "-", it is replaced with "stdin". -/// - If [`pet_names.enabled`] is `true`, `file_name` is replaced with a pet name. -/// - If [`random.enabled`] is `true`, `file_name` is replaced with a random string. +/// - If [`random_url.enabled`] is `true`, `file_name` is replaced with a pet name or random string. /// /// [`default_extension`]: crate::config::PasteConfig::default_extension -/// [`pet_names.enabled`]: crate::config::PetNamesConfig::enabled -/// [`random.enabled`]: crate::config::RandomConfig::enabled +/// [`random_url.enabled`]: crate::random::RandomURLConfig::enabled pub fn save(mut file_name: &str, bytes: &[u8], config: &Config) -> IoResult { if file_name == "-" { file_name = "stdin"; @@ -20,37 +17,14 @@ pub fn save(mut file_name: &str, bytes: &[u8], config: &Config) -> IoResult { - if config.paste.pet_names.enabled { - path.set_file_name(petname::petname( - config.paste.pet_names.words, - &config.paste.pet_names.separator, - )); - path.set_extension(extension); - } else if config.paste.random.enabled { - path.set_file_name( - rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(config.paste.random.length) - .map(char::from) - .collect::(), - ); + if let Some(url) = config.paste.random_url.generate() { + path.set_file_name(url); path.set_extension(extension); } } None => { - if config.paste.pet_names.enabled { - path.set_file_name(petname::petname( - config.paste.pet_names.words, - &config.paste.pet_names.separator, - )); - } else if config.paste.random.enabled { - path.set_file_name( - rand::thread_rng() - .sample_iter(&Alphanumeric) - .take(config.paste.random.length) - .map(char::from) - .collect::(), - ); + if let Some(url) = config.paste.random_url.generate() { + path.set_file_name(url); } path.set_extension( infer::get(bytes) @@ -71,7 +45,7 @@ pub fn save(mut file_name: &str, bytes: &[u8], config: &Config) -> IoResult IoResult<()> { let mut config = Config::default(); config.server.upload_path = env::current_dir()?; - config.paste.pet_names = PetNamesConfig { + config.paste.random_url = RandomURLConfig { enabled: true, - words: 3, - separator: String::from("_"), + words: Some(3), + separator: Some(String::from("_")), + type_: RandomURLType::PetName, + ..RandomURLConfig::default() }; let file_name = save("test.txt", &[65, 66, 67], &config)?; assert_eq!("ABC", fs::read_to_string(&file_name)?); @@ -97,10 +73,12 @@ mod test { fs::remove_file(file_name)?; config.paste.default_extension = String::from("bin"); - config.paste.pet_names.enabled = false; - config.paste.random = RandomConfig { + config.paste.random_url.enabled = false; + config.paste.random_url = RandomURLConfig { enabled: true, - length: 10, + length: Some(10), + type_: RandomURLType::Alphanumeric, + ..RandomURLConfig::default() }; let file_name = save("random", &[120, 121, 122], &config)?; assert_eq!("xyz", fs::read_to_string(&file_name)?); @@ -113,7 +91,7 @@ mod test { ); fs::remove_file(file_name)?; - config.paste.random.enabled = false; + config.paste.random_url.enabled = false; let file_name = save("test.file", &[116, 101, 115, 116], &config)?; assert_eq!("test.file", &file_name); assert_eq!("test", fs::read_to_string(&file_name)?); diff --git a/src/lib.rs b/src/lib.rs index f98fe74..a714c94 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,12 @@ -//! oops is a file upload/pastebin service. +//! rustypaste is a minimal file upload/pastebin service. #![warn(missing_docs, clippy::unwrap_used)] /// Configuration file parser. pub mod config; +/// Random URL generator. +pub mod random; + /// Server routes. pub mod server; diff --git a/src/random.rs b/src/random.rs new file mode 100644 index 0000000..d72c798 --- /dev/null +++ b/src/random.rs @@ -0,0 +1,87 @@ +use rand::{distributions::Alphanumeric, Rng}; + +/// Random URL configuration. +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +pub struct RandomURLConfig { + /// Use a random name instead of original file names. + pub enabled: bool, + /// Count of words that pet name will include. + pub words: Option, + /// Separator between the words. + pub separator: Option, + /// Length of the random string to generate. + pub length: Option, + /// Type of the random URL. + #[serde(rename = "type")] + pub type_: RandomURLType, +} + +impl RandomURLConfig { + /// Generates and returns a random URL (if `enabled`). + pub fn generate(&self) -> Option { + if self.enabled { + Some(match self.type_ { + RandomURLType::PetName => petname::petname( + self.words.unwrap_or(2), + self.separator.as_deref().unwrap_or("-"), + ), + RandomURLType::Alphanumeric => rand::thread_rng() + .sample_iter(&Alphanumeric) + .take(self.length.unwrap_or(8)) + .map(char::from) + .collect::(), + }) + } else { + None + } + } +} + +/// Type of the random URL. +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum RandomURLType { + /// Generate a random pet name. + PetName, + /// Generate a random alphanumeric string. + Alphanumeric, +} + +impl Default for RandomURLType { + fn default() -> Self { + Self::PetName + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_generate_url() { + let random_config = RandomURLConfig { + enabled: true, + words: Some(3), + separator: Some(String::from("~")), + type_: RandomURLType::PetName, + ..RandomURLConfig::default() + }; + let random_url = random_config.generate().unwrap(); + assert_eq!(3, random_url.split("~").collect::>().len()); + + let random_config = RandomURLConfig { + enabled: true, + length: Some(21), + type_: RandomURLType::Alphanumeric, + ..RandomURLConfig::default() + }; + let random_url = random_config.generate().unwrap(); + assert_eq!(21, random_url.len()); + + let random_config = RandomURLConfig { + enabled: false, + ..RandomURLConfig::default() + }; + assert!(random_config.generate().is_none()); + } +}