pyo3/conversions/std/
cstring.rs1#[cfg(feature = "experimental-inspect")]
2use crate::inspect::TypeHint;
3#[cfg(feature = "experimental-inspect")]
4use crate::type_object::PyTypeInfo;
5use crate::types::PyString;
6use crate::{Borrowed, Bound, FromPyObject, IntoPyObject, PyAny, PyErr, Python};
7use std::borrow::Cow;
8use std::ffi::{CStr, CString};
9use std::str::Utf8Error;
10#[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
11use {
12 crate::{exceptions::PyValueError, ffi},
13 std::slice,
14};
15
16impl<'py> IntoPyObject<'py> for &CStr {
17 type Target = PyString;
18 type Output = Bound<'py, Self::Target>;
19 type Error = Utf8Error;
20
21 #[cfg(feature = "experimental-inspect")]
22 const OUTPUT_TYPE: TypeHint = <&str>::OUTPUT_TYPE;
23
24 #[inline]
25 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
26 self.to_str()?.into_pyobject(py).map_err(|err| match err {})
27 }
28}
29
30impl<'py> IntoPyObject<'py> for CString {
31 type Target = PyString;
32 type Output = Bound<'py, Self::Target>;
33 type Error = Utf8Error;
34
35 #[cfg(feature = "experimental-inspect")]
36 const OUTPUT_TYPE: TypeHint = <&CStr>::OUTPUT_TYPE;
37
38 #[inline]
39 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
40 (&*self).into_pyobject(py)
41 }
42}
43
44impl<'py> IntoPyObject<'py> for &CString {
45 type Target = PyString;
46 type Output = Bound<'py, Self::Target>;
47 type Error = Utf8Error;
48
49 #[cfg(feature = "experimental-inspect")]
50 const OUTPUT_TYPE: TypeHint = <&CStr>::OUTPUT_TYPE;
51
52 #[inline]
53 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
54 (&**self).into_pyobject(py)
55 }
56}
57
58impl<'py> IntoPyObject<'py> for Cow<'_, CStr> {
59 type Target = PyString;
60 type Output = Bound<'py, Self::Target>;
61 type Error = Utf8Error;
62
63 #[cfg(feature = "experimental-inspect")]
64 const OUTPUT_TYPE: TypeHint = <&CStr>::OUTPUT_TYPE;
65
66 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
67 (*self).into_pyobject(py)
68 }
69}
70
71impl<'py> IntoPyObject<'py> for &Cow<'_, CStr> {
72 type Target = PyString;
73 type Output = Bound<'py, Self::Target>;
74 type Error = Utf8Error;
75
76 #[cfg(feature = "experimental-inspect")]
77 const OUTPUT_TYPE: TypeHint = <&CStr>::OUTPUT_TYPE;
78
79 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
80 (&**self).into_pyobject(py)
81 }
82}
83
84#[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
85impl<'a> FromPyObject<'a, '_> for &'a CStr {
86 type Error = PyErr;
87
88 #[cfg(feature = "experimental-inspect")]
89 const INPUT_TYPE: TypeHint = PyString::TYPE_HINT;
90
91 fn extract(obj: Borrowed<'a, '_, PyAny>) -> Result<Self, Self::Error> {
92 let obj = obj.cast::<PyString>()?;
93 let mut size = 0;
94 let ptr = unsafe { ffi::PyUnicode_AsUTF8AndSize(obj.as_ptr(), &mut size) };
96
97 if ptr.is_null() {
98 return Err(PyErr::fetch(obj.py()));
99 }
100
101 let slice = unsafe { slice::from_raw_parts(ptr.cast(), size as usize + 1) };
104
105 CStr::from_bytes_with_nul(slice).map_err(|err| PyValueError::new_err(err.to_string()))
106 }
107}
108
109impl<'a> FromPyObject<'a, '_> for Cow<'a, CStr> {
110 type Error = PyErr;
111
112 #[cfg(feature = "experimental-inspect")]
113 const INPUT_TYPE: TypeHint = PyString::TYPE_HINT;
114
115 fn extract(obj: Borrowed<'a, '_, PyAny>) -> Result<Self, Self::Error> {
116 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
117 {
118 Ok(Cow::Borrowed(obj.extract::<&CStr>()?))
119 }
120
121 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
122 {
123 Ok(Cow::Owned(obj.extract::<CString>()?))
124 }
125 }
126}
127impl FromPyObject<'_, '_> for CString {
128 type Error = PyErr;
129
130 #[cfg(feature = "experimental-inspect")]
131 const INPUT_TYPE: TypeHint = PyString::TYPE_HINT;
132
133 fn extract(obj: Borrowed<'_, '_, PyAny>) -> Result<Self, Self::Error> {
134 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
135 {
136 Ok(obj.extract::<&CStr>()?.to_owned())
137 }
138
139 #[cfg(not(any(Py_3_10, not(Py_LIMITED_API))))]
140 {
141 CString::new(&*obj.cast::<PyString>()?.to_cow()?).map_err(Into::into)
142 }
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149 use crate::types::string::PyStringMethods;
150 use crate::types::PyAnyMethods;
151 use crate::Python;
152
153 #[test]
154 fn test_into_pyobject() {
155 Python::attach(|py| {
156 let s = "Hello, Python!";
157 let cstr = CString::new(s).unwrap();
158
159 let py_string = cstr.as_c_str().into_pyobject(py).unwrap();
160 assert_eq!(py_string.to_cow().unwrap(), s);
161
162 let py_string = cstr.into_pyobject(py).unwrap();
163 assert_eq!(py_string.to_cow().unwrap(), s);
164 })
165 }
166
167 #[test]
168 fn test_extract_with_nul_error() {
169 Python::attach(|py| {
170 let s = "Hello\0Python";
171 let py_string = s.into_pyobject(py).unwrap();
172
173 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
174 {
175 let err = py_string.extract::<&CStr>();
176 assert!(err.is_err());
177 }
178
179 let err = py_string.extract::<CString>();
180 assert!(err.is_err());
181 })
182 }
183
184 #[test]
185 fn test_extract_cstr_and_cstring() {
186 Python::attach(|py| {
187 let s = "Hello, world!";
188 let cstr = CString::new(s).unwrap();
189 let py_string = cstr.as_c_str().into_pyobject(py).unwrap();
190
191 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
192 {
193 let extracted_cstr: &CStr = py_string.extract().unwrap();
194 assert_eq!(extracted_cstr.to_str().unwrap(), s);
195 }
196
197 let extracted_cstring: CString = py_string.extract().unwrap();
198 assert_eq!(extracted_cstring.to_str().unwrap(), s);
199 })
200 }
201
202 #[test]
203 fn test_cow_roundtrip() {
204 Python::attach(|py| {
205 let s = "Hello, world!";
206 let cstr = CString::new(s).unwrap();
207 let cow: Cow<'_, CStr> = Cow::Borrowed(cstr.as_c_str());
208
209 let py_string = cow.into_pyobject(py).unwrap();
210 assert_eq!(py_string.to_cow().unwrap(), s);
211
212 let roundtripped: Cow<'_, CStr> = py_string.extract().unwrap();
213 assert_eq!(roundtripped.as_ref(), cstr.as_c_str());
214 })
215 }
216}