1use polars::prelude::*;
2use pyo3::prelude::*;
3
4use super::datatype::PyDataTypeExpr;
5use crate::PyExpr;
6use crate::conversion::Wrap;
7use crate::error::PyPolarsErr;
8
9#[pymethods]
10impl PyExpr {
11 fn str_join(&self, delimiter: &str, ignore_nulls: bool) -> Self {
12 self.inner
13 .clone()
14 .str()
15 .join(delimiter, ignore_nulls)
16 .into()
17 }
18
19 #[pyo3(signature = (format, strict, exact, cache))]
20 fn str_to_date(&self, format: Option<String>, strict: bool, exact: bool, cache: bool) -> Self {
21 let format = format.map(|x| x.into());
22
23 let options = StrptimeOptions {
24 format,
25 strict,
26 exact,
27 cache,
28 };
29 self.inner.clone().str().to_date(options).into()
30 }
31
32 #[pyo3(signature = (format, time_unit, time_zone, strict, exact, cache, ambiguous))]
33 fn str_to_datetime(
34 &self,
35 format: Option<String>,
36 time_unit: Option<Wrap<TimeUnit>>,
37 time_zone: Wrap<Option<TimeZone>>,
38 strict: bool,
39 exact: bool,
40 cache: bool,
41 ambiguous: Self,
42 ) -> Self {
43 let format = format.map(|x| x.into());
44 let time_zone = time_zone.0;
45
46 let options = StrptimeOptions {
47 format,
48 strict,
49 exact,
50 cache,
51 };
52 self.inner
53 .clone()
54 .str()
55 .to_datetime(
56 time_unit.map(|tu| tu.0),
57 time_zone,
58 options,
59 ambiguous.inner,
60 )
61 .into()
62 }
63
64 #[pyo3(signature = (format, strict, cache))]
65 fn str_to_time(&self, format: Option<String>, strict: bool, cache: bool) -> Self {
66 let format = format.map(|x| x.into());
67
68 let options = StrptimeOptions {
69 format,
70 strict,
71 cache,
72 exact: true,
73 };
74 self.inner.clone().str().to_time(options).into()
75 }
76
77 fn str_strip_chars(&self, matches: Self) -> Self {
78 self.inner.clone().str().strip_chars(matches.inner).into()
79 }
80
81 fn str_strip_chars_start(&self, matches: Self) -> Self {
82 self.inner
83 .clone()
84 .str()
85 .strip_chars_start(matches.inner)
86 .into()
87 }
88
89 fn str_strip_chars_end(&self, matches: Self) -> Self {
90 self.inner
91 .clone()
92 .str()
93 .strip_chars_end(matches.inner)
94 .into()
95 }
96
97 fn str_strip_prefix(&self, prefix: Self) -> Self {
98 self.inner.clone().str().strip_prefix(prefix.inner).into()
99 }
100
101 fn str_strip_suffix(&self, suffix: Self) -> Self {
102 self.inner.clone().str().strip_suffix(suffix.inner).into()
103 }
104
105 fn str_slice(&self, offset: Self, length: Self) -> Self {
106 self.inner
107 .clone()
108 .str()
109 .slice(offset.inner, length.inner)
110 .into()
111 }
112
113 fn str_head(&self, n: Self) -> Self {
114 self.inner.clone().str().head(n.inner).into()
115 }
116
117 fn str_tail(&self, n: Self) -> Self {
118 self.inner.clone().str().tail(n.inner).into()
119 }
120
121 fn str_to_uppercase(&self) -> Self {
122 self.inner.clone().str().to_uppercase().into()
123 }
124
125 fn str_to_lowercase(&self) -> Self {
126 self.inner.clone().str().to_lowercase().into()
127 }
128
129 #[cfg(feature = "nightly")]
130 fn str_to_titlecase(&self) -> Self {
131 self.inner.clone().str().to_titlecase().into()
132 }
133
134 fn str_len_bytes(&self) -> Self {
135 self.inner.clone().str().len_bytes().into()
136 }
137
138 fn str_len_chars(&self) -> Self {
139 self.inner.clone().str().len_chars().into()
140 }
141
142 #[cfg(feature = "regex")]
143 fn str_replace_n(&self, pat: Self, val: Self, literal: bool, n: i64) -> Self {
144 self.inner
145 .clone()
146 .str()
147 .replace_n(pat.inner, val.inner, literal, n)
148 .into()
149 }
150
151 #[cfg(feature = "regex")]
152 fn str_replace_all(&self, pat: Self, val: Self, literal: bool) -> Self {
153 self.inner
154 .clone()
155 .str()
156 .replace_all(pat.inner, val.inner, literal)
157 .into()
158 }
159
160 fn str_normalize(&self, form: Wrap<UnicodeForm>) -> Self {
161 self.inner.clone().str().normalize(form.0).into()
162 }
163
164 fn str_reverse(&self) -> Self {
165 self.inner.clone().str().reverse().into()
166 }
167
168 fn str_pad_start(&self, length: PyExpr, fill_char: char) -> Self {
169 self.inner
170 .clone()
171 .str()
172 .pad_start(length.inner, fill_char)
173 .into()
174 }
175
176 fn str_pad_end(&self, length: PyExpr, fill_char: char) -> Self {
177 self.inner
178 .clone()
179 .str()
180 .pad_end(length.inner, fill_char)
181 .into()
182 }
183
184 fn str_zfill(&self, length: PyExpr) -> Self {
185 self.inner.clone().str().zfill(length.inner).into()
186 }
187
188 #[pyo3(signature = (pat, literal, strict))]
189 #[cfg(feature = "regex")]
190 fn str_contains(&self, pat: Self, literal: Option<bool>, strict: bool) -> Self {
191 match literal {
192 Some(true) => self.inner.clone().str().contains_literal(pat.inner).into(),
193 _ => self.inner.clone().str().contains(pat.inner, strict).into(),
194 }
195 }
196
197 #[pyo3(signature = (pat, literal, strict))]
198 #[cfg(feature = "regex")]
199 fn str_find(&self, pat: Self, literal: Option<bool>, strict: bool) -> Self {
200 match literal {
201 Some(true) => self.inner.clone().str().find_literal(pat.inner).into(),
202 _ => self.inner.clone().str().find(pat.inner, strict).into(),
203 }
204 }
205
206 fn str_ends_with(&self, sub: Self) -> Self {
207 self.inner.clone().str().ends_with(sub.inner).into()
208 }
209
210 fn str_starts_with(&self, sub: Self) -> Self {
211 self.inner.clone().str().starts_with(sub.inner).into()
212 }
213
214 fn str_hex_encode(&self) -> Self {
215 self.inner.clone().str().hex_encode().into()
216 }
217
218 #[cfg(feature = "binary_encoding")]
219 fn str_hex_decode(&self, strict: bool) -> Self {
220 self.inner.clone().str().hex_decode(strict).into()
221 }
222
223 fn str_base64_encode(&self) -> Self {
224 self.inner.clone().str().base64_encode().into()
225 }
226
227 #[cfg(feature = "binary_encoding")]
228 fn str_base64_decode(&self, strict: bool) -> Self {
229 self.inner.clone().str().base64_decode(strict).into()
230 }
231
232 #[pyo3(signature = (base, dtype=Some(Wrap(DataType::Int64)), strict=true))]
233 fn str_to_integer(&self, base: Self, dtype: Option<Wrap<DataType>>, strict: bool) -> Self {
234 self.inner
235 .clone()
236 .str()
237 .to_integer(base.inner, dtype.map(|wrap| wrap.0), strict)
238 .into()
239 }
240
241 #[cfg(feature = "extract_jsonpath")]
242 fn str_json_decode(&self, dtype: PyDataTypeExpr) -> Self {
243 self.inner.clone().str().json_decode(dtype.inner).into()
244 }
245
246 #[cfg(feature = "extract_jsonpath")]
247 fn str_json_path_match(&self, pat: Self) -> Self {
248 self.inner.clone().str().json_path_match(pat.inner).into()
249 }
250
251 fn str_extract(&self, pat: Self, group_index: usize) -> Self {
252 self.inner
253 .clone()
254 .str()
255 .extract(pat.inner, group_index)
256 .into()
257 }
258
259 fn str_extract_all(&self, pat: Self) -> Self {
260 self.inner.clone().str().extract_all(pat.inner).into()
261 }
262
263 #[cfg(feature = "extract_groups")]
264 fn str_extract_groups(&self, pat: &str) -> PyResult<Self> {
265 Ok(self
266 .inner
267 .clone()
268 .str()
269 .extract_groups(pat)
270 .map_err(PyPolarsErr::from)?
271 .into())
272 }
273
274 fn str_count_matches(&self, pat: Self, literal: bool) -> Self {
275 self.inner
276 .clone()
277 .str()
278 .count_matches(pat.inner, literal)
279 .into()
280 }
281
282 fn str_split(&self, by: Self) -> Self {
283 self.inner.clone().str().split(by.inner).into()
284 }
285
286 fn str_split_inclusive(&self, by: Self) -> Self {
287 self.inner.clone().str().split_inclusive(by.inner).into()
288 }
289
290 fn str_split_exact(&self, by: Self, n: usize) -> Self {
291 self.inner.clone().str().split_exact(by.inner, n).into()
292 }
293
294 fn str_split_exact_inclusive(&self, by: Self, n: usize) -> Self {
295 self.inner
296 .clone()
297 .str()
298 .split_exact_inclusive(by.inner, n)
299 .into()
300 }
301
302 fn str_splitn(&self, by: Self, n: usize) -> Self {
303 self.inner.clone().str().splitn(by.inner, n).into()
304 }
305
306 fn str_to_decimal(&self, scale: usize) -> Self {
307 self.inner.clone().str().to_decimal(scale).into()
308 }
309
310 #[cfg(feature = "find_many")]
311 fn str_contains_any(&self, patterns: PyExpr, ascii_case_insensitive: bool) -> Self {
312 self.inner
313 .clone()
314 .str()
315 .contains_any(patterns.inner, ascii_case_insensitive)
316 .into()
317 }
318 #[cfg(feature = "find_many")]
319 fn str_replace_many(
320 &self,
321 patterns: PyExpr,
322 replace_with: PyExpr,
323 ascii_case_insensitive: bool,
324 ) -> Self {
325 self.inner
326 .clone()
327 .str()
328 .replace_many(patterns.inner, replace_with.inner, ascii_case_insensitive)
329 .into()
330 }
331
332 #[cfg(feature = "find_many")]
333 fn str_extract_many(
334 &self,
335 patterns: PyExpr,
336 ascii_case_insensitive: bool,
337 overlapping: bool,
338 ) -> Self {
339 self.inner
340 .clone()
341 .str()
342 .extract_many(patterns.inner, ascii_case_insensitive, overlapping)
343 .into()
344 }
345
346 #[cfg(feature = "find_many")]
347 fn str_find_many(
348 &self,
349 patterns: PyExpr,
350 ascii_case_insensitive: bool,
351 overlapping: bool,
352 ) -> Self {
353 self.inner
354 .clone()
355 .str()
356 .find_many(patterns.inner, ascii_case_insensitive, overlapping)
357 .into()
358 }
359
360 #[cfg(feature = "regex")]
361 fn str_escape_regex(&self) -> Self {
362 self.inner.clone().str().escape_regex().into()
363 }
364
365 #[staticmethod]
366 fn str_format(f_string: String, exprs: Vec<PyExpr>) -> PyResult<Self> {
367 let exprs = exprs.into_iter().map(|e| e.inner).collect::<Vec<_>>();
368 Ok(format_str(&f_string, exprs)
369 .map_err(PyPolarsErr::from)?
370 .into())
371 }
372}