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
//! 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 CStr;
/// Conversion to a C-style string.
///
/// If using the derive macro, this will be a cheap conversion.
/// 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`