git_adr/wiki/
service.rs

1//! Wiki service abstraction.
2
3use crate::core::Adr;
4use crate::wiki::{github::GitHubWiki, gitlab::GitLabWiki};
5use crate::Error;
6
7/// Supported wiki platforms.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum WikiPlatform {
10    /// GitHub Wiki.
11    GitHub,
12    /// GitLab Wiki.
13    GitLab,
14}
15
16impl std::fmt::Display for WikiPlatform {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        match self {
19            Self::GitHub => write!(f, "github"),
20            Self::GitLab => write!(f, "gitlab"),
21        }
22    }
23}
24
25/// Configuration for wiki synchronization.
26#[derive(Debug, Clone)]
27pub struct WikiConfig {
28    /// The platform to use.
29    pub platform: WikiPlatform,
30    /// Repository identifier (owner/repo for GitHub, project path for GitLab).
31    pub repository: String,
32    /// API token for authentication.
33    pub token: Option<String>,
34    /// Base URL for self-hosted instances.
35    pub base_url: Option<String>,
36}
37
38impl WikiConfig {
39    /// Create a new wiki configuration.
40    #[must_use]
41    pub fn new(platform: WikiPlatform, repository: impl Into<String>) -> Self {
42        Self {
43            platform,
44            repository: repository.into(),
45            token: None,
46            base_url: None,
47        }
48    }
49
50    /// Set the API token.
51    #[must_use]
52    pub fn with_token(mut self, token: impl Into<String>) -> Self {
53        self.token = Some(token.into());
54        self
55    }
56
57    /// Set the base URL.
58    #[must_use]
59    pub fn with_base_url(mut self, url: impl Into<String>) -> Self {
60        self.base_url = Some(url.into());
61        self
62    }
63}
64
65/// Wiki service for synchronizing ADRs.
66#[derive(Debug)]
67pub struct WikiService {
68    config: WikiConfig,
69}
70
71impl WikiService {
72    /// Create a new wiki service.
73    #[must_use]
74    pub fn new(config: WikiConfig) -> Self {
75        Self { config }
76    }
77
78    /// Push an ADR to the wiki.
79    ///
80    /// # Errors
81    ///
82    /// Returns an error if the push fails.
83    pub fn push(&self, adr: &Adr) -> Result<(), Error> {
84        match self.config.platform {
85            WikiPlatform::GitHub => {
86                let parts: Vec<&str> = self.config.repository.split('/').collect();
87                if parts.len() != 2 {
88                    return Err(Error::WikiError {
89                        message: format!(
90                            "Invalid GitHub repository format: {}",
91                            self.config.repository
92                        ),
93                    });
94                }
95                let wiki = GitHubWiki::new(parts[0], parts[1]);
96                wiki.push(adr)
97            },
98            WikiPlatform::GitLab => {
99                let wiki = GitLabWiki::new(&self.config.repository);
100                wiki.push(adr)
101            },
102        }
103    }
104
105    /// Pull an ADR from the wiki.
106    ///
107    /// # Errors
108    ///
109    /// Returns an error if the pull fails.
110    pub fn pull(&self, id: &str) -> Result<Adr, Error> {
111        match self.config.platform {
112            WikiPlatform::GitHub => {
113                let parts: Vec<&str> = self.config.repository.split('/').collect();
114                if parts.len() != 2 {
115                    return Err(Error::WikiError {
116                        message: format!(
117                            "Invalid GitHub repository format: {}",
118                            self.config.repository
119                        ),
120                    });
121                }
122                let wiki = GitHubWiki::new(parts[0], parts[1]);
123                wiki.pull(id)
124            },
125            WikiPlatform::GitLab => {
126                let wiki = GitLabWiki::new(&self.config.repository);
127                wiki.pull(id)
128            },
129        }
130    }
131
132    /// Sync all ADRs with the wiki.
133    ///
134    /// # Errors
135    ///
136    /// Returns an error if sync fails.
137    pub fn sync(&self, adrs: &[Adr]) -> Result<SyncResult, Error> {
138        let mut result = SyncResult::default();
139
140        for adr in adrs {
141            match self.push(adr) {
142                Ok(()) => result.pushed += 1,
143                Err(e) => {
144                    result.errors.push(format!("{}: {e}", adr.id));
145                },
146            }
147        }
148
149        Ok(result)
150    }
151}
152
153/// Result of a wiki sync operation.
154#[derive(Debug, Default)]
155pub struct SyncResult {
156    /// Number of ADRs pushed.
157    pub pushed: usize,
158    /// Number of ADRs pulled.
159    pub pulled: usize,
160    /// Errors encountered.
161    pub errors: Vec<String>,
162}