#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("EDIFACT parse error: {0}")]
Parse(#[from] edifact_rs::EdifactError),
#[error("EDIFACT serialization error: {0}")]
Serialize(String),
#[error("required segment {0} is missing")]
MissingSegment(&'static str),
#[error("unknown DVGW document-name code (check BGM C002 DE 1001)")]
UnknownDocumentCode {
raw_code: String,
},
#[error(
"UNH carrier and BGM document code disagree: document {document} is carried by \
{expected} but UNH names a different message type"
)]
CarrierMismatch {
document: &'static str,
expected: &'static str,
raw_code: String,
},
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
}
pub(crate) fn sanitize_code(s: &str) -> String {
const MAX_CHARS: usize = 16;
s.chars()
.take(MAX_CHARS)
.map(|c| {
if c.is_ascii_alphanumeric() || c == '.' {
c
} else {
'?'
}
})
.collect()
}
#[cfg(test)]
mod tests {
use super::sanitize_code;
#[test]
fn truncates_on_character_boundaries() {
let hostile = "ü".repeat(17);
assert_eq!(sanitize_code(&hostile), "?".repeat(16));
}
#[test]
fn passes_plain_codes_through() {
assert_eq!(sanitize_code("ORDRSP"), "ORDRSP");
assert_eq!(sanitize_code("5.11a"), "5.11a");
assert_eq!(sanitize_code("X1G\u{1b}[31m"), "X1G??31m");
}
}