(gdb: &GlobalDb, source: &HookImportSource)
| 101 | } |
| 102 | |
| 103 | async fn import_source(gdb: &GlobalDb, source: &HookImportSource) -> HookImportSourceOutcome { |
| 104 | let mut result = HookImportSourceOutcome { |
| 105 | path: source.path.clone(), |
| 106 | imported: 0, |
| 107 | skipped: 0, |
| 108 | error: None, |
| 109 | }; |
| 110 | let Ok(metadata) = std::fs::metadata(&source.path) else { |
| 111 | return result; |
| 112 | }; |
| 113 | let file_len = metadata.len(); |
| 114 | let mtime = metadata |
| 115 | .modified() |
| 116 | .ok() |
| 117 | .and_then(|time| time.duration_since(std::time::UNIX_EPOCH).ok()) |
| 118 | .map_or(0, |duration| duration.as_secs()); |
| 119 | |
| 120 | let cursor_key = import_cursor_key(&source.path); |
| 121 | let start = match gdb.get_parse_offset(&cursor_key).await { |
| 122 | // Truncated/rotated files restart from the top. |
| 123 | Some(cursor) if cursor.byte_offset <= file_len => cursor.byte_offset, |
| 124 | _ => 0, |
| 125 | }; |
| 126 | if start == file_len { |
| 127 | return result; |
| 128 | } |
| 129 | |
| 130 | let text = match read_from_offset(&source.path, start) { |
| 131 | Ok(text) => text, |
| 132 | Err(err) => { |
| 133 | result.error = Some(err); |
| 134 | return result; |
| 135 | } |
| 136 | }; |
| 137 | // Only consume up to the last complete line; a concurrent writer may have |
| 138 | // an unfinished row at EOF. |
| 139 | let consumed = text.rfind('\n').map_or(0, |index| index + 1); |
| 140 | if consumed == 0 { |
| 141 | return result; |
| 142 | } |
| 143 | |
| 144 | let mut batch = Vec::new(); |
| 145 | for line in text[..consumed].lines() { |
| 146 | match hook_row_to_analytics_event(line, source.default_project_root.as_deref()) { |
| 147 | Some(event) => batch.push(event), |
| 148 | None => result.skipped += 1, |
| 149 | } |
| 150 | } |
| 151 | for chunk in batch.chunks(IMPORT_BATCH_SIZE) { |
| 152 | if let Err(err) = gdb.append_analytics_events(chunk).await { |
| 153 | result.error = Some(err); |
| 154 | return result; |
| 155 | } |
| 156 | result.imported += chunk.len() as u64; |
| 157 | } |
| 158 | |
| 159 | gdb.set_parse_offset( |
| 160 | &cursor_key, |
no test coverage detected