1use serde::{Deserialize, Serialize};
7
8const SHORT_OID: usize = 7;
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12#[serde(tag = "kind", rename_all = "snake_case")]
13pub enum ReviewSource {
14 WorkingTree,
15 Commit {
16 oid: String,
17 },
18 Range {
19 oldest: String,
20 newest: String,
21 },
22 Pr {
25 number: u64,
26 },
27}
28
29impl ReviewSource {
30 pub fn commit(oid: impl Into<String>) -> Self {
31 Self::Commit { oid: oid.into() }
32 }
33
34 pub fn range(oldest: impl Into<String>, newest: impl Into<String>) -> Self {
35 Self::Range {
36 oldest: oldest.into(),
37 newest: newest.into(),
38 }
39 }
40
41 pub fn pr(number: u64) -> Self {
42 Self::Pr { number }
43 }
44
45 pub fn key(&self) -> String {
49 match self {
50 Self::WorkingTree => "working".to_owned(),
51 Self::Commit { oid } => format!("commit-{oid}"),
52 Self::Range { oldest, newest } => format!("range-{oldest}-{newest}"),
53 Self::Pr { number } => format!("pr-{number}"),
54 }
55 }
56
57 pub fn label(&self) -> String {
59 match self {
60 Self::WorkingTree => "working tree".to_owned(),
61 Self::Commit { oid } => format!("commit {}", short(oid)),
62 Self::Range { oldest, newest } => {
63 format!("range {}..{}", short(oldest), short(newest))
64 }
65 Self::Pr { number } => format!("PR #{number}"),
66 }
67 }
68}
69
70fn short(oid: &str) -> &str {
71 oid.get(..SHORT_OID).unwrap_or(oid)
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77
78 #[test]
79 fn keys_are_deterministic_and_distinct_per_source() {
80 assert_eq!(ReviewSource::WorkingTree.key(), "working");
81 assert_eq!(ReviewSource::commit("abc123").key(), "commit-abc123");
82 assert_eq!(ReviewSource::range("aaa", "bbb").key(), "range-aaa-bbb");
83 assert_eq!(ReviewSource::pr(42).key(), "pr-42");
84 }
85
86 #[test]
87 fn labels_shorten_oids() {
88 assert_eq!(ReviewSource::WorkingTree.label(), "working tree");
89 assert_eq!(
90 ReviewSource::commit("0123456789abcdef").label(),
91 "commit 0123456"
92 );
93 assert_eq!(
94 ReviewSource::range("0123456789", "fedcba9876").label(),
95 "range 0123456..fedcba9"
96 );
97 }
98
99 #[test]
100 fn short_oid_tolerates_a_short_string() {
101 assert_eq!(ReviewSource::commit("ab").label(), "commit ab");
102 }
103
104 #[test]
105 fn round_trips_through_json_as_a_tagged_descriptor() {
106 for source in [
107 ReviewSource::WorkingTree,
108 ReviewSource::commit("abc"),
109 ReviewSource::range("aaa", "bbb"),
110 ] {
111 let json = serde_json::to_string(&source).expect("serialize");
112 let back: ReviewSource = serde_json::from_str(&json).expect("deserialize");
113 assert_eq!(source, back);
114 }
115 }
116}