1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum ProofStatusKind {
11 Unspecified,
12 Pending,
13 Verified,
14 Failed,
15}
16
17impl ProofStatusKind {
18 pub fn from_i32(status: i32) -> Self {
20 match status {
21 1 => Self::Pending,
22 2 => Self::Verified,
23 3 => Self::Failed,
24 _ => Self::Unspecified,
25 }
26 }
27
28 pub fn label(self) -> &'static str {
30 match self {
31 Self::Verified => "verified",
32 Self::Pending => "pending",
33 Self::Failed => "failed",
34 Self::Unspecified => "unspecified",
35 }
36 }
37}
38
39pub fn proof_status_label(kind: ProofStatusKind) -> &'static str {
41 kind.label()
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum HostRepoPlanError {
47 MissingHostOrRepo,
49}
50
51impl HostRepoPlanError {
52 pub fn message(self) -> &'static str {
54 match self {
55 Self::MissingHostOrRepo => {
56 "a host and repo are required (e.g. `heddle prove github.com owner/repo`); \
57 for other actions use `heddle prove submit <challenge_id>` or `heddle prove list`"
58 }
59 }
60 }
61}
62
63impl std::fmt::Display for HostRepoPlanError {
64 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65 f.write_str(self.message())
66 }
67}
68
69impl std::error::Error for HostRepoPlanError {}
70
71pub fn require_host_repo<'a>(
74 host: Option<&'a str>,
75 repo: Option<&'a str>,
76) -> Result<(&'a str, &'a str), HostRepoPlanError> {
77 match (host, repo) {
78 (Some(h), Some(r)) => Ok((h, r)),
79 _ => Err(HostRepoPlanError::MissingHostOrRepo),
80 }
81}
82
83pub fn timestamp_secs_u64(seconds: Option<i64>) -> u64 {
85 seconds.map(|s| s.max(0) as u64).unwrap_or(0)
86}
87
88pub fn format_unix_secs_label(secs: u64) -> String {
90 if secs == 0 {
91 return String::new();
92 }
93 chrono::DateTime::from_timestamp(secs as i64, 0)
94 .map(|d| d.to_rfc3339())
95 .unwrap_or_else(|| secs.to_string())
96}
97
98pub fn proof_submit_followup(kind: ProofStatusKind, challenge_id: &str) -> Option<String> {
100 match kind {
101 ProofStatusKind::Verified => Some("Your control of the repo is verified.".to_string()),
102 ProofStatusKind::Pending => Some(format!(
103 "The marker was not found yet. Push the file, then retry: heddle prove submit {challenge_id}"
104 )),
105 ProofStatusKind::Failed => Some(format!(
106 "Verification failed. Check the marker line + path, then retry: heddle prove submit {challenge_id}"
107 )),
108 ProofStatusKind::Unspecified => None,
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn status_label_covers_every_variant() {
118 assert_eq!(proof_status_label(ProofStatusKind::Verified), "verified");
119 assert_eq!(proof_status_label(ProofStatusKind::Pending), "pending");
120 assert_eq!(proof_status_label(ProofStatusKind::Failed), "failed");
121 assert_eq!(
122 proof_status_label(ProofStatusKind::Unspecified),
123 "unspecified"
124 );
125 assert_eq!(ProofStatusKind::from_i32(0), ProofStatusKind::Unspecified);
126 assert_eq!(ProofStatusKind::from_i32(1), ProofStatusKind::Pending);
127 assert_eq!(ProofStatusKind::from_i32(2), ProofStatusKind::Verified);
128 assert_eq!(ProofStatusKind::from_i32(3), ProofStatusKind::Failed);
129 assert_eq!(ProofStatusKind::from_i32(99), ProofStatusKind::Unspecified);
130 }
131
132 #[test]
133 fn host_repo_guard() {
134 assert_eq!(
135 require_host_repo(Some("github.com"), Some("owner/repo")).unwrap(),
136 ("github.com", "owner/repo")
137 );
138 assert_eq!(
139 require_host_repo(None, Some("owner/repo")),
140 Err(HostRepoPlanError::MissingHostOrRepo)
141 );
142 assert_eq!(
143 require_host_repo(Some("github.com"), None),
144 Err(HostRepoPlanError::MissingHostOrRepo)
145 );
146 assert!(
147 HostRepoPlanError::MissingHostOrRepo
148 .message()
149 .contains("host and repo")
150 );
151 }
152
153 #[test]
154 fn timestamp_and_submit_followup() {
155 assert_eq!(timestamp_secs_u64(None), 0);
156 assert_eq!(timestamp_secs_u64(Some(-1)), 0);
157 assert_eq!(format_unix_secs_label(0), "");
158 assert!(!format_unix_secs_label(1_700_000_000).is_empty());
159
160 assert_eq!(
161 proof_submit_followup(ProofStatusKind::Verified, "c1").as_deref(),
162 Some("Your control of the repo is verified.")
163 );
164 assert!(
165 proof_submit_followup(ProofStatusKind::Pending, "c1")
166 .unwrap()
167 .contains("heddle prove submit c1")
168 );
169 assert!(
170 proof_submit_followup(ProofStatusKind::Failed, "c2")
171 .unwrap()
172 .contains("heddle prove submit c2")
173 );
174 assert_eq!(
175 proof_submit_followup(ProofStatusKind::Unspecified, "c"),
176 None
177 );
178 }
179}