diff --git a/bin-github/src/github/mod.rs b/bin-github/src/github/mod.rs index 88c9498d5..bc569dc4a 100644 --- a/bin-github/src/github/mod.rs +++ b/bin-github/src/github/mod.rs @@ -53,7 +53,7 @@ impl GitHub { println!("Local, Skipping commit for {command}"); return Ok(None); } - command!(["checkout", "dist"]); + command!(["checkout", "dist-f"]); command!([ "add", format!("commands/{}.yml", urlencoding::encode(command)).as_str() @@ -76,14 +76,14 @@ impl GitHub { println!("Local, Skipping commit for {ns}::{handler}"); return Ok(None); } - command!(["checkout", "dist"]); + command!(["checkout", "dist-f"]); command!(["add", format!("events/{ns}/{handler}.yml").as_str()]); command!([ "commit", "-m", format!("Update Event `{ns}::{handler}`").as_str() ]); - command!(["push", "origin", "dist"]); + command!(["push", "origin", "dist-f"]); Ok(None) } @@ -92,10 +92,10 @@ impl GitHub { println!("Local, Skipping commit for version"); return; } - command!(["checkout", "dist"]); + command!(["checkout", "dist-f"]); command!(["add", "version.txt"]); command!(["commit", "-m", "Update version"]); - command!(["push", "origin", "dist"]); + command!(["push", "origin", "dist-f"]); } } diff --git a/clients/rust/build.rs b/clients/rust/build.rs index c4077adb3..1d08d267b 100644 --- a/clients/rust/build.rs +++ b/clients/rust/build.rs @@ -17,13 +17,13 @@ pub fn main() { } let repo = Repository::open(&tmp).unwrap_or_else(|_| { git2::build::RepoBuilder::new() - .branch("dist") + .branch("dist-f") .clone("https://github.com/acemod/arma3-wiki", &tmp) .map_err(|e| format!("Failed to clone repository: {e}")) .unwrap() }); repo.find_remote("origin") - .and_then(|mut r| r.fetch(&["dist"], None, None)) + .and_then(|mut r| r.fetch(&["dist-f"], None, None)) .map_err(|e| format!("Failed to fetch remote: {e}")) .unwrap(); let fetch_head = repo diff --git a/clients/rust/src/functions.rs b/clients/rust/src/functions.rs new file mode 100644 index 000000000..a122fe27a --- /dev/null +++ b/clients/rust/src/functions.rs @@ -0,0 +1,42 @@ +use std::{collections::HashMap, fs::File}; + +use crate::model::Function; + +/// Packages of functions (e.g. from different mods) +pub struct Functions { + functions: HashMap>, +} + +impl Functions { + #[must_use] + pub const fn new(functions: HashMap>) -> Self { + Self { functions } + } + #[must_use] + pub fn get(&self, name: &str) -> Option<&Vec> { + self.functions.get(&name.to_lowercase()) + } + pub fn iter(&self) -> impl Iterator)> { + self.functions.iter() + } + /// Reads functions from a YAML file. + /// # Errors + /// Returns an error string if the file cannot be read or parsed as YAML. + pub fn from_file(file: File) -> Result, String> { + let functions: Vec = serde_yaml::from_reader(file).map_err(|e| format!("{e}"))?; + Ok(functions) + } + /// Reads functions from a YAML string. + /// # Errors + /// Returns an error string if the string cannot be read or parsed as YAML. + pub fn from_string(data: &str) -> Result, String> { + let functions: Vec = serde_yaml::from_str(data).map_err(|e| format!("{e}"))?; + Ok(functions) + } + /// Converts a vector of functions to a YAML string. + /// # Errors + /// Returns an error string if serialization to YAML fails. + pub fn to_string(functions: &Vec) -> Result { + serde_yaml::to_string(functions).map_err(|e| format!("{e}")) + } +} diff --git a/clients/rust/src/lib.rs b/clients/rust/src/lib.rs index 8419400c1..30f64431d 100644 --- a/clients/rust/src/lib.rs +++ b/clients/rust/src/lib.rs @@ -3,11 +3,13 @@ use std::{ }; use commands::Commands; +use functions::Functions; use git2::Repository; -use model::{Command, EventHandlerNamespace, ParsedEventHandler, Version}; +use model::{Command, EventHandlerNamespace, Function, ParsedEventHandler, Version}; use rust_embed::RustEmbed; pub mod commands; +pub mod functions; pub mod model; #[derive(RustEmbed)] @@ -22,6 +24,8 @@ pub struct Wiki { custom: Vec, /// Whether the wiki was just updated. updated: bool, + /// Function packages + functions: Functions, } impl Wiki { @@ -45,6 +49,11 @@ impl Wiki { self.updated } + #[must_use] + pub const fn functions(&self) -> &Functions { + &self.functions + } + #[must_use] pub fn load(force_pull: bool) -> Self { #[cfg(feature = "remote")] @@ -130,7 +139,7 @@ impl Wiki { repo } else { git2::build::RepoBuilder::new() - .branch("dist") + .branch("dist-f") .clone("https://github.com/acemod/arma3-wiki", &appdata) .map_err(|e| format!("Failed to clone repository: {e}"))? }; @@ -166,6 +175,25 @@ impl Wiki { } event_handlers.insert(*ns, handlers); } + let mut functions = HashMap::new(); + if let Ok(func_dir) = std::fs::read_dir(appdata.join("functions")) { + for entry in func_dir { + let Ok(entry) = entry else { + continue; + }; + let path = entry.path(); + let file_stem = path.file_stem().unwrap_or_default().to_string_lossy(); + if !path.is_file() || file_stem.is_empty() { + continue; + } + let file = std::fs::File::open(&path) + .map_err(|e| format!("Failed to open function file: {e}"))?; + let file_funcs: Vec = Functions::from_file(file).map_err(|e| { + format!("Failed to parse function file {}: {e}", path.display()) + })?; + functions.insert(file_stem.to_lowercase(), file_funcs); + } + } Ok(Self { version: Version::from_wiki( std::fs::read_to_string(appdata.join("version.txt")) @@ -177,6 +205,7 @@ impl Wiki { event_handlers, updated, custom: Vec::new(), + functions: Functions::new(functions), }) } @@ -188,6 +217,7 @@ impl Wiki { pub fn load_dist() -> Self { let mut commands = HashMap::new(); let mut event_handlers = HashMap::new(); + let mut functions = HashMap::new(); for entry in Asset::iter() { let path = entry.as_ref(); if path.starts_with("commands/") @@ -213,6 +243,23 @@ impl Wiki { .or_insert_with(Vec::new) .push(handler); } + } else if path.starts_with("functions/") { + let v_path = std::path::Path::new(path); + let file_stem = v_path.file_stem().unwrap_or_default().to_string_lossy(); + if file_stem.is_empty() + || !v_path + .extension() + .is_some_and(|ext| ext.eq_ignore_ascii_case("yml")) + { + continue; + } + let data = Asset::get(path).unwrap().data; + let file_funcs: Vec = Functions::from_string( + std::str::from_utf8(&data).unwrap() + ).unwrap_or_else(|e| { + panic!("Failed to parse function file {path}: {e}") + }); + functions.insert(file_stem.to_lowercase(), file_funcs); } } Self { @@ -226,12 +273,13 @@ impl Wiki { event_handlers, updated: false, custom: Vec::new(), + functions: Functions::new(functions), } } fn update_git(repo: &Repository) -> Result<(), String> { repo.find_remote("origin") - .and_then(|mut r| r.fetch(&["dist"], None, None)) + .and_then(|mut r| r.fetch(&["dist-f"], None, None)) .map_err(|e| format!("Failed to fetch remote: {e}"))?; let fetch_head = repo .find_reference("FETCH_HEAD") diff --git a/clients/rust/src/model/command.rs b/clients/rust/src/model/command.rs index 862b6bc55..47a4c1665 100644 --- a/clients/rust/src/model/command.rs +++ b/clients/rust/src/model/command.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; +#[allow(unused_imports, reason = "only used in wiki feature")] use super::{Locality, ParseError, Since, Syntax}; #[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] @@ -328,6 +329,7 @@ impl Command { Ok((command, errors)) } + #[allow(dead_code, reason = "only used in wiki feature")] fn get_cmd_name(name: &str) -> &str { match name { "!_a" => "!", diff --git a/clients/rust/src/model/function.rs b/clients/rust/src/model/function.rs new file mode 100644 index 000000000..6be863c86 --- /dev/null +++ b/clients/rust/src/model/function.rs @@ -0,0 +1,54 @@ +use super::{Param, Value}; + +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +#[serde(default)] +pub struct Function { + /// Function name, if known + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + name: Option, + /// Return type, if known + #[serde(default)] + #[serde(skip_serializing_if = "Option::is_none")] + ret: Option, + #[serde(default)] + params: Vec, + #[serde(default)] + example: String, +} +impl Function { + #[must_use] + pub const fn new( + name: Option, + ret: Option, + params: Vec, + example: String, + ) -> Self { + Self { + name, + ret, + params, + example, + } + } + #[must_use] + pub const fn name(&self) -> Option<&String> { + self.name.as_ref() + } + #[must_use] + pub const fn ret(&self) -> Option<&Value> { + self.ret.as_ref() + } + #[must_use] + pub fn params(&self) -> &[Param] { + &self.params + } + #[must_use] + pub fn param_get(&self, index: usize) -> Option<&Param> { + self.params.get(index) + } + #[must_use] + pub fn example(&self) -> &str { + &self.example + } +} diff --git a/clients/rust/src/model/mod.rs b/clients/rust/src/model/mod.rs index ab78d8161..92b1e365d 100644 --- a/clients/rust/src/model/mod.rs +++ b/clients/rust/src/model/mod.rs @@ -1,6 +1,7 @@ mod call; mod command; mod event_handler; +mod function; mod locality; mod param; mod since; @@ -11,6 +12,7 @@ mod version; pub use call::{Arg, Call}; pub use command::Command; pub use event_handler::{EventHandler, EventHandlerNamespace, ParsedEventHandler}; +pub use function::Function; pub use locality::Locality; pub use param::Param; pub use since::Since; diff --git a/clients/rust/src/model/param.rs b/clients/rust/src/model/param.rs index c99913b34..659eda2bc 100644 --- a/clients/rust/src/model/param.rs +++ b/clients/rust/src/model/param.rs @@ -1,7 +1,9 @@ use serde::{Deserialize, Serialize}; +#[allow(unused_imports, reason = "only used in wiki feature")] use crate::model::Version; +#[allow(unused_imports, reason = "only used in wiki feature")] use super::{ParseError, Since, Value}; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] diff --git a/clients/rust/src/model/syntax.rs b/clients/rust/src/model/syntax.rs index d8cd2ba93..d9d31cdf7 100644 --- a/clients/rust/src/model/syntax.rs +++ b/clients/rust/src/model/syntax.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; +#[allow(unused_imports, reason = "only used in wiki feature")] use super::{Call, Locality, Param, ParseError, Since, Value}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] diff --git a/clients/rust/src/model/value.rs b/clients/rust/src/model/value.rs index 12b3f99e8..a1feaecaf 100644 --- a/clients/rust/src/model/value.rs +++ b/clients/rust/src/model/value.rs @@ -79,6 +79,7 @@ pub enum Value { } // regex once cell +#[allow(dead_code, reason = "only used in wiki feature")] static REGEX_TYPE: OnceLock = OnceLock::new(); // static REGEX_ARRAY_IN_FORMAT: OnceLock = OnceLock::new();