From 8e43d1b9058d4adebf9b61470d8fc17607403fa8 Mon Sep 17 00:00:00 2001 From: Agatha Rose Date: Tue, 23 Jun 2020 01:19:02 +0300 Subject: [PATCH] Move long commands to separate files --- src/commands/brainfuck.rs | 54 +++++ src/commands/define.rs | 40 ++++ src/{ => commands}/embed-docs.txt | 0 src/commands/embed.rs | 118 ++++++++++ src/commands/help.rs | 51 +++++ src/commands/mod.rs | 6 + src/commands/pinned.rs | 55 +++++ src/commands/ship.rs | 62 +++++ src/main.rs | 365 +----------------------------- src/utils.rs | 6 +- 10 files changed, 398 insertions(+), 359 deletions(-) create mode 100644 src/commands/brainfuck.rs create mode 100644 src/commands/define.rs rename src/{ => commands}/embed-docs.txt (100%) create mode 100644 src/commands/embed.rs create mode 100644 src/commands/help.rs create mode 100644 src/commands/mod.rs create mode 100644 src/commands/pinned.rs create mode 100644 src/commands/ship.rs diff --git a/src/commands/brainfuck.rs b/src/commands/brainfuck.rs new file mode 100644 index 0000000..dc3b442 --- /dev/null +++ b/src/commands/brainfuck.rs @@ -0,0 +1,54 @@ +use serenity::{ + framework::standard::{macros::command, Args, CommandError, CommandResult}, + model::channel::Message, + prelude::*, +}; + +// brainfuck interpreter +#[command] +#[aliases("bf", "brainfrick")] +fn brainfuck(ctx: &mut Context, message: &Message, args: Args) -> CommandResult { + use brainfrick::Brainfuck; + + let input = match args.rest().trim() { + "" => { + return Err(CommandError(s!("Called without input!"))); + } + v @ _ => v, + }; + let output = Brainfuck::execute(input); + + match output { + Ok(v) => { + let _ = message.channel_id.send_message(&ctx.http, |m| { + m.embed(|e| { + e.title("Brainfuck interpreter") + .description(format!( + "Input\n```brainfuck\n{}\n```\nOutput:\n```{}\n```", + input, v + )) + .author(|a| { + a.name(&message.author.name) + .icon_url(message.author.avatar_url().unwrap()) + }) + .colour(0xffd1dc) + }) + }); + } + Err(err) => { + let _ = message.channel_id.send_message(&ctx.http, |m| { + m.embed(|e| { + e.title("Brainfuck interpreter") + .description(format!("Error at:\n```\n{}:{}\n```", err.line(), err.col())) + .author(|a| { + a.name(&message.author.name) + .icon_url(message.author.avatar_url().unwrap()) + }) + .colour(0xff6961) + }) + }); + } + } + + Ok(()) +} diff --git a/src/commands/define.rs b/src/commands/define.rs new file mode 100644 index 0000000..e376416 --- /dev/null +++ b/src/commands/define.rs @@ -0,0 +1,40 @@ +use serenity::{ + framework::standard::{macros::command, Args, CommandError, CommandResult}, + model::channel::Message, + prelude::*, +}; + +// Urban Dictionary lookup +#[command] +#[aliases("what's this")] +fn define(ctx: &mut Context, message: &Message, args: Args) -> CommandResult { + let text: String = args.rest().trim().to_string(); + let defs = &urbandict::get_definitions(&text); + if !args.is_empty() { + match defs { + Err(_e) => { + return Err(CommandError(s!("Invalid query >w<"))); + } + Ok(v) => { + if !v.is_empty() { + let def = &v[0]; + let _ = message.channel_id.send_message(&ctx.http, |m| { + m.embed(|e| { + e.title(format!("Query: {}, Author: {}", text, def.author)) + .field( + "Definition: ", + def.definition.replace(|c| c == '[' || c == ']', ""), + false, + ) + .color(0xffd1dc) + }) + }); + } else { + return Err(CommandError(s!("No results!"))); + } + } + } + } + + Ok(()) +} diff --git a/src/embed-docs.txt b/src/commands/embed-docs.txt similarity index 100% rename from src/embed-docs.txt rename to src/commands/embed-docs.txt diff --git a/src/commands/embed.rs b/src/commands/embed.rs new file mode 100644 index 0000000..4b48344 --- /dev/null +++ b/src/commands/embed.rs @@ -0,0 +1,118 @@ +use serenity::{ + framework::standard::{macros::command, Args, CommandError, CommandResult}, + model::channel::Message, + prelude::*, +}; +#[command] +fn embed(ctx: &mut Context, message: &Message, args: Args) -> CommandResult { + use serde::Deserialize; + use serenity::utils::Colour; + use std::{fs, io::prelude::*}; + + #[derive(Deserialize)] + struct EmbedProperties { + author: Option<(String, String)>, + colour: Option, + description: Option, + fields: Option>, + footer: Option<(String, String)>, + image: Option, + thumbnail: Option, + timestamp: Option, + title: Option, + url: Option, + } + + // print documentation from src/embed-docs.txt + if s!(&args.rest().trim()) == "help" { + let mut file = fs::File::open("./src/commands/embed-docs.txt")?; + let mut help_string = String::new(); + file.read_to_string(&mut help_string)?; + + let _ = message.channel_id.say(&ctx.http, help_string); + + return Ok(()); + } + + let input_embed: EmbedProperties = match toml::from_str(&args.rest().trim()) { + Ok(v) => v, + Err(e) => { + return Err(CommandError(format!("Deserialization error: {:?}", e))); + } + }; + + let _ = message.channel_id.send_message(&ctx.http, |m| { + m.embed(|e| { + // Set embed author unless empty + if input_embed.author.is_some() { + let auth = input_embed.author.unwrap(); + e.author(|a| { + //assuming first array element is name and second is icon url + a.name(auth.0); + a.icon_url(auth.1); + + a + }); + } + + // Set embed colour unless empty + if input_embed.colour.is_some() { + e.color(Colour::new( + u32::from_str_radix(&input_embed.colour.unwrap(), 16) + .ok() + .unwrap_or(0x000000), + )); + } + + // Set embed description unless empty + if input_embed.description.is_some() { + e.description(input_embed.description.unwrap()); + } + // Set embed fields unless empty + if input_embed.fields.is_some() { + e.fields(input_embed.fields.unwrap()); + } + + // Set embed footer unless empty + if input_embed.footer.is_some() { + let foot = input_embed.footer.unwrap(); + e.footer(|f| { + //assuming first array element is name and second is icon url + f.text(foot.0); + f.icon_url(foot.1); + + f + }); + } + + if input_embed.image.is_some() { + e.image(input_embed.image.unwrap()); + } + + if input_embed.thumbnail.is_some() { + e.thumbnail(input_embed.thumbnail.unwrap()); + } + + if input_embed.timestamp.is_some() { + e.timestamp(input_embed.timestamp.unwrap()); + } + + if input_embed.title.is_some() { + e.title(input_embed.title.unwrap()); + } + + if input_embed.url.is_some() { + e.url(input_embed.url.unwrap()); + } + + e + }); + m + }); + + let _ = message + .channel_id + .say(&ctx.http, format!("Embed requested by: {}", message.author)); + + Ok(()) +} diff --git a/src/commands/help.rs b/src/commands/help.rs new file mode 100644 index 0000000..5fa6fac --- /dev/null +++ b/src/commands/help.rs @@ -0,0 +1,51 @@ +use serenity::{ + framework::standard::{macros::command, CommandResult}, + model::channel::Message, + prelude::*, +}; + +#[command] +fn help(ctx: &mut Context, message: &Message) -> CommandResult { + let _ = message.channel_id.send_message(&ctx.http, |m| { + m.embed(|e| { + e.title("Availble commands:") + .description("All commands are case-insensitive") + .fields(vec![ + ("owo!init", "Introduce me", true), + ("owo!ping", "Pong", true), + ("owo!sausage", "Sosig", true), + ("owo!help", "Help the fellow humanz!", true), + ("owo!info", "Show information about me!", true), + ( + "owo!what's this ``word``", + "Find a definition of word", + true, + ), + ( + "owo!embed ``[args]`` *OR* help", + "Create an embed from a Toml object", + true, + ), + ("owo!desc", "Display channel's topic", true), + ( + "owo!pinned ``num`` ````", + "Display channel's Nth pinned message. Channel name is optional", + true, + ), + ("owo!pfp ``@username``", "Post user's profile picture", true), + ("owo!brainfuck ``input``", "Execute input code", true), + ("owo!ship ``[names]``", "*Shipping intensifies*", true), + ("owo!headpat ``name``", "Headpat someone", true), + ("owo!owo ``text``", "owoify input text", true), + ("\u{200B}", "**Admin commands:**", false), + ("owo!halt", "Kill the bot process", true), + ("owo!status ``[args]``", "Sets the bot status", true), + ("owo!servers", "List the servers I'm in", true), + ("owo!host", "Display host info", true), + ]) + .color(0xffd1dc) + }) + }); + + Ok(()) +} diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..a33683a --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,6 @@ +pub mod brainfuck; +pub mod define; +pub mod embed; +pub mod help; +pub mod pinned; +pub mod ship; diff --git a/src/commands/pinned.rs b/src/commands/pinned.rs new file mode 100644 index 0000000..385d3eb --- /dev/null +++ b/src/commands/pinned.rs @@ -0,0 +1,55 @@ +use serenity::{ + framework::standard::{macros::command, Args, CommandError, CommandResult}, + model::{channel::Message, id::ChannelId}, + prelude::*, +}; + +// Prints Nth pinned message +#[command] +fn pinned(ctx: &mut Context, message: &Message, mut args: Args) -> CommandResult { + // defaults to latest pinned message if no args are provided + let mut idx = args.single::().unwrap_or(1); + // Makes pinned messages 1-indexed + if idx != 0 { + idx -= 1; + } + let target_channel = match args.single::() { + Ok(v) => v, + Err(_) => message.channel_id, + }; + let pinned = match target_channel.pins(&ctx.http) { + Ok(v) => v, + Err(e) => { + return Err(CommandError(s!(format!( + "Could not get pinned messages! Error: {}", + e + )))); + } + }; + if pinned.is_empty() { + return Err(CommandError(s!("No pinned messages found!"))); + } + if idx > pinned.len() - 1 { + return Err(CommandError(s!("Index out of bounds!"))); + } + + let _ = message.channel_id.send_message(&ctx.http, |m| { + m.embed(|e| { + e.title(format!("Pinned message #{}", idx + 1)) + .description(&pinned[idx].content) + .timestamp(&pinned[idx].timestamp); + e.author(|a| { + a.name(&pinned[idx].author.name) + .icon_url(&pinned[idx].author.avatar_url().unwrap()) + }); + e.colour(0xffd1dc); + if !&pinned[idx].attachments.is_empty() { + e.image(&pinned[idx].attachments[0].url); + } + + e + }) + }); + + Ok(()) +} diff --git a/src/commands/ship.rs b/src/commands/ship.rs new file mode 100644 index 0000000..41c2bd6 --- /dev/null +++ b/src/commands/ship.rs @@ -0,0 +1,62 @@ +use crate::utils::calculate_hash; +use rand::Rng; +use serenity::{ + framework::standard::{macros::command, Args, CommandResult}, + model::channel::Message, + prelude::*, +}; + +#[command] +fn ship(ctx: &mut Context, message: &Message, args: Args) -> CommandResult { + use rand::{rngs::StdRng, SeedableRng}; + + // Get input names + let names: String = args.rest().trim().to_string(); + // Calculate compatibility based on hash + let compat: u64 = StdRng::seed_from_u64(calculate_hash(&names)).gen_range(50, 100); + + // Initialize a bar to display compatibility percentage + let mut compbar = String::from("----------"); + compbar.insert_str((compat / 10) as usize, ":purple_heart:"); + + // Convert names to a Vec + let names = names + .split_whitespace() + .map(|x| x.to_owned()) + .collect::>(); + + // Concatenate names together + let shipname: Result = match names.len() { + 0 => Err(s!("Invalid input!")), + 1 => Ok(names[0].clone()), + _ => { + let mut first_halves = String::new(); + for name in &names[0..names.len() - 1] { + first_halves += &name[0..name.len() / 2]; + } + let first_halves = first_halves.as_str(); + let last_half = &names[names.len() - 1][(names.len() / 2) + 1..]; + + Ok(format!("{}{}", first_halves, last_half)) + } + }; + + if let Err(e) = shipname { + let _ = message.channel_id.say(&ctx.http, e); + } else { + let _ = message.channel_id.send_message(&ctx.http, |m| { + m.embed(|e| { + e.title(format!("Original names: {}", args.rest().trim())) + .description(format!( + "Ship name:\n**{}**\nCompatibility: **{}%**\n{}", + shipname.unwrap(), + compat, + compbar + )) + .color(0xffd1dc) + }) + }); + } + + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index 621c42d..e9dd8df 100755 --- a/src/main.rs +++ b/src/main.rs @@ -15,12 +15,7 @@ use serenity::{ Args, CheckResult, CommandError, CommandOptions, CommandResult, DispatchError, Reason, StandardFramework, }, - model::{ - channel::Message, - gateway::Ready, - id::{ChannelId, UserId}, - user::OnlineStatus, - }, + model::{channel::Message, gateway::Ready, id::UserId, user::OnlineStatus}, prelude::*, }; use std::{env, process, sync::Arc}; @@ -29,6 +24,9 @@ use std::{env, process, sync::Arc}; mod utils; use utils::*; +mod commands; +use commands::{brainfuck::*, define::*, embed::*, help::*, pinned::*, ship::*}; + struct Handler; struct ShardManagerContainer; @@ -76,7 +74,6 @@ lazy_static! { vec![UserId(254310746450690048), UserId(687740609703706630)]; } - fn main() { let mut client = Client::new(&env::var("DISCORD_TOKEN").expect("Invalid token"), Handler) .expect("Error creating client"); @@ -176,9 +173,9 @@ fn ping(ctx: &mut Context, message: &Message) -> CommandResult { let shard_manager = match data.get::() { Some(v) => v, None => { - return Err(CommandError( - s!("There was a problem getting the shard manager!"), - )) + return Err(CommandError(s!( + "There was a problem getting the shard manager!" + ))) } }; @@ -298,120 +295,6 @@ fn host(ctx: &mut Context, message: &Message) -> CommandResult { Ok(()) } -#[command] -fn embed(ctx: &mut Context, message: &Message, args: Args) -> CommandResult { - use serde::Deserialize; - use serenity::utils::Colour; - use std::{fs, io::prelude::*}; - - #[derive(Deserialize)] - struct EmbedProperties { - author: Option<(String, String)>, - colour: Option, - description: Option, - fields: Option>, - footer: Option<(String, String)>, - image: Option, - thumbnail: Option, - timestamp: Option, - title: Option, - url: Option, - } - - // print documentation from src/embed-docs.txt - if s!(&args.rest().trim()) == "help" { - let mut file = fs::File::open("./src/embed-docs.txt")?; - let mut help_string = String::new(); - file.read_to_string(&mut help_string)?; - - let _ = message.channel_id.say(&ctx.http, help_string); - - return Ok(()); - } - - let input_embed: EmbedProperties = match toml::from_str(&args.rest().trim()) { - Ok(v) => v, - Err(e) => { - return Err(CommandError(format!("Deserialization error: {:?}", e))); - } - }; - - let _ = message.channel_id.send_message(&ctx.http, |m| { - m.embed(|e| { - // Set embed author unless empty - if input_embed.author.is_some() { - let auth = input_embed.author.unwrap(); - e.author(|a| { - //assuming first array element is name and second is icon url - a.name(auth.0); - a.icon_url(auth.1); - - a - }); - } - - // Set embed colour unless empty - if input_embed.colour.is_some() { - e.color(Colour::new( - u32::from_str_radix(&input_embed.colour.unwrap(), 16) - .ok() - .unwrap_or(0x000000), - )); - } - - // Set embed description unless empty - if input_embed.description.is_some() { - e.description(input_embed.description.unwrap()); - } - // Set embed fields unless empty - if input_embed.fields.is_some() { - e.fields(input_embed.fields.unwrap()); - } - - // Set embed footer unless empty - if input_embed.footer.is_some() { - let foot = input_embed.footer.unwrap(); - e.footer(|f| { - //assuming first array element is name and second is icon url - f.text(foot.0); - f.icon_url(foot.1); - - f - }); - } - - if input_embed.image.is_some() { - e.image(input_embed.image.unwrap()); - } - - if input_embed.thumbnail.is_some() { - e.thumbnail(input_embed.thumbnail.unwrap()); - } - - if input_embed.timestamp.is_some() { - e.timestamp(input_embed.timestamp.unwrap()); - } - - if input_embed.title.is_some() { - e.title(input_embed.title.unwrap()); - } - - if input_embed.url.is_some() { - e.url(input_embed.url.unwrap()); - } - - e - }); - m - }); - - let _ = message - .channel_id - .say(&ctx.http, format!("Embed requested by: {}", message.author)); - - Ok(()) -} - // generate a random number using a keysmash as seed #[command] fn bottom_rng(ctx: &mut Context, message: &Message, mut args: Args) -> CommandResult { @@ -445,61 +328,6 @@ fn bottom_rng(ctx: &mut Context, message: &Message, mut args: Args) -> CommandRe Ok(()) } -#[command] -fn ship(ctx: &mut Context, message: &Message, args: Args) -> CommandResult { - use rand::{rngs::StdRng, SeedableRng}; - - // Get input names - let names: String = args.rest().trim().to_string(); - // Calculate compatibility based on hash - let compat: u64 = StdRng::seed_from_u64(calculate_hash(&names)).gen_range(50, 100); - - // Initialize a bar to display compatibility percentage - let mut compbar = String::from("----------"); - compbar.insert_str((compat / 10) as usize, ":purple_heart:"); - - // Convert names to a Vec - let names = names - .split_whitespace() - .map(|x| x.to_owned()) - .collect::>(); - - // Concatenate names together - let shipname: Result = match names.len() { - 0 => Err(s!("Invalid input!")), - 1 => Ok(names[0].clone()), - _ => { - let mut first_halves = String::new(); - for name in &names[0..names.len() - 1] { - first_halves += &name[0..name.len() / 2]; - } - let first_halves = first_halves.as_str(); - let last_half = &names[names.len() - 1][(names.len() / 2) + 1..]; - - Ok(format!("{}{}", first_halves, last_half)) - } - }; - - if let Err(e) = shipname { - let _ = message.channel_id.say(&ctx.http, e); - } else { - let _ = message.channel_id.send_message(&ctx.http, |m| { - m.embed(|e| { - e.title(format!("Original names: {}", args.rest().trim())) - .description(format!( - "Ship name:\n**{}**\nCompatibility: **{}%**\n{}", - shipname.unwrap(), - compat, - compbar - )) - .color(0xffd1dc) - }) - }); - } - - Ok(()) -} - #[command] #[aliases("pat")] fn headpat(ctx: &mut Context, message: &Message, args: Args) -> CommandResult { @@ -590,52 +418,6 @@ fn sausage(ctx: &mut Context, message: &Message) -> CommandResult { Ok(()) } -#[command] -fn help(ctx: &mut Context, message: &Message) -> CommandResult { - let _ = message.channel_id.send_message(&ctx.http, |m| { - m.embed(|e| { - e.title("Availble commands:") - .description("All commands are case-insensitive") - .fields(vec![ - ("owo!init", "Introduce me", true), - ("owo!ping", "Pong", true), - ("owo!sausage", "Sosig", true), - ("owo!help", "Help the fellow humanz!", true), - ("owo!info", "Show information about me!", true), - ( - "owo!what's this ``word``", - "Find a definition of word", - true, - ), - ( - "owo!embed ``[args]`` *OR* help", - "Create an embed from a Toml object", - true, - ), - ("owo!desc", "Display channel's topic", true), - ( - "owo!pinned ``num`` ````", - "Display channel's Nth pinned message. Channel name is optional", - true, - ), - ("owo!pfp ``@username``", "Post user's profile picture", true), - ("owo!brainfuck ``input``", "Execute input code", true), - ("owo!ship ``[names]``", "*Shipping intensifies*", true), - ("owo!headpat ``name``", "Headpat someone", true), - ("owo!owo ``text``", "owoify input text", true), - ("\u{200B}", "**Admin commands:**", false), - ("owo!halt", "Kill the bot process", true), - ("owo!status ``[args]``", "Sets the bot status", true), - ("owo!servers", "List the servers I'm in", true), - ("owo!host", "Display host info", true), - ]) - .color(0xffd1dc) - }) - }); - - Ok(()) -} - #[command] fn info(ctx: &mut Context, message: &Message, args: Args) -> CommandResult { if !args.is_empty() { @@ -660,41 +442,6 @@ fn info(ctx: &mut Context, message: &Message, args: Args) -> CommandResult { Ok(()) } -// Urban Dictionary lookup -#[command] -#[aliases("what's this")] -fn define(ctx: &mut Context, message: &Message, args: Args) -> CommandResult { - let text: String = args.rest().trim().to_string(); - let defs = &urbandict::get_definitions(&text); - if !args.is_empty() { - match defs { - Err(_e) => { - return Err(CommandError(s!("Invalid query >w<"))); - } - Ok(v) => { - if !v.is_empty() { - let def = &v[0]; - let _ = message.channel_id.send_message(&ctx.http, |m| { - m.embed(|e| { - e.title(format!("Query: {}, Author: {}", text, def.author)) - .field( - "Definition: ", - def.definition.replace(|c| c == '[' || c == ']', ""), - false, - ) - .color(0xffd1dc) - }) - }); - } else { - return Err(CommandError(s!("No results!"))); - } - } - } - } - - Ok(()) -} - #[command] fn pfp(ctx: &mut Context, message: &Message) -> CommandResult { // Get username from first mention, otherwise use current username @@ -764,101 +511,3 @@ fn desc(ctx: &mut Context, message: &Message) -> CommandResult { Ok(()) } - -// Prints Nth pinned message -#[command] -fn pinned(ctx: &mut Context, message: &Message, mut args: Args) -> CommandResult { - // defaults to latest pinned message if no args are provided - let mut idx = args.single::().unwrap_or(1); - // Makes pinned messages 1-indexed - if idx != 0 { - idx -= 1; - } - let target_channel = match args.single::() { - Ok(v) => v, - Err(_) => message.channel_id, - }; - let pinned = match target_channel.pins(&ctx.http) { - Ok(v) => v, - Err(e) => { - return Err(CommandError( - s!(format!("Could not get pinned messages! Error: {}", e)), - )); - } - }; - if pinned.is_empty() { - return Err(CommandError(s!("No pinned messages found!"))); - } - if idx > pinned.len() - 1 { - return Err(CommandError(s!("Index out of bounds!"))); - } - - let _ = message.channel_id.send_message(&ctx.http, |m| { - m.embed(|e| { - e.title(format!("Pinned message #{}", idx + 1)) - .description(&pinned[idx].content) - .timestamp(&pinned[idx].timestamp); - e.author(|a| { - a.name(&pinned[idx].author.name) - .icon_url(&pinned[idx].author.avatar_url().unwrap()) - }); - e.colour(0xffd1dc); - if !&pinned[idx].attachments.is_empty() { - e.image(&pinned[idx].attachments[0].url); - } - - e - }) - }); - - Ok(()) -} - -// brainfuck interpreter -#[command] -#[aliases("bf", "brainfrick")] -fn brainfuck(ctx: &mut Context, message: &Message, args: Args) -> CommandResult { - use brainfrick::Brainfuck; - - let input = match args.rest().trim() { - "" => { - return Err(CommandError(s!("Called without input!"))); - } - v @ _ => v, - }; - let output = Brainfuck::execute(input); - - match output { - Ok(v) => { - let _ = message.channel_id.send_message(&ctx.http, |m| { - m.embed(|e| { - e.title("Brainfuck interpreter") - .description(format!( - "Input\n```brainfuck\n{}\n```\nOutput:\n```{}\n```", - input, v - )) - .author(|a| { - a.name(&message.author.name) - .icon_url(message.author.avatar_url().unwrap()) - }) - .colour(0xffd1dc) - }) - }); - } - Err(err) => { - let _ = message.channel_id.send_message(&ctx.http, |m| { - m.embed(|e| { - e.title("Brainfuck interpreter") - .description(format!("Error at:\n```\n{}:{}\n```", err.line(), err.col())) - .author(|a| { - a.name(&message.author.name) - .icon_url(message.author.avatar_url().unwrap()) - }) - .colour(0xff6961) - }) - }); - } - } - - Ok(()) -} diff --git a/src/utils.rs b/src/utils.rs index 28c8fc6..e1a2bf1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,9 @@ -use std::{collections::hash_map::DefaultHasher, hash::{Hasher, Hash}}; +use std::{ + collections::hash_map::DefaultHasher, + hash::{Hash, Hasher}, +}; +// shorter version of .to_string() macro_rules!s( ( $e:expr ) => ( ($e).to_string() ) ); // Calculates hash of a type that implements Hash