This commit is contained in:
2026-04-13 08:04:43 -03:00
parent 0486f2a285
commit 3a5a7a17a3
71 changed files with 9385 additions and 0 deletions

613
vendor/arma-rs/tests/derive.rs vendored Normal file
View File

@@ -0,0 +1,613 @@
mod derive {
use arma_rs::{FromArma, FromArmaError, IntoArma, Value};
fn sort_value_array(value: &mut Value) -> &Value {
if let Value::Array(values) = value {
values.sort_by(|a, b| a.partial_cmp(b).unwrap());
}
value
}
#[derive(Debug, PartialEq, Default)]
enum ValueStringImpl {
#[default]
Even,
Odd,
}
impl std::fmt::Display for ValueStringImpl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Even => write!(f, "even"),
Self::Odd => write!(f, "odd"),
}
}
}
impl std::str::FromStr for ValueStringImpl {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.chars().count() % 2 {
0 => Ok(Self::Even),
_ => Ok(Self::Odd),
}
}
}
#[test]
#[cfg(not(miri))]
fn compile() {
let tests = trybuild::TestCases::new();
tests.compile_fail("tests/derive/*fail*.rs");
tests.pass("tests/derive/*pass*.rs");
}
mod map {
use super::*;
#[test]
fn derive() {
#[derive(FromArma, IntoArma, Debug, PartialEq)]
struct DeriveTest {
first: String,
second: bool,
}
let serialized = DeriveTest {
first: "first".to_string(),
second: true,
};
let deserialized = Value::Array(vec![
Value::Array(vec![
Value::String("first".to_string()),
Value::String("first".to_string()),
]),
Value::Array(vec![
Value::String("second".to_string()),
Value::Boolean(true),
]),
]);
assert_eq!(sort_value_array(&mut serialized.to_arma()), &deserialized);
assert_eq!(
DeriveTest::from_arma(deserialized.to_string()),
Ok(serialized)
);
}
#[test]
fn transparent() {
#[derive(FromArma, IntoArma, Debug, PartialEq)]
#[arma(transparent)]
struct DeriveTest {
expected: String,
}
let serialized = DeriveTest {
expected: "expected".to_string(),
};
let deserialized = Value::String("expected".to_string());
assert_eq!(serialized.to_arma(), deserialized);
assert_eq!(
DeriveTest::from_arma(deserialized.to_string()),
Ok(serialized)
);
}
#[test]
fn from_str_field() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest {
#[arma(from_str)]
expected: ValueStringImpl,
}
let input = Value::Array(vec![Value::Array(vec![
Value::String("expected".to_string()),
Value::String("odd".to_string()),
])]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest {
expected: ValueStringImpl::Odd,
})
);
}
#[test]
fn to_string_field() {
#[derive(IntoArma, Debug, PartialEq)]
struct DeriveTest {
#[arma(to_string)]
expected: ValueStringImpl,
}
assert_eq!(
DeriveTest {
expected: ValueStringImpl::Odd,
}
.to_arma(),
Value::Array(vec![Value::Array(vec![
Value::String("expected".to_string()),
Value::String("odd".to_string()),
])])
);
}
#[test]
fn from_str_default_field() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest {
#[arma(from_str, default)]
expected: ValueStringImpl,
}
let input = Value::Array(vec![]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest {
expected: ValueStringImpl::default(),
})
);
}
#[test]
fn default() {
#[derive(FromArma, Default, Debug, PartialEq)]
#[arma(default)]
struct DeriveTest {
first: String,
second: bool,
}
let input = Value::Array(vec![]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest::default())
);
let input = Value::Array(vec![Value::Array(vec![
Value::String("first".to_string()),
Value::String("first".to_string()),
])]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest {
first: "first".to_string(),
..DeriveTest::default()
})
);
let input = Value::Array(vec![Value::Array(vec![
Value::String("second".to_string()),
Value::Boolean(true),
])]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest {
second: true,
..DeriveTest::default()
})
);
let input = Value::Array(vec![
Value::Array(vec![
Value::String("first".to_string()),
Value::String("first".to_string()),
]),
Value::Array(vec![
Value::String("second".to_string()),
Value::Boolean(true),
]),
]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest {
first: "first".to_string(),
second: true
})
);
}
#[test]
fn default_field() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest {
first: String,
#[arma(default)]
second: bool,
}
let input = Value::Array(vec![Value::Array(vec![
Value::String("first".to_string()),
Value::String("first".to_string()),
])]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest {
first: "first".to_string(),
second: false
})
);
let input = Value::Array(vec![
Value::Array(vec![
Value::String("first".to_string()),
Value::String("first".to_string()),
]),
Value::Array(vec![
Value::String("second".to_string()),
Value::Boolean(true),
]),
]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest {
first: "first".to_string(),
second: true
})
);
}
#[test]
fn default_field_precedence() {
#[derive(FromArma, Debug, PartialEq)]
#[arma(default)]
struct DeriveTest {
first: String,
#[arma(default)]
second: bool,
}
impl Default for DeriveTest {
fn default() -> Self {
Self {
first: "first".to_string(),
second: true,
}
}
}
let input = Value::Array(vec![]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest {
first: "first".to_string(),
second: false
})
);
}
#[test]
fn error_missing() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest {
_expected: String,
}
let input = Value::Array(vec![]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Err(FromArmaError::MissingField("_expected".to_string()))
);
}
#[test]
fn error_unknown() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest {
_expected: String,
}
let input = Value::Array(vec![
Value::Array(vec![
Value::String("_expected".to_string()),
Value::String("_expected".to_string()),
]),
Value::Array(vec![
Value::String("unknown".to_string()),
Value::String("unknown".to_string()),
]),
]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Err(FromArmaError::UnknownField("unknown".to_string()))
);
}
#[test]
fn error_duplicate() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest {
_expected: String,
}
let input = Value::Array(vec![
Value::Array(vec![
Value::String("_expected".to_string()),
Value::String("first".to_string()),
]),
Value::Array(vec![
Value::String("_expected".to_string()),
Value::String("second".to_string()),
]),
]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Err(FromArmaError::DuplicateField("_expected".to_string()))
);
}
#[test]
fn default_error_unknown() {
#[derive(FromArma, Default, Debug, PartialEq)]
#[arma(default)]
struct DeriveTest {
_expected: String,
}
let input = Value::Array(vec![Value::Array(vec![
Value::String("unknown".to_string()),
Value::String("unknown".to_string()),
])]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Err(FromArmaError::UnknownField("unknown".to_string()))
);
}
#[test]
fn default_field_error_missing() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest {
_expected: String,
#[arma(default)]
_default: String,
}
let input = Value::Array(vec![]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Err(FromArmaError::MissingField("_expected".to_string()))
);
}
}
mod tuple {
use super::*;
#[test]
fn derive() {
#[derive(FromArma, IntoArma, Debug, PartialEq)]
struct DeriveTest(String, bool);
let serialized = DeriveTest("first".to_string(), true);
let deserialized = Value::Array(vec![
Value::String("first".to_string()),
Value::Boolean(true),
]);
assert_eq!(serialized.to_arma(), deserialized);
assert_eq!(
DeriveTest::from_arma(deserialized.to_string()),
Ok(serialized)
);
}
#[test]
fn from_string_field() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest(String, #[arma(from_str)] ValueStringImpl);
let input = Value::Array(vec![
Value::String("first".to_string()),
Value::String("odd".to_string()),
]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest("first".to_string(), ValueStringImpl::Odd))
);
}
#[test]
fn to_string_field() {
#[derive(IntoArma, Debug, PartialEq)]
struct DeriveTest(String, #[arma(to_string)] ValueStringImpl);
assert_eq!(
DeriveTest("first".to_string(), ValueStringImpl::Odd).to_arma(),
Value::Array(vec![
Value::String("first".to_string()),
Value::String("odd".to_string()),
])
);
}
#[test]
fn from_str_default_field() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest(String, #[arma(from_str, default)] ValueStringImpl);
let input = Value::Array(vec![Value::String("first".to_string())]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest("first".to_string(), ValueStringImpl::default()))
);
}
#[test]
fn default() {
#[derive(FromArma, Default, Debug, PartialEq)]
#[arma(default)]
struct DeriveTest(String, bool);
let input = Value::Array(vec![]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest::default())
);
let input = Value::Array(vec![Value::String("first".to_string())]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest("first".to_string(), false))
);
let input = Value::Array(vec![
Value::String("first".to_string()),
Value::Boolean(true),
]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest("first".to_string(), true))
);
}
#[test]
fn default_field() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest(String, #[arma(default)] bool);
let input = Value::Array(vec![Value::String("first".to_string())]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest("first".to_string(), false))
);
let input = Value::Array(vec![
Value::String("first".to_string()),
Value::Boolean(true),
]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest("first".to_string(), true))
);
}
#[test]
fn default_multi_field() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest(String, #[arma(default)] bool, #[arma(default)] bool);
let input = Value::Array(vec![Value::String("first".to_string())]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest("first".to_string(), false, false))
);
let input = Value::Array(vec![
Value::String("first".to_string()),
Value::Boolean(true),
]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest("first".to_string(), true, false))
);
let input = Value::Array(vec![
Value::String("first".to_string()),
Value::Boolean(true),
Value::Boolean(true),
]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest("first".to_string(), true, true))
);
}
#[test]
fn default_field_precedence() {
#[derive(FromArma, Debug, PartialEq)]
#[arma(default)]
struct DeriveTest(String, #[arma(default)] bool);
impl Default for DeriveTest {
fn default() -> Self {
Self("first".to_string(), true)
}
}
let input = Value::Array(vec![]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest("first".to_string(), false))
);
}
#[test]
fn error_length() {
#[derive(FromArma, Default, Debug, PartialEq)]
struct DeriveTest(String, String);
let input = Value::Array(vec![
Value::String("first".to_string()),
Value::String("second".to_string()),
Value::String("third".to_string()),
Value::String("forth".to_string()),
]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Err(arma_rs::FromArmaError::InvalidLength {
expected: 2,
actual: 4,
})
);
}
#[test]
fn default_field_error() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest(String, #[arma(default)] String);
let input = Value::Array(vec![]);
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Err(arma_rs::FromArmaError::InvalidLength {
expected: 2,
actual: 0,
})
);
}
}
mod newtype {
use super::*;
#[test]
fn derive() {
#[derive(FromArma, IntoArma, Debug, PartialEq)]
struct DeriveTest(String);
let serialized = DeriveTest("expected".to_string());
let deserialized = Value::String("expected".to_string());
assert_eq!(serialized.to_arma(), deserialized);
assert_eq!(
DeriveTest::from_arma(deserialized.to_string()),
Ok(serialized)
);
}
#[test]
fn from_str() {
#[derive(FromArma, Debug, PartialEq)]
struct DeriveTest(#[arma(from_str)] ValueStringImpl);
let input = Value::String("odd".to_string());
assert_eq!(
DeriveTest::from_arma(input.to_string()),
Ok(DeriveTest(ValueStringImpl::Odd))
);
}
#[test]
fn to_string() {
#[derive(IntoArma, Debug, PartialEq)]
struct DeriveTest(#[arma(to_string)] ValueStringImpl);
assert_eq!(
DeriveTest(ValueStringImpl::Odd).to_arma(),
Value::String("odd".to_string()),
);
}
}
}

View File

@@ -0,0 +1,6 @@
use arma_rs::{FromArma, IntoArma};
#[derive(FromArma, IntoArma)]
enum DeriveTest {}
fn main() {}

View File

@@ -0,0 +1,15 @@
error: enums aren't supported
--> tests/derive/fail_enum.rs:3:10
|
3 | #[derive(FromArma, IntoArma)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `FromArma` (in Nightly builds, run with -Z macro-backtrace for more info)
error: enums aren't supported
--> tests/derive/fail_enum.rs:3:20
|
3 | #[derive(FromArma, IntoArma)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `IntoArma` (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@@ -0,0 +1,65 @@
use arma_rs::{FromArma, IntoArma};
#[derive(FromArma, IntoArma)]
#[arma]
struct NoList {
test: u32,
}
#[derive(FromArma, IntoArma)]
#[arma("literal")]
struct Literal {
test: u32,
}
#[derive(FromArma, IntoArma)]
#[arma(unknown)]
struct Unknown {
test: u32,
}
#[derive(FromArma, IntoArma)]
#[arma(unknown::path)]
struct UnknownPath {
test: u32,
}
#[derive(FromArma, IntoArma)]
#[arma(default, default)]
struct Duplicate {
test: u32,
}
#[derive(FromArma, IntoArma)]
#[arma(default)]
#[arma(default)]
struct StackedDuplicate {
test: u32,
}
#[derive(FromArma, IntoArma)]
struct FieldUnknown {
#[arma(unknown)]
test: u32,
}
#[derive(FromArma, IntoArma)]
struct FieldDuplicate {
#[arma(default, default)]
test: u32,
}
#[derive(FromArma, IntoArma)]
struct FieldStackedDuplicate {
#[arma(default)]
#[arma(default)]
test: u32,
}
#[derive(FromArma, IntoArma)]
#[arma(unknown, default, default)]
struct MultipleErrors {
test: u32,
}
fn main() {}

View File

@@ -0,0 +1,65 @@
error: expected attribute arguments in parentheses: #[arma(...)]
--> tests/derive/fail_struct_attributes.rs:4:3
|
4 | #[arma]
| ^^^^
error: unexpected literal in nested attribute, expected ident
--> tests/derive/fail_struct_attributes.rs:10:8
|
10 | #[arma("literal")]
| ^^^^^^^^^
error: unknown arma container attribute `unknown`
--> tests/derive/fail_struct_attributes.rs:16:8
|
16 | #[arma(unknown)]
| ^^^^^^^
error: unknown arma container attribute `unknown::path`
--> tests/derive/fail_struct_attributes.rs:22:8
|
22 | #[arma(unknown::path)]
| ^^^^^^^^^^^^^
error: duplicate arma attribute `default`
--> tests/derive/fail_struct_attributes.rs:28:17
|
28 | #[arma(default, default)]
| ^^^^^^^
error: duplicate arma attribute `default`
--> tests/derive/fail_struct_attributes.rs:35:8
|
35 | #[arma(default)]
| ^^^^^^^
error: unknown arma field attribute `unknown`
--> tests/derive/fail_struct_attributes.rs:42:12
|
42 | #[arma(unknown)]
| ^^^^^^^
error: duplicate arma attribute `default`
--> tests/derive/fail_struct_attributes.rs:48:21
|
48 | #[arma(default, default)]
| ^^^^^^^
error: duplicate arma attribute `default`
--> tests/derive/fail_struct_attributes.rs:55:12
|
55 | #[arma(default)]
| ^^^^^^^
error: unknown arma container attribute `unknown`
--> tests/derive/fail_struct_attributes.rs:60:8
|
60 | #[arma(unknown, default, default)]
| ^^^^^^^
error: duplicate arma attribute `default`
--> tests/derive/fail_struct_attributes.rs:60:26
|
60 | #[arma(unknown, default, default)]
| ^^^^^^^

View File

@@ -0,0 +1,16 @@
use arma_rs::{FromArma, IntoArma};
#[derive(FromArma, IntoArma)]
#[arma(transparent, default)]
struct Container {
first: String,
}
#[derive(FromArma, IntoArma)]
#[arma(transparent)]
struct Field {
#[arma(default)]
first: String,
}
fn main() {}

View File

@@ -0,0 +1,11 @@
error: #[arma(default)] and #[arma(transparent)] cannot be used together
--> tests/derive/fail_struct_map_transparent_default.rs:4:21
|
4 | #[arma(transparent, default)]
| ^^^^^^^
error: #[arma(default)] and #[arma(transparent)] cannot be used together
--> tests/derive/fail_struct_map_transparent_default.rs:12:12
|
12 | #[arma(default)]
| ^^^^^^^

View File

@@ -0,0 +1,10 @@
use arma_rs::{FromArma, IntoArma};
#[derive(FromArma, IntoArma)]
#[arma(transparent)]
struct TooManyFields {
first: String,
second: String,
}
fn main() {}

View File

@@ -0,0 +1,5 @@
error: #[arma(transparent)] structs must have exactly one field
--> tests/derive/fail_struct_map_transparent_multi.rs:4:8
|
4 | #[arma(transparent)]
| ^^^^^^^^^^^

View File

@@ -0,0 +1,10 @@
use arma_rs::{FromArma, IntoArma};
#[derive(FromArma, IntoArma)]
#[arma(default)]
struct Container(u32);
#[derive(FromArma, IntoArma)]
struct Field(#[arma(default)] u32);
fn main() {}

View File

@@ -0,0 +1,11 @@
error: #[arma(default)] cannot be used on new type structs
--> tests/derive/fail_struct_newtype_default.rs:4:8
|
4 | #[arma(default)]
| ^^^^^^^
error: #[arma(default)] cannot be used on new type structs
--> tests/derive/fail_struct_newtype_default.rs:8:21
|
8 | struct Field(#[arma(default)] u32);
| ^^^^^^^

View File

@@ -0,0 +1,6 @@
use arma_rs::{FromArma, IntoArma};
#[derive(FromArma, IntoArma)]
struct DeriveTest(#[arma(default)] String, u32, bool);
fn main() {}

View File

@@ -0,0 +1,11 @@
error: field must have #[arma(default)] because previous field 0 has #[arma(default)]
--> tests/derive/fail_struct_tuple_default_field_first.rs:4:44
|
4 | struct DeriveTest(#[arma(default)] String, u32, bool);
| ^^^
error: field must have #[arma(default)] because previous field 0 has #[arma(default)]
--> tests/derive/fail_struct_tuple_default_field_first.rs:4:49
|
4 | struct DeriveTest(#[arma(default)] String, u32, bool);
| ^^^^

View File

@@ -0,0 +1,7 @@
use arma_rs::{FromArma, IntoArma};
#[derive(FromArma, IntoArma)]
#[arma(transparent)]
struct DeriveTest(String, u32);
fn main() {}

View File

@@ -0,0 +1,5 @@
error: #[arma(transparent)] cannot be used on tuple like structs
--> tests/derive/fail_struct_tuple_transparent.rs:4:8
|
4 | #[arma(transparent)]
| ^^^^^^^^^^^

View File

@@ -0,0 +1,12 @@
use arma_rs::{FromArma, IntoArma};
#[derive(FromArma, IntoArma)]
struct Unit;
#[derive(FromArma, IntoArma)]
struct EmptyMap {}
#[derive(FromArma, IntoArma)]
struct EmptyTuple();
fn main() {}

View File

@@ -0,0 +1,47 @@
error: unit-like structs aren't supported
--> tests/derive/fail_struct_unit.rs:3:10
|
3 | #[derive(FromArma, IntoArma)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `FromArma` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unit-like structs aren't supported
--> tests/derive/fail_struct_unit.rs:3:20
|
3 | #[derive(FromArma, IntoArma)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `IntoArma` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unit-like structs aren't supported
--> tests/derive/fail_struct_unit.rs:6:10
|
6 | #[derive(FromArma, IntoArma)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `FromArma` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unit-like structs aren't supported
--> tests/derive/fail_struct_unit.rs:6:20
|
6 | #[derive(FromArma, IntoArma)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `IntoArma` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unit-like structs aren't supported
--> tests/derive/fail_struct_unit.rs:9:10
|
9 | #[derive(FromArma, IntoArma)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `FromArma` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unit-like structs aren't supported
--> tests/derive/fail_struct_unit.rs:9:20
|
9 | #[derive(FromArma, IntoArma)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `IntoArma` (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@@ -0,0 +1,8 @@
use arma_rs::{FromArma, IntoArma};
#[derive(FromArma, IntoArma)]
union DeriveTest {
first: u32,
}
fn main() {}

View File

@@ -0,0 +1,15 @@
error: unions aren't supported
--> tests/derive/fail_union.rs:3:10
|
3 | #[derive(FromArma, IntoArma)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `FromArma` (in Nightly builds, run with -Z macro-backtrace for more info)
error: unions aren't supported
--> tests/derive/fail_union.rs:3:20
|
3 | #[derive(FromArma, IntoArma)]
| ^^^^^^^^
|
= note: this error originates in the derive macro `IntoArma` (in Nightly builds, run with -Z macro-backtrace for more info)

View File

@@ -0,0 +1,21 @@
use arma_rs::{FromArma, FromArmaError, IntoArma, Value};
#[derive(IntoArma)]
struct CustomFrom(u32);
impl FromArma for CustomFrom {
fn from_arma(_: String) -> Result<Self, FromArmaError> {
todo!()
}
}
#[derive(FromArma)]
struct CustomInto(u32);
impl IntoArma for CustomInto {
fn to_arma(&self) -> Value {
todo!()
}
}
fn main() {}

View File

@@ -0,0 +1,22 @@
use arma_rs::{FromArma, IntoArma};
#[derive(FromArma, IntoArma)]
struct Newtype<T: IntoArma + FromArma>(T);
#[derive(FromArma, IntoArma)]
struct Tuple<A, B>(A, B)
where
A: IntoArma + FromArma,
B: IntoArma + FromArma;
#[derive(FromArma, IntoArma)]
struct Map<A, B>
where
A: IntoArma + FromArma,
B: IntoArma + FromArma,
{
first: A,
second: B,
}
fn main() {}

485
vendor/arma-rs/tests/emulate.rs vendored Normal file
View File

@@ -0,0 +1,485 @@
#[cfg(feature = "extension")]
mod extension {
use std::{
collections::HashMap,
ffi::{CStr, CString},
sync::{Arc, Once, RwLock},
};
use arma_rs::{CallbackError, Context, Extension};
macro_rules! platform_extern {
($($func_body:tt)*) => {
#[cfg(windows)]
extern "stdcall" $($func_body)*
#[cfg(not(windows))]
extern "C" $($func_body)*
};
}
type Stack = Arc<RwLock<HashMap<String, Vec<(String, String, String)>>>>;
static mut CALLBACK_STACK: Option<Stack> = None;
static CALLBACK_STACK_INIT: Once = Once::new();
fn get_callback_stack() -> Stack {
unsafe {
CALLBACK_STACK_INIT.call_once(|| {
CALLBACK_STACK = Some(Arc::new(RwLock::new(HashMap::new())));
});
CALLBACK_STACK.as_ref().unwrap().clone()
}
}
fn callback_handler(scope: String, name: *const i8, func: *const i8, data: *const i8) -> i32 {
let stack = get_callback_stack();
unsafe {
let name = CStr::from_ptr(name).to_str().unwrap().to_string();
let func = CStr::from_ptr(func).to_str().unwrap().to_string();
let data = CStr::from_ptr(data).to_str().unwrap().to_string();
let mut stack = stack.write().unwrap();
stack.entry(scope).or_default().push((name, func, data));
}
2
}
mod c_interface_full {
use super::*;
#[test]
fn extension() {
let mut extension = Extension::build()
.command("hello", || -> &'static str { "Hello" })
.command("welcome", |name: String| -> String {
format!("Welcome {name}")
})
.command(
"callback",
|ctx: Context, id: String| -> Result<(), CallbackError> {
ctx.callback_data("callback", "fired", id)
},
)
.finish();
platform_extern!(
fn callback(name: *const i8, func: *const i8, data: *const i8) -> i32 {
callback_handler("c_interface_full".to_string(), name, func, data)
}
);
extension.register_callback(callback);
extension.run_callbacks();
let stack = get_callback_stack();
assert_eq!(stack.read().unwrap().get("c_interface_full"), None);
unsafe {
let mut output = [0i8; 1024];
let ptr = CString::new("callback").unwrap().into_raw();
let ptr_hello = CString::new("\"hello\"").unwrap().into_raw();
let code = extension.handle_call(
ptr,
output.as_mut_ptr(),
1024,
Some(vec![ptr_hello].as_mut_ptr()),
Some(1),
true,
);
assert_eq!(code, 0);
let _ = CString::from_raw(ptr);
let _ = CString::from_raw(ptr_hello);
};
unsafe {
let mut output = [0i8; 1024];
let ptr = CString::new("hello").unwrap().into_raw();
extension.handle_call(ptr, output.as_mut_ptr(), 1024, None, None, true);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok("Hello"));
let _ = CString::from_raw(ptr);
}
unsafe {
let mut output = [0i8; 1024];
let ptr = CString::new("welcome").unwrap().into_raw();
let ptr_john = CString::new("\"John\"").unwrap().into_raw();
extension.handle_call(
ptr,
output.as_mut_ptr(),
1024,
Some(vec![ptr_john].as_mut_ptr()),
Some(1),
true,
);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok("Welcome John"));
let _ = CString::from_raw(ptr);
let _ = CString::from_raw(ptr_john);
}
std::thread::sleep(std::time::Duration::from_millis(50));
assert_eq!(
stack.read().unwrap().get("c_interface_full").unwrap().len(),
1
);
}
}
#[test]
fn c_interface_builder() {
let extension = Extension::build().finish();
assert_eq!(extension.version(), "0.0.0");
assert!(!extension.allow_no_args());
let extension = Extension::build().version("1.0.0".to_string()).finish();
assert_eq!(extension.version(), "1.0.0".to_string());
let extension = Extension::build().allow_no_args().finish();
assert!(extension.allow_no_args());
}
mod c_interface_invalid_calls {
use super::*;
#[test]
fn extension() {
let mut extension = Extension::build()
.command(
"callback_invalid_name",
|ctx: Context| -> Result<(), CallbackError> {
ctx.callback_null("call\0back", "fired")
},
)
.command(
"callback_invalid_func",
|ctx: Context| -> Result<(), CallbackError> {
ctx.callback_null("callback", "fir\0ed")
},
)
.command(
"callback_invalid_data",
|ctx: Context| -> Result<(), CallbackError> {
ctx.callback_data("callback", "fired", "dat\0a")
},
)
.command(
"callback_valid_null",
|ctx: Context| -> Result<(), CallbackError> {
ctx.callback_null("callback", "fired")
},
)
.command(
"callback_valid_data",
|ctx: Context| -> Result<(), CallbackError> {
ctx.callback_data("callback", "fired", "data")
},
)
.finish();
platform_extern!(
fn callback(name: *const i8, func: *const i8, data: *const i8) -> i32 {
callback_handler("c_interface_invalid_calls".to_string(), name, func, data)
}
);
extension.register_callback(callback);
extension.run_callbacks();
let ptr = CString::new("hello").unwrap().into_raw();
unsafe {
let mut output = [0i8; 1024];
let code = extension.handle_call(ptr, output.as_mut_ptr(), 1024, None, None, true);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok(""));
assert_eq!(code, 1);
let _ = CString::from_raw(ptr);
}
// Unknown function name
unsafe {
let mut output = [0i8; 1024];
let ptr = CString::new("invalid").unwrap().into_raw();
let code = extension.handle_call(ptr, output.as_mut_ptr(), 1024, None, None, true);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok(""));
assert_eq!(code, 1);
let _ = CString::from_raw(ptr);
}
// Invalid callback name
unsafe {
let mut output = [0i8; 1024];
let ptr = CString::new("callback_invalid_name").unwrap().into_raw();
let code = extension.handle_call(ptr, output.as_mut_ptr(), 1024, None, None, true);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok("null"));
assert_eq!(code, 0);
let _ = CString::from_raw(ptr);
}
// Invalid callback func
unsafe {
let mut output = [0i8; 1024];
let ptr = CString::new("callback_invalid_func").unwrap().into_raw();
let code = extension.handle_call(ptr, output.as_mut_ptr(), 1024, None, None, true);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok("null"));
assert_eq!(code, 0);
let _ = CString::from_raw(ptr);
}
// Invalid callback data
unsafe {
let mut output = [0i8; 1024];
let ptr = CString::new("callback_invalid_data").unwrap().into_raw();
let code = extension.handle_call(ptr, output.as_mut_ptr(), 1024, None, None, true);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok("null"));
assert_eq!(code, 0);
let _ = CString::from_raw(ptr);
}
// Valid null callback
unsafe {
let mut output = [0i8; 1024];
let ptr = CString::new("callback_valid_null").unwrap().into_raw();
let code = extension.handle_call(ptr, output.as_mut_ptr(), 1024, None, None, true);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok("null"));
assert_eq!(code, 0);
let _ = CString::from_raw(ptr);
}
// Valid data callback
unsafe {
let mut output = [0i8; 1024];
let ptr = CString::new("callback_valid_data").unwrap().into_raw();
let code = extension.handle_call(ptr, output.as_mut_ptr(), 1024, None, None, true);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok("null"));
assert_eq!(code, 0);
let _ = CString::from_raw(ptr);
}
std::thread::sleep(std::time::Duration::from_millis(500));
let stack = get_callback_stack();
assert_eq!(
stack
.read()
.unwrap()
.get("c_interface_invalid_calls")
.unwrap()
.len(),
2
);
}
}
#[test]
fn c_interface_errors() {
let extension = Extension::build()
.command("add_no_context", |a: i32, b: i32| {
let _ = a + b;
})
.command("add_no_context_return", |a: i32, b: i32| a + b)
.command("add_context", |_ctx: Context, a: i32, b: i32| {
let _ = a + b;
})
.command("add_context_return", |_ctx: Context, a: i32, b: i32| a + b)
.command("overflow", |ctx: Context| "X".repeat(ctx.buffer_len() + 1))
.command("result", |error: bool| -> Result<String, String> {
if error {
Err(String::from("told to error"))
} else {
Ok(String::from("told to succeed"))
}
})
.finish();
// Valid
unsafe {
let ptr1 = CString::new("1").unwrap().into_raw();
let ptr2 = CString::new("2").unwrap().into_raw();
for (func, result) in [
("add_no_context", "null"),
("add_no_context_return", "3"),
("add_context", "null"),
("add_context_return", "3"),
] {
let mut output = [0i8; 1024];
let ptr = CString::new(func).unwrap().into_raw();
let code = extension.handle_call(
ptr,
output.as_mut_ptr(),
1024,
Some(vec![ptr1, ptr2].as_mut_ptr()),
Some(2),
true,
);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok(result));
assert_eq!(code, 0);
let _ = CString::from_raw(ptr);
}
let _ = CString::from_raw(ptr1);
let _ = CString::from_raw(ptr2);
}
// Invalid too many arguments
unsafe {
let ptr1 = CString::new("1").unwrap().into_raw();
let ptr2 = CString::new("2").unwrap().into_raw();
let ptr3 = CString::new("3").unwrap().into_raw();
for func in [
"add_no_context",
"add_no_context_return",
"add_context",
"add_context_return",
] {
let mut output = [0i8; 1024];
let ptr = CString::new(func).unwrap().into_raw();
let code = extension.handle_call(
ptr,
output.as_mut_ptr(),
1024,
Some(vec![ptr1, ptr2, ptr3].as_mut_ptr()),
Some(3),
true,
);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok(""));
assert_eq!(code, 23);
let _ = CString::from_raw(ptr);
}
let _ = CString::from_raw(ptr1);
let _ = CString::from_raw(ptr2);
let _ = CString::from_raw(ptr3);
}
// Invalid too few arguments
unsafe {
let ptr1 = CString::new("1").unwrap().into_raw();
for func in [
"add_no_context",
"add_no_context_return",
"add_context",
"add_context_return",
] {
let mut output = [0i8; 1024];
let ptr = CString::new(func).unwrap().into_raw();
let code = extension.handle_call(
ptr,
output.as_mut_ptr(),
1024,
Some(vec![ptr1].as_mut_ptr()),
Some(1),
true,
);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok(""));
assert_eq!(code, 21);
let _ = CString::from_raw(ptr);
}
let _ = CString::from_raw(ptr1);
}
// Valid type conversion
unsafe {
let ptr1 = CString::new("1").unwrap().into_raw();
let ptr2 = CString::new("\"2\"").unwrap().into_raw();
for (func, result) in [
("add_no_context", "null"),
("add_no_context_return", "3"),
("add_context", "null"),
("add_context_return", "3"),
] {
let mut output = [0i8; 1024];
let ptr = CString::new(func).unwrap().into_raw();
let code = extension.handle_call(
ptr,
output.as_mut_ptr(),
1024,
Some(vec![ptr1, ptr2].as_mut_ptr()),
Some(2),
true,
);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok(result));
assert_eq!(code, 0);
let _ = CString::from_raw(ptr);
}
let _ = CString::from_raw(ptr1);
let _ = CString::from_raw(ptr2);
}
// Invalid type
unsafe {
let ptr1 = CString::new("1").unwrap().into_raw();
let ptr2 = CString::new("\"two\"").unwrap().into_raw();
for func in [
"add_no_context",
"add_no_context_return",
"add_context",
"add_context_return",
] {
let mut output = [0i8; 1024];
let ptr = CString::new(func).unwrap().into_raw();
let code = extension.handle_call(
ptr,
output.as_mut_ptr(),
1024,
Some(vec![ptr1, ptr2].as_mut_ptr()),
Some(2),
true,
);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok(""));
assert_eq!(code, 31);
let _ = CString::from_raw(ptr);
}
let _ = CString::from_raw(ptr1);
let _ = CString::from_raw(ptr2);
}
// Overflow
unsafe {
let ptr = CString::new("overflow").unwrap().into_raw();
let mut output = [0i8; 1024];
let code = extension.handle_call(ptr, output.as_mut_ptr(), 1024, None, None, true);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok(""));
assert_eq!(code, 4);
let _ = CString::from_raw(ptr);
}
// Result - true
unsafe {
let mut output = [0i8; 1024];
let ptr = CString::new("result").unwrap().into_raw();
let ptr_true = CString::new("true").unwrap().into_raw();
let code = extension.handle_call(
ptr,
output.as_mut_ptr(),
1024,
Some(vec![ptr_true].as_mut_ptr()),
Some(1),
true,
);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok("told to error"));
assert_eq!(code, 9);
let _ = CString::from_raw(ptr);
let _ = CString::from_raw(ptr_true);
}
// Result - false
unsafe {
let ptr = CString::new("result").unwrap().into_raw();
let ptr_false = CString::new("false").unwrap().into_raw();
let mut output = [0i8; 1024];
let code = extension.handle_call(
ptr,
output.as_mut_ptr(),
1024,
Some(vec![ptr_false].as_mut_ptr()),
Some(1),
true,
);
let cstring = CStr::from_ptr(output.as_ptr()).to_str();
assert_eq!(cstring, Ok("told to succeed"));
assert_eq!(code, 0);
let _ = CString::from_raw(ptr);
let _ = CString::from_raw(ptr_false);
}
}
}

