const_cstr_fork/lib.rs
1// Copyright (c) 2015 const-cstr developers
2// Copyright (c) 2017 const-cstr-fork developers
3// Licensed under the Apache License, Version 2.0
4// <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
6// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
7// at your option. All files in the project carrying such
8// notice may not be copied, modified, or distributed except
9// according to those terms.
10//! Create static C-compatible strings from Rust string literals.
11//!
12//! Example
13//! -------
14//!
15//! ```rust
16//! #[macro_use] extern crate const_cstr_fork;
17//! // Just for the `libc::c_char` type alias.
18//! extern crate libc;
19//!
20//! use std::ffi::CStr;
21//!
22//! const_cstr! {
23//! HELLO_CSTR = "Hello, world!";
24//!
25//! // Multiple declarations can be made with one invocation.
26//! // GOODNIGHT_CSTR = "Goodnight, sun!";
27//!
28//! // But only with the same visibility:
29//! // pub GOODNIGHT_CSTR = "Goodnight, sun!";
30//! // ^~~ Error: expected identifier, found `pub`
31//! }
32//!
33//! // Imagine this is an `extern "C"` function linked from some other lib.
34//! unsafe fn print_c_string(cstr: *const libc::c_char) {
35//! println!("{}", CStr::from_ptr(cstr).to_str().unwrap());
36//! }
37//!
38//! fn main() {
39//! // When just passed a literal, returns an rvalue instead.
40//! let goodnight_cstr = const_cstr!("Goodnight, sun!");
41//!
42//! unsafe {
43//! print_c_string(HELLO_CSTR.as_ptr());
44//! print_c_string(goodnight_cstr.as_ptr());
45//! }
46//! }
47//! ```
48//!
49//! Prints:
50//!
51//! ```notest
52//! Hello, world!
53//! Goodnight, sun!
54//! ```
55
56#![allow(non_snake_case)]
57
58extern crate libc;
59
60use std::ffi::CStr;
61
62pub trait ToPointerCString
63{
64 #[inline(always)]
65 fn to_ptr(&self) -> *const ::libc::c_char;
66}
67
68/// A type representing a static C-compatible string.
69///
70/// Note
71/// ----
72/// Use ONLY the `const_cstr!` macro to create an instance of this struct.
73/// The macro will include the NUL byte for you.
74#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
75pub struct ConstCStr {
76 /// The wrapped string value. Not intended to be used for manual initialization.
77 /// Public only to allow initialization by the `const_cstr!` macro.
78 ///
79 /// Includes the NUL terminating byte. Use `to_str()` to get an `&'static str`
80 /// without the NUL terminating byte.
81 pub cValue: &'static str,
82
83 /// Excludes the terminating byte
84 pub rustValue: &'static str,
85}
86
87impl ToPointerCString for ConstCStr
88{
89 #[inline(always)]
90 fn to_ptr(&self) -> *const ::libc::c_char
91 {
92 self.as_ptr()
93 }
94}
95
96impl ToPointerCString for CStr
97{
98 #[inline(always)]
99 fn to_ptr(&self) -> *const ::libc::c_char
100 {
101 self.as_ptr() as *const ::libc::c_char
102 }
103}
104
105impl ConstCStr {
106 /// Returns the wrapped string, without the NUL terminating byte.
107 ///
108 /// Compare to `CStr::to_str()` which checks that the string is valid UTF-8 first,
109 /// since it starts from an arbitrary pointer instead of a Rust string slice.
110 #[inline(always)]
111 pub fn to_str(&self) -> &'static str {
112 self.rustValue
113 }
114
115 /// Returns the wrapped string as a byte slice, **without** the NUL terminating byte.
116 #[inline(always)]
117 pub fn to_bytes(&self) -> &'static [u8] {
118 self.rustValue.as_bytes()
119 }
120
121 /// Returns the wrapped string as a byte slice, *with** the NUL terminating byte.
122 #[inline(always)]
123 pub fn to_bytes_with_nul(&self) -> &'static [u8] {
124 self.cValue.as_bytes()
125 }
126
127 /// Returns a pointer to the beginning of the wrapped string.
128 ///
129 /// Suitable for passing to any function that expects a C-compatible string.
130 /// Since the underlying string is guaranteed to be `'static`,
131 /// the pointer should always be valid.
132 #[inline(always)]
133 pub fn as_ptr(&self) -> *const libc::c_char {
134 self.cValue.as_ptr() as *const libc::c_char
135 }
136
137 /// Returns the wrapped string as an `&'static CStr`, skipping the length check that
138 /// `CStr::from_ptr()` performs (since we know the length already).
139 #[inline(always)]
140 pub fn as_cstr(&self) -> &'static CStr {
141 unsafe {
142 CStr::from_ptr(self.cValue.as_ptr() as *const i8)
143 }
144 }
145}
146
147/// Create a C-compatible string as an rvalue or a `const` binding.
148/// Appends a NUL byte to the passed string.
149///
150/// Multiple `const` declarations can be created with one invocation, but only with the same
151/// visibility (`pub` or not).
152///
153/// See crate root documentation for example usage.
154///
155/// Note
156/// ----
157/// For logical consistency, the passed string(s) should not contain any NUL bytes.
158/// Remember that functions consuming a C-string will only see up to the first NUL byte.
159#[macro_export]
160macro_rules! const_cstr {
161 ($strval:expr) => (
162 $crate::ConstCStr
163 {
164 rustValue: $strval,
165 cValue: concat!($strval, "\0")
166 }
167 );
168 ($($strname:ident = $strval:expr);+;) => (
169 $(
170 const $strname: $crate::ConstCStr = const_cstr!($strval);
171 )+
172 );
173 ($(pub $strname:ident = $strval:expr);+;) => (
174 $(
175 pub const $strname: $crate::ConstCStr = const_cstr!($strval);
176 )+
177 );
178}
179
180#[test]
181fn test_creates_valid_str() {
182 const_cstr! {
183 HELLO_CSTR = "Hello, world!";
184 }
185
186 let cstr = unsafe { CStr::from_ptr(HELLO_CSTR.as_ptr()) };
187
188 assert_eq!(HELLO_CSTR.to_str(), cstr.to_str().unwrap());
189}
190