Skip to main content

commit_bridge/domain/
target_repo.rs

1//! Domain type to represent a target repository hosted on GitHub.
2
3use crate::error::ValidationError;
4use rovo::schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7/// The target GitHub repository in owner/repo format.
8#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
9#[serde(try_from = "String", into = "String")]
10pub struct TargetRepo(String);
11
12impl TargetRepo {
13    /// Create a new `TargetRepo`.
14    pub fn new(value: String) -> Result<Self, ValidationError> {
15        Self::try_from(value)
16    }
17
18    /// Get the inner string.
19    pub fn into_inner(self) -> String {
20        self.0
21    }
22
23    /// Get a reference to the inner string.
24    pub fn as_str(&self) -> &str {
25        &self.0
26    }
27}
28
29impl std::fmt::Display for TargetRepo {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        write!(f, "{}", self.0)
32    }
33}
34
35crate::derive_sqlx_traits!(TargetRepo);
36
37impl TryFrom<String> for TargetRepo {
38    type Error = ValidationError;
39
40    fn try_from(value: String) -> Result<Self, Self::Error> {
41        let parts: Vec<&str> = value.split('/').collect();
42        match parts.as_slice() {
43            [owner, repo] if !owner.is_empty() && !repo.is_empty() => Ok(TargetRepo(value)),
44            _ => {
45                tracing::warn!(
46                    "Validation failed for TargetRepo: {}. Must be in owner/repo format",
47                    value
48                );
49                Err(ValidationError::InvalidValue(format!(
50                    "Target repository must be in owner/repo format: {}",
51                    value
52                )))
53            }
54        }
55    }
56}
57
58impl From<TargetRepo> for String {
59    fn from(val: TargetRepo) -> Self {
60        val.0
61    }
62}