1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
use crate::consts::{LORE, RESPONSES};
use bottomify::bottom::{decode_string, encode_string};
use include_dir::{include_dir, Dir};
use rand::seq::SliceRandom;
use std::collections::HashMap;
use std::vec;
const FILES: Dir = include_dir!("src/copypastas");
pub fn parse_snowflake_from_env<T, F: Fn(u64) -> T>(key: &str, f: F) -> Option<T> {
std::env::var(key).ok().and_then(|v| v.parse().map(&f).ok())
}
pub fn parse_snowflakes_from_env<T, F: Fn(u64) -> T>(key: &str, f: F) -> Option<Vec<T>> {
std::env::var(key).ok().and_then(|gs| {
gs.split(',')
.map(|g| g.parse().map(&f))
.collect::<Result<Vec<_>, _>>()
.ok()
})
}
/*
* chooses a random element from an array
*/
fn random_choice<const N: usize>(arr: [&str; N]) -> String {
let mut rng = rand::thread_rng();
let resp = arr.choose(&mut rng).expect("couldn't choose random value!");
(*resp).to_string()
}
/*
* pub functions to get random elements
* from our consts
*/
pub fn get_random_response() -> String {
random_choice(RESPONSES)
}
pub fn get_random_lore() -> String {
random_choice(LORE)
}
// waiting for `round_char_boundary` to stabilize
pub fn floor_char_boundary(s: &str, index: usize) -> usize {
if index >= s.len() {
s.len()
} else {
let lower_bound = index.saturating_sub(3);
let new_index = s.as_bytes()[lower_bound..=index]
.iter()
.rposition(|&b| (b as i8) >= -0x40); // b.is_utf8_char_boundary
// Can be made unsafe but whatever
lower_bound + new_index.unwrap()
}
}
// waiting for `int_roundings` to stabilize
fn div_ceil(a: usize, b: usize) -> usize {
(a + b - 1) / b
}
/*
* splits a message into multiple parts so that
* it can fit discord's character limit
*/
fn split_msg(mut msg: String) -> Vec<String> {
const CHAR_LIMIT: usize = 2000;
let mut msgs = Vec::with_capacity(div_ceil(msg.len(), CHAR_LIMIT));
while msg.len() > CHAR_LIMIT {
msgs.push(msg.split_off(floor_char_boundary(&msg, CHAR_LIMIT)));
}
msgs
}
/*
* gets a random copypasta from include/
*/
pub fn get_copypasta(name: &str) -> Vec<String> {
let mut files: HashMap<&str, &str> = HashMap::new();
for file in FILES.files() {
let name = file.path().file_stem().unwrap().to_str().unwrap();
let contents = file.contents_utf8().unwrap();
// refer to files by their name w/o extension
files.insert(name, contents);
}
if files.contains_key(&name) {
let reply = files[name].to_string();
split_msg(reply)
} else {
vec![format!("couldn't find {name:?} in files")]
}
}
/*
* encodes a message into bottom
*/
pub fn bottom_encode(msg: &str) -> String {
encode_string(&msg)
}
/*
* decodes a bottom string into english
*/
pub fn bottom_decode(msg: &str) -> String {
let decoded = decode_string(&msg);
match decoded {
Ok(ret) => ret,
Err(why) => {
println!("couldn't decode {msg:?}! ({why:?})");
"couldn't decode that! sowwy 🥺".to_string()
}
}
}
/*
* converts celsius to fahrenheit
*/
pub fn celsius_to_fahrenheit(c: f64) -> f64 {
(c * (9.0 / 5.0)) + 32.0
}
/*
* converts fahrenheit to celsius
*/
pub fn fahrenheit_to_celsius(f: f64) -> f64 {
(f - 32.0) * (5.0 / 9.0)
}
|