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