367
vendor/arma-rs/tests/main.rs vendored Normal file
View File

@@ -0,0 +1,367 @@
#[cfg(feature = "extension")]
mod extension {
use arma_rs::{Context, ContextState, Extension, Group};
#[test]
fn root_command() {
let extension = Extension::build()
.command("hello", || -> &'static str { "Hello" })
.finish()
.testing();
let (result, _) = extension.call("hello", None);
assert_eq!(result, "Hello");
}
#[test]
fn root_command_with_args() {
let extension = Extension::build()
.command("hello", |name: String| -> String {
format!("Hello {name}")
})
.finish()
.testing();
let (result, _) = extension.call("hello", Some(vec![String::from("John")]));
assert_eq!(result, "Hello John");
}
#[test]
fn root_command_no_return() {
let extension = Extension::build().command("nop", || {}).finish().testing();
let (result, code) = extension.call("nop", None);
assert_eq!(code, 0);
assert_eq!(result, "null");
}
#[test]
fn root_command_with_args_no_return() {
let extension = Extension::build()
.command("nop", |_: i8| {})
.finish()
.testing();
let (result, code) = extension.call("nop", Some(vec![String::from("4")]));
assert_eq!(code, 0);
assert_eq!(result, "null");
}
#[test]
fn group_command() {
let extension = Extension::build()
.group(
"english",
Group::new().command("hello", || -> &'static str { "Hello" }),
)
.finish()
.testing();
let (result, _) = extension.call("english:hello", None);
assert_eq!(result, "Hello");
}
#[test]
fn group_command_with_args() {
let extension = Extension::build()
.group(
"english",
Group::new().command("hello", |name: String| -> String {
format!("Hello {name}")
}),
)
.finish()
.testing();
let (result, _) = extension.call("english:hello", Some(vec![String::from("John")]));
assert_eq!(result, "Hello John");
}
#[test]
fn sub_group_command() {
let extension = Extension::build()
.group(
"greeting",
Group::new().group(
"english",
Group::new().command("hello", || -> &'static str { "Hello" }),
),
)
.finish()
.testing();
let (result, _) = extension.call("greeting:english:hello", None);
assert_eq!(result, "Hello");
}
#[test]
fn sub_group_command_with_args() {
let extension = Extension::build()
.group(
"greeting",
Group::new().group(
"english",
Group::new().command("hello", |name: String| -> String {
format!("Hello {name}")
}),
),
)
.finish()
.testing();
let (result, _) =
extension.call("greeting:english:hello", Some(vec![String::from("John")]));
assert_eq!(result, "Hello John");
}
#[test]
fn result_ok() {
let extension = Extension::build()
.command("result", || -> Result<&str, &str> { Ok("Ok") })
.finish()
.testing();
let (result, code) = extension.call("result", None);
assert_eq!(code, 0);
assert_eq!(result, "Ok");
}
#[test]
fn result_err() {
let extension = Extension::build()
.command("result", || -> Result<&str, &str> { Err("Err") })
.finish()
.testing();
let (result, code) = extension.call("result", None);
assert_eq!(code, 9);
assert_eq!(result, "Err");
}
#[test]
fn result_unit_ok() {
let extension = Extension::build()
.command("result", || -> Result<(), &str> { Ok(()) })
.finish()
.testing();
let (result, code) = extension.call("result", None);
assert_eq!(code, 0);
assert_eq!(result, "null");
}
#[test]
fn result_unit_err() {
let extension = Extension::build()
.command("result", || -> Result<&str, ()> { Err(()) })
.finish()
.testing();
let (result, code) = extension.call("result", None);
assert_eq!(code, 9);
assert_eq!(result, "null");
}
#[test]
fn result_unit_both() {
let extension = Extension::build()
.command("result", || -> Result<(), ()> { Ok(()) })
.finish()
.testing();
let (result, code) = extension.call("result", None);
assert_eq!(code, 0);
assert_eq!(result, "null");
}
#[test]
fn not_found() {
let extension = Extension::build().finish().testing();
let (result, code) = extension.call("hello", None);
assert_eq!(code, 1);
assert_eq!(result, "");
}
#[test]
fn invalid_arg_count() {
let extension = Extension::build()
.command("hello", || -> &'static str { "Hello" })
.finish()
.testing();
let (result, code) = extension.call("hello", Some(vec![String::from("John")]));
assert_eq!(code, 21);
assert_eq!(result, "");
}
#[test]
fn invalid_arg_type() {
let extension = Extension::build()
.command("hello", |_: i32| -> &'static str { "Hello" })
.finish()
.testing();
let (result, code) = extension.call("hello", Some(vec![String::from("John")]));
assert_eq!(code, 30);
assert_eq!(result, "");
}
#[test]
fn invalid_arg_type_position() {
let extension = Extension::build()
.command("hello", |_: String, _: i32| -> &'static str { "Hello" })
.finish()
.testing();
let (result, code) = extension.call(
"hello",
Some(vec![String::from("John"), String::from("John")]),
);
assert_eq!(code, 31);
assert_eq!(result, "");
}
#[test]
fn filled_output() {
let extension = Extension::build()
.command("hello", |ctx: Context| -> String {
"X".repeat(ctx.buffer_len())
})
.finish()
.testing();
let (result, _) = extension.call("hello", None);
assert_eq!(result.len(), extension.context().buffer_len());
}
#[test]
fn filled_output_with_args() {
let extension = Extension::build()
.command("hello", |ctx: Context, item: String| -> String {
item.repeat(ctx.buffer_len())
})
.finish()
.testing();
let (result, _) = extension.call("hello", Some(vec![String::from('X')]));
assert_eq!(result.len(), extension.context().buffer_len());
}
#[test]
fn output_overflow() {
let extension = Extension::build()
.command("hello", |ctx: Context| -> String {
"X".repeat(ctx.buffer_len() + 1)
})
.finish()
.testing();
let (_, code) = extension.call("hello", None);
assert_eq!(code, 4);
}
#[test]
fn output_overflow_with_args() {
let extension = Extension::build()
.command("hello", |ctx: Context, item: String| -> String {
item.repeat(ctx.buffer_len() + 1)
})
.finish()
.testing();
let (_, code) = extension.call("hello", Some(vec![String::from('X')]));
assert_eq!(code, 4);
}
#[test]
fn application_error_ok() {
let extension = Extension::build()
.command("hello", || -> Result<&str, &str> { Ok("Ok") })
.finish()
.testing();
let (_, code) = extension.call("hello", None);
assert_eq!(code, 0);
}
#[test]
fn application_error_err() {
let extension = Extension::build()
.command("hello", || -> Result<&str, &str> { Err("Error") })
.finish()
.testing();
let (_, code) = extension.call("hello", None);
assert_eq!(code, 9);
}
#[test]
fn state_build() {
let extension = Extension::build()
.state(String::from("foobar"))
.finish()
.testing();
let value = extension.state().try_get::<String>();
assert_eq!(value, Some(&String::from("foobar")));
}
#[test]
fn state_new() {
let extension = Extension::build()
.command("new", |ctx: Context, new: String| ctx.global().set(new))
.finish()
.testing();
let (_, _) = extension.call("new", Some(vec![String::from("foobar")]));
let value = extension.state().try_get::<String>();
assert_eq!(value, Some(&String::from("foobar")));
}
#[test]
fn state_freeze() {
let extension = Extension::build()
.command("new", |ctx: Context, new: String| ctx.global().set(new))
.freeze_state()
.finish()
.testing();
assert!(extension.state().is_frozen());
let (_, _) = extension.call("new", Some(vec![String::from("foobar")]));
let value = extension.state().try_get::<String>();
assert_eq!(value, None);
}
#[test]
fn state_change() {
use std::sync::atomic::{AtomicUsize, Ordering};
let extension = Extension::build()
.state(AtomicUsize::new(42))
.command("set", |ctx: Context, new: usize| {
ctx.global()
.get::<AtomicUsize>()
.expect("state not found")
.store(new, Ordering::Relaxed)
})
.finish()
.testing();
let (_, _) = extension.call("set", Some(vec![String::from("21")]));
let value = extension
.state()
.get::<AtomicUsize>()
.load(Ordering::Relaxed);
assert_eq!(value, 21);
}
mod call_context {
use arma_rs::{CallContext, Caller, Extension, Mission, Server, Source};
#[test]
fn call() {
let extension = Extension::build()
.command("call_ctx", |call_context: CallContext| -> String {
format!(
"{:?},{:?},{:?},{:?}",
call_context.caller(),
call_context.source(),
call_context.mission(),
call_context.server()
)
})
.finish()
.testing();
let (result, _) = extension.call_with_context(
"call_ctx",
None,
Caller::Steam(123),
Source::Pbo(String::from("pbo")),
Mission::Mission(String::from("mission")),
Server::Multiplayer(String::from("server")),
0,
);
assert_eq!(
result,
"Steam(123),Pbo(\"pbo\"),Mission(\"mission\"),Multiplayer(\"server\")"
);
}
}
}