use crate::{
AsObject, PyObjectRef, PyResult, VirtualMachine,
builtins::{PyList, PyTuple},
class::PyClassImpl,
stdlib::_ast::NodeAst,
};
use rustpython_common::wtf8::Wtf8Buf;
fn repr_ast_list(vm: &VirtualMachine, items: Vec<PyObjectRef>, depth: usize) -> PyResult<Wtf8Buf> {
if items.is_empty() {
let empty_list: PyObjectRef = vm.ctx.new_list(vec![]).into();
return Ok(empty_list.repr(vm)?.as_wtf8().to_owned());
}
let mut parts: Vec<Wtf8Buf> = Vec::new();
let first = &items[0];
let last = items.last().unwrap();
for (idx, item) in [first, last].iter().enumerate() {
if idx == 1 && items.len() == 1 {
break;
}
let repr = if item.fast_isinstance(&NodeAst::make_static_type()) {
repr_ast_node(vm, item, depth.saturating_sub(1))?
} else {
item.repr(vm)?.as_wtf8().to_owned()
};
parts.push(repr);
}
let mut rendered = Wtf8Buf::from("[");
if !parts.is_empty() {
rendered.push_wtf8(&parts[0]);
}
if items.len() > 2 {
if !parts[0].is_empty() {
rendered.push_wtf8(", ...".as_ref());
}
if parts.len() > 1 {
rendered.push_wtf8(", ".as_ref());
rendered.push_wtf8(&parts[1]);
}
} else if parts.len() > 1 {
rendered.push_wtf8(", ".as_ref());
rendered.push_wtf8(&parts[1]);
}
rendered.push_wtf8("]".as_ref());
Ok(rendered)
}
fn repr_ast_tuple(vm: &VirtualMachine, items: Vec<PyObjectRef>, depth: usize) -> PyResult<Wtf8Buf> {
if items.is_empty() {
let empty_tuple: PyObjectRef = vm.ctx.empty_tuple.clone().into();
return Ok(empty_tuple.repr(vm)?.as_wtf8().to_owned());
}
let mut parts: Vec<Wtf8Buf> = Vec::new();
let first = &items[0];
let last = items.last().unwrap();
for (idx, item) in [first, last].iter().enumerate() {
if idx == 1 && items.len() == 1 {
break;
}
let repr = if item.fast_isinstance(&NodeAst::make_static_type()) {
repr_ast_node(vm, item, depth.saturating_sub(1))?
} else {
item.repr(vm)?.as_wtf8().to_owned()
};
parts.push(repr);
}
let mut rendered = Wtf8Buf::from("(");
if !parts.is_empty() {
rendered.push_wtf8(&parts[0]);
}
if items.len() > 2 {
if !parts[0].is_empty() {
rendered.push_wtf8(", ...".as_ref());
}
if parts.len() > 1 {
rendered.push_wtf8(", ".as_ref());
rendered.push_wtf8(&parts[1]);
}
} else if parts.len() > 1 {
rendered.push_wtf8(", ".as_ref());
rendered.push_wtf8(&parts[1]);
}
if items.len() == 1 {
rendered.push_wtf8(",".as_ref());
}
rendered.push_wtf8(")".as_ref());
Ok(rendered)
}
pub(crate) fn repr_ast_node(
vm: &VirtualMachine,
obj: &PyObjectRef,
depth: usize,
) -> PyResult<Wtf8Buf> {
let cls = obj.class();
if depth == 0 {
let mut s = Wtf8Buf::from(&*cls.name());
s.push_wtf8("(...)".as_ref());
return Ok(s);
}
let fields = cls.get_attr(vm.ctx.intern_str("_fields"));
let fields = match fields {
Some(fields) => fields.try_to_value::<Vec<crate::builtins::PyStrRef>>(vm)?,
None => {
let mut s = Wtf8Buf::from(&*cls.name());
s.push_wtf8("(...)".as_ref());
return Ok(s);
}
};
if fields.is_empty() {
let mut s = Wtf8Buf::from(&*cls.name());
s.push_wtf8("()".as_ref());
return Ok(s);
}
let mut rendered = Wtf8Buf::from(&*cls.name());
rendered.push_wtf8("(".as_ref());
for (idx, field) in fields.iter().enumerate() {
let value = obj.get_attr(field, vm)?;
let value_repr = if value.fast_isinstance(vm.ctx.types.list_type) {
let list = value
.downcast::<PyList>()
.expect("list type should downcast");
repr_ast_list(vm, list.borrow_vec().to_vec(), depth)?
} else if value.fast_isinstance(vm.ctx.types.tuple_type) {
let tuple = value
.downcast::<PyTuple>()
.expect("tuple type should downcast");
repr_ast_tuple(vm, tuple.as_slice().to_vec(), depth)?
} else if value.fast_isinstance(&NodeAst::make_static_type()) {
repr_ast_node(vm, &value, depth.saturating_sub(1))?
} else {
value.repr(vm)?.as_wtf8().to_owned()
};
if idx > 0 {
rendered.push_wtf8(", ".as_ref());
}
rendered.push_wtf8(field.as_wtf8());
rendered.push_wtf8("=".as_ref());
rendered.push_wtf8(&value_repr);
}
rendered.push_wtf8(")".as_ref());
Ok(rendered)
}