2022-12-10 02:10:14 +00:00
|
|
|
use clap::{Parser, Subcommand, Args};
|
2022-12-17 05:03:41 +00:00
|
|
|
//use std::io::Write as _;
|
|
|
|
use std::fmt::Write as _;
|
2022-12-10 02:10:14 +00:00
|
|
|
|
|
|
|
/// An alternative plugin store
|
|
|
|
#[derive(Parser, Debug, Clone)]
|
|
|
|
#[command(author, version, about, long_about = None, propagate_version = true)]
|
|
|
|
pub struct CliArgs {
|
|
|
|
/// Cache results for a period
|
|
|
|
#[arg(name = "cache", long)]
|
|
|
|
pub cache_duration: Option<i64>,
|
2023-12-31 01:43:05 +00:00
|
|
|
/// Local server port (default: 22252)
|
2023-07-01 00:21:24 +01:00
|
|
|
#[arg(name = "port", short, long)]
|
|
|
|
pub server_port: Option<u16>,
|
2022-12-10 02:10:14 +00:00
|
|
|
/// Storage adapter
|
|
|
|
#[command(subcommand)]
|
|
|
|
pub storage: StorageArgs,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl CliArgs {
|
|
|
|
pub fn get() -> Self {
|
|
|
|
Self::parse()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Subcommand, Debug, Clone)]
|
|
|
|
pub enum StorageArgs {
|
|
|
|
/// Use default storage settings (filesystem)
|
|
|
|
Default,
|
|
|
|
/// Use the filesystem
|
|
|
|
Filesystem(FilesystemArgs),
|
2022-12-17 05:03:41 +00:00
|
|
|
/// Use an existing online store
|
|
|
|
Proxy(ProxyArgs),
|
|
|
|
/// Use no storage system
|
|
|
|
Empty,
|
|
|
|
/// Combine multiple storages together
|
|
|
|
Merge(MergeArgs)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StorageArgs {
|
|
|
|
// A cursed syntax with super simple parsing for describing storage settings
|
|
|
|
pub fn from_descriptor(chars: &mut std::str::Chars) -> Result<Self, String> {
|
|
|
|
//let mut chars = descriptor.chars();
|
|
|
|
if let Some(char0) = chars.next() {
|
2023-07-01 00:21:24 +01:00
|
|
|
let desc = match char0 {
|
2022-12-17 05:03:41 +00:00
|
|
|
'd' | '_' => Self::Default,
|
|
|
|
'f' => Self::Filesystem(FilesystemArgs::from_descriptor(chars)?),
|
|
|
|
'p' => Self::Proxy(ProxyArgs::from_descriptor(chars)?),
|
|
|
|
'e' | ' ' => Self::Empty,
|
|
|
|
'm' | '+' => Self::Merge(MergeArgs::from_descriptor(chars)?),
|
|
|
|
c => return Err(format!("Unexpected char {}, expected a descriptor prefix from {{d f p e m}}", c)),
|
2023-07-01 00:21:24 +01:00
|
|
|
};
|
|
|
|
log::info!("Parsed descriptor as {}", desc.clone().to_descriptor());
|
|
|
|
Ok(desc)
|
2022-12-17 05:03:41 +00:00
|
|
|
} else {
|
|
|
|
Err(format!("Empty storage descriptor"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn to_descriptor(self) -> String {
|
|
|
|
match self {
|
|
|
|
Self::Default => "d".to_owned(),
|
|
|
|
Self::Filesystem(fs) => format!("f{}", fs.to_descriptor()),
|
|
|
|
Self::Proxy(px) => format!("p{}", px.to_descriptor()),
|
|
|
|
Self::Empty => "e".to_owned(),
|
|
|
|
Self::Merge(ls) => format!("m{}", ls.to_descriptor()),
|
|
|
|
}
|
|
|
|
}
|
2022-12-10 02:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Args, Debug, Clone)]
|
|
|
|
pub struct FilesystemArgs {
|
|
|
|
#[arg(name = "folder", default_value_t = {"./store".into()})]
|
|
|
|
pub root: String,
|
|
|
|
#[arg(name = "domain", default_value_t = {"http://localhost:22252".into()})]
|
|
|
|
pub domain_root: String,
|
|
|
|
#[arg(name = "stats", long)]
|
|
|
|
pub enable_stats: bool,
|
|
|
|
}
|
2022-12-17 05:03:41 +00:00
|
|
|
|
|
|
|
impl FilesystemArgs {
|
|
|
|
fn from_descriptor(chars: &mut std::str::Chars) -> Result<Self, String> {
|
|
|
|
if let Some(char1) = chars.next() {
|
|
|
|
if char1 != '{' {
|
|
|
|
return Err(format!("Expected {{, got {}", char1));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Err(format!("Filesystem descriptor too short"));
|
|
|
|
}
|
|
|
|
let mut root = None;
|
|
|
|
let mut domain = None;
|
|
|
|
let mut stats = false;
|
|
|
|
let mut buffer = Vec::<char>::new();
|
|
|
|
let mut for_variable: Option<String> = None;
|
|
|
|
let mut in_string = false;
|
|
|
|
for c in chars {
|
2023-07-01 00:21:24 +01:00
|
|
|
//println!("Handling char: '{}' (in_string? {}, root: {:?}, domain: {:?}, stats: {:?}, buf: {:?}, for_variable: {:?})", c, in_string, root, domain, stats, buffer, for_variable);
|
2022-12-17 05:03:41 +00:00
|
|
|
match c {
|
2023-07-01 00:21:24 +01:00
|
|
|
'}' => if !in_string {return
|
|
|
|
Ok(Self {
|
|
|
|
root: root.unwrap_or_else(|| "./store".into()),
|
|
|
|
domain_root: domain.unwrap_or_else(|| "http://localhost:22252".into()),
|
|
|
|
enable_stats: stats,
|
|
|
|
})
|
|
|
|
} else { buffer.push('}') },
|
|
|
|
'\"' => in_string = !in_string,
|
2022-12-17 05:03:41 +00:00
|
|
|
'=' => if !in_string {
|
|
|
|
let value: String = buffer.drain(..).collect();
|
|
|
|
if for_variable.is_some() {
|
|
|
|
return Err("Unexpected = in filesystem descriptor".to_owned());
|
|
|
|
} else {
|
|
|
|
for_variable = Some(value);
|
|
|
|
}
|
2023-07-01 00:21:24 +01:00
|
|
|
} else { buffer.push('=') },
|
2024-05-11 03:10:53 +01:00
|
|
|
',' => if !in_string {
|
2023-07-01 00:21:24 +01:00
|
|
|
let value: String = buffer.drain(..).collect();
|
2022-12-17 05:03:41 +00:00
|
|
|
if let Some(var) = for_variable.take() {
|
|
|
|
let var_trimmed = var.trim();
|
|
|
|
match &var_trimmed as &str {
|
|
|
|
"r" | "root" => root = Some(value),
|
|
|
|
"d" | "domain" => domain = Some(value),
|
|
|
|
"s" | "stats" => stats = value == "1" || value == "y",
|
|
|
|
v => return Err(format!("Unexpected variable name {} in filesystem descriptor", v)),
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Err("Unexpected , in filesystem descriptor".to_owned())
|
|
|
|
}
|
2023-07-01 00:21:24 +01:00
|
|
|
} else { buffer.push(',') }
|
2022-12-17 05:03:41 +00:00
|
|
|
c => buffer.push(c),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err("Unexpected end of descriptor".to_owned())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_descriptor(self) -> String {
|
2023-07-01 00:21:24 +01:00
|
|
|
format!("{{root=\"{}\",domain=\"{}\",stats={},}}", self.root, self.domain_root, self.enable_stats as u8)
|
2022-12-17 05:03:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Args, Debug, Clone)]
|
|
|
|
pub struct ProxyArgs {
|
|
|
|
/// Proxy offerings from another store
|
|
|
|
#[arg(name = "store", long, default_value_t = {"https://plugins.deckbrew.xyz".into()})]
|
|
|
|
pub proxy_store: String,
|
2024-05-11 03:10:53 +01:00
|
|
|
/// Proxy artifact (plugin download zips) urls
|
|
|
|
#[arg(name = "artifacts", short, long)]
|
|
|
|
pub intercept_artifacts: bool,
|
|
|
|
/// Proxy preview image urls
|
|
|
|
#[arg(name = "images", short, long)]
|
|
|
|
pub intercept_images: bool,
|
2024-05-11 03:45:52 +01:00
|
|
|
#[arg(name = "domain", short, long, default_value_t = {"http://localhost:22252".into()})]
|
2024-05-11 03:10:53 +01:00
|
|
|
pub domain_root: String,
|
2022-12-17 05:03:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ProxyArgs {
|
|
|
|
fn from_descriptor(chars: &mut std::str::Chars) -> Result<Self, String> {
|
|
|
|
if let Some(char1) = chars.next() {
|
|
|
|
if char1 != '{' {
|
|
|
|
return Err(format!("Expected {{, got {}", char1));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Err(format!("Proxy descriptor too short"));
|
|
|
|
}
|
|
|
|
let mut buffer = Vec::new();
|
2024-05-11 03:10:53 +01:00
|
|
|
let mut for_variable: Option<String> = None;
|
|
|
|
let mut in_string = false;
|
2023-07-01 00:21:24 +01:00
|
|
|
let mut escaped = false;
|
2024-05-11 03:10:53 +01:00
|
|
|
let mut proxy_store = None;
|
|
|
|
let mut intercept_artifacts = false;
|
|
|
|
let mut intercept_images = false;
|
|
|
|
let mut domain = None;
|
2022-12-17 05:03:41 +00:00
|
|
|
for c in chars {
|
|
|
|
match c {
|
2024-05-11 03:10:53 +01:00
|
|
|
'}' if !escaped && !in_string => {
|
2023-07-01 00:21:24 +01:00
|
|
|
return
|
|
|
|
Ok(Self {
|
2024-05-11 03:10:53 +01:00
|
|
|
proxy_store: if let Some(url) = proxy_store {
|
|
|
|
url
|
|
|
|
} else if buffer.is_empty() {
|
|
|
|
"https://plugins.deckbrew.xyz".into()
|
|
|
|
} else {
|
|
|
|
buffer.iter().collect()
|
|
|
|
},
|
|
|
|
intercept_artifacts,
|
|
|
|
intercept_images,
|
|
|
|
domain_root: domain.unwrap_or_else(|| "http://localhost:22252".into()),
|
2023-07-01 00:21:24 +01:00
|
|
|
})
|
|
|
|
}
|
2024-05-11 03:10:53 +01:00
|
|
|
'"' if !escaped => in_string = !in_string,
|
|
|
|
'=' if !escaped && !in_string => {
|
|
|
|
let value: String = buffer.drain(..).collect();
|
|
|
|
if for_variable.is_some() {
|
|
|
|
return Err("Unexpected = in filesystem descriptor".to_owned());
|
|
|
|
} else {
|
|
|
|
for_variable = Some(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
',' if !escaped && !in_string => {
|
|
|
|
let value: String = buffer.drain(..).collect();
|
|
|
|
if let Some(var) = for_variable.take() {
|
|
|
|
let var_trimmed = var.trim();
|
|
|
|
match &var_trimmed as &str {
|
|
|
|
"intercept" => {
|
|
|
|
intercept_images = true;
|
|
|
|
intercept_artifacts = true;
|
|
|
|
},
|
|
|
|
"img" | "images" | "intercept_images" => intercept_images = true,
|
|
|
|
"dl" | "downloads" | "artifacts" | "intercept_artifacts" => intercept_artifacts = true,
|
|
|
|
"url" | "store" | "proxy_store" => proxy_store = Some(value),
|
|
|
|
"d" | "domain" => domain = Some(value),
|
|
|
|
v => return Err(format!("Unexpected variable name {} in filesystem descriptor", v)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
'\\' if !escaped => escaped = true,
|
2023-07-01 00:21:24 +01:00
|
|
|
c => {
|
|
|
|
if escaped {
|
|
|
|
escaped = false;
|
2024-05-11 03:10:53 +01:00
|
|
|
//buffer.push('\\');
|
2023-07-01 00:21:24 +01:00
|
|
|
}
|
|
|
|
buffer.push(c)
|
|
|
|
},
|
2022-12-17 05:03:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
Err("Unexpected end of descriptor".to_owned())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_descriptor(self) -> String {
|
2024-05-11 03:10:53 +01:00
|
|
|
match (self.intercept_artifacts, self.intercept_images) {
|
|
|
|
(true, true) => format!("{{url=\"{}\",intercept,}}", self.proxy_store),
|
|
|
|
(true, false) => format!("{{url=\"{}\",intercept_artifacts,}}", self.proxy_store),
|
|
|
|
(false, true) => format!("{{url=\"{}\",intercept_images,}}", self.proxy_store),
|
|
|
|
(false, false) => format!("{{{}}}", self.proxy_store),
|
|
|
|
}
|
|
|
|
|
2022-12-17 05:03:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Args, Debug, Clone)]
|
|
|
|
pub struct MergeArgs {
|
|
|
|
/// Settings descriptor
|
|
|
|
pub settings: Vec<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl MergeArgs {
|
|
|
|
fn from_descriptor(chars: &mut std::str::Chars) -> Result<Self, String> {
|
|
|
|
if let Some(char1) = chars.next() {
|
|
|
|
if char1 != '[' {
|
|
|
|
return Err(format!("Expected [, got {}", char1));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return Err(format!("Merge descriptor too short"));
|
|
|
|
}
|
|
|
|
let mut others = Vec::new();
|
|
|
|
loop {
|
|
|
|
if let Some(char_n) = chars.next() {
|
|
|
|
if char_n != '(' {
|
|
|
|
return Err(format!("Expected (, got {}", char_n));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
others.push(StorageArgs::from_descriptor(chars)?.to_descriptor());
|
|
|
|
if let Some(char_n) = chars.next() {
|
|
|
|
if char_n != ')' {
|
|
|
|
return Err(format!("Expected ), got {}", char_n));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(c) = chars.next() {
|
|
|
|
match c {
|
|
|
|
',' => {},
|
|
|
|
']' => {
|
|
|
|
if others.len() < 2 {
|
|
|
|
return Err("Merge args too short (0-1 descriptors is useless!)".to_owned());
|
|
|
|
}
|
|
|
|
return Ok(Self {
|
|
|
|
settings: others,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
c => return Err(format!("Unexpected char {}, expected ] or ,", c))
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
Err("Unexpected end of descriptor".to_owned())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn to_descriptor(self) -> String {
|
|
|
|
let mut out = "[".to_owned();
|
|
|
|
for descriptor in self.settings {
|
|
|
|
write!(&mut out, "({})", descriptor).unwrap();
|
|
|
|
}
|
|
|
|
write!(&mut out, "]").unwrap();
|
|
|
|
out
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn generate_args(&self) -> Result<Vec<StorageArgs>, String> {
|
|
|
|
let mut results = Vec::with_capacity(self.settings.len());
|
|
|
|
for args in &self.settings {
|
|
|
|
results.push(StorageArgs::from_descriptor(&mut args.chars())?);
|
|
|
|
}
|
|
|
|
Ok(results)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn storage_descriptor() {
|
2024-05-11 03:10:53 +01:00
|
|
|
let descriptor = "f{root=\"\",domain=\"\",stats=0,}";
|
2022-12-17 05:03:41 +00:00
|
|
|
let parsed = StorageArgs::from_descriptor(&mut descriptor.chars());
|
|
|
|
parsed.expect("StorageArgs parse error");
|
|
|
|
let descriptor = "p{}";
|
|
|
|
let parsed = StorageArgs::from_descriptor(&mut descriptor.chars());
|
|
|
|
parsed.expect("StorageArgs parse error");
|
|
|
|
let descriptor = "m[(p{}),(d)]";
|
|
|
|
let parsed = StorageArgs::from_descriptor(&mut descriptor.chars());
|
|
|
|
parsed.expect("StorageArgs parse error");
|
|
|
|
let descriptor = "d";
|
|
|
|
let parsed = StorageArgs::from_descriptor(&mut descriptor.chars());
|
|
|
|
parsed.expect("StorageArgs parse error");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn filesys_descriptor() {
|
2024-05-11 03:10:53 +01:00
|
|
|
let descriptor = "{root=\"\",domain=\"\",stats=0,}";
|
2022-12-17 05:03:41 +00:00
|
|
|
let parsed = FilesystemArgs::from_descriptor(&mut descriptor.chars());
|
|
|
|
parsed.expect("FilesystemArgs parse error");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn proxy_descriptor() {
|
|
|
|
let descriptor = "{}";
|
|
|
|
let parsed = ProxyArgs::from_descriptor(&mut descriptor.chars());
|
|
|
|
parsed.expect("ProxyArgs parse error");
|
|
|
|
}
|
|
|
|
|
2024-05-11 03:10:53 +01:00
|
|
|
#[test]
|
|
|
|
fn proxy_descriptor_complex() {
|
|
|
|
let descriptor = "{url=\"https://plugins.example.com\", intercept, intercept_image,}";
|
|
|
|
let parsed = ProxyArgs::from_descriptor(&mut descriptor.chars());
|
|
|
|
parsed.expect("ProxyArgs parse error");
|
|
|
|
}
|
|
|
|
|
2022-12-17 05:03:41 +00:00
|
|
|
#[test]
|
|
|
|
fn merge_descriptor() {
|
|
|
|
let descriptor = "[(f{}),(p{}),( )]";
|
|
|
|
let parsed = MergeArgs::from_descriptor(&mut descriptor.chars());
|
|
|
|
parsed.expect("MergeArgs parse error");
|
|
|
|
}
|
|
|
|
}
|