artichoke_backend/convert/
string.rs1use std::borrow::Cow;
2use std::str;
3
4use crate::Artichoke;
5use crate::convert::UnboxRubyError;
6use crate::core::TryConvertMut;
7use crate::error::Error;
8use crate::types::Rust;
9use crate::value::Value;
10
11impl TryConvertMut<String, Value> for Artichoke {
12 type Error = Error;
13
14 fn try_convert_mut(&mut self, value: String) -> Result<Value, Self::Error> {
15 self.try_convert_mut(value.into_bytes())
18 }
19}
20
21impl TryConvertMut<&str, Value> for Artichoke {
22 type Error = Error;
23
24 fn try_convert_mut(&mut self, value: &str) -> Result<Value, Self::Error> {
25 self.try_convert_mut(value.as_bytes())
28 }
29}
30
31impl<'a> TryConvertMut<Cow<'a, str>, Value> for Artichoke {
32 type Error = Error;
33
34 fn try_convert_mut(&mut self, value: Cow<'a, str>) -> Result<Value, Self::Error> {
35 match value {
36 Cow::Borrowed(string) => self.try_convert_mut(string),
37 Cow::Owned(string) => self.try_convert_mut(string),
38 }
39 }
40}
41
42impl TryConvertMut<Value, String> for Artichoke {
43 type Error = Error;
44
45 fn try_convert_mut(&mut self, value: Value) -> Result<String, Self::Error> {
46 let bytes = self.try_convert_mut(value)?;
47 let string = String::from_utf8(bytes).map_err(|_| UnboxRubyError::new(&value, Rust::String))?;
50 Ok(string)
51 }
52}
53
54impl<'a> TryConvertMut<Value, &'a str> for Artichoke {
55 type Error = Error;
56
57 fn try_convert_mut(&mut self, value: Value) -> Result<&'a str, Self::Error> {
58 let bytes = self.try_convert_mut(value)?;
59 let string = str::from_utf8(bytes).map_err(|_| UnboxRubyError::new(&value, Rust::String))?;
62 Ok(string)
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use crate::test::prelude::*;
69
70 #[test]
71 fn fail_convert() {
72 let mut interp = interpreter();
73 let value = interp.eval(b"Object.new").unwrap();
75 let result = value.try_convert_into_mut::<String>(&mut interp);
76 assert!(result.is_err());
77 }
78
79 #[test]
80 fn prop_convert_to_string() {
81 let mut interp = interpreter();
82 run_arbitrary::<String>(|s| {
83 let value = interp.try_convert_mut(s.clone()).unwrap();
84 let string: Vec<u8> = interp.try_convert_mut(value).unwrap();
85 assert_eq!(string, s.as_bytes());
86 });
87 }
88
89 #[test]
90 fn prop_string_with_value() {
91 let mut interp = interpreter();
92 run_arbitrary::<String>(|s| {
93 let value = interp.try_convert_mut(s.clone()).unwrap();
94 assert_eq!(value.to_s(&mut interp), s.as_bytes());
95 });
96 }
97
98 #[test]
99 #[cfg(feature = "core-regexp")]
100 fn prop_utf8string_borrowed() {
101 let mut interp = interpreter();
102 run_arbitrary::<String>(|s| {
103 let value = interp.try_convert_mut(s.as_str()).unwrap();
105 let len = value
106 .funcall(&mut interp, "length", &[], None)
107 .and_then(|value| value.try_convert_into::<usize>(&interp))
108 .unwrap();
109 assert_eq!(len, s.chars().count());
110
111 let zero = interp.convert(0);
112 let first = value
113 .funcall(&mut interp, "[]", &[zero], None)
114 .and_then(|value| value.try_convert_into_mut::<Option<String>>(&mut interp))
115 .unwrap();
116 let mut iter = s.chars();
117 if let Some(ch) = iter.next() {
118 assert_eq!(first, Some(ch.to_string()));
119 } else {
120 assert!(first.is_none());
121 }
122
123 let recovered: String = interp.try_convert_mut(value).unwrap();
124 assert_eq!(recovered, s);
125 });
126 }
127
128 #[test]
129 #[cfg(feature = "core-regexp")]
130 fn prop_utf8string_owned() {
131 let mut interp = interpreter();
132 run_arbitrary::<String>(|s| {
133 let value = interp.try_convert_mut(s.clone()).unwrap();
135 let len = value
136 .funcall(&mut interp, "length", &[], None)
137 .and_then(|value| value.try_convert_into::<usize>(&interp))
138 .unwrap();
139 assert_eq!(len, s.chars().count());
140
141 let zero = interp.convert(0);
142 let first = value
143 .funcall(&mut interp, "[]", &[zero], None)
144 .and_then(|value| value.try_convert_into_mut::<Option<String>>(&mut interp))
145 .unwrap();
146 let mut iter = s.chars();
147 if let Some(ch) = iter.next() {
148 assert_eq!(first, Some(ch.to_string()));
149 } else {
150 assert!(first.is_none());
151 }
152
153 let recovered: String = interp.try_convert_mut(value).unwrap();
154 assert_eq!(recovered, s);
155 });
156 }
157
158 #[test]
159 fn prop_borrowed_roundtrip() {
160 let mut interp = interpreter();
161 run_arbitrary::<String>(|s| {
162 let value = interp.try_convert_mut(s.as_str()).unwrap();
163 let roundtrip = value.try_convert_into_mut::<String>(&mut interp).unwrap();
164 assert_eq!(roundtrip, s);
165 });
166 }
167
168 #[test]
169 fn prop_owned_roundtrip() {
170 let mut interp = interpreter();
171 run_arbitrary::<String>(|s| {
172 let value = interp.try_convert_mut(s.clone()).unwrap();
173 let roundtrip = value.try_convert_into_mut::<String>(&mut interp).unwrap();
174 assert_eq!(roundtrip, s);
175 });
176 }
177
178 #[test]
179 fn prop_roundtrip_err() {
180 let mut interp = interpreter();
181 for b in [true, false] {
182 let value = interp.convert(b);
183 let result = value.try_convert_into_mut::<String>(&mut interp);
184 assert!(result.is_err());
185 }
186 }
187}