idakit_sys/ty_build.rs
1//! Type-write result codes shared with the generated type-build bridge.
2//!
3//! [`SigWriteCode`] and [`TypeApplyCode`] pin idakit's decode of the generated bridge's
4//! `TypeWriteResult.code`/`SigWriteResult.code` to the values `idakit-sys-codegen`'s `type_build`
5//! domain spec generates (`crate::SIG_*`/`crate::TYPE_*`). Each is a facade sentinel, not a raw SDK
6//! value, and each call site validates with `TryFrom` rather than matching a bare `c_int`, so a
7//! drifted or unmodelled code is a typed rejection instead of a silent fallthrough. The member-edit
8//! pre-failure sentinels (`TEDIT_*`) and the recipe opcodes (`RECIPE_*`) are generated consts too;
9//! see `crate::TEDIT_NO_TYPE` and `crate::RECIPE_VOID` and siblings.
10
11use num_enum::{IntoPrimitive, TryFromPrimitive};
12use strum::VariantArray;
13
14/// The outcome of a signature-surgery call (`func_set_rettype`, `func_set_argtype`,
15/// `func_rename_arg`, `func_set_cc`, `func_prepend_this`).
16///
17/// The complete closed set those calls return: `rebuild_and_apply` and the pre-failure checks
18/// ahead of it never leak a raw `tinfo_code_t`, unlike the til member-edit codes (see
19/// [`crate::TEDIT_NO_TYPE`]).
20#[derive(
21 Clone, Copy, PartialEq, Eq, Hash, Debug, TryFromPrimitive, IntoPrimitive, VariantArray,
22)]
23#[repr(i32)]
24pub enum SigWriteCode {
25 /// A prototype-surgery edit succeeded.
26 Ok = 0,
27 /// The address carries no function type to edit.
28 NoPrototype = 1,
29 /// A parameter index was past the last parameter.
30 ArgRange = 2,
31 /// A replacement-type recipe did not build.
32 Build = 3,
33 /// `create_func` or `apply_tinfo` rejected the rebuilt signature.
34 Apply = 4,
35}
36
37/// The outcome of a whole-item type apply (`apply_type_decl`, `apply_named_type`, `clear_type`,
38/// `apply_type_recipe`, `tinfo_apply`).
39///
40/// The complete closed set those calls return: none of them ever forward a raw `tinfo_code_t`.
41#[derive(
42 Clone, Copy, PartialEq, Eq, Hash, Debug, TryFromPrimitive, IntoPrimitive, VariantArray,
43)]
44#[repr(i32)]
45pub enum TypeApplyCode {
46 /// Result of a successful type apply.
47 Ok = 0,
48 /// A bad input to a type apply: an unparseable declaration, a named type that does not exist,
49 /// or a malformed recipe.
50 ErrInput = 1,
51 /// `apply_tinfo` rejected the parsed/resolved/built type at the address.
52 ErrApply = 2,
53}
54
55#[cfg(test)]
56mod tests {
57 use assert2::assert;
58 use rstest::rstest;
59
60 use super::*;
61
62 /// Every `SigWriteCode` variant round-trips its raw value, so a `TryFrom` that stops agreeing
63 /// with `Into` fails here. Pinning the discriminants to the facade is
64 /// [`sig_write_code_pins_the_generated_facade_values`]'s job.
65 #[test]
66 fn sig_write_code_round_trips() {
67 for &code in SigWriteCode::VARIANTS {
68 assert!(SigWriteCode::try_from(i32::from(code)) == Ok(code));
69 }
70 }
71
72 /// A code past the last modelled `SigWriteCode` variant is rejected, not absorbed.
73 #[rstest]
74 #[case::just_past_the_set(5)]
75 #[case::far_past_the_set(100)]
76 #[case::negative(-1)]
77 #[case::i32_min(i32::MIN)]
78 #[case::i32_max(i32::MAX)]
79 fn sig_write_code_rejects_values_outside_the_set(#[case] raw: i32) {
80 assert!(SigWriteCode::try_from(raw).is_err());
81 }
82
83 /// Each variant's discriminant matches the generated `crate::SIG_*` const, a real drift check
84 /// between the hand-written enum and the single-sourced facade value.
85 #[rstest]
86 #[case::ok(SigWriteCode::Ok, crate::SIG_OK)]
87 #[case::no_prototype(SigWriteCode::NoPrototype, crate::SIG_NO_PROTOTYPE)]
88 #[case::arg_range(SigWriteCode::ArgRange, crate::SIG_ARG_RANGE)]
89 #[case::build(SigWriteCode::Build, crate::SIG_BUILD)]
90 #[case::apply(SigWriteCode::Apply, crate::SIG_APPLY)]
91 fn sig_write_code_pins_the_generated_facade_values(
92 #[case] code: SigWriteCode,
93 #[case] raw: i32,
94 ) {
95 assert!(i32::from(code) == raw);
96 }
97
98 /// Every `TypeApplyCode` variant round-trips its raw value.
99 #[test]
100 fn type_apply_code_round_trips() {
101 for &code in TypeApplyCode::VARIANTS {
102 assert!(TypeApplyCode::try_from(i32::from(code)) == Ok(code));
103 }
104 }
105
106 /// A code past the last modelled `TypeApplyCode` variant is rejected, not absorbed.
107 #[rstest]
108 #[case::just_past_the_set(3)]
109 #[case::far_past_the_set(100)]
110 #[case::negative(-1)]
111 #[case::i32_min(i32::MIN)]
112 #[case::i32_max(i32::MAX)]
113 fn type_apply_code_rejects_values_outside_the_set(#[case] raw: i32) {
114 assert!(TypeApplyCode::try_from(raw).is_err());
115 }
116
117 /// Each variant's discriminant matches the generated `crate::TYPE_*` const.
118 #[rstest]
119 #[case::ok(TypeApplyCode::Ok, crate::TYPE_OK)]
120 #[case::err_input(TypeApplyCode::ErrInput, crate::TYPE_ERR_INPUT)]
121 #[case::err_apply(TypeApplyCode::ErrApply, crate::TYPE_ERR_APPLY)]
122 fn type_apply_code_pins_the_generated_facade_values(
123 #[case] code: TypeApplyCode,
124 #[case] raw: i32,
125 ) {
126 assert!(i32::from(code) == raw);
127 }
128}