1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
#![warn(missing_docs)] //! A crate for defining C-style string enums. //! //! C APIs sometimes require string constants. One could define a bunch of `&CStr` constants using the //! [`constr_cstr`](https://docs.rs/const-cstr/) crate, but this becomes unergonomic with a large number of constants. //! It also does not allow the type checking Rust's enums provide. //! //! This crate provides two traits for converting between to and from `&CStr`: `AsCStr` and `FromCStr`. It also provides //! derive macros for implementing these traits on enums. The implementations provided //! by the derive macros perform no allocations, using only static `[u8]` buffers. //! //! ``` //! use cstr_enum::*; //! use std::ffi::CStr; //! use std::os::raw::c_char; //! //! #[derive(Debug, Eq, PartialEq, FromCStr, AsCStr)] //! enum Constants { //! Apple, //! Bacon, //! Cat = 1337, // user discriminants supported //! } //! //! assert_eq!(Constants::Apple.as_cstr().to_bytes_with_nul(), b"Apple\0"); //! //! let returned_from_c_api = CStr::from_bytes_with_nul(b"Cat\0").unwrap(); //! assert_eq!(Constants::from_cstr(returned_from_c_api), Ok(Constants::Cat)); //! //! let returned_from_c_api = CStr::from_bytes_with_nul(b"unknown\0").unwrap(); //! assert_eq!( //! Constants::from_cstr(returned_from_c_api), //! Err("unexpected string while parsing for Constants variant") //! ); //! ``` //! Both derive macros allow the re-naming of enum variants using the `cstr(name="string literal")` attribute on enum variants. //! ``` //! # use cstr_enum::*; //! # //! #[derive(Debug, Eq, PartialEq, FromCStr, AsCStr)] //! enum Constants { //! #[cstr(name="pork")] //! Bacon, //! } //! //! assert_eq!(Constants::Bacon.as_cstr().to_bytes_with_nul(), b"pork\0"); //! ``` //! Nul bytes in the supplied string will be rejected at compile time. //! ```compile_fail //! # use cstr_enum::*; //! # //! #[derive(Debug, Eq, PartialEq, FromCStr, AsCStr)] //! enum Constants { //! #[cstr(name="p\0rk")] //! Bacon, //! } //! ``` //! ```text //! error: string cannot contain nul bytes //! | #[cstr(name="p\0rk")] //! | ^^^^^^^ //! ``` //! When deriving `AsCStr`, enum variants may contain fields: //! ``` //! # use cstr_enum::*; //! # //! #[derive(Debug, AsCStr)] //! enum Constants { //! Foo{ bar: u8 }, //! Baz(u8, u16) //! } //! assert_eq!(Constants::Foo{ bar: 0 }.as_cstr().to_bytes_with_nul(), b"Foo\0"); //! assert_eq!(Constants::Baz(0,0).as_cstr().to_bytes_with_nul(), b"Baz\0"); //! ``` //! This is not the case deriving `FromCStr`: //! ```compile_fail //! # use cstr_enum::*; //! # //! #[derive(Debug, FromCStr)] //! enum Constants { //! Foo{ bar: u8 }, //! Baz(u8, u16) //! } //! ``` //! ```text //! error: variant cannot have fields //! | Foo{ bar: u8 }, //! | ^^^^^^^^^^^^^^ //! ``` //! //! Conversion between Rust strings ([`str`] and [`String`]) is not supported by this crate. Instead, check out //! the [`strum`](https://docs.rs/strum/) crate. use std::ffi::CStr; /// Conversion to a C-style string. /// /// If using the derive macro, this will be a cheap conversion. pub trait AsCStr { /// Represent self as a [`&CStr`](std::ffi::CStr) fn as_cstr(&self) -> &CStr; } /// Conversion from a C-style string /// /// This trait should be used the same way as [`std::str::FromStr`], although /// a separate `.parse()` implementation is not provided `&str` pub trait FromCStr { /// The error type returned if parsing fails. /// /// If using the derive macro, this will be `&'static str`. type Err : Sized; /// Parse the `&CStr` for an instance of `Self`. /// /// If using the derive macro, this will be a `match` statement over `&'static [u8]`. fn from_cstr(s: &CStr) -> Result<Self, Self::Err> where Self: Sized; } pub use cstr_enum_derive::*;