refeff-io 0.2.0

FEFF file-format readers and writers (feff.inp, .dat/.bin handoffs, PAD encoding) for the refeff FEFF10 port
Documentation
use crate::error::{IoError, Result};

pub(super) fn parse_element(line: usize, token: &str) -> Result<String> {
    let valid_len = (1..=2).contains(&token.len());
    let valid_chars = token.chars().all(|ch| ch.is_ascii_alphabetic());
    if valid_len && valid_chars {
        Ok(canonical_symbol(token))
    } else {
        Err(IoError::ConfigInpParse {
            field: "element",
            line,
            token: token.to_string(),
        })
    }
}

pub(super) fn parse_orbital(line: usize, token: &str) -> Result<String> {
    let orbital = token.to_ascii_lowercase();
    if is_allowed_orbital(&orbital) {
        Ok(orbital)
    } else {
        Err(IoError::ConfigInpParse {
            field: "orbital",
            line,
            token: token.to_string(),
        })
    }
}

pub(super) fn parse_i32(line: usize, field: &'static str, token: &str) -> Result<i32> {
    token.parse::<i32>().map_err(|_| IoError::ConfigInpParse {
        field,
        line,
        token: token.to_string(),
    })
}

pub(super) fn parse_f64(line: usize, field: &'static str, token: &str) -> Result<f64> {
    token.parse::<f64>().map_err(|_| IoError::ConfigInpParse {
        field,
        line,
        token: token.to_string(),
    })
}

pub(super) fn validate_finite(field: &'static str, value: f64) -> Result<()> {
    if value.is_finite() {
        Ok(())
    } else {
        Err(invalid_config_inp(field, "value must be finite"))
    }
}

pub(super) fn invalid_config_inp(field: &'static str, message: impl Into<String>) -> IoError {
    IoError::InvalidConfigInp {
        field,
        message: message.into(),
    }
}

pub(super) fn canonical_noble_gas(token: &str) -> Option<String> {
    match token.to_ascii_uppercase().as_str() {
        "HE" => Some("He".to_string()),
        "NE" => Some("Ne".to_string()),
        "AR" => Some("Ar".to_string()),
        "KR" => Some("Kr".to_string()),
        "XE" => Some("Xe".to_string()),
        "HG" => Some("Hg".to_string()),
        "RN" => Some("Rn".to_string()),
        _ => None,
    }
}

pub(super) fn is_zero_base_token(token: &str) -> bool {
    token == "0"
}

pub(super) fn canonical_symbol(token: &str) -> String {
    let mut chars = token.chars();
    let mut symbol = String::new();
    if let Some(first) = chars.next() {
        symbol.push(first.to_ascii_uppercase());
    }
    if let Some(second) = chars.next() {
        symbol.push(second.to_ascii_lowercase());
    }
    symbol
}

pub(super) fn is_allowed_orbital(orbital: &str) -> bool {
    matches!(
        orbital,
        "1s" | "2s"
            | "2p"
            | "3s"
            | "3p"
            | "3d"
            | "4s"
            | "4p"
            | "4d"
            | "4f"
            | "5s"
            | "5p"
            | "5d"
            | "5f"
            | "5g"
            | "6s"
            | "6p"
            | "6d"
            | "6f"
            | "7s"
            | "7p"
            | "7d"
            | "8s"
            | "8p"
    )
}

pub(super) fn is_s_orbital(orbital: &str) -> bool {
    orbital.ends_with('s')
}