Append link to gitlab to create link between open project and gitlab #7

Merged
julienfastre merged 3 commits from feature/add-gitlab-link into main 2025-11-18 08:45:34 +00:00
4 changed files with 78 additions and 14 deletions
Showing only changes of commit 7bf758f421 - Show all commits

39
Cargo.lock generated
View File

@@ -17,6 +17,15 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "aho-corasick"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "android_system_properties" name = "android_system_properties"
version = "0.1.5" version = "0.1.5"
@@ -180,6 +189,7 @@ dependencies = [
"gitlab", "gitlab",
"log", "log",
"open", "open",
"regex",
"reqwest", "reqwest",
"serde", "serde",
"serde_json", "serde_json",
@@ -1155,6 +1165,35 @@ dependencies = [
"getrandom 0.3.4", "getrandom 0.3.4",
] ]
[[package]]
name = "regex"
version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
[[package]] [[package]]
name = "reqwest" name = "reqwest"
version = "0.12.24" version = "0.12.24"

View File

@@ -27,6 +27,7 @@ simple-home-dir = "0.5.2"
log = "0.4.28" log = "0.4.28"
open = "5.3.2" open = "5.3.2"
serde_json = "1.0.145" serde_json = "1.0.145"
regex = "1.12.2"
[package.metadata.deb] [package.metadata.deb]
maintainer = "Julien Fastré <julien.fastre@champs-libres.coop>" maintainer = "Julien Fastré <julien.fastre@champs-libres.coop>"

View File

@@ -65,10 +65,30 @@ fn add_related_issues_section(issue: &IssueRelated) -> Vec<String> {
fn convert_issue_link_items(issue: &IssueRelated) -> String { fn convert_issue_link_items(issue: &IssueRelated) -> String {
match issue { match issue {
IssueRelated::OpenProjectIssue(issue_url) => format!("- [{}]({})", issue_url, issue_url), IssueRelated::OpenProjectIssue(issue_url) => {
let m = format!("- [{}]({})", issue_url, issue_url);
match extract_id(issue_url) {
Some(n) => format!("{}\n- OP#{}", m, n),
None => m,
}
}
} }
} }
use regex::Regex;
fn extract_id(url: &str) -> Option<u32> {
// Cette regex capture le dernier segment numérique si lURL se termine par :
// - /work_packages/<id>
// - /wp/<id>
// Et accepte n'importe quel segment avant.
let re = Regex::new(r"/(?:work_packages|wp)/(\d+)$").unwrap();
re.captures(url)
.and_then(|caps| caps.get(1))
.and_then(|m| m.as_str().parse::<u32>().ok())
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::planning::utils::{append_related_issues, IssueRelated}; use crate::planning::utils::{append_related_issues, IssueRelated};
@@ -80,7 +100,8 @@ mod tests {
r#" r#"
## Related issues ## Related issues
- [https://example/wp/1](https://example/wp/1)"#, - [https://example/wp/1](https://example/wp/1)
- OP#1"#,
append_related_issues(&issue, &("".to_string())) append_related_issues(&issue, &("".to_string()))
); );
} }
@@ -97,7 +118,8 @@ Some content
## Related issues ## Related issues
- [https://example/wp/1](https://example/wp/1)"#, - [https://example/wp/1](https://example/wp/1)
- OP#1"#,
append_related_issues( append_related_issues(
&issue, &issue,
&"Something happens.\n\ &"Something happens.\n\
@@ -123,7 +145,8 @@ Some content
## Related issues ## Related issues
- [https://example/wp/2](https://example/wp/2) - [https://example/wp/2](https://example/wp/2)
- [https://example/wp/1](https://example/wp/1)"#, - [https://example/wp/1](https://example/wp/1)
- OP#1"#,
append_related_issues( append_related_issues(
&issue, &issue,
&r#"Something happens. &r#"Something happens.
@@ -154,6 +177,7 @@ Some content
- [https://example/wp/2](https://example/wp/2) - [https://example/wp/2](https://example/wp/2)
- [https://example/wp/1](https://example/wp/1) - [https://example/wp/1](https://example/wp/1)
- OP#1
## Other content ## Other content

View File

@@ -172,10 +172,10 @@ mod tests {
fn deserializes_issue2work_variant() { fn deserializes_issue2work_variant() {
// Given: a JSON with the external tag "type" matching the enum variant name // Given: a JSON with the external tag "type" matching the enum variant name
let payload = json!({ let payload = json!({
"type": "Issue2Work", "type": "Issue2Work",
"url": "https://example.com/issues/123", "url": "https://example.com/issues/123",
"project": "test" "project": "test"
}); });
// When: deserializing to InputMessage // When: deserializing to InputMessage
let msg: InputMessage = serde_json::from_value(payload).expect("valid enum JSON"); let msg: InputMessage = serde_json::from_value(payload).expect("valid enum JSON");
@@ -192,10 +192,10 @@ mod tests {
#[test] #[test]
fn fails_on_unknown_type_tag() { fn fails_on_unknown_type_tag() {
let payload = json!({ let payload = json!({
"type": "Unknown", "type": "Unknown",
"url": "https://example.com/issues/123", "url": "https://example.com/issues/123",
"project": "test" "project": "test"
}); });
let err = serde_json::from_value::<InputMessage>(payload).unwrap_err(); let err = serde_json::from_value::<InputMessage>(payload).unwrap_err();
// Basic sanity check that it's a data error mentioning the unrecognized variant // Basic sanity check that it's a data error mentioning the unrecognized variant
@@ -210,8 +210,8 @@ mod tests {
fn fails_when_missing_required_fields() { fn fails_when_missing_required_fields() {
// Missing "url" and "project" // Missing "url" and "project"
let payload = json!({ let payload = json!({
"type": "Issue2Work" "type": "Issue2Work"
}); });
let err = serde_json::from_value::<InputMessage>(payload).unwrap_err(); let err = serde_json::from_value::<InputMessage>(payload).unwrap_err();
let msg = err.to_string(); let msg = err.to_string();