git_adr/export/
json.rs

1//! JSON export functionality.
2
3use crate::core::Adr;
4use crate::export::{ExportResult, Exporter};
5use crate::Error;
6use serde::Serialize;
7use std::path::Path;
8
9/// JSON exporter.
10#[derive(Debug, Default)]
11pub struct JsonExporter {
12    /// Pretty print JSON output.
13    pub pretty: bool,
14}
15
16impl JsonExporter {
17    /// Create a new JSON exporter.
18    #[must_use]
19    pub fn new() -> Self {
20        Self { pretty: true }
21    }
22
23    /// Disable pretty printing.
24    #[must_use]
25    pub fn compact(mut self) -> Self {
26        self.pretty = false;
27        self
28    }
29}
30
31/// JSON representation of an ADR.
32#[derive(Debug, Serialize)]
33struct AdrJson<'a> {
34    id: &'a str,
35    commit: &'a str,
36    title: &'a str,
37    status: String,
38    date: Option<String>,
39    tags: &'a [String],
40    authors: &'a [String],
41    deciders: &'a [String],
42    body: &'a str,
43}
44
45impl<'a> AdrJson<'a> {
46    fn from_adr(adr: &'a Adr) -> Self {
47        Self {
48            id: &adr.id,
49            commit: &adr.commit,
50            title: &adr.frontmatter.title,
51            status: adr.frontmatter.status.to_string(),
52            date: adr
53                .frontmatter
54                .date
55                .as_ref()
56                .map(|d| d.datetime().to_rfc3339()),
57            tags: &adr.frontmatter.tags,
58            authors: &adr.frontmatter.authors,
59            deciders: &adr.frontmatter.deciders,
60            body: &adr.body,
61        }
62    }
63}
64
65impl Exporter for JsonExporter {
66    fn export(&self, adr: &Adr, path: &Path) -> Result<(), Error> {
67        let json_adr = AdrJson::from_adr(adr);
68
69        let content = if self.pretty {
70            serde_json::to_string_pretty(&json_adr)
71        } else {
72            serde_json::to_string(&json_adr)
73        }
74        .map_err(|e| Error::ExportError {
75            message: format!("Failed to serialize ADR: {e}"),
76        })?;
77
78        std::fs::write(path, content).map_err(|e| Error::IoError {
79            message: format!("Failed to write {}: {e}", path.display()),
80        })
81    }
82
83    fn export_all(&self, adrs: &[Adr], dir: &Path) -> Result<ExportResult, Error> {
84        std::fs::create_dir_all(dir).map_err(|e| Error::IoError {
85            message: format!("Failed to create directory {}: {e}", dir.display()),
86        })?;
87
88        let mut result = ExportResult::default();
89
90        // Export individual files
91        for adr in adrs {
92            let path = dir.join(format!("{}.json", adr.id));
93            match self.export(adr, &path) {
94                Ok(()) => {
95                    result.exported += 1;
96                    result.files.push(path.display().to_string());
97                },
98                Err(e) => {
99                    result.errors.push(format!("{}: {e}", adr.id));
100                },
101            }
102        }
103
104        // Export combined file
105        let all_path = dir.join("all.json");
106        let all_json: Vec<AdrJson> = adrs.iter().map(AdrJson::from_adr).collect();
107
108        let content = if self.pretty {
109            serde_json::to_string_pretty(&all_json)
110        } else {
111            serde_json::to_string(&all_json)
112        }
113        .map_err(|e| Error::ExportError {
114            message: format!("Failed to serialize ADRs: {e}"),
115        })?;
116
117        std::fs::write(&all_path, content).map_err(|e| Error::IoError {
118            message: format!("Failed to write {}: {e}", all_path.display()),
119        })?;
120
121        result.files.push(all_path.display().to_string());
122
123        Ok(result)
124    }
125}