///|
/// The rule of an environment-variable prefix that a text breaks.
pub enum EnvPrefixRule {
/// The prefix is empty.
Empty
/// A lower-case ASCII letter is not allowed.
LowerCaseLetter(Char)
/// The first character is not an upper-case ASCII letter or underscore.
FirstNotLetterOrUnderscore
/// The prefix holds a character that is not an upper-case ASCII letter, an ASCII digit,
/// or `_`.
CharNotAllowed(Char)
} derive(Eq, Debug)
///|
pub extend EnvPrefixRule with Eq::{equal, not_equal}
///|
pub extend EnvPrefixRule with @debug.Debug::{to_repr}
///|
/// A text that is not an environment-variable name. Callers can match the cases,
/// but only `EnvPrefix::parse` makes them.
pub suberror EnvPrefixError {
/// The text `text` breaks the rule `rule`.
InvalidEnvPrefix(text~ : String, rule~ : EnvPrefixRule)
} derive(Debug)
///|
pub extend EnvPrefixError with @debug.Debug::{to_repr}
///|
/// Text for people that describes the fault.
pub fn EnvPrefixError::message(self : EnvPrefixError) -> String {
let InvalidEnvPrefix(text~, rule~) = self
let reason = match rule {
Empty => "the prefix is empty"
LowerCaseLetter(char) => "the lower-case letter \{char} is not allowed"
FirstNotLetterOrUnderscore => "the prefix must start with an upper-case ASCII letter or underscore"
CharNotAllowed(char) =>
"the character \{char.to_string().escape()} is not allowed; use upper-case ASCII letters, digits and '_'"
}
"Invalid environment-variable prefix \{text.escape()}: \{reason}"
}
///|
/// A variable prefix starting with an upper-case ASCII letter or underscore, followed
/// by upper-case ASCII letters, digits and underscores.
pub struct EnvPrefix {
priv text : String
} derive(Eq, Hash, Debug)
///|
pub extend EnvPrefix with Eq::{equal, not_equal}
///|
pub extend EnvPrefix with Hash::{hash, hash_combine}
///|
pub extend EnvPrefix with @debug.Debug::{to_repr}
///|
/// Parse an environment-variable prefix, for example `ACME` or `MY_TOOL`.
pub fn EnvPrefix::parse(text : String) -> EnvPrefix raise EnvPrefixError {
guard text.iter().head() is Some(first) else {
raise InvalidEnvPrefix(text~, rule=Empty)
}
for char in text {
if char >= 'a' && char <= 'z' {
raise InvalidEnvPrefix(text~, rule=LowerCaseLetter(char))
}
}
guard is_ascii_letter(first) || first == '_' else {
raise InvalidEnvPrefix(text~, rule=FirstNotLetterOrUnderscore)
}
for char in text {
guard is_ascii_letter(char) || is_digit(char) || char == '_' else {
raise InvalidEnvPrefix(text~, rule=CharNotAllowed(char))
}
}
{ text, }
}
///|
/// The name as text, which `EnvPrefix::parse` reads back to an equal name.
pub impl Show for EnvPrefix with output(self, logger) {
logger.write_string(self.text)
}
///|
pub extend EnvPrefix with Show::{output, to_string}
///|
/// Derive the default prefix from a valid application name. This cannot fail.
pub fn EnvPrefix::from_app_name(name : AppName) -> EnvPrefix {
{ text: name.to_string().to_upper().replace_all(old="-", new="_") }
}