| 170 | } |
| 171 | |
| 172 | fn parse_field_value(s: &str) -> Result<FieldValue, IlpError> { |
| 173 | if s.is_empty() { |
| 174 | return Err(IlpError::InvalidFieldValue("empty value".into())); |
| 175 | } |
| 176 | |
| 177 | // String: "..." ILP allows \" and \\ escape sequences inside string fields. |
| 178 | if s.starts_with('"') && s.ends_with('"') && s.len() >= 2 { |
| 179 | let inner = &s[1..s.len() - 1]; |
| 180 | if inner.contains('\\') { |
| 181 | // Unescape: \" → " and \\ → \ |
| 182 | let unescaped = inner.replace("\\\"", "\"").replace("\\\\", "\\"); |
| 183 | return Ok(FieldValue::Str(unescaped)); |
| 184 | } |
| 185 | return Ok(FieldValue::Str(inner.to_string())); |
| 186 | } |
| 187 | |
| 188 | // Bool. |
| 189 | match s { |
| 190 | "t" | "T" | "true" | "True" | "TRUE" => return Ok(FieldValue::Bool(true)), |
| 191 | "f" | "F" | "false" | "False" | "FALSE" => return Ok(FieldValue::Bool(false)), |
| 192 | _ => {} |
| 193 | } |
| 194 | |
| 195 | // Integer: ends with 'i'. |
| 196 | if let Some(num) = s.strip_suffix('i') { |
| 197 | return num |
| 198 | .parse::<i64>() |
| 199 | .map(FieldValue::Int) |
| 200 | .map_err(|e| IlpError::InvalidFieldValue(format!("{s}: {e}"))); |
| 201 | } |
| 202 | |
| 203 | // Unsigned integer: ends with 'u'. |
| 204 | if let Some(num) = s.strip_suffix('u') { |
| 205 | return num |
| 206 | .parse::<u64>() |
| 207 | .map(FieldValue::UInt) |
| 208 | .map_err(|e| IlpError::InvalidFieldValue(format!("{s}: {e}"))); |
| 209 | } |
| 210 | |
| 211 | // Float (default). |
| 212 | s.parse::<f64>() |
| 213 | .map(FieldValue::Float) |
| 214 | .map_err(|e| IlpError::InvalidFieldValue(format!("{s}: {e}"))) |
| 215 | } |
| 216 | |
| 217 | #[cfg(test)] |
| 218 | mod tests { |