#![cfg(any(test, feature = "alloc"))]
extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;

const ASCII_MAX: u8 = i8::MAX as u8;

pub trait StringExt {
    fn is_ascii(&self) -> bool;
}

impl StringExt for soroban_sdk::String {
    fn is_ascii(&self) -> bool {
        let mut bytes: Vec<u8> = vec![0; self.len() as usize];
        self.copy_into_slice(&mut bytes);

        for &byte in bytes.iter() {
            if byte > ASCII_MAX {
                return false;
            }
        }
        true
    }
}

#[cfg(test)]
mod tests {
    use stellar_axelar_std::{Env, String};

    use super::*;
    use crate as stellar_axelar_std;

    #[test]
    fn validate_ascii_strings_are_ascii() {
        let test_cases = [
            "",
            "Hello, world!",
            "The quick brown fox jumps over the lazy dog.",
            "1234567890",
            "!@#$%^&*()_+-=[]{}|;:',.<>/?",
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
            "abcdefghijklmnopqrstuvwxyz",
        ];

        let env = Env::default();
        for ascii_string in test_cases {
            let soroban_ascii_string = String::from_str(&env, ascii_string);
            assert!(soroban_ascii_string.is_ascii());
        }
    }

    #[test]
    fn validate_non_ascii_strings_not_ascii() {
        let test_cases = [
            "Hello, 世界!",
            "こんにちは世界",
            "Привет, мир!",
            "안녕하세요 세계",
            "مرحبا بالعالم",
            "Bonjour le monde 🌍",
            "¡Hola, mundo!",
            "Γειά σου Κόσμε",
            "नमस्ते दुनिया",
            "שלום עולם",
            "你好，世界",
        ];

        let env = Env::default();
        for ascii_string in test_cases {
            let soroban_non_ascii_string = String::from_str(&env, ascii_string);
            assert!(!soroban_non_ascii_string.is_ascii());
        }
    }
}
