///|
/// Formatting and value conversion helpers.
///|
fn stringify_value_with_env(
value : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[String, RuntimeError] {
let _ = globals
let _ = builtins
let _ = io
Ok(value_to_string(value))
}
///|
fn hex_digit(n : Int32) -> Char {
if n < 10 {
('0'.to_int() + n).to_char().unwrap()
} else {
('a'.to_int() + (n - 10)).to_char().unwrap()
}
}
///|
fn hex_width(codepoint : Int32, width : Int32) -> String {
let buf = StringBuilder()
let mut i = 0
while i < width {
let shift = 4 * (width - 1 - i)
let digit = (codepoint >> shift) & 0xF
buf.write_char(hex_digit(digit))
i = i + 1
}
buf.to_string()
}
///|
fn ascii_escape(text : String) -> String {
let buf = StringBuilder()
for ch in text.to_array() {
let code = ch.to_int()
if code < 0x80 {
buf.write_char(ch)
} else if code <= 0xFFFF {
buf.write_string("\\u")
buf.write_string(hex_width(code, 4))
} else {
buf.write_string("\\U")
buf.write_string(hex_width(code, 8))
}
}
buf.to_string()
}
///|
fn fstring_value_to_text(
value : Value,
conv : Char?,
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[String, RuntimeError] {
match conv {
None => stringify_value_with_env(value, globals, builtins, io)
Some('s') =>
match
eval_builtin_call("str", [value], [], locals, globals, builtins, io) {
Ok(Some(Value::Str(text))) => Ok(text)
Ok(Some(other)) => Ok(value_to_string(other))
Ok(None) => stringify_value_with_env(value, globals, builtins, io)
Err(err) => Err(err)
}
Some('r') =>
match
eval_builtin_call("repr", [value], [], locals, globals, builtins, io) {
Ok(Some(Value::Str(text))) => Ok(text)
Ok(Some(other)) => Ok(value_to_string(other))
Ok(None) => stringify_value_with_env(value, globals, builtins, io)
Err(err) => Err(err)
}
Some('a') => {
let base = match
eval_builtin_call("repr", [value], [], locals, globals, builtins, io) {
Ok(Some(Value::Str(text))) => text
Ok(Some(other)) => value_to_string(other)
Ok(None) => value_to_string(value)
Err(err) => return Err(err)
}
Ok(ascii_escape(base))
}
Some(_) => stringify_value_with_env(value, globals, builtins, io)
}
}
///|
fn format_string_width(
text : String,
spec : String,
default_align : Char,
) -> String? {
if spec.length() == 0 {
return None
}
let chars = spec.to_array()
let mut fill = ' '
let mut align : Char? = None
let mut i = 0
if chars.length() >= 2 &&
(chars[1] == '<' || chars[1] == '>' || chars[1] == '^') {
fill = chars[0]
align = Some(chars[1])
i = 2
} else if chars[0] == '<' || chars[0] == '>' || chars[0] == '^' {
align = Some(chars[0])
i = 1
} else if is_digit(chars[0]) {
align = Some(default_align)
i = 0
} else {
return None
}
let start = i
while i < chars.length() && is_digit(chars[i]) {
i += 1
}
if start == i || i != chars.length() {
return None
}
let width_text = substring(spec, start, i)
let width = parse_decimal_int(width_text)
if width <= 0 {
return Some(text)
}
let len = text.length()
if len >= width {
return Some(text)
}
let pad = width - len
let left = match align.unwrap_or(default_align) {
'<' => 0
'>' => pad
'^' => pad / 2
_ => 0
}
let right = pad - left
let buf = StringBuilder()
for i = 0; i < left; i = i + 1 {
buf.write_char(fill)
}
buf.write_string(text)
for i = 0; i < right; i = i + 1 {
buf.write_char(fill)
}
Some(buf.to_string())
}
///|
fn parse_decimal_int(text : String) -> Int32 {
let chars = text.to_array()
let mut n = 0
for c in chars {
if is_digit(c) {
n = n * 10 + (c.to_int() - 48)
}
}
n
}
///|
fn fstring_int_to_base_string(
value : @bigint.BigInt,
base : Int32,
uppercase : Bool,
) -> String {
if value == 0N {
return "0"
}
let digits_table : Array[Char] = "0123456789abcdef".to_array()
let upper_table : Array[Char] = "0123456789ABCDEF".to_array()
let table = if uppercase { upper_table } else { digits_table }
let mut n = value
let b = @bigint.BigInt::from_int(base)
let digits : Array[Char] = []
while n > 0N {
let d = (n % b).to_int()
digits.push(table[d])
n = n / b
}
let buf = StringBuilder()
let mut i = digits.length()
while i > 0 {
i = i - 1
buf.write_char(digits[i])
}
buf.to_string()
}
///|
fn format_int_with_spec(value : @bigint.BigInt, spec : String) -> String? {
if spec.length() == 0 {
return None
}
let chars = spec.to_array()
let mut i = 0
let mut alternate = false
if chars[0] == '#' {
alternate = true
i = 1
}
let mut zero_pad = false
if i < chars.length() && chars[i] == '0' {
zero_pad = true
i += 1
}
let width_start = i
while i < chars.length() && is_digit(chars[i]) {
i += 1
}
let width = if i > width_start {
parse_decimal_int(substring(spec, width_start, i))
} else {
0
}
if i >= chars.length() {
return None
}
let mut grouping : Char? = None
if i < chars.length() - 1 && (chars[i] == '_' || chars[i] == ',') {
grouping = Some(chars[i])
i += 1
}
if i >= chars.length() {
return None
}
let typ = chars[i]
if i + 1 != chars.length() {
return None
}
let neg = value < 0N
let abs_value = if neg { -value } else { value }
let (prefix, base, uppercase, decimal) = match typ {
'd' => ("", 10, false, true)
'x' => ("0x", 16, false, false)
'X' => ("0X", 16, true, false)
'o' => ("0o", 8, false, false)
'b' => ("0b", 2, false, false)
_ => return None
}
fn group_decimal_underscores(text : String) -> String {
let chars = text.to_array()
if chars.length() <= 3 {
return text
}
let buf = StringBuilder()
let mut i = 0
let rem = chars.length() % 3
let first = if rem == 0 { 3 } else { rem }
while i < chars.length() {
if i > 0 {
buf.write_char('_')
}
let chunk_len = if i == 0 { first } else { 3 }
for j = 0; j < chunk_len; j = j + 1 {
buf.write_char(chars[i + j])
}
i = i + chunk_len
}
buf.to_string()
}
let mut digits = if decimal {
abs_value.to_string()
} else {
fstring_int_to_base_string(abs_value, base, uppercase)
}
match grouping {
Some('_') => if decimal { digits = group_decimal_underscores(digits) }
_ => ()
}
let sign = if neg { "-" } else { "" }
let alt_prefix = if alternate && !decimal { prefix } else { "" }
let mut core = sign.to_string() + alt_prefix + digits
if width > 0 && core.length() < width {
let pad = width - core.length()
if zero_pad {
let buf = StringBuilder()
buf.write_string(sign)
buf.write_string(alt_prefix)
for i = 0; i < pad; i = i + 1 {
buf.write_char('0')
}
buf.write_string(digits)
core = buf.to_string()
} else {
let buf = StringBuilder()
for i = 0; i < pad; i = i + 1 {
buf.write_char(' ')
}
buf.write_string(core)
core = buf.to_string()
}
}
Some(core)
}
///|
fn eval_fstring_text(
text : String,
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[String, RuntimeError] {
let buf = StringBuilder()
let chars = text.to_array()
let mut i = 0
while i < chars.length() {
let c = chars[i]
if c == '{' {
if i + 1 < chars.length() && chars[i + 1] == '{' {
buf.write_char('{')
i += 2
continue
}
let after_open = substring(text, i + 1, text.length())
let close_rel = find_fstring_expr_close(after_open)
if close_rel is None {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SyntaxError: f-string: expecting '}'".to_string(),
),
)
}
let close = i + 1 + close_rel.unwrap()
let inner = substring(text, i + 1, close)
if inner.trim(chars=" \t").length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SyntaxError: f-string: empty expression not allowed".to_string(),
),
)
}
let (expr_and_conv, format_spec_raw) = match
find_top_level_format_colon(inner) {
Some(pos) =>
(substring(inner, 0, pos), substring(inner, pos + 1, inner.length()))
None => (inner, "".to_string())
}
let (expr_raw, conv) = match
find_top_level_fstring_conversion_bang(expr_and_conv) {
Some(pos) =>
if pos + 1 < expr_and_conv.length() {
let conv_char = expr_and_conv.to_array()[pos + 1]
(substring(expr_and_conv, 0, pos), Some(conv_char))
} else {
(expr_and_conv, None)
}
None => (expr_and_conv, None)
}
let debug_eq = find_top_level_fstring_debug_equal(expr_raw)
let (expr_text, debug_prefix) = match debug_eq {
Some(pos) => {
let mut prefix_end = pos + 1
let expr_chars = expr_raw.to_array()
while prefix_end < expr_chars.length() {
let c = expr_chars[prefix_end]
if c == ' ' || c == '\t' {
prefix_end += 1
} else {
break
}
}
(
substring(expr_raw, 0, pos).trim(chars=" \t").to_owned(),
Some(substring(expr_raw, 0, prefix_end)),
)
}
None => (expr_raw.trim(chars=" \t").to_owned(), None)
}
let expr = match parse_expr(expr_text) {
Ok(value) => value
Err(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SyntaxError: f-string: invalid syntax".to_string(),
),
)
}
let value = match
eval_expr_with_env(expr, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let rendered = match
fstring_value_to_text(value, conv, locals, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
let format_spec = if format_spec_raw.trim(chars=" \t").length() == 0 {
"".to_string()
} else if format_spec_raw.contains("{") || format_spec_raw.contains("}") {
match
eval_fstring_text(format_spec_raw, locals, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
} else {
format_spec_raw
}
let formatted = if format_spec.length() == 0 {
rendered
} else {
match conv {
Some(_) =>
match format_string_width(rendered, format_spec, '<') {
Some(v) => v
None => rendered
}
None =>
match value {
Value::Int(n) =>
match format_int_with_spec(n, format_spec) {
Some(v) => v
None =>
match format_string_width(rendered, format_spec, '>') {
Some(v) => v
None => rendered
}
}
_ =>
match format_string_width(rendered, format_spec, '<') {
Some(v) => v
None => rendered
}
}
}
}
match debug_prefix {
Some(prefix) => {
buf.write_string(prefix)
buf.write_string(formatted)
}
None => buf.write_string(formatted)
}
i = close + 1
continue
}
if c == '}' {
if i + 1 < chars.length() && chars[i + 1] == '}' {
buf.write_char('}')
i += 2
continue
}
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SyntaxError: f-string: single '}' is not allowed".to_string(),
),
)
}
buf.write_char(c)
i += 1
}
Ok(buf.to_string())
}
///|
fn bytes_subsequence_contains(
haystack : Array[Int32],
needle : Array[Int32],
) -> Bool {
if needle.length() == 0 {
return true
}
if needle.length() > haystack.length() {
return false
}
let limit = haystack.length() - needle.length()
let mut i = 0
while i <= limit {
let mut matched = true
let mut j = 0
while j < needle.length() {
if haystack[i + j] != needle[j] {
matched = false
break
}
j = j + 1
}
if matched {
return true
}
i = i + 1
}
false
}
///|
fn bytes_contains(haystack : Array[Int32], needle : Value) -> Bool {
match needle {
Value::Int(v) => {
let idx = match bigint_to_int_checked(v) {
Ok(value) => value
Err(_) => return false
}
if idx < 0 || idx > 255 {
return false
}
for byte in haystack {
if byte == idx {
return true
}
}
false
}
Value::Bool(v) => {
let idx = if v { 1 } else { 0 }
for byte in haystack {
if byte == idx {
return true
}
}
false
}
Value::Bytes(values)
| Value::ByteArray(values)
| Value::MemoryView(values) => bytes_subsequence_contains(haystack, values)
_ => false
}
}
///|
fn to_complex(value : Value) -> Result[(Double, Double), RuntimeError] {
match value {
Value::Complex(real, imag) => Ok((real, imag))
Value::Float(v) => Ok((v, 0.0))
Value::Int(v) =>
match bigint_to_double_checked(v) {
Ok(value) => Ok((value, 0.0))
Err(err) => Err(err)
}
Value::Bool(v) => Ok((if v { 1.0 } else { 0.0 }, 0.0))
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"expected number".to_string(),
),
)
}
}
///|
fn pad_left(text : String, width : Int32, pad_char : Char) -> String {
if width <= text.length() {
text
} else {
let buf = StringBuilder()
let pad_len = width - text.length()
for _i = 0; _i < pad_len; _i = _i + 1 {
buf.write_char(pad_char)
}
buf.write_string(text)
buf.to_string()
}
}
///|
fn pad_right(text : String, width : Int32, pad_char : Char) -> String {
if width <= text.length() {
text
} else {
let buf = StringBuilder()
buf.write_string(text)
let pad_len = width - text.length()
for _i = 0; _i < pad_len; _i = _i + 1 {
buf.write_char(pad_char)
}
buf.to_string()
}
}
///|
fn pad_numeric(text : String, width : Int32, zero_pad : Bool) -> String {
if !zero_pad {
return pad_left(text, width, ' ')
}
if width <= text.length() {
return text
}
match text.get_char(0) {
Some('-') => {
let chars = text.to_array()
let rest_chars : Array[Char] = []
for idx = 1; idx < chars.length(); idx = idx + 1 {
rest_chars.push(chars[idx])
}
let rest = String::from_array(rest_chars)
let pad_len = width - rest.length() - 1
if pad_len <= 0 {
text
} else {
let buf = StringBuilder()
buf.write_char('-')
for _i = 0; _i < pad_len; _i = _i + 1 {
buf.write_char('0')
}
buf.write_string(rest)
buf.to_string()
}
}
_ => pad_left(text, width, '0')
}
}
///|
fn format_decimal_value(value : Value) -> Result[String, RuntimeError] {
match value {
Value::Int(v) => Ok(v.to_string())
Value::Bool(v) => Ok(if v { "1" } else { "0" })
Value::Float(v) => Ok(v.to_int64().to_string())
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"expected number".to_string(),
),
)
}
}
///|
fn format_hex_value(
value : Value,
uppercase : Bool,
) -> Result[String, RuntimeError] {
match value {
Value::Int(v) => {
let neg = v < 0N
let abs_value = if neg { -v } else { v }
let mut out = abs_value.to_string(radix=16)
if uppercase {
out = out.to_upper()
}
if neg {
out = "-" + out
}
Ok(out)
}
Value::Bool(v) => Ok(if v { "1" } else { "0" })
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"expected number".to_string(),
),
)
}
}
///|
fn format_octal_value(value : Value) -> Result[String, RuntimeError] {
match value {
Value::Int(v) => Ok(bigint_to_base_string(v, 8, ""))
Value::Bool(v) => Ok(if v { "1" } else { "0" })
Value::Float(v) =>
Ok(bigint_to_base_string(@bigint.BigInt::from_int64(v.to_int64()), 8, ""))
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"expected number".to_string(),
),
)
}
}
///|
fn round_float_to_int64(value : Double) -> Int64 {
if value >= 0.0 {
@math.floor(value + 0.5).to_int64()
} else {
@math.ceil(value - 0.5).to_int64()
}
}
///|
fn format_float_fixed(value : Double, precision : Int32) -> String {
// Minimal fixed-point formatting for `%f` used throughout the CPython stdlib.
// Only intended for small precisions (e.g. `%.3f`).
if precision <= 0 {
let rounded = round_float_to_int64(value)
return rounded.to_string()
}
let neg = value < 0.0
let abs_value = if neg { -value } else { value }
let mut scale : Int64 = 1
let mut i = 0
while i < precision && scale < 1_000_000_000_000_000_000L {
scale = scale * 10L
i = i + 1
}
// If precision is too large for Int64 scaling, fall back to default formatting.
if i != precision {
return (if neg { "-" } else { "" }) + abs_value.to_string()
}
let scaled = round_float_to_int64(abs_value * scale.to_double())
let int_part = scaled / scale
let frac_part = scaled % scale
let frac_text_raw = frac_part.to_string()
let frac_text = pad_left(frac_text_raw, precision, '0')
(if neg { "-" } else { "" }) + int_part.to_string() + "." + frac_text
}
///|
fn format_float_value(
value : Value,
precision_opt : Int32?,
) -> Result[String, RuntimeError] {
let precision = match precision_opt {
Some(p) => p
None => 6
}
match value {
Value::Float(v) => Ok(format_float_fixed(v, precision))
Value::Int(v) =>
match bigint_to_double_checked(v) {
Ok(d) => Ok(format_float_fixed(d, precision))
Err(err) => Err(err)
}
Value::Bool(v) =>
Ok(format_float_fixed(if v { 1.0 } else { 0.0 }, precision))
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"expected number".to_string(),
),
)
}
}
///|
fn format_percent_string(
format : String,
value : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[String, RuntimeError] {
touch_env([], globals, builtins, io)
let mut mapping_pairs : Array[(Value, Value)]? = None
let mapping_value = value
let args = match value {
Value::Tuple(values) => values
Value::Instance(inst) =>
match get_named_value(inst.dict, tuple_storage_name) {
Some(Value::Tuple(values)) => values
_ => [value]
}
Value::Dict(pairs) => {
mapping_pairs = Some(pairs)
[value]
}
_ => [value]
}
let buf = StringBuilder()
let chars = format.to_array()
let mut arg_index = 0
let mut used_positional = false
let mut i = 0
while i < chars.length() {
let ch = chars[i]
if ch != '%' {
buf.write_char(ch)
i = i + 1
continue
}
if i + 1 >= chars.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"incomplete format".to_string(),
),
)
}
if chars[i + 1] == '%' {
buf.write_char('%')
i = i + 2
continue
}
let mut j = i + 1
let mut key_name : String? = None
if chars[j] == '(' {
let mut k = j + 1
while k < chars.length() && chars[k] != ')' {
k = k + 1
}
if k >= chars.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"incomplete format".to_string(),
),
)
}
let key_chars : Array[Char] = []
for idx = j + 1; idx < k; idx = idx + 1 {
key_chars.push(chars[idx])
}
key_name = Some(String::from_array(key_chars))
j = k + 1
}
if j >= chars.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"incomplete format".to_string(),
),
)
}
fn star_int_value(name : String, v : Value) -> Result[Int32, RuntimeError] {
match v {
Value::Int(b) => Ok(b.to_int64().to_int())
Value::Bool(b) => Ok(if b { 1 } else { 0 })
Value::Float(f) => Ok(f.to_int64().to_int())
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
name + " is not an integer".to_string(),
),
)
}
}
let mut left_adjust = false
let mut zero_pad = false
while j < chars.length() {
if chars[j] == '-' {
left_adjust = true
zero_pad = false
j = j + 1
continue
}
if chars[j] == '0' && !left_adjust {
zero_pad = true
j = j + 1
continue
}
break
}
let mut width = 0
if j < chars.length() && chars[j] == '*' {
if arg_index >= args.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"not enough arguments for format string".to_string(),
),
)
}
let w = match star_int_value("width".to_string(), args[arg_index]) {
Ok(v) => v
Err(err) => return Err(err)
}
arg_index = arg_index + 1
used_positional = true
if w < 0 {
left_adjust = true
width = -w
} else {
width = w
}
j = j + 1
} else {
while j < chars.length() && chars[j].is_ascii_digit() {
width = width * 10 + (chars[j].to_int() - '0'.to_int())
j = j + 1
}
}
// Optional precision: `%.3f`
let mut precision : Int32? = None
if j < chars.length() && chars[j] == '.' {
j = j + 1
if j < chars.length() && chars[j] == '*' {
if arg_index >= args.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"not enough arguments for format string".to_string(),
),
)
}
let p = match star_int_value("precision".to_string(), args[arg_index]) {
Ok(v) => v
Err(err) => return Err(err)
}
arg_index = arg_index + 1
used_positional = true
precision = Some(p)
j = j + 1
} else {
let mut p = 0
while j < chars.length() && chars[j].is_ascii_digit() {
p = p * 10 + (chars[j].to_int() - '0'.to_int())
j = j + 1
}
precision = Some(p)
}
}
if j >= chars.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"incomplete format".to_string(),
),
)
}
let arg = match key_name {
Some(key) => {
let key_value = Value::Str(key)
match mapping_pairs {
Some(pairs) => {
let index = match dict_find_index(pairs, key_value) {
Ok(v) => v
Err(err) => return Err(err)
}
match index {
Some(pos) => pairs[pos].1
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Key,
"key not found".to_string(),
),
)
}
}
None => {
// Mapping-based formatting should accept any object that implements
// `__getitem__` (e.g. `instance.__dict__` wrapper used by logging).
let getitem = match
get_attr_from_value(
mapping_value, "__getitem__", globals, builtins, io,
) {
Ok(v) => v
Err(err) =>
return match err.kind {
RuntimeErrorKind::Attribute =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"format requires a mapping".to_string(),
),
)
_ => Err(err)
}
}
match
call_callable_with_env(
getitem,
[key_value],
[],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
}
}
}
None => {
if arg_index >= args.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"not enough arguments for format string".to_string(),
),
)
}
let arg = args[arg_index]
arg_index = arg_index + 1
used_positional = true
arg
}
}
let spec = chars[j]
fn repr_text(
arg : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[String, RuntimeError] {
match eval_builtin_call("repr", [arg], [], [], globals, builtins, io) {
Ok(Some(Value::Str(text))) => Ok(text)
Ok(Some(other)) => Ok(value_to_string(other))
Ok(None) => Ok(value_to_string(arg))
Err(err) => Err(err)
}
}
let raw = match spec {
's' => Ok(value_to_string(arg))
'r' => repr_text(arg, globals, builtins, io)
'c' =>
match arg {
Value::Str(s) =>
if s.to_array().length() == 1 {
Ok(s)
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"%c requires int or char".to_string(),
),
)
}
_ => {
let code = match arg {
Value::Int(v) => v
Value::Bool(v) => if v { 1N } else { 0N }
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"%c requires int or char".to_string(),
),
)
}
if code < 0N || code > 0x10FFFFN {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"OverflowError: %c arg not in range(0x110000)".to_string(),
),
)
}
Ok(char_to_string(Int32::unsafe_to_char(code.to_int64().to_int())))
}
}
'd' | 'i' => format_decimal_value(arg)
'o' => format_octal_value(arg)
'x' => format_hex_value(arg, false)
'X' => format_hex_value(arg, true)
'f' | 'F' =>
match format_float_value(arg, precision) {
Ok(v) => Ok(v)
Err(err) => Err(err)
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"unsupported format character".to_string(),
),
)
}
let formatted = match raw {
Ok(text) =>
if left_adjust {
pad_right(text, width, ' ')
} else {
match spec {
'd' | 'i' | 'o' | 'x' | 'X' => pad_numeric(text, width, zero_pad)
_ => pad_left(text, width, if zero_pad { '0' } else { ' ' })
}
}
Err(err) => return Err(err)
}
buf.write_string(formatted)
i = j + 1
}
if used_positional && arg_index < args.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"not all arguments converted during string formatting".to_string(),
),
)
}
Ok(buf.to_string())
}