Append a hit to the CSV output file
(file_path: &str, hit: &CsvHit)
| 85 | |
| 86 | // Append a hit to the CSV output file |
| 87 | pub async fn append_csv_hit(file_path: &str, hit: &CsvHit) -> Result<(), Error> { |
| 88 | // Read the existing file to avoid header rewriting issues |
| 89 | let existing_content = match tokio::fs::try_exists(file_path).await { |
| 90 | Ok(true) => tokio::fs::read_to_string(file_path).await?, |
| 91 | _ => String::new(), |
| 92 | }; |
| 93 | |
| 94 | // Open file for writing |
| 95 | let file = tokio::fs::OpenOptions::new() |
| 96 | .write(true) |
| 97 | .create(true) |
| 98 | .truncate(true) |
| 99 | .open(file_path) |
| 100 | .await?; |
| 101 | |
| 102 | let mut writer = BufWriter::new(file); |
| 103 | |
| 104 | // Create a CSV writer for the new record |
| 105 | let mut csv_writer = csv::WriterBuilder::new() |
| 106 | .from_writer(vec![]); |
| 107 | |
| 108 | // Serialize the hit |
| 109 | csv_writer.serialize(hit)?; |
| 110 | |
| 111 | // Get the CSV content as bytes |
| 112 | let mut csv_content = csv_writer.into_inner()?; |
| 113 | |
| 114 | // If there's existing content, we need to handle appending properly |
| 115 | if !existing_content.is_empty() { |
| 116 | // Write existing content first |
| 117 | writer.write_all(existing_content.as_bytes()).await?; |
| 118 | |
| 119 | // For the new content, skip the header line |
| 120 | let new_content = String::from_utf8(csv_content)?; |
| 121 | let lines: Vec<&str> = new_content.lines().collect(); |
| 122 | |
| 123 | // Only take the data line (skip header) |
| 124 | if lines.len() > 1 { |
| 125 | csv_content = lines[1].as_bytes().to_vec(); |
| 126 | writer.write_all(&csv_content).await?; |
| 127 | writer.write_all(b"\n").await?; |
| 128 | } |
| 129 | } else { |
| 130 | // No existing content, write everything including header |
| 131 | writer.write_all(&csv_content).await?; |
| 132 | } |
| 133 | |
| 134 | writer.flush().await?; |
| 135 | |
| 136 | Ok(()) |
| 137 | } |