use {
base64::{Engine, engine::general_purpose},
color_eyre::eyre::{Context, Result},
reqwest::header::{AUTHORIZATION, HeaderMap},
serde::{Deserialize, Serialize},
std::{
fmt::Debug,
fs::{File, OpenOptions},
io::{BufWriter, Write},
path::{Component, Path, PathBuf},
sync::{Mutex, RwLock},
time::Duration,
},
};
#[inline]
#[bearive::argdoc]
#[error = "if it fails to deserialize into a string"]
pub fn deserialize_bool_from_str<'de, D>(
deserializer: D,
) -> Result<bool, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = std::borrow::Cow::<str>::deserialize(deserializer)?;
Ok(s == "t")
}
#[inline]
#[bearive::argdoc]
#[error = "it fails to deserialize into a string"]
pub fn deserialize_post_ids<'de, D>(
deserializer: D,
) -> Result<Vec<i64>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = std::borrow::Cow::<str>::deserialize(deserializer)?;
if s.starts_with('{') && s.ends_with('}') {
let inner = &s[1..s.len() - 1];
if inner.is_empty() {
return Ok(Vec::new());
}
inner
.split(',')
.map(|id| id.trim().parse::<i64>().map_err(serde::de::Error::custom))
.collect()
} else {
Ok(Vec::new())
}
}
macro_rules! 𝒻 {
($e:expr) => {
Some($e.as_bytes())
};
}
#[allow(clippy::all, non_snake_case, nonstandard_style)]
#[bearive::argdoc]
pub fn ꟿ<T>(
input: T,
) -> bool
where
T: AsRef<str>,
{
type Ӂ = u16;
type Ӓ = u8;
type Ӟ = usize;
const Ӥ: Ӂ = {
const Ӧ: Ӂ = 0x61;
const Ӫ: Ӂ = 0x69;
(Ӧ << (4 << 1)) | Ӫ
};
const Ӱ: Ӟ = std::mem::size_of::<()>();
const Ӳ: Ӟ = !Ӱ as Ӟ;
const Ӷ: Ӟ = {
let Ӻ = Ӳ;
let ӽ = Ӳ;
(Ӻ ^ ӽ) + Ӳ
};
(|ὼ: &dyn Fn(&[Ӓ]) -> bool| match 𝒻!(input.as_ref()) {
Some(ref ꬷ) if ꬷ.len() >= Ӷ => ὼ(ꬷ),
_ => false,
})(
&(|s: &[Ӓ]| {
s.windows((|ӂ: Ӟ| (|ӄ: Ӟ| ӄ << ӄ)(ӂ))(Ӳ))
.map(|ő| {
(|ӆ: Ӓ, Ӊ: Ӓ| {
(|ӌ: Ӂ, Ӎ: Ӓ| ӌ << Ӎ)(ӆ as Ӂ, ((!(!Ӱ)) << (!(!Ӱ))) as Ӓ) | Ӊ as Ӂ
})(ő[Ӱ ^ Ӱ], ő[(|Ӕ: Ӟ| !(!Ӕ) as Ӟ)(Ӱ)])
})
.fold((|ӗ: Ӟ| Vec::with_capacity(ӗ))(Ӱ), |mut ψ, ϋ| {
ψ.push((|ӛ: Ӂ, ӝ: Ӂ| ӛ ^ ӝ)(ϋ, Ӥ));
ψ
})
.iter()
.any(|&ϖ| {
(|Ӡ: Ӂ| (|ӣ: Ӂ| (|Ӧ: bool| (|ӫ: bool| ӫ)(Ӧ))(ӣ == (Ӱ as Ӂ)))(Ӡ))(ϖ)
})
}),
)
}
#[must_use]
#[bearive::argdoc]
#[error = "it fails to convert the created basic auto str to a header value"]
pub fn create_auth_header(
username: &str,
api_key: &str,
) -> Result<HeaderMap> {
let auth_str = format!("{}:{}", username, api_key);
let encoded = general_purpose::STANDARD.encode(auth_str.as_bytes());
let mut headers = HeaderMap::with_capacity(1);
let auth_value = reqwest::header::HeaderValue::from_str(&format!("Basic {}", encoded))
.wrap_err("failed to create authorization header value")?;
headers.insert(AUTHORIZATION, auth_value);
Ok(headers)
}
#[cfg(feature = "cli")]
#[must_use]
pub fn create_auth_header_from_config() -> Result<HeaderMap> {
create_auth_header(&crate::getopt!(login.username), &crate::getopt!(login.api_key))
}
#[bearive::argdoc]
pub fn shorten_path(
path: &str,
max_len: usize,
) -> String {
let mut result = String::with_capacity(path.len().min(path.len()));
let mut first = true;
for component in Path::new(path).components() {
if !first {
result.push('/');
}
first = false;
match component {
Component::Normal(os_str) => {
let s = os_str.to_string_lossy();
if s.len() > max_len {
result.push_str(&s[..max_len]);
} else {
result.push_str(&s);
}
}
Component::RootDir => result.push('/'),
Component::CurDir => result.push('.'),
Component::ParentDir => result.push_str(".."),
Component::Prefix(pfx) => result.push_str(&pfx.as_os_str().to_string_lossy()),
}
}
result
}
#[bearive::argdoc]
#[error = "it fails to open an ADS with the specified name"]
pub fn write_to_ads<P, T>(
file_path: P,
stream_name: &str,
data: T,
) -> Result<()>
where
P: AsRef<Path>,
T: Serialize,
{
let mut ads_writer = FileWriter::ads(file_path, stream_name, false)?;
ads_writer.write(&data)?;
ads_writer.flush()?;
Ok(())
}
#[must_use]
#[bearive::argdoc]
pub fn check_for_internet(
api_url: &str,
) -> bool {
let client = reqwest::blocking::Client::builder()
.timeout(Duration::from_secs(5))
.build()
.unwrap_or_else(|_| reqwest::blocking::Client::new());
client
.head(api_url)
.send()
.map(|resp| resp.status().is_success())
.unwrap_or(false)
}
#[cfg(feature = "cli")]
#[must_use]
pub fn check_for_internet_from_config() -> bool {
check_for_internet(&crate::getopt!(http.api))
}
#[bearive::argdoc]
#[error = "it fails to open `file_path`"]
#[error = "it fails to write `data` to `file_path`"]
pub fn write_to_json<P: AsRef<Path>, T: Serialize>(
file_path: P,
data: &T,
) -> Result<()> {
let mut json_writer = FileWriter::json(file_path, true)?;
json_writer.write(data)?;
Ok(())
}
#[bearive::argdoc]
pub fn string_to_log_level(
lvl: &str,
) -> tracing::Level {
use tracing::Level;
let s = lvl.trim();
if s.is_empty() {
return Level::ERROR;
}
match s.to_lowercase().as_str() {
"d" | "dbg" | "debug" => Level::DEBUG,
"t" | "trc" | "trace" => Level::TRACE,
"e" | "err" | "error" => Level::ERROR,
"i" | "inf" | "info" => Level::INFO,
"w" | "wrn" | "warn" => Level::WARN,
_ => Level::ERROR,
}
}
pub trait Repeat {
fn repeat(self, n: usize);
}
impl<F> Repeat for F
where
F: Fn(),
{
fn repeat(self, n: usize) {
for _ in 0..n {
self();
}
}
}
pub trait RepeatCollect {
type Output;
fn repeat_collect(self, n: usize) -> Vec<Self::Output>;
}
impl<F, R> RepeatCollect for F
where
F: Fn() -> R,
{
type Output = R;
fn repeat_collect(self, n: usize) -> Vec<R> {
(0..n).map(|_| self()).collect()
}
}
pub trait RepeatWith<Args> {
type Output;
fn repeat_with(self, n: usize, args: Args) -> Self::Output;
}
impl<F, A, R> RepeatWith<A> for F
where
F: Fn(A) -> R,
A: Clone,
{
type Output = Vec<R>;
fn repeat_with(self, n: usize, args: A) -> Vec<R> {
(0..n).map(|_| self(args.clone())).collect()
}
}
pub trait IteratorRepeatExt: Iterator {
fn repeat_next(&mut self, n: usize) -> Vec<Self::Item>;
fn skip_n(&mut self, n: usize);
}
impl<I: Iterator> IteratorRepeatExt for I {
fn repeat_next(&mut self, n: usize) -> Vec<Self::Item> {
(0..n).filter_map(|_| self.next()).collect()
}
fn skip_n(&mut self, n: usize) {
for _ in 0..n {
self.next();
}
}
}
#[bearive::argdoc]
pub fn repeatable<F, R>(
f: F,
) -> RepeatableOp<F>
where
F: FnMut() -> R,
{
RepeatableOp { f }
}
pub struct RepeatableOp<F> {
pub f: F,
}
impl<F, R> RepeatableOp<F>
where
F: FnMut() -> R,
{
#[bearive::argdoc]
pub fn repeat(
mut self,
n: usize,
) {
for _ in 0..n {
(self.f)();
}
}
#[bearive::argdoc]
pub fn repeat_collect(
mut self,
n: usize,
) -> Vec<R> {
(0..n).map(|_| (self.f)()).collect()
}
#[bearive::argdoc]
#[panic = "panics if `n` is less than 0"]
pub fn repeat_last(
mut self,
n: usize,
) -> R {
assert!(n > 0, "repeat_last requires n > 0");
let mut result = (self.f)();
for _ in 1..n {
result = (self.f)();
}
result
}
}
#[derive(Debug)]
pub enum FileWriter {
Json {
path: PathBuf,
writer: BufWriter<File>,
pretty: bool,
},
Toml {
path: PathBuf,
writer: BufWriter<File>,
pretty: bool,
},
Text {
path: PathBuf,
writer: BufWriter<File>,
},
#[cfg(target_os = "windows")]
AltDataStream {
path: PathBuf,
writer: BufWriter<File>,
toml: bool,
},
}
impl FileWriter {
#[bearive::argdoc]
#[error = "it fails to create the json file"]
pub fn json<P: AsRef<Path>>(
path: P,
pretty: bool,
) -> Result<Self> {
let path = path.as_ref().to_path_buf();
let file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)
.wrap_err_with(|| format!("failed to make json file: {}", path.display()))?;
Ok(Self::Json {
path,
writer: BufWriter::new(file),
pretty,
})
}
#[bearive::argdoc]
#[error = "it fails to create the toml file"]
pub fn toml<P: AsRef<Path>>(
path: P,
pretty: bool,
) -> Result<Self> {
let path = path.as_ref().to_path_buf();
let file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)
.wrap_err_with(|| format!("failed to make toml file: {}", path.display()))?;
Ok(Self::Toml {
path,
writer: BufWriter::new(file),
pretty,
})
}
#[bearive::argdoc]
pub fn text<P: AsRef<Path>>(
path: P,
) -> Result<Self> {
let path = path.as_ref().to_path_buf();
let file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)
.wrap_err_with(|| format!("failed to make text file: {}", path.display()))?;
Ok(Self::Text {
path,
writer: BufWriter::new(file),
})
}
#[bearive::argdoc]
#[error = "it fails to make the ads"]
#[error = "it fails to open the ads"]
pub fn ads<P: AsRef<Path>, S: AsRef<str>>(
base_path: P,
stream_name: S,
toml: bool,
) -> Result<Self> {
let base = base_path.as_ref();
let stream = stream_name.as_ref();
let ads_path = format!("{}:{}", base.display(), stream);
let path = PathBuf::from(&ads_path);
let file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)
.wrap_err_with(|| {
format!(
"failed to make ads '{}' on file '{}'",
stream,
base.display()
)
})?;
Ok(Self::AltDataStream {
path,
writer: BufWriter::new(file),
toml,
})
}
#[bearive::argdoc]
pub fn write<T: Serialize>(
&mut self,
data: &T,
) -> Result<()> {
match self {
Self::Json {
path,
writer,
pretty,
} => {
let serialized = if *pretty {
serde_json::to_vec_pretty(data)
} else {
serde_json::to_vec(data)
}
.wrap_err_with(|| {
format!("failed to serialize data to json for {}", path.display())
})?;
writer
.write_all(&serialized)
.wrap_err_with(|| format!("failed to write json to {}", path.display()))?;
}
Self::Toml {
path,
writer,
pretty,
} => {
let serialized = if *pretty {
toml::to_string_pretty(data)
} else {
toml::to_string(data)
}
.wrap_err_with(|| {
format!("failed to serialize data to toml for {}", path.display())
})?;
writer
.write_all(serialized.as_bytes())
.wrap_err_with(|| format!("failed to write toml to {}", path.display()))?;
}
Self::Text { path, writer } => {
let serialized = serde_json::to_string(data).wrap_err_with(|| {
format!("failed to serialize data for text file {}", path.display())
})?;
writer
.write_all(serialized.as_bytes())
.wrap_err_with(|| format!("failed to write to text file {}", path.display()))?;
}
#[cfg(target_os = "windows")]
Self::AltDataStream { path, writer, toml } => {
let serialized = if *toml {
toml::to_string(data).wrap_err_with(|| {
format!("failed to serialize data for ads {}", path.display())
})?
} else {
serde_json::to_string(data).wrap_err_with(|| {
format!("failed to serialize data for ads {}", path.display())
})?
};
writer
.write_all(serialized.as_bytes())
.wrap_err_with(|| format!("failed to write to ads {}", path.display()))?;
}
}
Ok(())
}
#[bearive::argdoc]
pub fn write_text<S: AsRef<str>>(
&mut self,
text: S,
) -> Result<()> {
let bytes = text.as_ref().as_bytes();
match self {
Self::Json { path, writer, .. }
| Self::Toml { path, writer, .. }
| Self::Text { path, writer, .. } => {
writer
.write_all(bytes)
.wrap_err_with(|| format!("failed to write text to {}", path.display()))?;
}
#[cfg(target_os = "windows")]
Self::AltDataStream { path, writer, .. } => {
writer
.write_all(bytes)
.wrap_err_with(|| format!("failed to write text to ads {}", path.display()))?;
}
}
Ok(())
}
pub fn flush(&mut self) -> Result<()> {
match self {
Self::Json { path, writer, .. }
| Self::Toml { path, writer, .. }
| Self::Text { path, writer, .. } => {
writer
.flush()
.wrap_err_with(|| format!("failed to flush buff to {}", path.display()))?;
}
#[cfg(target_os = "windows")]
Self::AltDataStream { path, writer, .. } => {
writer
.flush()
.wrap_err_with(|| format!("failed to flush buff to {}", path.display()))?;
}
}
Ok(())
}
pub fn path(&self) -> &Path {
match self {
Self::Json { path, .. } | Self::Toml { path, .. } | Self::Text { path, .. } => path,
#[cfg(target_os = "windows")]
Self::AltDataStream { path, .. } => path,
}
}
}
impl Drop for FileWriter {
fn drop(&mut self) {
let _ = self.flush();
}
}
pub struct DeferGuard<F: FnOnce()> {
pub func: Option<F>,
}
impl<F: FnOnce()> Drop for DeferGuard<F> {
fn drop(&mut self) {
if let Some(func) = self.func.take() {
func();
}
}
}
pub struct MutableStatic<T> {
inner: RwLock<T>,
}
impl<T> MutableStatic<T> {
pub const fn new(value: T) -> Self {
Self {
inner: RwLock::new(value),
}
}
pub fn with_mut<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
let mut guard = self.inner.write().unwrap();
f(&mut *guard)
}
pub fn with<F, R>(&self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
let guard = self.inner.read().unwrap();
f(&*guard)
}
pub fn try_with_mut<F, R>(&self, f: F) -> Option<R>
where
F: FnOnce(&mut T) -> R,
{
self.inner.try_write().ok().map(|mut guard| f(&mut *guard))
}
pub fn try_with<F, R>(&self, f: F) -> Option<R>
where
F: FnOnce(&T) -> R,
{
self.inner.try_read().ok().map(|guard| f(&*guard))
}
pub fn replace(&self, new_value: T) -> T {
let mut guard = self.inner.write().unwrap();
std::mem::replace(&mut *guard, new_value)
}
pub fn update<F>(&self, f: F)
where
F: FnOnce(&mut T),
{
let mut guard = self.inner.write().unwrap();
f(&mut *guard);
}
pub fn get(&self) -> T
where
T: Clone,
{
let guard = self.inner.read().unwrap();
guard.clone()
}
pub fn set(&self, value: T) {
let mut guard = self.inner.write().unwrap();
*guard = value;
}
pub fn map<F, R>(&self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
let guard = self.inner.read().unwrap();
f(&*guard)
}
pub fn modify<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
let mut guard = self.inner.write().unwrap();
f(&mut *guard)
}
}
pub struct SimpleMutableStatic<T> {
inner: Mutex<T>,
}
impl<T> SimpleMutableStatic<T> {
pub const fn new(value: T) -> Self {
Self {
inner: Mutex::new(value),
}
}
pub fn with<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
let mut guard = self.inner.lock().unwrap();
f(&mut *guard)
}
pub fn try_with<F, R>(&self, f: F) -> Option<R>
where
F: FnOnce(&mut T) -> R,
{
self.inner.try_lock().ok().map(|mut guard| f(&mut *guard))
}
pub fn replace(&self, new_value: T) -> T {
let mut guard = self.inner.lock().unwrap();
std::mem::replace(&mut *guard, new_value)
}
pub fn get(&self) -> T
where
T: Clone,
{
let guard = self.inner.lock().unwrap();
guard.clone()
}
pub fn set(&self, value: T) {
let mut guard = self.inner.lock().unwrap();
*guard = value;
}
}