strop-engine 0.31.1

strop editor engine: documents, grammar dispatch, services, sessions — no terminal
Documentation
//! Editable filename fields use the lossless Directory spelling, not input URIs.
use std::path::PathBuf;
pub(super) fn decode(text: &str) -> Result<PathBuf, String> {
    let mut bytes = Vec::with_capacity(text.len());
    let mut characters = text.chars();
    while let Some(character) = characters.next() {
        if character == '\\' {
            match characters.next().ok_or("unfinished filename escape")? {
                '\\' => bytes.push(b'\\'),
                'n' => bytes.push(b'\n'),
                'r' => bytes.push(b'\r'),
                't' => bytes.push(b'\t'),
                'x' => {
                    let high = characters
                        .next()
                        .and_then(|value| value.to_digit(16))
                        .ok_or("\\x needs two hexadecimal digits")?;
                    let low = characters
                        .next()
                        .and_then(|value| value.to_digit(16))
                        .ok_or("\\x needs two hexadecimal digits")?;
                    bytes.push(((high << 4) | low) as u8);
                }
                'u' => {
                    if characters.next() != Some('{') {
                        return Err("Unicode filename escapes use \\u{HEX}".into());
                    }
                    let mut value = 0_u32;
                    let mut count = 0;
                    loop {
                        let character = characters
                            .next()
                            .ok_or("unfinished Unicode filename escape")?;
                        if character == '}' {
                            break;
                        }
                        count += 1;
                        if count > 6 {
                            return Err("Unicode filename escape is too long".into());
                        }
                        value = value
                            .checked_mul(16)
                            .and_then(|value| {
                                character
                                    .to_digit(16)
                                    .and_then(|digit| value.checked_add(digit))
                            })
                            .ok_or("invalid Unicode filename escape")?;
                    }
                    if count == 0 {
                        return Err("empty Unicode filename escape".into());
                    }
                    let character =
                        char::from_u32(value).ok_or("invalid Unicode scalar in filename")?;
                    let mut encoded = [0; 4];
                    bytes.extend_from_slice(character.encode_utf8(&mut encoded).as_bytes());
                }
                _ => return Err("unknown filename escape; use \\\\ for a literal backslash".into()),
            }
        } else {
            if character.is_control()
                || matches!(character, '\u{202a}'..='\u{202e}' | '\u{2066}'..='\u{2069}')
            {
                return Err("control characters in filenames must use visible escapes".into());
            }
            let mut encoded = [0; 4];
            bytes.extend_from_slice(character.encode_utf8(&mut encoded).as_bytes());
        }
    }
    if bytes.contains(&0) {
        return Err("a filename cannot contain NUL".into());
    }
    let path =
        strop_workspace::addr::uri::bytes_to_path(bytes).map_err(|error| error.to_string())?;
    if path.is_absolute() || path.has_root() {
        return Err("draft destinations are relative to their Directory; use Move for an absolute destination".into());
    }
    if path.file_name().is_none() {
        return Err("a filename needs a nonempty final component".into());
    }
    Ok(path)
}