algosul-core 0.0.5

Some user-friendly yet personalized tools
Documentation
use std::{
    borrow::Borrow,
    ffi::{CStr, CString, OsStr, OsString},
    str::FromStr,
};

use unicode_xid::UnicodeXID;
pub trait StrExt {
    type Owned: Borrow<Self>;
    fn is_valid_ident(&self) -> bool;
    fn to_valid_ident(&self) -> Self::Owned;
}
impl StrExt for str {
    type Owned = String;

    fn is_valid_ident(&self) -> bool {
        let mut chars = self.chars();
        match chars.next() {
            Some(c) if c.is_xid_start() || c == '_' => {
                chars.all(|c| c.is_xid_continue())
            }
            _ => false,
        }
    }

    fn to_valid_ident(&self) -> Self::Owned {
        let mut chars = self.chars();
        let mut buffer = String::new();
        let first = chars.next();
        let first = first
            .filter(|&c| {
                if c.is_ascii_digit() {
                    buffer.push('_');
                    return true;
                };
                c.is_xid_start()
            })
            .unwrap_or('_');
        buffer.push(first);
        chars.for_each(|c| {
            if c.is_xid_continue() { buffer.push(c) } else { buffer.push('_') }
        });
        buffer
    }
}
impl StrExt for OsStr {
    type Owned = OsString;

    fn is_valid_ident(&self) -> bool {
        self.to_str().map(str::is_valid_ident).unwrap_or(false)
    }

    fn to_valid_ident(&self) -> Self::Owned {
        OsString::from(self.to_string_lossy().to_valid_ident())
    }
}
impl StrExt for CStr {
    type Owned = CString;

    fn is_valid_ident(&self) -> bool {
        self.to_str().map(str::is_valid_ident).unwrap_or(false)
    }

    fn to_valid_ident(&self) -> Self::Owned {
        CString::from_str(&self.to_string_lossy().to_valid_ident()).unwrap()
    }
}
#[cfg(test)]
mod tests {
    use crate::codegen::ident::StrExt;
    #[test]
    fn test_is_valid_ident() {
        let valids = ["_", "_abc", "_123", "abc_", "hello_world"];
        for s in valids {
            assert!(s.is_valid_ident(), "{s:?} should be valid");
        }
        assert!("_".is_valid_ident());
        assert!("_abc".is_valid_ident());
        assert!("_123".is_valid_ident());
        assert!("abc".is_valid_ident());
        let invalids = ["", "0", " ", "_0123-", "-", "!hello", "hello-world"];
        for s in invalids {
            assert!(!s.is_valid_ident(), "{s:?} should not be valid");
        }
    }
}