Parse a changelist entry from the protocol format. Format: `{sequence}.{hash}.{merkle}` or `{sequence}.{hash}.{merkle}.` # Examples ``` use atomic_remote::types::ChangelistEntry; let entry = ChangelistEntry::parse("0.ABC123.DEF456").unwrap(); assert_eq!(entry.sequence, 0); assert_eq!(entry.hash, "ABC123"); assert_eq!(entry.merkle, "DEF456"); assert!(!entry.tagged); let tagged = ChangelistEntr
(line: &str)
| 196 | /// assert!(tagged.tagged); |
| 197 | /// ``` |
| 198 | pub fn parse(line: &str) -> Result<Self, ParseChangelistError> { |
| 199 | let line = line.trim(); |
| 200 | if line.is_empty() { |
| 201 | return Err(ParseChangelistError::Empty); |
| 202 | } |
| 203 | |
| 204 | // Check for trailing dot (tagged entry) |
| 205 | let (content, tagged) = if line.ends_with('.') { |
| 206 | // Could be tagged (4 parts with empty last) or just ending in dot |
| 207 | let trimmed = line.trim_end_matches('.'); |
| 208 | // Count dots in trimmed content |
| 209 | let dot_count = trimmed.chars().filter(|&c| c == '.').count(); |
| 210 | if dot_count >= 2 { |
| 211 | (trimmed, true) |
| 212 | } else { |
| 213 | (line, false) |
| 214 | } |
| 215 | } else { |
| 216 | (line, false) |
| 217 | }; |
| 218 | |
| 219 | let parts: Vec<&str> = content.split('.').collect(); |
| 220 | |
| 221 | if parts.len() < 3 { |
| 222 | return Err(ParseChangelistError::InvalidFormat(line.to_string())); |
| 223 | } |
| 224 | |
| 225 | let sequence: u64 = parts[0] |
| 226 | .parse() |
| 227 | .map_err(|_| ParseChangelistError::InvalidSequence(parts[0].to_string()))?; |
| 228 | |
| 229 | let hash = parts[1].to_string(); |
| 230 | let merkle = parts[2].to_string(); |
| 231 | |
| 232 | if hash.is_empty() { |
| 233 | return Err(ParseChangelistError::InvalidFormat(line.to_string())); |
| 234 | } |
| 235 | if merkle.is_empty() { |
| 236 | return Err(ParseChangelistError::InvalidFormat(line.to_string())); |
| 237 | } |
| 238 | |
| 239 | Ok(Self { |
| 240 | sequence, |
| 241 | hash, |
| 242 | merkle, |
| 243 | tagged, |
| 244 | }) |
| 245 | } |
| 246 | |
| 247 | /// Format this entry as a protocol line. |
| 248 | pub fn to_protocol_line(&self) -> String { |