ic_host_tools/candid/text/
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!(f, "Candid output has {actual} bytes, exceeding {limit}")
34 }
35 Self::Utf8(_) => f.write_str("Candid extractor output is not UTF-8"),
36 Self::OutputLimit { limit } => write!(f, "normalized Candid exceeds {limit} bytes"),
37 Self::Allocation(_) => f.write_str("Candid normalization allocation failed"),
38 }
39 }
40}
41impl std::error::Error for NormalizationError {
42 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
43 match self {
44 Self::Utf8(source) => Some(source),
45 Self::Allocation(source) => Some(source),
46 _ => None,
47 }
48 }
49}
50
51pub fn normalize(bytes: &[u8], max_bytes: usize) -> Result<String, NormalizationError> {
62 if bytes.len() > max_bytes {
63 return Err(NormalizationError::InputLimit {
64 actual: bytes.len(),
65 limit: max_bytes,
66 });
67 }
68 let text = std::str::from_utf8(bytes).map_err(NormalizationError::Utf8)?;
69 let mut length = 0_usize;
70 for line in text.lines() {
71 length = length
72 .checked_add(line.trim_end().len())
73 .and_then(|length| length.checked_add(1))
74 .filter(|&length| length <= max_bytes)
75 .ok_or(NormalizationError::OutputLimit { limit: max_bytes })?;
76 }
77 let mut normalized = String::new();
78 normalized
79 .try_reserve_exact(length)
80 .map_err(NormalizationError::Allocation)?;
81 for line in text.lines() {
82 normalized.push_str(line.trim_end());
83 normalized.push('\n');
84 }
85 Ok(normalized)
86}