Skip to main content

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}