#![warn(missing_docs)]
pub mod addon;
pub mod lang;
pub mod modifications;
pub mod output;
pub mod pkg;
pub mod versions;
use std::{fmt::Display, str::FromStr};
use anyhow::anyhow;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
pub mod util {
use schemars::JsonSchema;
use serde::Deserialize;
pub fn yes_no(string: &str) -> Option<bool> {
match string {
"yes" => Some(true),
"no" => Some(false),
_ => None,
}
}
pub fn is_valid_identifier(id: &str) -> bool {
for c in id.chars() {
if !c.is_ascii() {
return false;
}
if c.is_ascii_punctuation() {
match c {
'_' | '-' | '.' => {}
_ => return false,
}
}
if c.is_ascii_whitespace() {
return false;
}
}
true
}
#[derive(Deserialize, Debug, Clone, JsonSchema)]
#[serde(untagged)]
pub enum DeserListOrSingle<T> {
Single(T),
List(Vec<T>),
}
impl<T> Default for DeserListOrSingle<T> {
fn default() -> Self {
Self::List(Vec::default())
}
}
impl<T: Clone> DeserListOrSingle<T> {
pub fn get_vec(&self) -> Vec<T> {
match &self {
Self::Single(val) => vec![val.clone()],
Self::List(list) => list.clone(),
}
}
pub fn merge(&mut self, other: Self) {
let mut self_vec = self.get_vec();
self_vec.extend(other.iter().cloned());
*self = Self::List(self_vec);
}
pub fn iter(&self) -> DeserListOrSingleIter<'_, T> {
match &self {
Self::Single(val) => {
DeserListOrSingleIter(DeserListOrSingleIterState::Single(Some(val)))
}
Self::List(list) => {
DeserListOrSingleIter(DeserListOrSingleIterState::List(list.iter()))
}
}
}
}
pub struct DeserListOrSingleIter<'a, T>(DeserListOrSingleIterState<'a, T>);
enum DeserListOrSingleIterState<'a, T> {
Single(Option<&'a T>),
List(std::slice::Iter<'a, T>),
}
impl<'a, T> Iterator for DeserListOrSingleIter<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
match &mut self.0 {
DeserListOrSingleIterState::Single(val) => val.take(),
DeserListOrSingleIterState::List(slice_iter) => slice_iter.next(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_id_validation() {
assert!(is_valid_identifier("hello"));
assert!(is_valid_identifier("Hello"));
assert!(is_valid_identifier("H3110"));
assert!(is_valid_identifier("hello-world"));
assert!(is_valid_identifier("hello_world"));
assert!(is_valid_identifier("hello.world"));
assert!(!is_valid_identifier("hello*world"));
assert!(!is_valid_identifier("hello\nworld"));
assert!(!is_valid_identifier("hello world"));
}
#[test]
fn test_deser_list_or_single_iter() {
let item = DeserListOrSingle::Single(7);
assert_eq!(item.iter().next(), Some(&7));
let item = DeserListOrSingle::List(vec![1, 2, 3]);
let mut iter = item.iter();
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&3));
}
}
}
pub mod later {
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
pub enum Later<T> {
#[default]
Empty,
Full(T),
}
impl<T> Later<T> {
pub fn new() -> Self {
Self::Empty
}
pub fn fill(&mut self, value: T) {
*self = Self::Full(value);
}
pub fn ensure_full(&mut self, f: impl Fn() -> T) {
if self.is_empty() {
self.fill(f());
}
}
pub fn is_empty(&self) -> bool {
matches!(self, Self::Empty)
}
pub fn is_full(&self) -> bool {
matches!(self, Self::Full(..))
}
pub fn get(&self) -> &T {
if let Self::Full(value) = self {
value
} else {
self.fail();
}
}
pub fn get_mut(&mut self) -> &mut T {
if let Self::Full(value) = self {
value
} else {
self.fail();
}
}
pub fn get_val(self) -> T {
if let Self::Full(value) = self {
value
} else {
self.fail();
}
}
pub fn into_option(self) -> Option<T> {
match self {
Self::Empty => None,
Self::Full(val) => Some(val),
}
}
fn fail(&self) -> ! {
panic!("Value in Later<T> does not exist");
}
}
impl<T: Clone> Later<T> {
pub fn get_clone(&self) -> T {
if let Self::Full(value) = self {
value.clone()
} else {
self.fail();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_later_fill() {
let mut later = Later::new();
later.fill(7);
later.get();
}
#[test]
#[should_panic(expected = "Value in Later<T> does not exist")]
fn test_later_fail() {
let later: Later<i32> = Later::new();
later.get();
}
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Side {
Client,
Server,
}
impl Side {
pub fn parse_from_str(string: &str) -> Option<Self> {
match string {
"client" => Some(Self::Client),
"server" => Some(Self::Server),
_ => None,
}
}
}
impl FromStr for Side {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse_from_str(s).ok_or(anyhow!("Not a valid side"))
}
}
impl Display for Side {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::Client => "client",
Self::Server => "server",
}
)
}
}