Skip to content

Commit 521f664

Browse files
Merge pull request RustPython#229 from AgentMacklin/master
Added some of the missing str methods
2 parents a29e882 + 7ac22d9 commit 521f664

File tree

1 file changed

+126
-0
lines changed

1 file changed

+126
-0
lines changed

vm/src/obj/objstr.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use super::objsequence::PySliceableSequence;
77
use super::objtype;
88
use num_bigint::ToBigInt;
99
use num_traits::ToPrimitive;
10+
use std::fmt;
1011
use std::hash::{Hash, Hasher};
1112

1213
pub fn init(context: &PyContext) {
@@ -43,6 +44,14 @@ pub fn init(context: &PyContext) {
4344
"startswith",
4445
context.new_rustfunc(str_startswith),
4546
);
47+
context.set_attr(&str_type, "isalnum", context.new_rustfunc(str_isalnum));
48+
context.set_attr(&str_type, "isnumeric", context.new_rustfunc(str_isnumeric));
49+
context.set_attr(&str_type, "isdigit", context.new_rustfunc(str_isdigit));
50+
context.set_attr(&str_type, "title", context.new_rustfunc(str_title));
51+
context.set_attr(&str_type, "swapcase", context.new_rustfunc(str_swapcase));
52+
context.set_attr(&str_type, "isalpha", context.new_rustfunc(str_isalpha));
53+
context.set_attr(&str_type, "replace", context.new_rustfunc(str_replace));
54+
context.set_attr(&str_type, "center", context.new_rustfunc(str_center));
4655
}
4756

4857
pub fn get_value(obj: &PyObjectRef) -> String {
@@ -238,6 +247,82 @@ fn str_endswith(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
238247
Ok(vm.ctx.new_bool(value.ends_with(pat.as_str())))
239248
}
240249

250+
fn str_swapcase(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
251+
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
252+
let value = get_value(&s);
253+
let mut swapped_str = String::with_capacity(value.len());
254+
for c in value.chars() {
255+
// to_uppercase returns an iterator, to_ascii_uppercase returns the char
256+
if c.is_lowercase() {
257+
swapped_str.push(c.to_ascii_uppercase());
258+
} else if c.is_uppercase() {
259+
swapped_str.push(c.to_ascii_lowercase());
260+
} else {
261+
swapped_str.push(c);
262+
}
263+
}
264+
Ok(vm.ctx.new_str(swapped_str))
265+
}
266+
267+
fn str_replace(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
268+
arg_check!(
269+
vm,
270+
args,
271+
required = [
272+
(s, Some(vm.ctx.str_type())),
273+
(old, Some(vm.ctx.str_type())),
274+
(rep, Some(vm.ctx.str_type()))
275+
],
276+
optional = [(n, None)]
277+
);
278+
let s = get_value(&s);
279+
let old_str = get_value(&old);
280+
let rep_str = get_value(&rep);
281+
let num_rep: usize = match n {
282+
Some(num) => objint::to_int(vm, num, 10)?.to_usize().unwrap(),
283+
None => 1,
284+
};
285+
let new_str = s.replacen(&old_str, &rep_str, num_rep);
286+
Ok(vm.ctx.new_str(new_str))
287+
}
288+
289+
fn str_title(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
290+
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
291+
let value = get_value(&s);
292+
let titled_str = value
293+
.split(' ')
294+
.map(|w| {
295+
let mut c = w.chars();
296+
match c.next() {
297+
None => String::new(),
298+
Some(f) => f
299+
.to_uppercase()
300+
.chain(c.flat_map(|t| t.to_lowercase()))
301+
.collect(),
302+
}
303+
})
304+
.collect::<Vec<_>>()
305+
.join(" ");
306+
Ok(vm.ctx.new_str(titled_str))
307+
}
308+
309+
// TODO: add ability to specify fill character, can't pass it to format!()
310+
fn str_center(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
311+
arg_check!(
312+
vm,
313+
args,
314+
required = [(s, Some(vm.ctx.str_type())), (len, Some(vm.ctx.int_type()))] // optional = [(chars, None)]
315+
);
316+
let value = get_value(&s);
317+
let len = objint::get_value(&len).to_usize().unwrap();
318+
// let rep_char = match chars {
319+
// Some(c) => get_value(&c),
320+
// None => " ".to_string(),
321+
// };
322+
let new_str = format!("{:^1$}", value, len);
323+
Ok(vm.ctx.new_str(new_str))
324+
}
325+
241326
fn str_startswith(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
242327
arg_check!(
243328
vm,
@@ -263,6 +348,47 @@ fn str_contains(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
263348
Ok(vm.ctx.new_bool(value.contains(needle.as_str())))
264349
}
265350

351+
fn str_isalnum(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
352+
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
353+
let is_alnum = get_value(&s).chars().all(|c| c.is_alphanumeric());
354+
Ok(vm.ctx.new_bool(is_alnum))
355+
}
356+
357+
fn str_isnumeric(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
358+
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
359+
let is_numeric = get_value(&s).chars().all(|c| c.is_numeric());
360+
Ok(vm.ctx.new_bool(is_numeric))
361+
}
362+
363+
fn str_isalpha(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
364+
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
365+
let is_alpha = get_value(&s).chars().all(|c| c.is_alphanumeric());
366+
Ok(vm.ctx.new_bool(is_alpha))
367+
}
368+
369+
fn str_isdigit(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
370+
arg_check!(vm, args, required = [(s, Some(vm.ctx.str_type()))]);
371+
let value = get_value(&s);
372+
// python's isdigit also checks if exponents are digits, these are the unicodes for exponents
373+
let valid_unicodes: [u16; 10] = [
374+
0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079,
375+
];
376+
let mut is_digit: bool = true;
377+
for c in value.chars() {
378+
if !c.is_digit(10) {
379+
// checking if char is exponent
380+
let char_as_uni: u16 = c as u16;
381+
if valid_unicodes.contains(&char_as_uni) {
382+
continue;
383+
} else {
384+
is_digit = false;
385+
break;
386+
}
387+
}
388+
}
389+
Ok(vm.ctx.new_bool(is_digit))
390+
}
391+
266392
fn str_getitem(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult {
267393
arg_check!(
268394
vm,

0 commit comments

Comments
 (0)