servicepoint_binding_c 0.15.0

C bindings for the servicepoint crate.
Documentation
use crate::{
    macros::{derive_clone, derive_free, wrap},
    mem::{
        heap_clone, heap_drop, heap_move, heap_move_nonnull, heap_move_ok,
        heap_remove,
    },
};
use servicepoint::{
    BitVecCommand, BitmapCommand, BrightnessGridCommand, CharGridCommand,
    ClearCommand, Cp437GridCommand, FadeOutCommand, GlobalBrightnessCommand,
    HardResetCommand, Packet, TypedCommand,
};
use std::ptr::{null_mut, NonNull};

/// Pointer to one of the available command structs.
#[repr(C)]
#[allow(missing_docs)]
pub union CommandUnion {
    pub null: *mut u8,
    pub bitmap: NonNull<BitmapCommand>,
    pub bit_vec: NonNull<BitVecCommand>,
    pub brightness_grid: NonNull<BrightnessGridCommand>,
    pub char_grid: NonNull<CharGridCommand>,
    pub cp437_grid: NonNull<Cp437GridCommand>,
    pub global_brightness: NonNull<GlobalBrightnessCommand>,
    pub clear: NonNull<ClearCommand>,
    #[allow(deprecated)]
    pub bitmap_legacy: NonNull<servicepoint::BitmapLegacyCommand>,
    pub hard_reset: NonNull<HardResetCommand>,
    pub fade_out: NonNull<FadeOutCommand>,
}

/// Specifies the kind of command struct.
///
/// This is _not_ equivalent to the [servicepoint::CommandCode]s.
#[repr(u8)]
#[allow(missing_docs)]
pub enum CommandTag {
    Invalid = 0,
    Bitmap,
    BitVec,
    BrightnessGrid,
    CharGrid,
    Cp437Grid,
    GlobalBrightness,
    Clear,
    HardReset,
    FadeOut,
    BitmapLegacy,
}

/// This struct represents a pointer to one of the possible command structs.
///
/// Only ever access `data` with the correct data type as specified by `tag`!
///
/// Rust equivalent: [TypedCommand].
#[repr(C)]
pub struct GenericCommand {
    /// Specifies which kind of command struct is contained in `data`
    pub tag: CommandTag,
    /// The pointer to the command struct
    pub data: CommandUnion,
}

impl GenericCommand {
    pub(crate) const INVALID: GenericCommand = GenericCommand {
        tag: CommandTag::Invalid,
        data: CommandUnion { null: null_mut() },
    };
}

impl Clone for GenericCommand {
    fn clone(&self) -> Self {
        unsafe {
            match self.tag {
                CommandTag::Clear => GenericCommand {
                    tag: CommandTag::Clear,
                    data: CommandUnion {
                        clear: heap_clone(self.data.clear),
                    },
                },
                CommandTag::CharGrid => GenericCommand {
                    tag: CommandTag::CharGrid,
                    data: CommandUnion {
                        char_grid: heap_clone(self.data.char_grid),
                    },
                },
                CommandTag::Cp437Grid => GenericCommand {
                    tag: CommandTag::Cp437Grid,
                    data: CommandUnion {
                        cp437_grid: heap_clone(self.data.cp437_grid),
                    },
                },
                CommandTag::Bitmap => GenericCommand {
                    tag: CommandTag::Bitmap,
                    data: CommandUnion {
                        bitmap: heap_clone(self.data.bitmap),
                    },
                },
                CommandTag::GlobalBrightness => GenericCommand {
                    tag: CommandTag::GlobalBrightness,
                    data: CommandUnion {
                        global_brightness: heap_clone(
                            self.data.global_brightness,
                        ),
                    },
                },
                CommandTag::BrightnessGrid => GenericCommand {
                    tag: CommandTag::BrightnessGrid,
                    data: CommandUnion {
                        brightness_grid: heap_clone(self.data.brightness_grid),
                    },
                },
                CommandTag::BitVec => GenericCommand {
                    tag: CommandTag::BitVec,
                    data: CommandUnion {
                        bit_vec: heap_clone(self.data.bit_vec),
                    },
                },
                CommandTag::HardReset => GenericCommand {
                    tag: CommandTag::HardReset,
                    data: CommandUnion {
                        hard_reset: heap_clone(self.data.hard_reset),
                    },
                },
                CommandTag::FadeOut => GenericCommand {
                    tag: CommandTag::FadeOut,
                    data: CommandUnion {
                        fade_out: heap_clone(self.data.fade_out),
                    },
                },
                #[allow(deprecated)]
                CommandTag::BitmapLegacy => GenericCommand {
                    tag: CommandTag::BitmapLegacy,
                    data: CommandUnion {
                        bitmap_legacy: heap_clone(self.data.bitmap_legacy),
                    },
                },
                CommandTag::Invalid => GenericCommand::INVALID,
            }
        }
    }
}

