157 lines
4.2 KiB
Rust
157 lines
4.2 KiB
Rust
use gotcha_gitea::models;
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
pub enum Target {
|
|
Commit {
|
|
owner: String,
|
|
repository: String,
|
|
sha: String,
|
|
},
|
|
Issue {
|
|
owner: String,
|
|
repository: String,
|
|
number: i64,
|
|
},
|
|
Pull {
|
|
owner: String,
|
|
repository: String,
|
|
number: i64,
|
|
},
|
|
}
|
|
|
|
pub fn target(activity: &models::Activity) -> Option<Target> {
|
|
use models::activity::OpType;
|
|
|
|
let (owner, repository) = activity
|
|
.repo
|
|
.as_ref()?
|
|
.full_name
|
|
.as_deref()?
|
|
.split_once('/')?;
|
|
let pair = || (owner.to_string(), repository.to_string());
|
|
match activity.op_type? {
|
|
OpType::CommitRepo | OpType::MirrorSyncPush => {
|
|
let (owner, repository) = pair();
|
|
Some(Target::Commit {
|
|
owner,
|
|
repository,
|
|
sha: commit_sha(activity.content.as_deref()?)?,
|
|
})
|
|
}
|
|
OpType::CreateIssue | OpType::CloseIssue | OpType::ReopenIssue | OpType::CommentIssue => {
|
|
let (owner, repository) = pair();
|
|
Some(Target::Issue {
|
|
owner,
|
|
repository,
|
|
number: issue_number(activity)?,
|
|
})
|
|
}
|
|
OpType::CreatePullRequest
|
|
| OpType::MergePullRequest
|
|
| OpType::AutoMergePullRequest
|
|
| OpType::ClosePullRequest
|
|
| OpType::ReopenPullRequest
|
|
| OpType::CommentPull
|
|
| OpType::ApprovePullRequest
|
|
| OpType::RejectPullRequest
|
|
| OpType::PullReviewDismissed
|
|
| OpType::PullRequestReadyForReview => {
|
|
let (owner, repository) = pair();
|
|
Some(Target::Pull {
|
|
owner,
|
|
repository,
|
|
number: issue_number(activity)?,
|
|
})
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn issue_number(activity: &models::Activity) -> Option<i64> {
|
|
activity
|
|
.comment
|
|
.as_ref()
|
|
.and_then(|comment| {
|
|
comment
|
|
.pull_request_url
|
|
.as_deref()
|
|
.or(comment.issue_url.as_deref())
|
|
})
|
|
.and_then(url_number)
|
|
.or_else(|| {
|
|
activity
|
|
.content
|
|
.as_deref()?
|
|
.split(|character: char| !character.is_ascii_digit())
|
|
.find(|part| !part.is_empty())?
|
|
.parse()
|
|
.ok()
|
|
})
|
|
}
|
|
|
|
fn url_number(url: &str) -> Option<i64> {
|
|
url.trim_end_matches('/').rsplit('/').next()?.parse().ok()
|
|
}
|
|
|
|
fn commit_sha(content: &str) -> Option<String> {
|
|
let payload: serde_json::Value = serde_json::from_str(content).ok()?;
|
|
payload.get("HeadCommit").and_then(find_sha).or_else(|| {
|
|
payload
|
|
.get("Commits")?
|
|
.as_array()?
|
|
.last()
|
|
.and_then(find_sha)
|
|
})
|
|
}
|
|
|
|
fn find_sha(value: &serde_json::Value) -> Option<String> {
|
|
value.as_object()?.iter().find_map(|(key, value)| {
|
|
(key.to_ascii_lowercase().contains("sha"))
|
|
.then(|| value.as_str())
|
|
.flatten()
|
|
.filter(|sha| sha.len() >= 7)
|
|
.map(str::to_string)
|
|
})
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use models::activity::OpType;
|
|
|
|
fn activity(op_type: OpType, content: &str) -> models::Activity {
|
|
models::Activity {
|
|
op_type: Some(op_type),
|
|
content: Some(content.into()),
|
|
repo: Some(Box::new(models::Repository {
|
|
full_name: Some("octo/demo".into()),
|
|
..Default::default()
|
|
})),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn decodes_linkable_activity_targets() {
|
|
assert_eq!(
|
|
target(&activity(OpType::CreatePullRequest, "42|feature")),
|
|
Some(Target::Pull {
|
|
owner: "octo".into(),
|
|
repository: "demo".into(),
|
|
number: 42,
|
|
})
|
|
);
|
|
assert_eq!(
|
|
target(&activity(
|
|
OpType::CommitRepo,
|
|
r#"{"HeadCommit":{"Sha1":"0123456789abcdef"}}"#,
|
|
)),
|
|
Some(Target::Commit {
|
|
owner: "octo".into(),
|
|
repository: "demo".into(),
|
|
sha: "0123456789abcdef".into(),
|
|
})
|
|
);
|
|
}
|
|
}
|