// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
fn clamp_range(start : Int, end : Int, len : Int) -> (Int, Int) {
let mut s = start
if s < 0 {
s = 0
}
let e = if end < 0 || end > len { len } else { end }
let s2 = if s > e { e } else { s }
(s2, e)
}
fn digit_value(c : Char) -> Int? {
let lower = c.to_ascii_lowercase()
if lower.is_ascii_digit() {
Some(lower.to_int() - '0'.to_int())
} else if lower >= 'a' && lower <= 'z' {
Some(10 + lower.to_int() - 'a'.to_int())
} else {
None
}
}
fn parse_unsigned(
s : StringView,
start : Int,
end : Int,
base : Int,
) -> (UInt, Int, Int)? {
if base < 2 || base > 36 {
return None
}
let mut idx = start
let mut value = 0U
let mut count = 0
while idx < end {
match s.get_char(idx) {
None => break
Some(c) =>
match digit_value(c) {
None => break
Some(digit) => {
if digit >= base {
break
}
value = value * base.reinterpret_as_uint() + digit.reinterpret_as_uint()
idx = idx + 1
count = count + 1
}
}
}
}
if count == 0 {
None
} else {
Some((value, idx, count))
}
}
///|
/// Parse a signed integer from `s[start:end)`. Returns (value, next_index) on success.
pub fn parse_int(
s : StringView,
start? : Int = 0,
end? : Int = -1,
whole? : Bool = false,
) -> (Int, Int)? {
let (start, end) = clamp_range(start, end, s.length())
if start >= end {
return None
}
let mut idx = start
let mut sign = 1
match s.get_char(idx) {
Some('+') => idx = idx + 1
Some('-') => {
sign = -1
idx = idx + 1
}
_ => ()
}
let parsed = parse_unsigned(s, idx, end, 10)
match parsed {
None => None
Some((value, next_idx, _)) => {
if whole && next_idx != end {
None
} else {
Some((value.reinterpret_as_int() * sign, next_idx))
}
}
}
}
///|
/// Parse an unsigned integer from `s[start:end)` with optional base. Returns (value, next_index).
pub fn parse_uint(
s : StringView,
start? : Int = 0,
end? : Int = -1,
whole? : Bool = false,
base? : Int = 10,
) -> (UInt, Int)? {
let (start, end) = clamp_range(start, end, s.length())
if start >= end {
return None
}
let mut idx = start
match s.get_char(idx) {
Some('+') => idx = idx + 1
Some('-') => return None
_ => ()
}
let parsed = parse_unsigned(s, idx, end, base)
match parsed {
None => None
Some((value, next_idx, _)) => {
if whole && next_idx != end {
None
} else {
Some((value, next_idx))
}
}
}
}
fn parse_exponent(s : StringView, start : Int, end : Int) -> (Int, Int)? {
let mut idx = start
if idx >= end {
return None
}
let mut sign = 1
match s.get_char(idx) {
Some('+') => idx = idx + 1
Some('-') => {
sign = -1
idx = idx + 1
}
_ => ()
}
let parsed = parse_unsigned(s, idx, end, 10)
match parsed {
None => None
Some((value, next_idx, count)) => {
if count == 0 {
None
} else {
Some((value.reinterpret_as_int() * sign, next_idx))
}
}
}
}
///|
/// Parse a double from `s[start:end)`. Returns (value, next_index) on success.
pub fn parse_double(
s : StringView,
start? : Int = 0,
end? : Int = -1,
whole? : Bool = false,
) -> (Double, Int)? {
let (start, end) = clamp_range(start, end, s.length())
if start >= end {
return None
}
let mut idx = start
let mut sign = 1.0
match s.get_char(idx) {
Some('+') => idx = idx + 1
Some('-') => {
sign = -1.0
idx = idx + 1
}
_ => ()
}
let mut value = 0.0
let mut int_digits = 0
while idx < end {
match s.get_char(idx) {
Some(c) if c.is_ascii_digit() => {
value = value * 10.0 + (c.to_int() - '0'.to_int()).to_double()
idx = idx + 1
int_digits = int_digits + 1
}
_ => break
}
}
let mut frac_digits = 0
if idx < end {
match s.get_char(idx) {
Some('.') => {
idx = idx + 1
let mut scale = 0.1
while idx < end {
match s.get_char(idx) {
Some(c) if c.is_ascii_digit() => {
value = value + (c.to_int() - '0'.to_int()).to_double() * scale
scale = scale * 0.1
idx = idx + 1
frac_digits = frac_digits + 1
}
_ => break
}
}
}
_ => ()
}
}
if int_digits == 0 && frac_digits == 0 {
return None
}
if idx < end {
match s.get_char(idx) {
Some('e') | Some('E') => {
let exp_start = idx
idx = idx + 1
match parse_exponent(s, idx, end) {
None => idx = exp_start
Some((exp, next_idx)) => {
idx = next_idx
if exp != 0 {
let mut pow10 = 1.0
let mut count = exp.abs()
while count > 0 {
pow10 = pow10 * 10.0
count = count - 1
}
if exp < 0 {
value = value / pow10
} else {
value = value * pow10
}
}
}
}
}
_ => ()
}
}
if whole && idx != end {
None
} else {
Some((value * sign, idx))
}
}