agentics_contracts/validation/
github.rs1use agentics_domain::models::github::GithubPullRequestNumber;
4use agentics_domain::models::urls::{GithubPullRequestUrl, GithubRepoRemote};
5use agentics_error::{Result, ServiceError};
6
7#[derive(Debug, Clone)]
9pub struct GithubPullRequestRef {
10 repo_url: GithubRepoRemote,
11 pr_url: GithubPullRequestUrl,
12 pr_number: GithubPullRequestNumber,
13}
14
15impl GithubPullRequestRef {
16 pub fn try_new(
18 repo_url: GithubRepoRemote,
19 pr_url: GithubPullRequestUrl,
20 pr_number: GithubPullRequestNumber,
21 ) -> Result<Self> {
22 let pr_repo_key = pr_url
23 .repository_key()
24 .map_err(|e| ServiceError::Validation(e.to_string()))?;
25 if repo_url.repository_key() != &pr_repo_key {
26 return Err(ServiceError::Validation(format!(
27 "pr_url repository `{pr_repo_key}` must match repo_url repository `{}`",
28 repo_url.repository_key()
29 )));
30 }
31 let pr_url_number = pr_url
32 .number()
33 .map_err(|e| ServiceError::Validation(e.to_string()))?;
34 if pr_number.as_str() != pr_url_number {
35 return Err(ServiceError::Validation(format!(
36 "pr_url pull request number `{pr_url_number}` must match pr_number `{pr_number}`"
37 )));
38 }
39
40 Ok(Self {
41 repo_url,
42 pr_url,
43 pr_number,
44 })
45 }
46
47 pub fn repo_url(&self) -> &GithubRepoRemote {
49 &self.repo_url
50 }
51
52 pub fn pr_url(&self) -> &GithubPullRequestUrl {
54 &self.pr_url
55 }
56
57 pub fn pr_number(&self) -> &GithubPullRequestNumber {
59 &self.pr_number
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use agentics_domain::models::github::GithubPullRequestNumber;
66 use agentics_domain::models::urls::{GithubPullRequestUrl, GithubRepoRemote};
67
68 use super::GithubPullRequestRef;
69
70 #[test]
71 fn validates_matching_pull_request_reference() {
72 let reference = GithubPullRequestRef::try_new(
73 GithubRepoRemote::try_new("https://github.com/Agentics-Reifying/Agentics-Challenges")
74 .expect("repo"),
75 GithubPullRequestUrl::try_new(
76 "https://github.com/agentics-reifying/agentics-challenges/pull/42",
77 )
78 .expect("pr"),
79 GithubPullRequestNumber::try_new("42".to_string()).expect("number"),
80 )
81 .expect("reference should validate");
82
83 assert_eq!(
84 reference.repo_url().repository_key().as_str(),
85 "agentics-reifying/agentics-challenges"
86 );
87 assert_eq!(reference.pr_number().as_str(), "42");
88 }
89
90 #[test]
91 fn rejects_cross_field_mismatch() {
92 assert!(
93 GithubPullRequestRef::try_new(
94 GithubRepoRemote::try_new("https://github.com/agentics-reifying/agentics")
95 .expect("repo"),
96 GithubPullRequestUrl::try_new(
97 "https://github.com/agentics-reifying/agentics-challenges/pull/42",
98 )
99 .expect("pr"),
100 GithubPullRequestNumber::try_new("42".to_string()).expect("number"),
101 )
102 .is_err()
103 );
104
105 assert!(
106 GithubPullRequestRef::try_new(
107 GithubRepoRemote::try_new(
108 "git@github.com:agentics-reifying/agentics-challenges.git",
109 )
110 .expect("repo"),
111 GithubPullRequestUrl::try_new(
112 "https://github.com/agentics-reifying/agentics-challenges/pull/43",
113 )
114 .expect("pr"),
115 GithubPullRequestNumber::try_new("42".to_string()).expect("number"),
116 )
117 .is_err()
118 );
119 }
120}