1use std::io::BufRead;
2
3use crate::error::chronicle_error::GitSnafu;
4use crate::error::Result;
5use crate::export::ExportEntry;
6use crate::git::GitOps;
7use crate::schema;
8use snafu::ResultExt;
9
10#[derive(Debug, Clone)]
12pub struct ImportSummary {
13 pub imported: usize,
14 pub skipped_existing: usize,
15 pub skipped_not_found: usize,
16 pub skipped_invalid: usize,
17}
18
19pub fn import_annotations<R: BufRead>(
27 git_ops: &dyn GitOps,
28 reader: R,
29 force: bool,
30 dry_run: bool,
31) -> Result<ImportSummary> {
32 let mut summary = ImportSummary {
33 imported: 0,
34 skipped_existing: 0,
35 skipped_not_found: 0,
36 skipped_invalid: 0,
37 };
38
39 for line in reader.lines() {
40 let line = line.map_err(|e| crate::error::ChronicleError::Io {
41 source: e,
42 location: snafu::Location::default(),
43 })?;
44
45 let line = line.trim();
46 if line.is_empty() {
47 continue;
48 }
49
50 let entry: ExportEntry = match serde_json::from_str(line) {
51 Ok(e) => e,
52 Err(_) => {
53 summary.skipped_invalid += 1;
54 continue;
55 }
56 };
57
58 let annotation_json = serde_json::to_string(&entry.annotation).unwrap_or_default();
60 if schema::parse_annotation(&annotation_json).is_err() {
61 summary.skipped_invalid += 1;
62 continue;
63 }
64
65 let commit_exists = git_ops.commit_info(&entry.commit_sha).is_ok();
67
68 if !commit_exists {
69 summary.skipped_not_found += 1;
70 continue;
71 }
72
73 if !force {
75 let has_note = git_ops.note_exists(&entry.commit_sha).context(GitSnafu)?;
76 if has_note {
77 summary.skipped_existing += 1;
78 continue;
79 }
80 }
81
82 if !dry_run {
83 git_ops
85 .note_write(&entry.commit_sha, &annotation_json)
86 .context(GitSnafu)?;
87 }
88
89 summary.imported += 1;
90 }
91
92 Ok(summary)
93}