use crate::emoji::named::*; use anyhow::Result; use mindus::data::DataRead; use mindus::{Schematic, Serializable}; use poise::serenity_prelude::*; use std::mem::MaybeUninit; use std::path::{Path, PathBuf}; pub struct Dq { arr: [MaybeUninit; N], front: u8, len: u8, } impl Dq { pub fn new(first: T) -> Self { let mut dq = Dq { arr: unsafe { MaybeUninit::<[MaybeUninit; N]>::uninit().assume_init() }, front: 0, len: 1, }; dq.arr[0].write(first); dq } pub fn first(&mut self) -> T { unsafe { self.arr.get_unchecked(self.front as usize).assume_init() } } pub fn push_front(&mut self, elem: T) { // sub 1 match self.front { 0 => self.front = N as u8 - 1, n => self.front = n - 1, } self.len += 1; unsafe { self.arr.get_unchecked_mut(self.front as usize).write(elem) }; } pub fn iter(&self) -> impl Iterator + '_ { self.arr .iter() .cycle() .skip(self.front as _) .take((self.len as usize).min(N)) .map(|x| unsafe { x.assume_init() }) } } #[poise::command(slash_command)] /// Find a schematic by file pub async fn file( c: super::Context<'_>, #[description = "schematic file name"] file: String, ) -> Result<()> { let Some((file, ch)) = files().find(|(x, _)| x.file_name().unwrap().to_string_lossy() == file) else { return c .say(format!("{CANCEL} not found")) .await .map(|_| ()) .map_err(Into::into); }; c.say(format!( "{RIGHT} https://discord.com/channels/925674713429184564/{ch}/{}", flake(&file.file_name().unwrap().to_string_lossy()) )) .await .map(|_| ()) .map_err(Into::into) } #[poise::command(slash_command)] /// Find a schematic in the repo pub async fn find( c: super::Context<'_>, #[description = "schematic name"] name: String, ) -> Result<()> { let mut stack = Dq::<_, 5>::new(( 0.0, Data { channel: 0, message: 0, }, )); c.defer().await?; for (elem, data) in schems() { let cmp = rust_fuzzy_search::fuzzy_compare(elem.tags.get("name").unwrap(), &name); if stack.first().0 < cmp { stack.push_front((cmp, data)); } } if stack.iter().filter(|&(n, _)| n > 0.5).count() == 0 { return c .say(format!("{CANCEL} not found")) .await .map(|_| ()) .map_err(Into::into); } c.say( stack .iter() .filter(|&(n, _)| n > 0.5) .map(|(_, Data { channel, message })| { format!( "{RIGHT} https://discord.com/channels/925674713429184564/{channel}/{message}" ) }) .intersperse("\n".to_string()) .fold(String::new(), |acc, x| acc + &x), ) .await .map(|_| ()) .map_err(Into::into) } #[derive(Copy, Clone)] pub struct Data { pub channel: u64, pub message: u64, } pub fn dir(x: u64) -> Option> { std::fs::read_dir( Path::new("repos") .join("DESIGN_IT") .join(super::SPECIAL[&x].d), ) .ok() .map(|x| x.filter_map(Result::ok).map(move |f| f.path())) } pub fn files() -> impl Iterator { super::SPECIAL .keys() .filter_map(|&ch| dir(ch).map(|x| (x, ch))) .flat_map(|(fs, channel)| fs.map(move |f| (f, channel))) } pub fn load(f: &Path) -> Schematic { let dat = std::fs::read(f).unwrap(); let mut dat = DataRead::new(&dat); Schematic::deserialize(&mut dat).unwrap() } pub fn schems() -> impl Iterator { files().map(|(f, channel)| { let ts = load(&f); let x = f.file_name().unwrap().to_string_lossy(); ( ts, Data { channel, message: flake(&x), }, ) }) } pub fn flake(x: &str) -> u64 { u64::from_str_radix(&x[..x.len() - 5], 16).unwrap() } #[poise::command(slash_command)] /// Search for a schematic in the repo pub async fn search( c: super::Context<'_>, #[description = "base64 of the schematic"] base64: Option, #[description = "msch of the schematic"] msch: Option, ) -> Result<()> { let s = match base64 .and_then(|s| Schematic::deserialize_base64(&s).ok()) .or(match msch { Some(x) => x.download().await.ok().and_then(|x| { let mut s = DataRead::new(&x); Schematic::deserialize(&mut s).ok() }), None => None, }) { Some(x) => x, None => return c.say("no schematic").await.map(|_| ()).map_err(Into::into), }; c.defer().await?; if let Some((_, Data { channel, message })) = schems().find(|(ts, _)| &s == ts) { return c .say(format!( "{RIGHT} https://discord.com/channels/925674713429184564/{channel}/{message}", )) .await .map(|_| ()) .map_err(Into::into); } c.say(format!("{CANCEL} not found")) .await .map(|_| ()) .map_err(Into::into) }