style: format tauri backend files

This commit is contained in:
thibaud-lclr 2026-04-15 18:03:48 +02:00
parent 9ef1220650
commit c614796e5f
11 changed files with 119 additions and 76 deletions

View file

@ -12,8 +12,8 @@ pub fn set_tuleap_credentials(
username: String, username: String,
password: String, password: String,
) -> Result<TuleapCredentialsSafe, AppError> { ) -> Result<TuleapCredentialsSafe, AppError> {
let password_encrypted = crypto::encrypt(&state.encryption_key, &password) let password_encrypted =
.map_err(AppError::from)?; crypto::encrypt(&state.encryption_key, &password).map_err(AppError::from)?;
let db = state let db = state
.db .db

View file

@ -15,10 +15,7 @@ pub fn list_notifications(
} }
#[tauri::command] #[tauri::command]
pub fn mark_notification_read( pub fn mark_notification_read(state: State<'_, AppState>, id: String) -> Result<(), AppError> {
state: State<'_, AppState>,
id: String,
) -> Result<(), AppError> {
let conn = state.db.lock().map_err(|e| AppError::from(e.to_string()))?; let conn = state.db.lock().map_err(|e| AppError::from(e.to_string()))?;
Notification::mark_read(&conn, &id)?; Notification::mark_read(&conn, &id)?;
Ok(()) Ok(())

View file

@ -23,10 +23,7 @@ pub fn get_ticket_result(
} }
#[tauri::command] #[tauri::command]
pub fn retry_ticket( pub fn retry_ticket(state: State<'_, AppState>, ticket_id: String) -> Result<(), AppError> {
state: State<'_, AppState>,
ticket_id: String,
) -> Result<(), AppError> {
let conn = state.db.lock().map_err(|e| AppError::from(e.to_string()))?; let conn = state.db.lock().map_err(|e| AppError::from(e.to_string()))?;
let ticket = ProcessedTicket::get_by_id(&conn, &ticket_id)?; let ticket = ProcessedTicket::get_by_id(&conn, &ticket_id)?;
@ -47,7 +44,8 @@ pub fn retry_ticket(
if let Some(wt) = Worktree::get_by_ticket_id(&conn, &ticket_id)? { if let Some(wt) = Worktree::get_by_ticket_id(&conn, &ticket_id)? {
if wt.status == "Active" { if wt.status == "Active" {
let project_id = { let project_id = {
let tracker = crate::models::tracker::WatchedTracker::get_by_id(&conn, &ticket.tracker_id)?; let tracker =
crate::models::tracker::WatchedTracker::get_by_id(&conn, &ticket.tracker_id)?;
tracker.project_id tracker.project_id
}; };
let project = crate::models::project::Project::get_by_id(&conn, &project_id)?; let project = crate::models::project::Project::get_by_id(&conn, &project_id)?;
@ -64,10 +62,7 @@ pub fn retry_ticket(
} }
#[tauri::command] #[tauri::command]
pub fn cancel_ticket( pub fn cancel_ticket(state: State<'_, AppState>, ticket_id: String) -> Result<(), AppError> {
state: State<'_, AppState>,
ticket_id: String,
) -> Result<(), AppError> {
let conn = state.db.lock().map_err(|e| AppError::from(e.to_string()))?; let conn = state.db.lock().map_err(|e| AppError::from(e.to_string()))?;
let ticket = ProcessedTicket::get_by_id(&conn, &ticket_id)?; let ticket = ProcessedTicket::get_by_id(&conn, &ticket_id)?;

View file

@ -29,7 +29,9 @@ impl From<String> for AppError {
impl From<reqwest::Error> for AppError { impl From<reqwest::Error> for AppError {
fn from(e: reqwest::Error) -> Self { fn from(e: reqwest::Error) -> Self {
AppError { message: e.to_string() } AppError {
message: e.to_string(),
}
} }
} }

View file

@ -105,13 +105,9 @@ mod tests {
TuleapCredentials::upsert(&conn, "https://old.example.com", "old_user", "old_enc") TuleapCredentials::upsert(&conn, "https://old.example.com", "old_user", "old_enc")
.expect("first upsert should succeed"); .expect("first upsert should succeed");
let second = TuleapCredentials::upsert( let second =
&conn, TuleapCredentials::upsert(&conn, "https://new.example.com", "new_user", "new_enc")
"https://new.example.com", .expect("second upsert should succeed");
"new_user",
"new_enc",
)
.expect("second upsert should succeed");
// Only one record should exist // Only one record should exist
let creds = TuleapCredentials::get(&conn) let creds = TuleapCredentials::get(&conn)
@ -133,13 +129,9 @@ mod tests {
#[test] #[test]
fn test_get_returns_credentials() { fn test_get_returns_credentials() {
let conn = setup(); let conn = setup();
let created = TuleapCredentials::upsert( let created =
&conn, TuleapCredentials::upsert(&conn, "https://tuleap.example.com", "bob", "enc_pass")
"https://tuleap.example.com", .expect("upsert should succeed");
"bob",
"enc_pass",
)
.expect("upsert should succeed");
let fetched = TuleapCredentials::get(&conn) let fetched = TuleapCredentials::get(&conn)
.expect("get should succeed") .expect("get should succeed")

View file

@ -77,7 +77,10 @@ impl Notification {
} }
pub fn mark_read(conn: &Connection, id: &str) -> Result<()> { pub fn mark_read(conn: &Connection, id: &str) -> Result<()> {
conn.execute("UPDATE notifications SET read = 1 WHERE id = ?1", params![id])?; conn.execute(
"UPDATE notifications SET read = 1 WHERE id = ?1",
params![id],
)?;
Ok(()) Ok(())
} }

View file

@ -104,8 +104,14 @@ mod tests {
#[test] #[test]
fn test_insert_project_local_path() { fn test_insert_project_local_path() {
let conn = setup(); let conn = setup();
let project = Project::insert(&conn, "My Project", "/home/user/code/myproject", None, "main") let project = Project::insert(
.expect("insert should succeed"); &conn,
"My Project",
"/home/user/code/myproject",
None,
"main",
)
.expect("insert should succeed");
assert_eq!(project.name, "My Project"); assert_eq!(project.name, "My Project");
assert_eq!(project.path, "/home/user/code/myproject"); assert_eq!(project.path, "/home/user/code/myproject");
@ -127,7 +133,10 @@ mod tests {
) )
.expect("insert should succeed"); .expect("insert should succeed");
assert_eq!(project.cloned_from.as_deref(), Some("https://github.com/org/repo.git")); assert_eq!(
project.cloned_from.as_deref(),
Some("https://github.com/org/repo.git")
);
assert_eq!(project.base_branch, "stable"); assert_eq!(project.base_branch, "stable");
} }

View file

@ -16,7 +16,9 @@ pub fn apply_filters(
} }
fn matches_all_groups(artifact: &serde_json::Value, groups: &[FilterGroup]) -> bool { fn matches_all_groups(artifact: &serde_json::Value, groups: &[FilterGroup]) -> bool {
groups.iter().all(|g| matches_any_condition(artifact, &g.conditions)) groups
.iter()
.all(|g| matches_any_condition(artifact, &g.conditions))
} }
fn matches_any_condition(artifact: &serde_json::Value, conditions: &[Filter]) -> bool { fn matches_any_condition(artifact: &serde_json::Value, conditions: &[Filter]) -> bool {
@ -30,12 +32,10 @@ fn matches_condition(artifact: &serde_json::Value, condition: &Filter) -> bool {
let field_values = extract_artifact_field_values(artifact, &condition.field); let field_values = extract_artifact_field_values(artifact, &condition.field);
match condition.operator.as_str() { match condition.operator.as_str() {
"Equals" => { "Equals" => {
condition.value.len() == 1 condition.value.len() == 1 && field_values.iter().any(|v| v == &condition.value[0])
&& field_values.iter().any(|v| v == &condition.value[0])
} }
"NotEquals" => { "NotEquals" => {
condition.value.len() == 1 condition.value.len() == 1 && !field_values.iter().any(|v| v == &condition.value[0])
&& !field_values.iter().any(|v| v == &condition.value[0])
} }
"In" => field_values.iter().any(|v| condition.value.contains(v)), "In" => field_values.iter().any(|v| condition.value.contains(v)),
"NotIn" => !field_values.iter().any(|v| condition.value.contains(v)), "NotIn" => !field_values.iter().any(|v| condition.value.contains(v)),

View file

@ -1,8 +1,8 @@
use crate::models::credential::TuleapCredentials; use crate::models::credential::TuleapCredentials;
use crate::models::ticket::ProcessedTicket; use crate::models::ticket::ProcessedTicket;
use crate::models::tracker::WatchedTracker; use crate::models::tracker::WatchedTracker;
use crate::services::{crypto, filter_engine, notifier};
use crate::services::tuleap_client::TuleapClient; use crate::services::tuleap_client::TuleapClient;
use crate::services::{crypto, filter_engine, notifier};
use rusqlite::Connection; use rusqlite::Connection;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tauri::{AppHandle, Emitter}; use tauri::{AppHandle, Emitter};
@ -87,7 +87,10 @@ fn should_poll(tracker: &WatchedTracker) -> bool {
let last = match chrono::DateTime::parse_from_rfc3339(last_polled_at) { let last = match chrono::DateTime::parse_from_rfc3339(last_polled_at) {
Ok(dt) => dt, Ok(dt) => dt,
Err(e) => { Err(e) => {
eprintln!("poller: failed to parse last_polled_at '{}': {}", last_polled_at, e); eprintln!(
"poller: failed to parse last_polled_at '{}': {}",
last_polled_at, e
);
return true; // Treat as never polled on parse error return true; // Treat as never polled on parse error
} }
}; };
@ -116,7 +119,10 @@ async fn poll_single_tracker(
let artifacts = match client.get_artifacts(tracker.tracker_id).await { let artifacts = match client.get_artifacts(tracker.tracker_id).await {
Ok(a) => a, Ok(a) => a,
Err(e) => { Err(e) => {
eprintln!("poller: failed to fetch artifacts for tracker {}: {}", tracker.id, e); eprintln!(
"poller: failed to fetch artifacts for tracker {}: {}",
tracker.id, e
);
let _ = app_handle.emit( let _ = app_handle.emit(
"polling-error", "polling-error",
serde_json::json!({ serde_json::json!({
@ -147,10 +153,7 @@ async fn poll_single_tracker(
let mut inserted = Vec::new(); let mut inserted = Vec::new();
for artifact in &filtered { for artifact in &filtered {
let artifact_id = artifact let artifact_id = artifact.get("id").and_then(|v| v.as_i64()).unwrap_or(0) as i32;
.get("id")
.and_then(|v| v.as_i64())
.unwrap_or(0) as i32;
let artifact_title = artifact let artifact_title = artifact
.get("title") .get("title")
@ -158,8 +161,8 @@ async fn poll_single_tracker(
.unwrap_or("") .unwrap_or("")
.to_string(); .to_string();
let artifact_data = serde_json::to_string(artifact) let artifact_data =
.unwrap_or_else(|_| "{}".to_string()); serde_json::to_string(artifact).unwrap_or_else(|_| "{}".to_string());
match ProcessedTicket::insert_if_new( match ProcessedTicket::insert_if_new(
&conn, &conn,
@ -171,14 +174,20 @@ async fn poll_single_tracker(
Ok(Some(ticket)) => inserted.push(ticket), Ok(Some(ticket)) => inserted.push(ticket),
Ok(None) => {} Ok(None) => {}
Err(e) => { Err(e) => {
eprintln!("poller: failed to insert ticket (artifact {}): {}", artifact_id, e); eprintln!(
"poller: failed to insert ticket (artifact {}): {}",
artifact_id, e
);
} }
} }
} }
// 4. Update last_polled_at // 4. Update last_polled_at
if let Err(e) = WatchedTracker::update_last_polled(&conn, &tracker.id) { if let Err(e) = WatchedTracker::update_last_polled(&conn, &tracker.id) {
eprintln!("poller: failed to update last_polled_at for tracker {}: {}", tracker.id, e); eprintln!(
"poller: failed to update last_polled_at for tracker {}: {}",
tracker.id, e
);
} }
inserted inserted

View file

@ -39,7 +39,10 @@ impl TuleapClient {
for attempt in 1..=MAX_ATTEMPTS { for attempt in 1..=MAX_ATTEMPTS {
let started_at = Instant::now(); let started_at = Instant::now();
eprintln!("[tuleap] -> GET {} (attempt {}/{})", url, attempt, MAX_ATTEMPTS); eprintln!(
"[tuleap] -> GET {} (attempt {}/{})",
url, attempt, MAX_ATTEMPTS
);
let response = self let response = self
.http .http
@ -59,7 +62,8 @@ impl TuleapClient {
); );
// Retry transient HTTP failures. // Retry transient HTTP failures.
if (status == reqwest::StatusCode::TOO_MANY_REQUESTS || status.is_server_error()) if (status == reqwest::StatusCode::TOO_MANY_REQUESTS
|| status.is_server_error())
&& attempt < MAX_ATTEMPTS && attempt < MAX_ATTEMPTS
{ {
let delay_ms = BASE_DELAY_MS * 2u64.pow(attempt - 1); let delay_ms = BASE_DELAY_MS * 2u64.pow(attempt - 1);
@ -156,10 +160,7 @@ impl TuleapClient {
.await .await
.map_err(|e| format!("failed to parse response: {}", e))?; .map_err(|e| format!("failed to parse response: {}", e))?;
let artifacts = body let artifacts = body.as_array().cloned().unwrap_or_default();
.as_array()
.cloned()
.unwrap_or_default();
let fetched = artifacts.len(); let fetched = artifacts.len();
all_artifacts.extend(artifacts); all_artifacts.extend(artifacts);
@ -213,11 +214,7 @@ fn extract_field_values(field: &serde_json::Value) -> Vec<FieldValue> {
.get("values") .get("values")
.and_then(|v| v.as_array()) .and_then(|v| v.as_array())
.filter(|arr| !arr.is_empty()) .filter(|arr| !arr.is_empty())
.or_else(|| { .or_else(|| field.get("bind_value_objects").and_then(|v| v.as_array()));
field
.get("bind_value_objects")
.and_then(|v| v.as_array())
});
let arr = match candidates { let arr = match candidates {
Some(a) => a, Some(a) => a,
@ -269,10 +266,7 @@ pub fn extract_artifact_field_values(
None => return vec![], None => return vec![],
}; };
let field_type = entry let field_type = entry.get("type").and_then(|t| t.as_str()).unwrap_or("");
.get("type")
.and_then(|t| t.as_str())
.unwrap_or("");
match field_type { match field_type {
"sb" | "rb" => { "sb" | "rb" => {
@ -286,7 +280,9 @@ pub fn extract_artifact_field_values(
if let Some(id) = v.get("id") { if let Some(id) = v.get("id") {
match id { match id {
serde_json::Value::String(s) => push_unique(&mut out, s.clone()), serde_json::Value::String(s) => push_unique(&mut out, s.clone()),
serde_json::Value::Number(n) => push_unique(&mut out, n.to_string()), serde_json::Value::Number(n) => {
push_unique(&mut out, n.to_string())
}
_ => {} _ => {}
} }
} }
@ -309,7 +305,9 @@ pub fn extract_artifact_field_values(
if let Some(id) = v.get("id") { if let Some(id) = v.get("id") {
match id { match id {
serde_json::Value::String(s) => push_unique(&mut out, s.clone()), serde_json::Value::String(s) => push_unique(&mut out, s.clone()),
serde_json::Value::Number(n) => push_unique(&mut out, n.to_string()), serde_json::Value::Number(n) => {
push_unique(&mut out, n.to_string())
}
_ => {} _ => {}
} }
} }

View file

@ -33,13 +33,24 @@ pub fn create_worktree(
run_git( run_git(
project_path, project_path,
&["worktree", "add", wt_path_str, "-b", &branch_name, base_branch], &[
"worktree",
"add",
wt_path_str,
"-b",
&branch_name,
base_branch,
],
)?; )?;
Ok((wt_path_str.to_string(), branch_name)) Ok((wt_path_str.to_string(), branch_name))
} }
pub fn get_diff(project_path: &str, base_branch: &str, branch_name: &str) -> Result<String, String> { pub fn get_diff(
project_path: &str,
base_branch: &str,
branch_name: &str,
) -> Result<String, String> {
let range = format!("{}...{}", base_branch, branch_name); let range = format!("{}...{}", base_branch, branch_name);
run_git(project_path, &["diff", &range]) run_git(project_path, &["diff", &range])
} }
@ -96,7 +107,10 @@ pub fn delete_worktree(
worktree_path: &str, worktree_path: &str,
branch_name: &str, branch_name: &str,
) -> Result<(), String> { ) -> Result<(), String> {
run_git(project_path, &["worktree", "remove", worktree_path, "--force"])?; run_git(
project_path,
&["worktree", "remove", worktree_path, "--force"],
)?;
let _ = run_git(project_path, &["branch", "-D", branch_name]); let _ = run_git(project_path, &["branch", "-D", branch_name]);
Ok(()) Ok(())
} }
@ -119,7 +133,11 @@ mod tests {
let dir = tempfile::tempdir().expect("create temp dir"); let dir = tempfile::tempdir().expect("create temp dir");
let path = dir.path().to_str().unwrap(); let path = dir.path().to_str().unwrap();
Command::new("git").args(["init"]).current_dir(path).output().unwrap(); Command::new("git")
.args(["init"])
.current_dir(path)
.output()
.unwrap();
Command::new("git") Command::new("git")
.args(["config", "user.email", "test@test.com"]) .args(["config", "user.email", "test@test.com"])
.current_dir(path) .current_dir(path)
@ -132,7 +150,11 @@ mod tests {
.unwrap(); .unwrap();
std::fs::write(dir.path().join("README.md"), "# Test").unwrap(); std::fs::write(dir.path().join("README.md"), "# Test").unwrap();
Command::new("git").args(["add", "."]).current_dir(path).output().unwrap(); Command::new("git")
.args(["add", "."])
.current_dir(path)
.output()
.unwrap();
Command::new("git") Command::new("git")
.args(["commit", "-m", "init"]) .args(["commit", "-m", "init"])
.current_dir(path) .current_dir(path)
@ -184,7 +206,11 @@ mod tests {
let (wt_path, branch) = create_worktree(path, &base_branch, 2).unwrap(); let (wt_path, branch) = create_worktree(path, &base_branch, 2).unwrap();
std::fs::write(Path::new(&wt_path).join("fix.txt"), "fixed").unwrap(); std::fs::write(Path::new(&wt_path).join("fix.txt"), "fixed").unwrap();
Command::new("git").args(["add", "."]).current_dir(&wt_path).output().unwrap(); Command::new("git")
.args(["add", "."])
.current_dir(&wt_path)
.output()
.unwrap();
Command::new("git") Command::new("git")
.args(["commit", "-m", "fix"]) .args(["commit", "-m", "fix"])
.current_dir(&wt_path) .current_dir(&wt_path)
@ -205,7 +231,11 @@ mod tests {
let (wt_path, branch) = create_worktree(path, &base_branch, 3).unwrap(); let (wt_path, branch) = create_worktree(path, &base_branch, 3).unwrap();
std::fs::write(Path::new(&wt_path).join("a.txt"), "a").unwrap(); std::fs::write(Path::new(&wt_path).join("a.txt"), "a").unwrap();
Command::new("git").args(["add", "."]).current_dir(&wt_path).output().unwrap(); Command::new("git")
.args(["add", "."])
.current_dir(&wt_path)
.output()
.unwrap();
Command::new("git") Command::new("git")
.args(["commit", "-m", "first"]) .args(["commit", "-m", "first"])
.current_dir(&wt_path) .current_dir(&wt_path)
@ -213,7 +243,11 @@ mod tests {
.unwrap(); .unwrap();
std::fs::write(Path::new(&wt_path).join("b.txt"), "b").unwrap(); std::fs::write(Path::new(&wt_path).join("b.txt"), "b").unwrap();
Command::new("git").args(["add", "."]).current_dir(&wt_path).output().unwrap(); Command::new("git")
.args(["add", "."])
.current_dir(&wt_path)
.output()
.unwrap();
Command::new("git") Command::new("git")
.args(["commit", "-m", "second"]) .args(["commit", "-m", "second"])
.current_dir(&wt_path) .current_dir(&wt_path)
@ -266,7 +300,11 @@ mod tests {
let (wt_path, branch) = create_worktree(path, &base_branch, 7).unwrap(); let (wt_path, branch) = create_worktree(path, &base_branch, 7).unwrap();
std::fs::write(Path::new(&wt_path).join("fix.txt"), "the fix").unwrap(); std::fs::write(Path::new(&wt_path).join("fix.txt"), "the fix").unwrap();
Command::new("git").args(["add", "."]).current_dir(&wt_path).output().unwrap(); Command::new("git")
.args(["add", "."])
.current_dir(&wt_path)
.output()
.unwrap();
Command::new("git") Command::new("git")
.args(["commit", "-m", "apply fix"]) .args(["commit", "-m", "apply fix"])
.current_dir(&wt_path) .current_dir(&wt_path)