ic_host_text/normalize/
mod.rs1use std::fmt;
4
5#[cfg(test)]
6mod tests;
7
8#[derive(Debug)]
10pub enum NormalizationError {
11 InputLimit {
13 actual: usize,
15 limit: usize,
17 },
18 Utf8(std::str::Utf8Error),
20 OutputLimit {
22 limit: usize,
24 },
25 Allocation(std::collections::TryReserveError),
27}
28
29impl fmt::Display for NormalizationError {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 match self {
32 Self::InputLimit { actual, limit } => {
33 write!(
34 f,
35 "normalization input has {actual} bytes, exceeding {limit}"
36 )
37 }
38 Self::Utf8(_) => f.write_str("normalization input is not UTF-8"),
39 Self::OutputLimit { limit } => write!(f, "normalized text exceeds {limit} bytes"),
40 Self::Allocation(_) => f.write_str("line normalization allocation failed"),
41 }
42 }
43}
44impl std::error::Error for NormalizationError {
45 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
46 match self {
47 Self::Utf8(source) => Some(source),
48 Self::Allocation(source) => Some(source),
49 _ => None,
50 }
51 }
52}
53
54pub fn normalize_lines(bytes: &[u8], max_bytes: usize) -> Result<String, NormalizationError> {
65 if bytes.len() > max_bytes {
66 return Err(NormalizationError::InputLimit {
67 actual: bytes.len(),
68 limit: max_bytes,
69 });
70 }
71 let text = std::str::from_utf8(bytes).map_err(NormalizationError::Utf8)?;
72 let mut length = 0_usize;
73 for line in text.lines() {
74 length = length
75 .checked_add(line.trim_end().len())
76 .and_then(|length| length.checked_add(1))
77 .filter(|&length| length <= max_bytes)
78 .ok_or(NormalizationError::OutputLimit { limit: max_bytes })?;
79 }
80 let mut normalized = String::new();
81 normalized
82 .try_reserve_exact(length)
83 .map_err(NormalizationError::Allocation)?;
84 for line in text.lines() {
85 normalized.push_str(line.trim_end());
86 normalized.push('\n');
87 }
88 Ok(normalized)
89}