Skip to content

Commit 9d8f959

Browse files
authored
Merge pull request #78 from saagpatel/codex/refactor/app-error-small-files
refactor(src): migrate 3 small command files to AppError
2 parents 5281b03 + fcb9e32 commit 9d8f959

4 files changed

Lines changed: 91 additions & 52 deletions

File tree

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,42 @@
1+
use crate::error::AppError;
12
use crate::AppState;
23
use tauri::State;
34

45
#[tauri::command]
5-
pub fn check_fts5_enabled(state: State<'_, AppState>) -> Result<bool, String> {
6-
let db_lock = state.db.lock().map_err(|e| e.to_string())?;
7-
let db = db_lock.as_ref().ok_or("Database not initialized")?;
8-
db.verify_fts5().map_err(|e| e.to_string())
6+
pub fn check_fts5_enabled(state: State<'_, AppState>) -> Result<bool, AppError> {
7+
let db_lock = state.db.lock().map_err(|_| AppError::db_lock_failed())?;
8+
let db = db_lock.as_ref().ok_or_else(AppError::db_not_initialized)?;
9+
db.verify_fts5()
10+
.map_err(|e| AppError::db_query_failed(e.to_string()))
911
}
1012

1113
#[tauri::command]
12-
pub fn check_db_integrity(state: State<'_, AppState>) -> Result<bool, String> {
13-
let db_lock = state.db.lock().map_err(|e| e.to_string())?;
14-
let db = db_lock.as_ref().ok_or("Database not initialized")?;
14+
pub fn check_db_integrity(state: State<'_, AppState>) -> Result<bool, AppError> {
15+
let db_lock = state.db.lock().map_err(|_| AppError::db_lock_failed())?;
16+
let db = db_lock.as_ref().ok_or_else(AppError::db_not_initialized)?;
1517
db.check_integrity()
1618
.map(|_| true)
17-
.map_err(|e| e.to_string())
19+
.map_err(|e| AppError::db_query_failed(e.to_string()))
1820
}
1921

2022
#[tauri::command]
21-
pub fn get_vector_consent(state: State<'_, AppState>) -> Result<crate::db::VectorConsent, String> {
22-
let db_lock = state.db.lock().map_err(|e| e.to_string())?;
23-
let db = db_lock.as_ref().ok_or("Database not initialized")?;
24-
db.get_vector_consent().map_err(|e| e.to_string())
23+
pub fn get_vector_consent(
24+
state: State<'_, AppState>,
25+
) -> Result<crate::db::VectorConsent, AppError> {
26+
let db_lock = state.db.lock().map_err(|_| AppError::db_lock_failed())?;
27+
let db = db_lock.as_ref().ok_or_else(AppError::db_not_initialized)?;
28+
db.get_vector_consent()
29+
.map_err(|e| AppError::db_query_failed(e.to_string()))
2530
}
2631

2732
#[tauri::command]
2833
pub fn set_vector_consent(
2934
state: State<'_, AppState>,
3035
enabled: bool,
3136
encryption_supported: bool,
32-
) -> Result<(), String> {
33-
let db_lock = state.db.lock().map_err(|e| e.to_string())?;
34-
let db = db_lock.as_ref().ok_or("Database not initialized")?;
37+
) -> Result<(), AppError> {
38+
let db_lock = state.db.lock().map_err(|_| AppError::db_lock_failed())?;
39+
let db = db_lock.as_ref().ok_or_else(AppError::db_not_initialized)?;
3540
db.set_vector_consent(enabled, encryption_supported)
36-
.map_err(|e| e.to_string())
41+
.map_err(|e| AppError::db_query_failed(e.to_string()))
3742
}
Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
use crate::commands::model_commands::DecisionTree;
2+
use crate::error::AppError;
23
use crate::AppState;
34
use tauri::State;
45

56
pub(crate) fn list_decision_trees_impl(
67
state: State<'_, AppState>,
7-
) -> Result<Vec<DecisionTree>, String> {
8-
let db_lock = state.db.lock().map_err(|e| e.to_string())?;
9-
let db = db_lock.as_ref().ok_or("Database not initialized")?;
10-
db.list_decision_trees().map_err(|e| e.to_string())
8+
) -> Result<Vec<DecisionTree>, AppError> {
9+
let db_lock = state.db.lock().map_err(|_| AppError::db_lock_failed())?;
10+
let db = db_lock.as_ref().ok_or_else(AppError::db_not_initialized)?;
11+
db.list_decision_trees()
12+
.map_err(|e| AppError::db_query_failed(e.to_string()))
1113
}
1214

1315
pub(crate) fn get_decision_tree_impl(
1416
state: State<'_, AppState>,
1517
tree_id: String,
16-
) -> Result<DecisionTree, String> {
17-
let db_lock = state.db.lock().map_err(|e| e.to_string())?;
18-
let db = db_lock.as_ref().ok_or("Database not initialized")?;
19-
db.get_decision_tree(&tree_id).map_err(|e| e.to_string())
18+
) -> Result<DecisionTree, AppError> {
19+
let db_lock = state.db.lock().map_err(|_| AppError::db_lock_failed())?;
20+
let db = db_lock.as_ref().ok_or_else(AppError::db_not_initialized)?;
21+
db.get_decision_tree(&tree_id)
22+
.map_err(|e| AppError::db_query_failed(e.to_string()))
2023
}

src-tauri/src/commands/model_commands.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,15 +379,17 @@ pub fn is_ocr_available() -> bool {
379379
}
380380

381381
#[tauri::command]
382-
pub fn list_decision_trees(state: State<'_, AppState>) -> Result<Vec<DecisionTree>, String> {
382+
pub fn list_decision_trees(
383+
state: State<'_, AppState>,
384+
) -> Result<Vec<DecisionTree>, crate::error::AppError> {
383385
list_decision_trees_impl(state)
384386
}
385387

386388
#[tauri::command]
387389
pub fn get_decision_tree(
388390
state: State<'_, AppState>,
389391
tree_id: String,
390-
) -> Result<DecisionTree, String> {
392+
) -> Result<DecisionTree, crate::error::AppError> {
391393
get_decision_tree_impl(state, tree_id)
392394
}
393395

src-tauri/src/commands/pilot_feedback.rs

Lines changed: 55 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::error::{AppError, ErrorCategory, ErrorCode};
12
use crate::validation::{validate_output_file_within_home, ValidationError};
23
use crate::AppState;
34
use std::path::Path;
@@ -26,17 +27,34 @@ fn pilot_logging_enabled() -> bool {
2627
parse_bool_env(PILOT_LOGGING_POLICY_ENV, false)
2728
}
2829

29-
fn require_pilot_logging_enabled() -> Result<(), String> {
30+
fn require_pilot_logging_enabled() -> Result<(), AppError> {
3031
if pilot_logging_enabled() {
3132
Ok(())
3233
} else {
33-
Err(format!(
34-
"Pilot logging is disabled by policy. Set {}=1 and restart AssistSupport to enable.",
35-
PILOT_LOGGING_POLICY_ENV
34+
Err(AppError::new(
35+
ErrorCode::SECURITY_PERMISSION_DENIED,
36+
format!(
37+
"Pilot logging is disabled by policy. Set {}=1 and restart AssistSupport to enable.",
38+
PILOT_LOGGING_POLICY_ENV
39+
),
40+
ErrorCategory::Security,
3641
))
3742
}
3843
}
3944

45+
/// Map a `crate::feedback` string error to a properly-categorized AppError.
46+
///
47+
/// The feedback module returns stringly-typed errors today; until it migrates
48+
/// we categorize as Database (its real source) with the message as detail.
49+
fn feedback_err(op: &str, e: impl std::fmt::Display) -> AppError {
50+
AppError::new(
51+
ErrorCode::DB_QUERY_FAILED,
52+
format!("Pilot {} failed", op),
53+
ErrorCategory::Database,
54+
)
55+
.with_detail(e.to_string())
56+
}
57+
4058
#[derive(serde::Serialize, Clone)]
4159
pub struct PilotLoggingPolicy {
4260
pub enabled: bool,
@@ -60,11 +78,12 @@ pub fn log_pilot_query(
6078
query: String,
6179
response: String,
6280
operator_id: String,
63-
) -> Result<String, String> {
81+
) -> Result<String, AppError> {
6482
require_pilot_logging_enabled()?;
65-
let db_lock = state.db.lock().map_err(|e| e.to_string())?;
66-
let db = db_lock.as_ref().ok_or("Database not initialized")?;
83+
let db_lock = state.db.lock().map_err(|_| AppError::db_lock_failed())?;
84+
let db = db_lock.as_ref().ok_or_else(AppError::db_not_initialized)?;
6785
crate::feedback::log_query(db, &query, &response, &operator_id)
86+
.map_err(|e| feedback_err("query log", e))
6887
}
6988

7089
/// Submit user feedback on a pilot query response
@@ -77,10 +96,10 @@ pub fn submit_pilot_feedback(
7796
clarity: i32,
7897
helpfulness: i32,
7998
comment: Option<String>,
80-
) -> Result<String, String> {
99+
) -> Result<String, AppError> {
81100
require_pilot_logging_enabled()?;
82-
let db_lock = state.db.lock().map_err(|e| e.to_string())?;
83-
let db = db_lock.as_ref().ok_or("Database not initialized")?;
101+
let db_lock = state.db.lock().map_err(|_| AppError::db_lock_failed())?;
102+
let db = db_lock.as_ref().ok_or_else(AppError::db_not_initialized)?;
84103
crate::feedback::submit_feedback(
85104
db,
86105
&query_log_id,
@@ -90,31 +109,34 @@ pub fn submit_pilot_feedback(
90109
helpfulness,
91110
comment.as_deref(),
92111
)
112+
.map_err(|e| feedback_err("feedback submission", e))
93113
}
94114

95115
/// Get pilot dashboard summary stats
96116
#[tauri::command]
97-
pub fn get_pilot_stats(state: State<'_, AppState>) -> Result<crate::feedback::PilotStats, String> {
117+
pub fn get_pilot_stats(
118+
state: State<'_, AppState>,
119+
) -> Result<crate::feedback::PilotStats, AppError> {
98120
require_pilot_logging_enabled()?;
99-
let db_lock = state.db.lock().map_err(|e| e.to_string())?;
100-
let db = db_lock.as_ref().ok_or("Database not initialized")?;
101-
crate::feedback::get_pilot_stats(db)
121+
let db_lock = state.db.lock().map_err(|_| AppError::db_lock_failed())?;
122+
let db = db_lock.as_ref().ok_or_else(AppError::db_not_initialized)?;
123+
crate::feedback::get_pilot_stats(db).map_err(|e| feedback_err("stats read", e))
102124
}
103125

104126
/// Get all pilot query logs
105127
#[tauri::command]
106128
pub fn get_pilot_query_logs(
107129
state: State<'_, AppState>,
108-
) -> Result<Vec<crate::feedback::QueryLog>, String> {
130+
) -> Result<Vec<crate::feedback::QueryLog>, AppError> {
109131
require_pilot_logging_enabled()?;
110-
let db_lock = state.db.lock().map_err(|e| e.to_string())?;
111-
let db = db_lock.as_ref().ok_or("Database not initialized")?;
112-
crate::feedback::get_query_logs(db)
132+
let db_lock = state.db.lock().map_err(|_| AppError::db_lock_failed())?;
133+
let db = db_lock.as_ref().ok_or_else(AppError::db_not_initialized)?;
134+
crate::feedback::get_query_logs(db).map_err(|e| feedback_err("query logs read", e))
113135
}
114136

115137
/// Export pilot data to CSV
116138
#[tauri::command]
117-
pub fn export_pilot_data(state: State<'_, AppState>, path: String) -> Result<usize, String> {
139+
pub fn export_pilot_data(state: State<'_, AppState>, path: String) -> Result<usize, AppError> {
118140
require_pilot_logging_enabled()?;
119141

120142
let candidate = Path::new(&path);
@@ -124,19 +146,26 @@ pub fn export_pilot_data(state: State<'_, AppState>, path: String) -> Result<usi
124146
.unwrap_or("")
125147
.to_ascii_lowercase();
126148
if ext != "csv" {
127-
return Err("Export path must be a .csv file".into());
149+
return Err(AppError::invalid_format("Export path must be a .csv file"));
128150
}
129151

130152
let validated_path = validate_output_file_within_home(candidate).map_err(|e| match e {
131-
ValidationError::PathTraversal => "Export path must be within your home directory".into(),
132-
ValidationError::PathNotFound(_) => "Export parent directory does not exist".into(),
153+
ValidationError::PathTraversal => AppError::new(
154+
ErrorCode::VALIDATION_PATH_TRAVERSAL,
155+
"Export path must be within your home directory",
156+
ErrorCategory::Validation,
157+
),
158+
ValidationError::PathNotFound(_) => {
159+
AppError::invalid_path("Export parent directory does not exist")
160+
}
133161
ValidationError::InvalidFormat(msg) if msg.contains("sensitive") => {
134-
"This export path is blocked because it contains sensitive data".into()
162+
AppError::sensitive_path()
135163
}
136-
_ => format!("Invalid export path: {}", e),
164+
other => AppError::invalid_path(format!("Invalid export path: {}", other)),
137165
})?;
138166

139-
let db_lock = state.db.lock().map_err(|e| e.to_string())?;
140-
let db = db_lock.as_ref().ok_or("Database not initialized")?;
167+
let db_lock = state.db.lock().map_err(|_| AppError::db_lock_failed())?;
168+
let db = db_lock.as_ref().ok_or_else(AppError::db_not_initialized)?;
141169
crate::feedback::export::export_to_csv(db, validated_path.as_path())
170+
.map_err(|e| feedback_err("data export", e))
142171
}

0 commit comments

Comments
 (0)