pub(crate) async fn comment_exists(
db: &sqlx::SqlitePool,
external_id: &str,
) -> crate::Result<bool> {
let count = sqlx::query_scalar!(
"SELECT COUNT(*) as \"n!: i64\" FROM review_comments WHERE external_comment_id = ?1",
external_id
)
.fetch_one(db)
.await?;
Ok(count > 0)
}
#[derive(Debug, Default, Clone)]
pub(crate) struct CommentDurabilitySignal {
pub(crate) resolved: bool,
pub(crate) reactions_total: i64,
pub(crate) thumbs_up: i64,
pub(crate) thumbs_down: i64,
pub(crate) later_replies: Vec<String>,
}
impl CommentDurabilitySignal {
pub(crate) fn to_metadata_value(&self) -> Option<serde_json::Value> {
if !self.resolved
&& self.reactions_total == 0
&& self.thumbs_up == 0
&& self.thumbs_down == 0
&& self.later_replies.is_empty()
{
return None;
}
Some(serde_json::json!({
"resolved": self.resolved,
"reactionsTotal": self.reactions_total,
"thumbsUp": self.thumbs_up,
"thumbsDown": self.thumbs_down,
"laterReplies": &self.later_replies,
}))
}
}
pub(crate) fn comment_metadata_json(
file_path: Option<&str>,
source_repo: Option<&str>,
attached_repo: &str,
source_kind: Option<&str>,
signal: &CommentDurabilitySignal,
) -> String {
let mut obj = serde_json::Map::new();
obj.insert(
"filePath".to_owned(),
file_path.map_or(serde_json::Value::Null, |p| {
serde_json::Value::String(p.to_owned())
}),
);
if let Some(source_repo) = source_repo.map(str::trim).filter(|repo| !repo.is_empty()) {
obj.insert(
"sourceRepoFullName".to_owned(),
serde_json::Value::String(source_repo.to_owned()),
);
}
obj.insert(
"attachedRepoFullName".to_owned(),
serde_json::Value::String(attached_repo.to_owned()),
);
if let Some(kind) = source_kind {
obj.insert(
"sourceKind".to_owned(),
serde_json::Value::String(kind.to_owned()),
);
}
if let Some(serde_json::Value::Object(signal_obj)) = signal.to_metadata_value() {
obj.extend(signal_obj);
}
serde_json::Value::Object(obj).to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn neutral_signal_serializes_to_none_so_legacy_metadata_is_unchanged() {
let signal = CommentDurabilitySignal::default();
assert!(signal.to_metadata_value().is_none());
let json = comment_metadata_json(
Some("src/lib.rs"),
Some("acme/up"),
"acme/fork",
None,
&signal,
);
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(value["filePath"], "src/lib.rs");
assert_eq!(value["sourceRepoFullName"], "acme/up");
assert!(value.get("resolved").is_none());
assert!(value.get("reactionsTotal").is_none());
assert!(value.get("laterReplies").is_none());
}
#[test]
fn resolved_thread_with_replies_round_trips_through_metadata() {
let signal = CommentDurabilitySignal {
resolved: true,
reactions_total: 2,
thumbs_up: 2,
thumbs_down: 0,
later_replies: vec!["Done, thanks!".to_owned()],
};
let json = comment_metadata_json(
Some("src/lib.rs"),
Some("acme/up"),
"acme/fork",
Some("issue_comment"),
&signal,
);
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(value["resolved"], true);
assert_eq!(value["thumbsUp"], 2);
assert_eq!(value["reactionsTotal"], 2);
assert_eq!(value["sourceKind"], "issue_comment");
assert_eq!(value["laterReplies"][0], "Done, thanks!");
}
}