crev_data/proof/review/
code.rs1use crate::{
2 proof, proof::content::ValidationResult, serde_content_serialize, serde_draft_serialize,
3 ParseError, Result,
4};
5use crev_common::{
6 self,
7 serde::{as_base64, from_base64},
8};
9use derive_builder::Builder;
10use proof::{CommonOps, Content};
11use serde::{Deserialize, Serialize};
12use std::{self, default::Default, fmt, path::PathBuf};
13
14const CURRENT_CODE_REVIEW_PROOF_SERIALIZATION_VERSION: i64 = -1;
15
16fn cur_version() -> i64 {
17 CURRENT_CODE_REVIEW_PROOF_SERIALIZATION_VERSION
18}
19
20#[derive(Clone, Debug, Serialize, Deserialize)]
21pub struct File {
22 pub path: PathBuf,
23 #[serde(serialize_with = "as_base64", deserialize_with = "from_base64")]
24 pub digest: Vec<u8>,
25 #[serde(rename = "digest-type")]
26 #[serde(
27 skip_serializing_if = "proof::equals_default_digest_type",
28 default = "proof::default_digest_type"
29 )]
30 pub digest_type: String,
31}
32
33#[derive(Clone, Builder, Debug, Serialize, Deserialize)]
35pub struct Code {
38 #[serde(flatten)]
39 pub common: proof::Common,
40 #[serde(rename = "package")]
41 pub package: proof::PackageInfo,
42 #[serde(flatten)]
43 #[builder(default = "Default::default()")]
44 pub review: super::Review,
45 #[serde(skip_serializing_if = "String::is_empty", default = "Default::default")]
46 #[builder(default = "Default::default()")]
47 pub comment: String,
48 #[serde(
49 skip_serializing_if = "std::vec::Vec::is_empty",
50 default = "std::vec::Vec::new"
51 )]
52 #[builder(default = "Default::default()")]
53 pub files: Vec<File>,
54}
55
56impl Code {
57 pub const KIND: &'static str = "code review";
58}
59
60impl CodeBuilder {
61 pub fn from<VALUE: Into<crate::PublicId>>(&mut self, value: VALUE) -> &mut Self {
62 if let Some(ref mut common) = self.common {
63 common.from = value.into();
64 } else {
65 self.common = Some(proof::Common {
66 kind: Some(Code::KIND.into()),
67 version: cur_version(),
68 date: crev_common::now(),
69 from: value.into(),
70 original: None,
71 });
72 }
73 self
74 }
75}
76
77impl proof::CommonOps for Code {
78 fn common(&self) -> &proof::Common {
79 &self.common
80 }
81
82 fn kind(&self) -> &str {
83 self.common.kind.as_deref().unwrap_or(Self::KIND)
85 }
86}
87
88impl fmt::Display for Code {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 self.serialize_to(f).map_err(|_| fmt::Error)
91 }
92}
93
94#[derive(Clone, Debug, Serialize, Deserialize)]
96pub struct Draft {
97 review: super::Review,
98 #[serde(default = "Default::default")]
99 comment: String,
100}
101
102impl Draft {
103 pub fn parse(s: &str) -> Result<Self> {
104 Ok(serde_yaml::from_str(s).map_err(ParseError::Draft)?)
105 }
106}
107
108impl From<Code> for Draft {
109 fn from(code: Code) -> Self {
110 Draft {
111 review: code.review,
112 comment: code.comment,
113 }
114 }
115}
116
117impl proof::WithReview for Code {
118 fn review(&self) -> &super::Review {
119 &self.review
120 }
121}
122
123impl proof::content::Content for Code {
124 fn validate_data(&self) -> ValidationResult<()> {
125 self.ensure_kind_is(Code::KIND)?;
126 Ok(())
127 }
128
129 fn serialize_to(&self, fmt: &mut dyn std::fmt::Write) -> fmt::Result {
130 serde_content_serialize!(self, fmt);
131 Ok(())
132 }
133}
134
135impl proof::ContentWithDraft for Code {
136 fn to_draft(&self) -> proof::Draft {
137 proof::Draft {
138 title: format!(
139 "Code Review of {} files of {} {}",
140 self.files.len(),
141 self.package.id.id.name,
142 self.package.id.version
143 ),
144 body: Draft::from(self.clone()).to_string(),
145 }
146 }
147
148 fn apply_draft(&self, s: &str) -> Result<Self> {
149 let draft = Draft::parse(s)?;
150
151 let mut copy = self.clone();
152 copy.review = draft.review;
153 copy.comment = draft.comment;
154
155 copy.validate_data()?;
156 Ok(copy)
157 }
158}
159
160impl fmt::Display for Draft {
161 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
162 serde_draft_serialize!(self, fmt);
163 Ok(())
164 }
165}