Skip to main content

rustypy/pytypes/
pystring.rs

1//! An analog of a Python String.
2//!
3//! To return to Python you must use ```into_raw``` method and return a raw pointer.
4//! You can create them using the ```from``` trait method, from both ```&str``` and ```String```.
5//!
6//! # Safety
7//! When passed from Python you can convert from PyString to an owned string
8//! (```from_ptr_into_string``` method) or to a ```&str``` slice (to_str method), or
9//! to a PyString reference (```from_ptr``` method). Those operations are unsafe
10//! as they require dereferencing a raw pointer.
11//!
12//! # Examples
13//!
14//! ```
15//! use rustypy::PyString;
16//! let pystr = PyString::from("Hello world!");
17//!
18//! // prepare to return to Python:
19//! let ptr = pystr.into_raw();
20//! // convert from raw pointer to an owned String
21//! let rust_string = unsafe { PyString::from_ptr_to_string(ptr) };
22//! ```
23use libc::c_char;
24use std::ffi::CString;
25
26use std::convert::From;
27use std::fmt;
28
29/// An analog of a Python string.
30///
31/// Read the [module docs](index.html) for more information.
32#[derive(Clone, Debug, PartialEq, Eq, Hash)]
33pub struct PyString {
34    _inner: CString,
35}
36
37impl PyString {
38    /// Get a PyString from a previously boxed raw pointer.
39    ///
40    /// # Safety
41    /// Ensure that the passed ptr is valid
42    pub unsafe fn from_ptr(ptr: *mut PyString) -> PyString {
43        if ptr.is_null() {
44            panic!("trying to deref a null ptr!");
45        }
46        *Box::from_raw(ptr)
47    }
48
49    /// Constructs an owned String from a raw pointer.
50    ///
51    /// # Safety
52    /// Ensure that the passed ptr is valid
53    pub unsafe fn from_ptr_to_string(ptr: *mut PyString) -> String {
54        if ptr.is_null() {
55            panic!("trying to deref a null ptr!");
56        }
57        let pystr = *(Box::from_raw(ptr));
58        String::from(pystr._inner.to_str().unwrap())
59    }
60    /// Returns PyString as a raw pointer. Use this whenever you want to return
61    /// a PyString to Python.
62    pub fn into_raw(self) -> *mut PyString {
63        Box::into_raw(Box::new(self))
64    }
65
66    /// Return a PyString from a raw char pointer.
67    pub unsafe fn from_raw(ptr: *const c_char) -> PyString {
68        PyString {
69            _inner: CStr::from_ptr(ptr).to_owned(),
70        }
71    }
72}
73
74impl fmt::Display for PyString {
75    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
76        write!(f, "{}", String::from(self._inner.to_str().unwrap()))
77    }
78}
79
80impl<'a> From<&'a str> for PyString {
81    /// Copies a string slice to a PyString.
82    fn from(s: &'a str) -> PyString {
83        PyString {
84            _inner: CString::new(s).unwrap(),
85        }
86    }
87}
88
89impl From<String> for PyString {
90    /// Copies a String to a PyString.
91    fn from(s: String) -> PyString {
92        PyString {
93            _inner: CString::new(s).unwrap(),
94        }
95    }
96}
97
98impl From<PyString> for String {
99    fn from(s: PyString) -> String {
100        s.to_string()
101    }
102}
103
104/// Destructs the PyString, mostly to be used from Python.
105#[doc(hidden)]
106#[no_mangle]
107pub unsafe extern "C" fn pystring_free(ptr: *mut PyString) {
108    if ptr.is_null() {
109        return;
110    }
111    Box::from_raw(ptr);
112}
113
114use std::ffi::CStr;
115/// Creates a PyString wrapper from a raw c_char pointer
116#[doc(hidden)]
117#[no_mangle]
118pub unsafe extern "C" fn pystring_new(ptr: *const c_char) -> *mut PyString {
119    let pystr = PyString {
120        _inner: CStr::from_ptr(ptr).to_owned(),
121    };
122    pystr.into_raw()
123}
124
125/// Consumes the wrapper and returns a raw c_char pointer. Afterwards is not necessary
126/// to destruct it as it has already been consumed.
127#[doc(hidden)]
128#[no_mangle]
129pub unsafe extern "C" fn pystring_get_str(ptr: *mut PyString) -> *const c_char {
130    let pystr: PyString = PyString::from_ptr(ptr);
131    pystr._inner.into_raw()
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn pystring_operations() {
140        let source = "test string";
141        let owned_pystr = PyString::from(source).into_raw();
142        let back_from_py = unsafe { PyString::from_ptr_to_string(owned_pystr) };
143        assert_eq!(back_from_py, "test string");
144        {
145            String::from(source);
146        }
147    }
148}