{"id":13678206,"url":"https://github.com/BrettMayson/arma-rs","last_synced_at":"2025-04-29T12:34:07.398Z","repository":{"id":38020024,"uuid":"199932908","full_name":"BrettMayson/arma-rs","owner":"BrettMayson","description":null,"archived":false,"fork":false,"pushed_at":"2024-02-06T13:17:51.000Z","size":267,"stargazers_count":40,"open_issues_count":1,"forks_count":9,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-05-02T05:10:59.843Z","etag":null,"topics":["hacktoberfest-accepted"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BrettMayson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-07-31T21:42:12.000Z","updated_at":"2024-06-20T12:57:31.398Z","dependencies_parsed_at":"2024-06-20T12:57:28.146Z","dependency_job_id":null,"html_url":"https://github.com/BrettMayson/arma-rs","commit_stats":{"total_commits":153,"total_committers":6,"mean_commits":25.5,"dds":"0.28104575163398693","last_synced_commit":"b279b7b53c92df8a042f297ce012cd9bd0c76951"},"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrettMayson%2Farma-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrettMayson%2Farma-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrettMayson%2Farma-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BrettMayson%2Farma-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BrettMayson","download_url":"https://codeload.github.com/BrettMayson/arma-rs/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224173641,"owners_count":17268145,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacktoberfest-accepted"],"created_at":"2024-08-02T13:00:51.085Z","updated_at":"2024-11-11T20:31:12.302Z","avatar_url":"https://github.com/BrettMayson.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# arma-rs\n\n[Join the arma-rs Discord!](https://discord.gg/qXWUrrwy5d)\n[![codecov](https://codecov.io/gh/BrettMayson/arma-rs/branch/main/graph/badge.svg?token=A1H7SEZ434)](https://codecov.io/gh/BrettMayson/arma-rs)\n\nThe best way to make Arma 3 Extensions.\n\n## Usage\n\n```toml\n[dependencies]\narma-rs = \"1.11.10\"\n\n[lib]\nname = \"my_extension\"\ncrate-type = [\"cdylib\"]\n```\n\n### Hello World\n\n```rust\nuse arma_rs::{arma, Extension};\n\n#[arma]\nfn init() -\u003e Extension {\n    Extension::build()\n        .command(\"hello\", hello)\n        .command(\"welcome\", welcome)\n        .finish()\n}\n\npub fn hello() -\u003e \u0026'static str {\n    \"Hello\"\n}\n\npub fn welcome(name: String) -\u003e String {\n    format!(\"Welcome {}\", name)\n}\n```\n\n```sqf\n\"my_extension\" callExtension [\"hello\", []]; // Returns [\"Hello\", 0, 0]\n\"my_extension\" callExtension [\"welcome\", [\"John\"]]; // Returns [\"Welcome John\", 0, 0]\n```\n\n## Command Groups\n\nCommands can be grouped together, making your large projects much easier to manage.\n\n```rust\nuse arma_rs::{arma, Extension, Group};\n\n#[arma]\nfn init() -\u003e Extension {\n    Extension::build()\n        .group(\"hello\",\n            Group::new()\n                .command(\"english\", hello::english)\n                .group(\"english\",\n                    Group::new()\n                        .command(\"casual\", hello::english_casual)\n                )\n                .command(\"french\", hello::french),\n        )\n        .group(\"welcome\",\n            Group::new()\n                .command(\"english\", welcome::english)\n                .command(\"french\", welcome::french),\n        )\n        .finish()\n}\n\nmod hello {\n    pub fn english() -\u003e \u0026'static str {\n        \"Hello\"\n    }\n    pub fn english_casual() -\u003e \u0026'static str {\n        \"Hey\"\n    }\n    pub fn french() -\u003e \u0026'static str {\n        \"Bonjour\"\n    }\n}\n\nmod welcome {\n    pub fn english(name: String) -\u003e String {\n        format!(\"Welcome {}\", name)\n    }\n    pub fn french(name: String) -\u003e String {\n        format!(\"Bienvenue {}\", name)\n    }\n}\n```\n\nCommands groups are called by using the format `group:command`. You can nest groups as much as you want.\n\n```sqf\n\"my_extension\" callExtension [\"hello:english\", []]; // Returns [\"Hello\", 0, 0]\n\"my_extension\" callExtension [\"hello:english:casual\", []]; // Returns [\"Hey\", 0, 0]\n\"my_extension\" callExtension [\"hello:french\", []]; // Returns [\"Bonjour\", 0, 0]\n```\n\n## Callbacks\n\nExtension callbacks can be invoked anywhere in the extension by adding a variable of type `Context` to the start of a handler.\n\n```rust\nuse arma_rs::Context;\n\npub fn sleep(ctx: Context, duration: u64, id: String) {\n    std::thread::spawn(move || {\n        std::thread::sleep(std::time::Duration::from_secs(duration));\n        ctx.callback_data(\"example_timer\", \"done\", Some(id));\n    });\n}\n\npub fn group() -\u003e arma_rs::Group {\n    arma_rs::Group::new().command(\"sleep\", sleep)\n}\n```\n\n## Call Context\n\nSince Arma v2.11 additional context is provided each time the extension is called. This context can be accessed through the optional `ArmaCallContext` argument.\n\nSince Arma v2.18 the context is only requested from Arma when the functionh has `ArmaCallContext` as an argument.\n\n```rust\nuse arma_rs::{CallContext, CallContextStackTrace};\n\npub fn call_context(call_context: CallContext) -\u003e String {\n    format!(\n        \"{:?},{:?},{:?},{:?},{:?}\",\n        call_context.caller(),\n        call_context.source(),\n        call_context.mission(),\n        call_context.server(),\n        call_context.remote_exec_owner(),\n    )\n}\n\npub fn stack_trace(call_context: CallContextStackTrace) -\u003e String {\n    format!(\n        \"{:?}\\n{:?}\",\n        call_context.source(),\n        call_context.stack_trace()\n    )\n}\n\npub fn group() -\u003e arma_rs::Group {\n    arma_rs::Group::new()\n        .command(\"call_context\", call_context)\n        .command(\"stack_trace\", stack_trace)\n}\n```\n\n## Persistent State\n\nBoth the extension and command groups allow for type based persistent state values with at most one instance per type. These state values can then be accessed through the optional `Context` argument.\n\n### Global State\n\nExtension state is accessible from any command handler.\n\n```rust\nuse arma_rs::{arma, Context, ContextState, Extension};\n\nuse std::sync::atomic::{AtomicU32, Ordering};\n\n#[arma]\nfn init() -\u003e Extension {\n    Extension::build()\n        .command(\"counter_increment\", increment)\n        .state(AtomicU32::new(0))\n        .finish()\n}\n\npub fn increment(ctx: Context) -\u003e Result\u003c(), ()\u003e {\n    let Some(counter) = ctx.global().get::\u003cAtomicU32\u003e() else {\n        return Err(());\n    };\n    counter.fetch_add(1, Ordering::SeqCst);\n    Ok(())\n}\n```\n\n### Group State\n\nCommand group state is only accessible from command handlers within the same group.\n\n```rust\nuse arma_rs::{Context, ContextState, Extension};\n\nuse std::sync::atomic::{AtomicU32, Ordering};\n\npub fn increment(ctx: Context) -\u003e Result\u003c(), ()\u003e {\n    let Some(counter) = ctx.group().get::\u003cAtomicU32\u003e() else {\n        return Err(());\n    };\n    counter.fetch_add(1, Ordering::SeqCst);\n    Ok(())\n}\n\npub fn group() -\u003e arma_rs::Group {\n    arma_rs::Group::new()\n        .command(\"increment\", increment)\n        .state(AtomicU32::new(0))\n}\n```\n\n## Custom Types\n\nIf you're bringing your existing Rust library with your own types, you can easily define how they are converted to and from Arma.\n\n```rust\nuse arma_rs::{FromArma, IntoArma, Value, FromArmaError};\n\npub struct MemoryReport {\n    total: u64,\n    free: u64,\n    avail: u64,\n}\n\nimpl FromArma for MemoryReport {\n    fn from_arma(s: String) -\u003e Result\u003cSelf, FromArmaError\u003e {\n        let (total, free, avail) = \u003c(u64, u64, u64)\u003e::from_arma(s)?;\n        Ok(Self { total, free, avail })\n    }\n}\n\nimpl IntoArma for MemoryReport {\n    fn to_arma(\u0026self) -\u003e Value {\n        Value::Array(\n            vec![self.total, self.free, self.avail]\n                .into_iter()\n                .map(|v| v.to_string().to_arma())\n                .collect(),\n        )\n    }\n}\n```\n\n### Derive\n\nAlternatively you can derive these traits. Note that the derive and manual implementation examples slightly differ, as when deriving map like structs its represented as an hashmap rather than an array. For more information on data representation and attributes see: [FromArma](https://docs.rs/arma-rs/latest/arma_rs/derive.FromArma.html) and [IntoArma](https://docs.rs/arma-rs/latest/arma_rs/derive.IntoArma.html).\n\n```rust\nuse arma_rs::{FromArma, IntoArma};\n\n#[derive(FromArma, IntoArma)]\nstruct MemoryReport {\n    #[arma(to_string)]\n    total: u64,\n    #[arma(to_string)]\n    free: u64,\n    #[arma(to_string)]\n    avail: u64,\n}\n```\n\nDeriving is currently only supported for structs, this might change in the future.\n\n## Error Codes\n\nBy default arma-rs will only allow commands via `RvExtensionArgs`. Using `callExtension` with only a function name will return an empty string.\n\n```sqf\n\"my_extension\" callExtension \"hello:english\" // returns \"\"\n\"my_extension\" callExtension [\"hello:english\", []] // returns [\"Hello\", 0, 0]\n```\n\nThis behaviour can be changed by calling `.allow_no_args()` when building the extension. It is recommended not to use this, and to implement error handling instead.\n\n| Code | Description                                       |\n|------|---------------------------------------------------|\n|  0   | Success                                           |\n|  1   | Command not found                                 |\n|  2x  | Invalid argument count, x is received count       |\n|  3x  | Invalid argument type, x is argument position     |\n|  4   | Attempted to write a value larger than the buffer |\n|  9   | Application error, from using a Result            |\n\n### Error Examples\n\n```rust\nuse arma_rs::Context;\n\npub fn add(a: i32, b: i32) -\u003e i32 {\n    a + b\n}\n\npub fn overflow(ctx: Context) -\u003e String {\n    \"X\".repeat(ctx.buffer_len() + 1)\n}\n\npub fn should_error(error: bool) -\u003e Result\u003cString, String\u003e {\n  if error {\n    Err(String::from(\"told to error\"))\n  } else {\n    Ok(String::from(\"told to succeed\"))\n  }\n}\n```\n\n```sqf\n\"my_extension\" callExtension [\"add\", [1, 2]]; // Returns [\"3\", 0, 0]\n\"my_extension\" callExtension [\"sub\", [1, 2]]; // Returns [\"\", 1, 0]\n\"my_extension\" callExtension [\"add\", [1, 2, 3]]; // Returns [\"\", 23, 0], didn't expect 3 elements\n\"my_extension\" callExtension [\"add\", [1, \"two\"]]; // Returns [\"\", 31, 0], unable to parse the second argument\n\"my_extension\" callExtension [\"overflow\", []]; // Returns [\"\", 4, 0], the return size was larger than the buffer\n\"my_extension\" callExtension [\"should_error\", [true]]; // Returns [\"told to error\", 9, 0]\n\"my_extension\" callExtension [\"should_error\", [false]]; // Returns [\"told to succeed\", 0, 0]\n```\n\n## Testing\n\nTests can be created utilizing the `extension.call()` method.\n\n```rust,ignore\nmod tests {\n    #[test]\n    fn hello() {\n        let extension = init().testing();\n        let (output, _) = extension.call(\"hello:english\", None);\n        assert_eq!(output, \"hello\");\n    }\n\n    #[test]\n    fn welcome() {\n        let extension = init().testing();\n        let (output, _) =\n            extension.call(\"welcome:english\", Some(vec![\"John\".to_string()]));\n        assert_eq!(output, \"Welcome John\");\n    }\n\n    #[test]\n    fn sleep_1sec() {\n        let extension = Extension::build()\n            .group(\"timer\", super::group())\n            .finish()\n            .testing();\n        let (_, code) = extension.call(\n            \"timer:sleep\",\n            Some(vec![\"1\".to_string(), \"test\".to_string()]),\n        );\n        assert_eq!(code, 0);\n        let result = extension.callback_handler(\n            |name, func, data| {\n                assert_eq!(name, \"timer:sleep\");\n                assert_eq!(func, \"done\");\n                if let Some(Value::String(s)) = data {\n                    Result::Ok(s)\n                } else {\n                    Result::Err(\"Data was not a string\".to_string())\n                }\n            },\n            Duration::from_secs(2),\n        );\n        assert_eq!(Result::Ok(\"test\".to_string()), result);\n    }\n}\n```\n\n## Unit Loadout Array\n\narma-rs includes a [loadout module](https://docs.rs/arma-rs/latest/arma_rs/loadout/index.html) to assist with the handling of [Arma's Unit Loadout Array](https://community.bistudio.com/wiki/Unit_Loadout_Array).\n\n```rust\nuse arma_rs::{FromArma, loadout::{Loadout, InventoryItem, Weapon, Magazine}};\n\nlet l = r#\"[[],[],[],[\"U_Marshal\",[]],[],[],\"H_Cap_headphones\",\"G_Aviator\",[],[\"ItemMap\",\"ItemGPS\",\"\",\"ItemCompass\",\"ItemWatch\",\"\"]]\"#;\nlet mut loadout = Loadout::from_arma(l.to_string()).unwrap();\nloadout.set_secondary({\n    let mut weapon = Weapon::new(\"launch_B_Titan_short_F\".to_string());\n    weapon.set_primary_magazine(Magazine::new(\"Titan_AT\".to_string(), 1));\n    weapon\n});\nloadout.set_primary({\n    let mut weapon = Weapon::new(\"arifle_MXC_F\".to_string());\n    weapon.set_optic(\"optic_Holosight\".to_string());\n    weapon\n});\nlet uniform = loadout.uniform_mut();\nuniform.set_class(\"U_B_CombatUniform_mcam\".to_string());\nlet uniform_items = uniform.items_mut().unwrap();\nuniform_items.push(InventoryItem::new_item(\"FirstAidKit\".to_string(), 3));\nuniform_items.push(InventoryItem::new_magazine(\"30Rnd_65x39_caseless_mag\".to_string(), 5, 30));\n```\n\n## Common Rust Libraries\n\narma-rs supports some common Rust libraries.\nYou can enable their support by adding their name to the features of arma-rs.\n\n```toml\narma-rs = { version = \"1.8.0\", features = [\"chrono\"] }\n```\n\nPlease create an issue first if you would like to add support for a new library.\n\n### chrono\n\n[`crates.io`](https://crates.io/crates/chrono)\n\n#### chrono - Convert to Arma\n\n[`NaiveDateTime`](https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDateTime.html) and [`DateTime\u003cTimeZone\u003e`](https://docs.rs/chrono/latest/chrono/struct.DateTime.html) will be converted to [Arma's date array](https://community.bistudio.com/wiki/systemTimeUTC).\nThe timezone will always be converted to UTC.\n\n#### chrono - Convert From Arma\n\n[Arma's date array](https://community.bistudio.com/wiki/systemTimeUTC) can be converted to [`NaiveDateTime`](https://docs.rs/chrono/latest/chrono/naive/struct.NaiveDateTime.html).\n\n### uuid\n\n[`crates.io`](https://crates.io/crates/uuid)\n\n#### uuid - Convert To Arma\n\n[`Uuid`](https://docs.rs/uuid/latest/uuid/struct.Uuid.html) will be converted to a string.\n\n### serde_json\n\n[`crates.io`](https://crates.io/crates/serde_json)\n\n#### serde_json - Convert To Arma\n\nAny variant of [`serde_json::Value`](https://docs.serde.rs/serde_json/enum.Value.html) will be converted to the appropriate Arma type.\n\n## Building for x86 (32 Bit)\n\n```sh\nrustup toolchain install stable-i686-pc-windows-msvc\ncargo +stable-i686-pc-windows-msvc build\n```\n\n## Contributing\n\nPull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FBrettMayson%2Farma-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FBrettMayson%2Farma-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FBrettMayson%2Farma-rs/lists"}