impl Drop for GenericCommand {
    fn drop(&mut self) {
        unsafe {
            match self.tag {
                CommandTag::Invalid => (),
                CommandTag::Bitmap => heap_drop(self.data.bitmap),
                CommandTag::BitVec => heap_drop(self.data.bit_vec),
                CommandTag::BrightnessGrid => {
                    heap_drop(self.data.brightness_grid)
                }
                CommandTag::CharGrid => heap_drop(self.data.char_grid),
                CommandTag::Cp437Grid => heap_drop(self.data.cp437_grid),
                CommandTag::GlobalBrightness => {
                    heap_drop(self.data.global_brightness)
                }
                CommandTag::Clear => heap_drop(self.data.clear),
                CommandTag::HardReset => heap_drop(self.data.hard_reset),
                CommandTag::FadeOut => heap_drop(self.data.fade_out),
                CommandTag::BitmapLegacy => heap_drop(self.data.bitmap_legacy),
            }
        }

        *self = Self::INVALID;
    }
}

derive_clone!(GenericCommand);
derive_free!(GenericCommand);

wrap! {
    GenericCommand {
    functions:
        /// Tries to turn a [Packet] into a [GenericCommand].
        ///
        /// The packet is dropped in the process.
        ///
        /// Returns: pointer to new [GenericCommand] instance or NULL if parsing failed.
        fn try_from_packet(packet: move NonNull<Packet>) -> move NonNull<GenericCommand> {
            servicepoint::TypedCommand::try_from(packet)
                .map(|value| match value {
                    TypedCommand::Clear(clear) => GenericCommand {
                        tag: CommandTag::Clear,
                        data: CommandUnion {
                            clear: heap_move_nonnull(clear),
                        },
                    },
                    TypedCommand::CharGrid(char_grid) => GenericCommand {
                        tag: CommandTag::CharGrid,
                        data: CommandUnion {
                            char_grid: heap_move_nonnull(char_grid),
                        },
                    },
                    TypedCommand::Cp437Grid(cp437_grid) => GenericCommand {
                        tag: CommandTag::Cp437Grid,
                        data: CommandUnion {
                            cp437_grid: heap_move_nonnull(cp437_grid),
                        },
                    },
                    TypedCommand::Bitmap(bitmap) => GenericCommand {
                        tag: CommandTag::Bitmap,
                        data: CommandUnion {
                            bitmap: heap_move_nonnull(bitmap),
                        },
                    },
                    TypedCommand::Brightness(global_brightness) => GenericCommand {
                        tag: CommandTag::GlobalBrightness,
                        data: CommandUnion {
                            global_brightness: heap_move_nonnull(global_brightness),
                        },
                    },
                    TypedCommand::BrightnessGrid(brightness_grid) => GenericCommand {
                        tag: CommandTag::BrightnessGrid,
                        data: CommandUnion {
                            brightness_grid: heap_move_nonnull(brightness_grid),
                        },
                    },
                    TypedCommand::BitVec(bitvec) => GenericCommand {
                        tag: CommandTag::BitVec,
                        data: CommandUnion {
                            bit_vec: heap_move_nonnull(bitvec),
                        },
                    },
                    TypedCommand::HardReset(hard_reset) => GenericCommand {
                        tag: CommandTag::HardReset,
                        data: CommandUnion {
                            hard_reset: heap_move_nonnull(hard_reset),
                        },
                    },
                    TypedCommand::FadeOut(fade_out) => GenericCommand {
                        tag: CommandTag::FadeOut,
                        data: CommandUnion {
                            fade_out: heap_move_nonnull(fade_out),
                        },
                    },
                    #[allow(deprecated)]
                    TypedCommand::BitmapLegacy(bitmap_legacy) => GenericCommand {
                        tag: CommandTag::BitmapLegacy,
                        data: CommandUnion {
                            bitmap_legacy: heap_move_nonnull(bitmap_legacy),
                        },
                    },
                })
                .unwrap_or_else(move |_| GenericCommand {
                    tag: CommandTag::Invalid,
                    data: CommandUnion { null: null_mut() },
                })
        };
    methods:
        /// Tries to turn a [GenericCommand] into a [Packet].
        /// The [GenericCommand] gets consumed.
        ///
        /// Returns tag [CommandTag::Invalid] in case of an error.
        fn try_into_packet(move command) -> val *mut Packet {
            match command.tag {
                CommandTag::Invalid => null_mut(),
                CommandTag::Bitmap => {
                    heap_move_ok(unsafe { heap_remove(command.data.bitmap).try_into() })
                }
                CommandTag::BitVec => {
                    heap_move_ok(unsafe { heap_remove(command.data.bit_vec).try_into() })
                }
                CommandTag::BrightnessGrid => heap_move_ok(unsafe {
                    heap_remove(command.data.brightness_grid).try_into()
                }),
                CommandTag::CharGrid => heap_move_ok(unsafe {
                    heap_remove(command.data.char_grid).try_into()
                }),
                CommandTag::Cp437Grid => heap_move_ok(unsafe {
                    heap_remove(command.data.cp437_grid).try_into()
                }),
                CommandTag::GlobalBrightness => heap_move(unsafe {
                    heap_remove(command.data.global_brightness).into()
                }),
                CommandTag::Clear => {
                    heap_move(unsafe { heap_remove(command.data.clear).into() })
                }
                CommandTag::HardReset => {
                    heap_move(unsafe { heap_remove(command.data.hard_reset).into() })
                }
                CommandTag::FadeOut => {
                    heap_move(unsafe { heap_remove(command.data.fade_out).into() })
                }
                CommandTag::BitmapLegacy => {
                    heap_move(unsafe { heap_remove(command.data.bitmap_legacy).into() })
                }
            }
        };
    }
}