diff --git a/src-tauri/generator/table_names.rs b/src-tauri/generator/table_names.rs index 58664ed..c8bb2c5 100644 --- a/src-tauri/generator/table_names.rs +++ b/src-tauri/generator/table_names.rs @@ -20,11 +20,11 @@ struct TableDefinition { pub fn generate_table_names() { let out_dir = env::var("OUT_DIR").expect("OUT_DIR ist nicht gesetzt."); - println!("Generiere Tabellennamen nach {}", out_dir); + println!("Generiere Tabellennamen nach {out_dir}"); let schema_path = Path::new("../src/database/tableNames.json"); let dest_path = Path::new(&out_dir).join("tableNames.rs"); - let file = File::open(&schema_path).expect("Konnte tableNames.json nicht öffnen"); + let file = File::open(schema_path).expect("Konnte tableNames.json nicht öffnen"); let reader = BufReader::new(file); let schema: Schema = serde_json::from_reader(reader).expect("Konnte tableNames.json nicht parsen"); @@ -108,8 +108,7 @@ fn generate_table_constants(table: &TableDefinition, const_prefix: &str) -> Stri for (col_key, col_value) in &table.columns { let col_const_name = format!("COL_{}_{}", const_prefix, to_screaming_snake_case(col_key)); code.push_str(&format!( - "pub const {}: &str = \"{}\";\n", - col_const_name, col_value + "pub const {col_const_name}: &str = \"{col_value}\";\n" )); } diff --git a/src-tauri/src/crdt/hlc.rs b/src-tauri/src/crdt/hlc.rs index 47201d2..a68af23 100644 --- a/src-tauri/src/crdt/hlc.rs +++ b/src-tauri/src/crdt/hlc.rs @@ -74,15 +74,14 @@ impl HlcService { // Parse den String in ein Uuid-Objekt. let uuid = Uuid::parse_str(&node_id_str).map_err(|e| { HlcError::ParseNodeId(format!( - "Stored device ID is not a valid UUID: {}. Error: {}", - node_id_str, e + "Stored device ID is not a valid UUID: {node_id_str}. Error: {e}" )) })?; // Hol dir die rohen 16 Bytes und erstelle daraus die uhlc::ID. // Das `*` dereferenziert den `&[u8; 16]` zu `[u8; 16]`, was `try_from` erwartet. let node_id = ID::try_from(*uuid.as_bytes()).map_err(|e| { - HlcError::ParseNodeId(format!("Invalid node ID format from device store: {:?}", e)) + HlcError::ParseNodeId(format!("Invalid node ID format from device store: {e:?}")) })?; // 2. Erstelle eine HLC-Instanz mit stabiler Identität @@ -95,8 +94,7 @@ impl HlcService { if let Some(last_timestamp) = Self::load_last_timestamp(conn)? { hlc.update_with_timestamp(&last_timestamp).map_err(|e| { HlcError::Parse(format!( - "Failed to update HLC with persisted timestamp: {:?}", - e + "Failed to update HLC with persisted timestamp: {e:?}" )) })?; } @@ -119,7 +117,7 @@ impl HlcService { if let Some(s) = value.as_str() { // Das ist unser Erfolgsfall. Wir haben einen &str und können // eine Kopie davon zurückgeben. - println!("Gefundene und validierte Geräte-ID: {}", s); + println!("Gefundene und validierte Geräte-ID: {s}"); if Uuid::parse_str(s).is_ok() { // Erfolgsfall: Der Wert ist ein String UND eine gültige UUID. // Wir können die Funktion direkt mit dem Wert verlassen. @@ -183,19 +181,19 @@ impl HlcService { let hlc = hlc_guard.as_mut().ok_or(HlcError::NotInitialized)?; hlc.update_with_timestamp(timestamp) - .map_err(|e| HlcError::Parse(format!("Failed to update HLC: {:?}", e))) + .map_err(|e| HlcError::Parse(format!("Failed to update HLC: {e:?}"))) } /// Lädt den letzten persistierten Zeitstempel aus der Datenbank. fn load_last_timestamp(conn: &Connection) -> Result, HlcError> { - let query = format!("SELECT value FROM {} WHERE key = ?1", TABLE_CRDT_CONFIGS); + let query = format!("SELECT value FROM {TABLE_CRDT_CONFIGS} WHERE key = ?1"); match conn.query_row(&query, params![HLC_TIMESTAMP_TYPE], |row| { row.get::<_, String>(0) }) { Ok(state_str) => { let timestamp = Timestamp::from_str(&state_str).map_err(|e| { - HlcError::ParseTimestamp(format!("Invalid timestamp format: {:?}", e)) + HlcError::ParseTimestamp(format!("Invalid timestamp format: {e:?}")) })?; Ok(Some(timestamp)) } @@ -209,9 +207,8 @@ impl HlcService { let timestamp_str = timestamp.to_string(); tx.execute( &format!( - "INSERT INTO {} (key, value) VALUES (?1, ?2) - ON CONFLICT(key) DO UPDATE SET value = excluded.value", - TABLE_CRDT_CONFIGS + "INSERT INTO {TABLE_CRDT_CONFIGS} (key, value) VALUES (?1, ?2) + ON CONFLICT(key) DO UPDATE SET value = excluded.value" ), params![HLC_TIMESTAMP_TYPE, timestamp_str], )?; diff --git a/src-tauri/src/crdt/trigger.rs b/src-tauri/src/crdt/trigger.rs index e9e32ad..0e2af12 100644 --- a/src-tauri/src/crdt/trigger.rs +++ b/src-tauri/src/crdt/trigger.rs @@ -32,17 +32,16 @@ pub enum CrdtSetupError { impl Display for CrdtSetupError { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { - CrdtSetupError::DatabaseError(e) => write!(f, "Database error: {}", e), + CrdtSetupError::DatabaseError(e) => write!(f, "Database error: {e}"), CrdtSetupError::HlcColumnMissing { table_name, column_name, } => write!( f, - "Table '{}' is missing the required hlc column '{}'", - table_name, column_name + "Table '{table_name}' is missing the required hlc column '{column_name}'" ), CrdtSetupError::PrimaryKeyMissing { table_name } => { - write!(f, "Table '{}' has no primary key", table_name) + write!(f, "Table '{table_name}' has no primary key") } } } @@ -129,7 +128,7 @@ pub fn setup_triggers_for_table( let delete_trigger_sql = generate_delete_trigger_sql(table_name, &pks, &cols_to_track); if recreate { - drop_triggers_for_table(&tx, table_name)?; + drop_triggers_for_table(tx, table_name)?; } tx.execute_batch(&insert_trigger_sql)?; @@ -143,13 +142,11 @@ pub fn setup_triggers_for_table( pub fn get_table_schema(conn: &Connection, table_name: &str) -> RusqliteResult> { if !is_safe_identifier(table_name) { return Err(rusqlite::Error::InvalidParameterName(format!( - "Invalid or unsafe table name provided: {}", - table_name - )) - .into()); + "Invalid or unsafe table name provided: {table_name}" + ))); } - let sql = format!("PRAGMA table_info(\"{}\");", table_name); + let sql = format!("PRAGMA table_info(\"{table_name}\");"); let mut stmt = conn.prepare(&sql)?; let rows = stmt.query_map([], ColumnInfo::from_row)?; rows.collect() @@ -163,8 +160,7 @@ pub fn drop_triggers_for_table( ) -> Result<(), CrdtSetupError> { if !is_safe_identifier(table_name) { return Err(rusqlite::Error::InvalidParameterName(format!( - "Invalid or unsafe table name provided: {}", - table_name + "Invalid or unsafe table name provided: {table_name}" )) .into()); } @@ -177,8 +173,7 @@ pub fn drop_triggers_for_table( drop_trigger_sql(DELETE_TRIGGER_TPL.replace("{TABLE_NAME}", table_name)); let sql_batch = format!( - "{}\n{}\n{}", - drop_insert_trigger_sql, drop_update_trigger_sql, drop_delete_trigger_sql + "{drop_insert_trigger_sql}\n{drop_update_trigger_sql}\n{drop_delete_trigger_sql}" ); tx.execute_batch(&sql_batch)?; @@ -244,33 +239,22 @@ pub fn drop_triggers_for_table( fn generate_insert_trigger_sql(table_name: &str, pks: &[String], cols: &[String]) -> String { let pk_json_payload = pks .iter() - .map(|pk| format!("'{}', NEW.\"{}\"", pk, pk)) + .map(|pk| format!("'{pk}', NEW.\"{pk}\"")) .collect::>() .join(", "); let column_inserts = if cols.is_empty() { // Nur PKs -> einfacher Insert ins Log format!( - "INSERT INTO {log_table} (id, haex_timestamp, op_type, table_name, row_pks) - VALUES ({uuid_fn}(), NEW.\"{hlc_col}\", 'INSERT', '{table}', json_object({pk_payload}));", - log_table = TABLE_CRDT_LOGS, - uuid_fn = UUID_FUNCTION_NAME, - hlc_col = HLC_TIMESTAMP_COLUMN, - table = table_name, - pk_payload = pk_json_payload + "INSERT INTO {TABLE_CRDT_LOGS} (id, haex_timestamp, op_type, table_name, row_pks) + VALUES ({UUID_FUNCTION_NAME}(), NEW.\"{HLC_TIMESTAMP_COLUMN}\", 'INSERT', '{table_name}', json_object({pk_json_payload}));" ) } else { cols.iter().fold(String::new(), |mut acc, col| { writeln!( &mut acc, - "INSERT INTO {log_table} (id, haex_timestamp, op_type, table_name, row_pks, column_name, new_value) - VALUES ({uuid_fn}(), NEW.\"{hlc_col}\", 'INSERT', '{table}', json_object({pk_payload}), '{column}', json_object('value', NEW.\"{column}\"));", - log_table = TABLE_CRDT_LOGS, - uuid_fn = UUID_FUNCTION_NAME, - hlc_col = HLC_TIMESTAMP_COLUMN, - table = table_name, - pk_payload = pk_json_payload, - column = col + "INSERT INTO {TABLE_CRDT_LOGS} (id, haex_timestamp, op_type, table_name, row_pks, column_name, new_value) + VALUES ({UUID_FUNCTION_NAME}(), NEW.\"{HLC_TIMESTAMP_COLUMN}\", 'INSERT', '{table_name}', json_object({pk_json_payload}), '{col}', json_object('value', NEW.\"{col}\"));" ).unwrap(); acc }) @@ -290,14 +274,14 @@ fn generate_insert_trigger_sql(table_name: &str, pks: &[String], cols: &[String] /// Generiert das SQL zum Löschen eines Triggers. fn drop_trigger_sql(trigger_name: String) -> String { - format!("DROP TRIGGER IF EXISTS \"{}\";", trigger_name) + format!("DROP TRIGGER IF EXISTS \"{trigger_name}\";") } /// Generiert das SQL für den UPDATE-Trigger. fn generate_update_trigger_sql(table_name: &str, pks: &[String], cols: &[String]) -> String { let pk_json_payload = pks .iter() - .map(|pk| format!("'{}', NEW.\"{}\"", pk, pk)) + .map(|pk| format!("'{pk}', NEW.\"{pk}\"")) .collect::>() .join(", "); @@ -308,16 +292,10 @@ fn generate_update_trigger_sql(table_name: &str, pks: &[String], cols: &[String] for col in cols { writeln!( &mut body, - "INSERT INTO {log_table} (id, haex_timestamp, op_type, table_name, row_pks, column_name, new_value, old_value) - SELECT {uuid_fn}(), NEW.\"{hlc_col}\", 'UPDATE', '{table}', json_object({pk_payload}), '{column}', - json_object('value', NEW.\"{column}\"), json_object('value', OLD.\"{column}\") - WHERE NEW.\"{column}\" IS NOT OLD.\"{column}\";", - log_table = TABLE_CRDT_LOGS, - uuid_fn = UUID_FUNCTION_NAME, - hlc_col = HLC_TIMESTAMP_COLUMN, - table = table_name, - pk_payload = pk_json_payload, - column = col + "INSERT INTO {TABLE_CRDT_LOGS} (id, haex_timestamp, op_type, table_name, row_pks, column_name, new_value, old_value) + SELECT {UUID_FUNCTION_NAME}(), NEW.\"{HLC_TIMESTAMP_COLUMN}\", 'UPDATE', '{table_name}', json_object({pk_json_payload}), '{col}', + json_object('value', NEW.\"{col}\"), json_object('value', OLD.\"{col}\") + WHERE NEW.\"{col}\" IS NOT OLD.\"{col}\";" ).unwrap(); } } @@ -341,7 +319,7 @@ fn generate_update_trigger_sql(table_name: &str, pks: &[String], cols: &[String] fn generate_delete_trigger_sql(table_name: &str, pks: &[String], cols: &[String]) -> String { let pk_json_payload = pks .iter() - .map(|pk| format!("'{}', OLD.\"{}\"", pk, pk)) + .map(|pk| format!("'{pk}', OLD.\"{pk}\"")) .collect::>() .join(", "); @@ -352,28 +330,17 @@ fn generate_delete_trigger_sql(table_name: &str, pks: &[String], cols: &[String] for col in cols { writeln!( &mut body, - "INSERT INTO {log_table} (id, haex_timestamp, op_type, table_name, row_pks, column_name, old_value) - VALUES ({uuid_fn}(), OLD.\"{hlc_col}\", 'DELETE', '{table}', json_object({pk_payload}), '{column}', - json_object('value', OLD.\"{column}\"));", - log_table = TABLE_CRDT_LOGS, - uuid_fn = UUID_FUNCTION_NAME, - hlc_col = HLC_TIMESTAMP_COLUMN, - table = table_name, - pk_payload = pk_json_payload, - column = col + "INSERT INTO {TABLE_CRDT_LOGS} (id, haex_timestamp, op_type, table_name, row_pks, column_name, old_value) + VALUES ({UUID_FUNCTION_NAME}(), OLD.\"{HLC_TIMESTAMP_COLUMN}\", 'DELETE', '{table_name}', json_object({pk_json_payload}), '{col}', + json_object('value', OLD.\"{col}\"));" ).unwrap(); } } else { // Nur PKs -> minimales Delete Log writeln!( &mut body, - "INSERT INTO {log_table} (id, haex_timestamp, op_type, table_name, row_pks) - VALUES ({uuid_fn}(), OLD.\"{hlc_col}\", 'DELETE', '{table}', json_object({pk_payload}));", - log_table = TABLE_CRDT_LOGS, - uuid_fn = UUID_FUNCTION_NAME, - hlc_col = HLC_TIMESTAMP_COLUMN, - table = table_name, - pk_payload = pk_json_payload + "INSERT INTO {TABLE_CRDT_LOGS} (id, haex_timestamp, op_type, table_name, row_pks) + VALUES ({UUID_FUNCTION_NAME}(), OLD.\"{HLC_TIMESTAMP_COLUMN}\", 'DELETE', '{table_name}', json_object({pk_json_payload}));" ) .unwrap(); } diff --git a/src-tauri/src/database/core.rs b/src-tauri/src/database/core.rs index e6d5947..4df6344 100644 --- a/src-tauri/src/database/core.rs +++ b/src-tauri/src/database/core.rs @@ -47,7 +47,7 @@ pub fn open_and_init_db(path: &str, key: &str, create: bool) -> Result Result Result, DatabaseError> .join(" "); Parser::parse_sql(&dialect, &normalized_sql).map_err(|e| DatabaseError::ParseError { - reason: format!("Failed to parse SQL: {}", e), + reason: format!("Failed to parse SQL: {e}"), sql: sql.to_string(), }) } @@ -138,7 +137,7 @@ impl ValueConverter { serde_json::to_string(json_val) .map(SqlValue::Text) .map_err(|e| DatabaseError::SerializationError { - reason: format!("Failed to serialize JSON param: {}", e), + reason: format!("Failed to serialize JSON param: {e}"), }) } } @@ -258,7 +257,7 @@ pub fn select_with_crdt( params: Vec, connection: &DbConnection, ) -> Result>, DatabaseError> { - with_connection(&connection, |conn| { + with_connection(connection, |conn| { SqlExecutor::query_select(conn, &sql, ¶ms) }) } diff --git a/src-tauri/src/database/init.rs b/src-tauri/src/database/init.rs index 36b37c2..7fa14a7 100644 --- a/src-tauri/src/database/init.rs +++ b/src-tauri/src/database/init.rs @@ -36,8 +36,7 @@ pub fn ensure_triggers_initialized(conn: &mut Connection) -> Result = tx .query_row( @@ -57,7 +56,7 @@ pub fn ensure_triggers_initialized(conn: &mut Connection) -> Result>>); @@ -93,7 +92,7 @@ fn get_vault_path(app_handle: &AppHandle, vault_name: &str) -> Result Result Result Result, DatabaseErro if let Some(filename) = path.file_name().and_then(|n| n.to_str()) { if filename.ends_with(VAULT_EXTENSION) { // Entferne .db Endung für die Rückgabe - println!("Vault gefunden {}", filename.to_string()); + println!("Vault gefunden {filename}"); let metadata = fs::metadata(&path).map_err(|e| DatabaseError::IoError { path: path.to_string_lossy().to_string(), - reason: format!("Metadaten konnten nicht gelesen werden: {}", e), + reason: format!("Metadaten konnten nicht gelesen werden: {e}"), })?; let last_access_timestamp = metadata .accessed() .map_err(|e| DatabaseError::IoError { path: path.to_string_lossy().to_string(), - reason: format!("Zugriffszeit konnte nicht gelesen werden: {}", e), + reason: format!("Zugriffszeit konnte nicht gelesen werden: {e}"), })? .duration_since(UNIX_EPOCH) .unwrap_or_default() // Fallback für den seltenen Fall einer Zeit vor 1970 @@ -233,8 +231,8 @@ pub fn move_vault_to_trash( #[cfg(not(target_os = "android"))] { let vault_path = get_vault_path(&app_handle, &vault_name)?; - let vault_shm_path = format!("{}-shm", vault_path); - let vault_wal_path = format!("{}-wal", vault_path); + let vault_shm_path = format!("{vault_path}-shm"); + let vault_wal_path = format!("{vault_path}-wal"); if !Path::new(&vault_path).exists() { return Err(DatabaseError::IoError { @@ -252,14 +250,12 @@ pub fn move_vault_to_trash( let _ = trash::delete(&vault_wal_path); Ok(format!( - "Vault '{}' successfully moved to trash", - vault_name + "Vault '{vault_name}' successfully moved to trash" )) } else { // Fallback: Permanent deletion if trash fails println!( - "Trash not available, falling back to permanent deletion for vault '{}'", - vault_name + "Trash not available, falling back to permanent deletion for vault '{vault_name}'" ); delete_vault(app_handle, vault_name) } @@ -270,8 +266,8 @@ pub fn move_vault_to_trash( #[tauri::command] pub fn delete_vault(app_handle: AppHandle, vault_name: String) -> Result { let vault_path = get_vault_path(&app_handle, &vault_name)?; - let vault_shm_path = format!("{}-shm", vault_path); - let vault_wal_path = format!("{}-wal", vault_path); + let vault_shm_path = format!("{vault_path}-shm"); + let vault_wal_path = format!("{vault_path}-wal"); if !Path::new(&vault_path).exists() { return Err(DatabaseError::IoError { @@ -283,23 +279,23 @@ pub fn delete_vault(app_handle: AppHandle, vault_name: String) -> Result, ) -> Result { - println!("Creating encrypted vault with name: {}", vault_name); + println!("Creating encrypted vault with name: {vault_name}"); let vault_path = get_vault_path(&app_handle, &vault_name)?; - println!("Resolved vault path: {}", vault_path); + println!("Resolved vault path: {vault_path}"); // Prüfen, ob bereits eine Vault mit diesem Namen existiert if Path::new(&vault_path).exists() { return Err(DatabaseError::IoError { path: vault_path, - reason: format!("A vault with the name '{}' already exists", vault_name), + reason: format!("A vault with the name '{vault_name}' already exists"), }); } /* let resource_path = app_handle @@ -330,7 +326,7 @@ pub fn create_encrypted_database( .path() .resolve("database/vault.db", BaseDirectory::Resource) .map_err(|e| DatabaseError::PathResolutionError { - reason: format!("Failed to resolve template database: {}", e), + reason: format!("Failed to resolve template database: {e}"), })?; let template_content = @@ -339,20 +335,20 @@ pub fn create_encrypted_database( .read(&template_path) .map_err(|e| DatabaseError::IoError { path: template_path.display().to_string(), - reason: format!("Failed to read template database from resources: {}", e), + reason: format!("Failed to read template database from resources: {e}"), })?; let temp_path = app_handle .path() .resolve("temp_vault.db", BaseDirectory::AppLocalData) .map_err(|e| DatabaseError::PathResolutionError { - reason: format!("Failed to resolve temp database: {}", e), + reason: format!("Failed to resolve temp database: {e}"), })?; let temp_path_clone = temp_path.to_owned(); fs::write(temp_path, template_content).map_err(|e| DatabaseError::IoError { path: vault_path.to_string(), - reason: format!("Failed to write temporary template database: {}", e), + reason: format!("Failed to write temporary template database: {e}"), })?; /* if !template_path.exists() { return Err(DatabaseError::IoError { @@ -365,8 +361,7 @@ pub fn create_encrypted_database( let conn = Connection::open(&temp_path_clone).map_err(|e| DatabaseError::ConnectionFailed { path: temp_path_clone.display().to_string(), reason: format!( - "Fehler beim Öffnen der unverschlüsselten Quelldatenbank: {}", - e + "Fehler beim Öffnen der unverschlüsselten Quelldatenbank: {e}" ), })?; @@ -394,7 +389,7 @@ pub fn create_encrypted_database( let _ = fs::remove_file(&vault_path); let _ = fs::remove_file(&temp_path_clone); return Err(DatabaseError::QueryError { - reason: format!("Fehler während sqlcipher_export: {}", e), + reason: format!("Fehler während sqlcipher_export: {e}"), }); } @@ -419,11 +414,11 @@ pub fn create_encrypted_database( Ok(version) }) { Ok(version) => { - println!("SQLCipher ist aktiv! Version: {}", version); + println!("SQLCipher ist aktiv! Version: {version}"); } Err(e) => { eprintln!("FEHLER: SQLCipher scheint NICHT aktiv zu sein!"); - eprintln!("Der Befehl 'PRAGMA cipher_version;' schlug fehl: {}", e); + eprintln!("Der Befehl 'PRAGMA cipher_version;' schlug fehl: {e}"); eprintln!("Die Datenbank wurde wahrscheinlich NICHT verschlüsselt."); } } @@ -431,7 +426,7 @@ pub fn create_encrypted_database( conn.close() .map_err(|(_, e)| DatabaseError::ConnectionFailed { path: template_path.display().to_string(), - reason: format!("Fehler beim Schließen der Quelldatenbank: {}", e), + reason: format!("Fehler beim Schließen der Quelldatenbank: {e}"), })?; let _ = fs::remove_file(&temp_path_clone); @@ -448,19 +443,19 @@ pub fn open_encrypted_database( key: String, state: State<'_, AppState>, ) -> Result { - println!("Opening encrypted database vault_path: {}", vault_path); - println!("Resolved vault path: {}", vault_path); + println!("Opening encrypted database vault_path: {vault_path}"); + println!("Resolved vault path: {vault_path}"); if !Path::new(&vault_path).exists() { return Err(DatabaseError::IoError { path: vault_path.to_string(), - reason: format!("Vault '{}' does not exist", vault_path), + reason: format!("Vault '{vault_path}' does not exist"), }); } initialize_session(&app_handle, &vault_path, &key, &state)?; - Ok(format!("Vault '{}' opened successfully", vault_path)) + Ok(format!("Vault '{vault_path}' opened successfully")) } /// Opens the DB, initializes the HLC service, and stores both in the AppState. @@ -512,8 +507,7 @@ fn initialize_session( eprintln!("INFO: Setting 'triggers_initialized' flag via CRDT..."); let insert_sql = format!( - "INSERT INTO {} (id, key, type, value) VALUES (?, ?, ?, ?)", - TABLE_SETTINGS + "INSERT INTO {TABLE_SETTINGS} (id, key, type, value) VALUES (?, ?, ?, ?)" ); // execute_with_crdt erwartet Vec, kein params!-Makro diff --git a/src-tauri/src/extension/core/manager.rs b/src-tauri/src/extension/core/manager.rs index 1aa583f..316c083 100644 --- a/src-tauri/src/extension/core/manager.rs +++ b/src-tauri/src/extension/core/manager.rs @@ -10,10 +10,8 @@ use crate::extension::permissions::manager::PermissionManager; use crate::extension::permissions::types::ExtensionPermission; use crate::table_names::{TABLE_EXTENSIONS, TABLE_EXTENSION_PERMISSIONS}; use crate::AppState; -use serde_json::Value as JsonValue; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use std::fs; -use std::io::Cursor; use std::path::PathBuf; use std::sync::Mutex; use std::time::{Duration, SystemTime}; @@ -77,7 +75,7 @@ impl ExtensionManager { // Check for path traversal patterns if relative_path.contains("..") { return Err(ExtensionError::SecurityViolation { - reason: format!("Path traversal attempt: {}", relative_path), + reason: format!("Path traversal attempt: {relative_path}"), }); } @@ -104,7 +102,7 @@ impl ExtensionManager { if let Ok(canonical_path) = full_path.canonicalize() { if !canonical_path.starts_with(&canonical_base) { return Err(ExtensionError::SecurityViolation { - reason: format!("Path outside base directory: {}", relative_path), + reason: format!("Path outside base directory: {relative_path}"), }); } Ok(Some(canonical_path)) @@ -114,7 +112,7 @@ impl ExtensionManager { Ok(Some(full_path)) } else { Err(ExtensionError::SecurityViolation { - reason: format!("Path outside base directory: {}", relative_path), + reason: format!("Path outside base directory: {relative_path}"), }) } } @@ -131,13 +129,13 @@ impl ExtensionManager { if let Some(clean_path) = Self::validate_path_in_directory(extension_dir, icon, true)? { return Ok(Some(clean_path.to_string_lossy().to_string())); } else { - eprintln!("WARNING: Icon path specified in manifest not found: {}", icon); + eprintln!("WARNING: Icon path specified in manifest not found: {icon}"); // Continue to fallback logic } } // Fallback 1: Check haextension/favicon.ico - let haextension_favicon = format!("{}/favicon.ico", haextension_dir); + let haextension_favicon = format!("{haextension_dir}/favicon.ico"); if let Some(clean_path) = Self::validate_path_in_directory(extension_dir, &haextension_favicon, true)? { return Ok(Some(clean_path.to_string_lossy().to_string())); } @@ -162,11 +160,11 @@ impl ExtensionManager { .path() .app_cache_dir() .map_err(|e| ExtensionError::InstallationFailed { - reason: format!("Cannot get app cache dir: {}", e), + reason: format!("Cannot get app cache dir: {e}"), })?; let temp_id = uuid::Uuid::new_v4(); - let temp = cache_dir.join(format!("{}_{}", temp_prefix, temp_id)); + let temp = cache_dir.join(format!("{temp_prefix}_{temp_id}")); let zip_file_path = cache_dir.join(format!("{}_{}_{}.haextension", temp_prefix, temp_id, "temp")); // Write bytes to a temporary ZIP file first (important for Android file system) @@ -185,14 +183,14 @@ impl ExtensionManager { let mut archive = ZipArchive::new(zip_file).map_err(|e| { ExtensionError::InstallationFailed { - reason: format!("Invalid ZIP: {}", e), + reason: format!("Invalid ZIP: {e}"), } })?; archive .extract(&temp) .map_err(|e| ExtensionError::InstallationFailed { - reason: format!("Cannot extract ZIP: {}", e), + reason: format!("Cannot extract ZIP: {e}"), })?; // Clean up temporary ZIP file @@ -203,12 +201,12 @@ impl ExtensionManager { let haextension_dir = if config_path.exists() { let config_content = std::fs::read_to_string(&config_path) .map_err(|e| ExtensionError::ManifestError { - reason: format!("Cannot read haextension.config.json: {}", e), + reason: format!("Cannot read haextension.config.json: {e}"), })?; let config: serde_json::Value = serde_json::from_str(&config_content) .map_err(|e| ExtensionError::ManifestError { - reason: format!("Invalid haextension.config.json: {}", e), + reason: format!("Invalid haextension.config.json: {e}"), })?; let dir = config @@ -224,16 +222,16 @@ impl ExtensionManager { }; // Validate manifest path using helper function - let manifest_relative_path = format!("{}/manifest.json", haextension_dir); + let manifest_relative_path = format!("{haextension_dir}/manifest.json"); let manifest_path = Self::validate_path_in_directory(&temp, &manifest_relative_path, true)? .ok_or_else(|| ExtensionError::ManifestError { - reason: format!("manifest.json not found at {}/manifest.json", haextension_dir), + reason: format!("manifest.json not found at {haextension_dir}/manifest.json"), })?; let actual_dir = temp.clone(); let manifest_content = std::fs::read_to_string(&manifest_path).map_err(|e| ExtensionError::ManifestError { - reason: format!("Cannot read manifest: {}", e), + reason: format!("Cannot read manifest: {e}"), })?; let mut manifest: ExtensionManifest = serde_json::from_str(&manifest_content)?; @@ -440,8 +438,7 @@ impl ExtensionManager { eprintln!("DEBUG: Removing extension with ID: {}", extension.id); eprintln!( - "DEBUG: Extension name: {}, version: {}", - extension_name, extension_version + "DEBUG: Extension name: {extension_name}, version: {extension_version}" ); // Lösche Permissions und Extension-Eintrag in einer Transaktion @@ -460,7 +457,7 @@ impl ExtensionManager { PermissionManager::delete_permissions_in_transaction(&tx, &hlc_service, &extension.id)?; // Lösche Extension-Eintrag mit extension_id - let sql = format!("DELETE FROM {} WHERE id = ?", TABLE_EXTENSIONS); + let sql = format!("DELETE FROM {TABLE_EXTENSIONS} WHERE id = ?"); eprintln!("DEBUG: Executing SQL: {} with id = {}", sql, extension.id); SqlExecutor::execute_internal_typed( &tx, @@ -615,8 +612,7 @@ impl ExtensionManager { // 1. Extension-Eintrag erstellen mit generierter UUID let insert_ext_sql = format!( - "INSERT INTO {} (id, name, version, author, entry, icon, public_key, signature, homepage, description, enabled, single_instance) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - TABLE_EXTENSIONS + "INSERT INTO {TABLE_EXTENSIONS} (id, name, version, author, entry, icon, public_key, signature, homepage, description, enabled, single_instance) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ); SqlExecutor::execute_internal_typed( @@ -641,8 +637,7 @@ impl ExtensionManager { // 2. Permissions speichern let insert_perm_sql = format!( - "INSERT INTO {} (id, extension_id, resource_type, action, target, constraints, status) VALUES (?, ?, ?, ?, ?, ?, ?)", - TABLE_EXTENSION_PERMISSIONS + "INSERT INTO {TABLE_EXTENSION_PERMISSIONS} (id, extension_id, resource_type, action, target, constraints, status) VALUES (?, ?, ?, ?, ?, ?, ?)" ); for perm in &permissions { @@ -714,10 +709,9 @@ impl ExtensionManager { // Lade alle Daten aus der Datenbank let extensions = with_connection(&state.db, |conn| { let sql = format!( - "SELECT id, name, version, author, entry, icon, public_key, signature, homepage, description, enabled, single_instance FROM {}", - TABLE_EXTENSIONS + "SELECT id, name, version, author, entry, icon, public_key, signature, homepage, description, enabled, single_instance FROM {TABLE_EXTENSIONS}" ); - eprintln!("DEBUG: SQL Query before transformation: {}", sql); + eprintln!("DEBUG: SQL Query before transformation: {sql}"); let results = SqlExecutor::query_select(conn, &sql, &[])?; eprintln!("DEBUG: Query returned {} results", results.len()); @@ -779,7 +773,7 @@ impl ExtensionManager { for extension_data in extensions { let extension_id = extension_data.id; - eprintln!("DEBUG: Processing extension: {}", extension_id); + eprintln!("DEBUG: Processing extension: {extension_id}"); // Use public_key/name/version path structure let extension_path = self.get_extension_dir( @@ -792,8 +786,7 @@ impl ExtensionManager { // Check if extension directory exists if !extension_path.exists() { eprintln!( - "DEBUG: Extension directory missing for: {} at {:?}", - extension_id, extension_path + "DEBUG: Extension directory missing for: {extension_id} at {extension_path:?}" ); self.missing_extensions .lock() @@ -833,13 +826,12 @@ impl ExtensionManager { }; // Validate manifest.json path using helper function - let manifest_relative_path = format!("{}/manifest.json", haextension_dir); + let manifest_relative_path = format!("{haextension_dir}/manifest.json"); if Self::validate_path_in_directory(&extension_path, &manifest_relative_path, true)? .is_none() { eprintln!( - "DEBUG: manifest.json missing or invalid for: {} at {}/manifest.json", - extension_id, haextension_dir + "DEBUG: manifest.json missing or invalid for: {extension_id} at {haextension_dir}/manifest.json" ); self.missing_extensions .lock() @@ -855,7 +847,7 @@ impl ExtensionManager { continue; } - eprintln!("DEBUG: Extension loaded successfully: {}", extension_id); + eprintln!("DEBUG: Extension loaded successfully: {extension_id}"); let extension = Extension { id: extension_id.clone(), diff --git a/src-tauri/src/extension/core/protocol.rs b/src-tauri/src/extension/core/protocol.rs index 47a432e..76022b2 100644 --- a/src-tauri/src/extension/core/protocol.rs +++ b/src-tauri/src/extension/core/protocol.rs @@ -42,12 +42,12 @@ enum DataProcessingError { impl fmt::Display for DataProcessingError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - DataProcessingError::HexDecoding(e) => write!(f, "Hex-Dekodierungsfehler: {}", e), + DataProcessingError::HexDecoding(e) => write!(f, "Hex-Dekodierungsfehler: {e}"), DataProcessingError::Utf8Conversion(e) => { - write!(f, "UTF-8-Konvertierungsfehler: {}", e) + write!(f, "UTF-8-Konvertierungsfehler: {e}") } - DataProcessingError::JsonParsing(e) => write!(f, "JSON-Parsing-Fehler: {}", e), - DataProcessingError::Custom(msg) => write!(f, "Datenverarbeitungsfehler: {}", msg), + DataProcessingError::JsonParsing(e) => write!(f, "JSON-Parsing-Fehler: {e}"), + DataProcessingError::Custom(msg) => write!(f, "Datenverarbeitungsfehler: {msg}"), } } } @@ -101,7 +101,7 @@ pub fn resolve_secure_extension_asset_path( .all(|c| c.is_ascii_alphanumeric() || c == '-') { return Err(ExtensionError::ValidationError { - reason: format!("Invalid extension name: {}", extension_name), + reason: format!("Invalid extension name: {extension_name}"), }); } @@ -111,7 +111,7 @@ pub fn resolve_secure_extension_asset_path( .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '.') { return Err(ExtensionError::ValidationError { - reason: format!("Invalid extension version: {}", extension_version), + reason: format!("Invalid extension version: {extension_version}"), }); } @@ -146,11 +146,10 @@ pub fn resolve_secure_extension_asset_path( Ok(canonical_path) } else { eprintln!( - "SECURITY WARNING: Path traversal attempt blocked: {}", - requested_asset_path + "SECURITY WARNING: Path traversal attempt blocked: {requested_asset_path}" ); Err(ExtensionError::SecurityViolation { - reason: format!("Path traversal attempt: {}", requested_asset_path), + reason: format!("Path traversal attempt: {requested_asset_path}"), }) } } @@ -159,11 +158,10 @@ pub fn resolve_secure_extension_asset_path( Ok(final_path) } else { eprintln!( - "SECURITY WARNING: Invalid asset path: {}", - requested_asset_path + "SECURITY WARNING: Invalid asset path: {requested_asset_path}" ); Err(ExtensionError::SecurityViolation { - reason: format!("Invalid asset path: {}", requested_asset_path), + reason: format!("Invalid asset path: {requested_asset_path}"), }) } } @@ -184,7 +182,7 @@ pub fn extension_protocol_handler( // Only allow same-protocol requests or tauri origin // For null/empty origin (initial load), use wildcard - let protocol_prefix = format!("{}://", EXTENSION_PROTOCOL_NAME); + let protocol_prefix = format!("{EXTENSION_PROTOCOL_NAME}://"); let allowed_origin = if origin.starts_with(&protocol_prefix) || origin == get_tauri_origin() { origin } else if origin.is_empty() || origin == "null" { @@ -216,9 +214,9 @@ pub fn extension_protocol_handler( .and_then(|v| v.to_str().ok()) .unwrap_or(""); - println!("Protokoll Handler für: {}", uri_ref); - println!("Origin: {}", origin); - println!("Referer: {}", referer); + println!("Protokoll Handler für: {uri_ref}"); + println!("Origin: {origin}"); + println!("Referer: {referer}"); let path_str = uri_ref.path(); @@ -227,16 +225,16 @@ pub fn extension_protocol_handler( // - Desktop: haex-extension:///{assetPath} // - Android: http://localhost/{base64}/{assetPath} let host = uri_ref.host().unwrap_or(""); - println!("URI Host: {}", host); + println!("URI Host: {host}"); - let (info, segments_after_version) = if host == "localhost" || host == format!("{}.localhost", EXTENSION_PROTOCOL_NAME).as_str() { + let (info, segments_after_version) = if host == "localhost" || host == format!("{EXTENSION_PROTOCOL_NAME}.localhost").as_str() { // Android format: http://haex-extension.localhost/{base64}/{assetPath} // Extract base64 from first path segment - println!("Android format detected: http://{}/...", host); + println!("Android format detected: http://{host}/..."); let mut segments_iter = path_str.split('/').filter(|s| !s.is_empty()); if let Some(first_segment) = segments_iter.next() { - println!("First path segment (base64): {}", first_segment); + println!("First path segment (base64): {first_segment}"); match BASE64_STANDARD.decode(first_segment) { Ok(decoded_bytes) => match String::from_utf8(decoded_bytes) { Ok(json_str) => match serde_json::from_str::(&json_str) { @@ -252,29 +250,29 @@ pub fn extension_protocol_handler( (info, remaining) } Err(e) => { - eprintln!("Failed to parse JSON from base64 path: {}", e); + eprintln!("Failed to parse JSON from base64 path: {e}"); return Response::builder() .status(400) .header("Access-Control-Allow-Origin", allowed_origin) - .body(Vec::from(format!("Invalid extension info in base64 path: {}", e))) + .body(Vec::from(format!("Invalid extension info in base64 path: {e}"))) .map_err(|e| e.into()); } }, Err(e) => { - eprintln!("Failed to decode UTF-8 from base64 path: {}", e); + eprintln!("Failed to decode UTF-8 from base64 path: {e}"); return Response::builder() .status(400) .header("Access-Control-Allow-Origin", allowed_origin) - .body(Vec::from(format!("Invalid UTF-8 in base64 path: {}", e))) + .body(Vec::from(format!("Invalid UTF-8 in base64 path: {e}"))) .map_err(|e| e.into()); } }, Err(e) => { - eprintln!("Failed to decode base64 from path: {}", e); + eprintln!("Failed to decode base64 from path: {e}"); return Response::builder() .status(400) .header("Access-Control-Allow-Origin", allowed_origin) - .body(Vec::from(format!("Invalid base64 in path: {}", e))) + .body(Vec::from(format!("Invalid base64 in path: {e}"))) .map_err(|e| e.into()); } } @@ -311,35 +309,35 @@ pub fn extension_protocol_handler( (info, segments) } Err(e) => { - eprintln!("Failed to parse JSON from base64 host: {}", e); + eprintln!("Failed to parse JSON from base64 host: {e}"); return Response::builder() .status(400) .header("Access-Control-Allow-Origin", allowed_origin) - .body(Vec::from(format!("Invalid extension info in base64 host: {}", e))) + .body(Vec::from(format!("Invalid extension info in base64 host: {e}"))) .map_err(|e| e.into()); } }, Err(e) => { - eprintln!("Failed to decode UTF-8 from base64 host: {}", e); + eprintln!("Failed to decode UTF-8 from base64 host: {e}"); return Response::builder() .status(400) .header("Access-Control-Allow-Origin", allowed_origin) - .body(Vec::from(format!("Invalid UTF-8 in base64 host: {}", e))) + .body(Vec::from(format!("Invalid UTF-8 in base64 host: {e}"))) .map_err(|e| e.into()); } }, Err(e) => { - eprintln!("Failed to decode base64 host: {}", e); + eprintln!("Failed to decode base64 host: {e}"); return Response::builder() .status(400) .header("Access-Control-Allow-Origin", allowed_origin) - .body(Vec::from(format!("Invalid base64 in host: {}", e))) + .body(Vec::from(format!("Invalid base64 in host: {e}"))) .map_err(|e| e.into()); } } } else { // No base64 host - use path-based parsing (for localhost/Android/Windows) - parse_extension_info_from_path(path_str, origin, uri_ref, referer, &allowed_origin)? + parse_extension_info_from_path(path_str, origin, uri_ref, referer, allowed_origin)? }; // Construct asset path from remaining segments @@ -353,8 +351,8 @@ pub fn extension_protocol_handler( &raw_asset_path }; - println!("Path: {}", path_str); - println!("Asset to load: {}", asset_to_load); + println!("Path: {path_str}"); + println!("Asset to load: {asset_to_load}"); let absolute_secure_path = resolve_secure_extension_asset_path( app_handle, @@ -362,7 +360,7 @@ pub fn extension_protocol_handler( &info.public_key, &info.name, &info.version, - &asset_to_load, + asset_to_load, )?; println!("Resolved path: {}", absolute_secure_path.display()); @@ -497,7 +495,7 @@ fn parse_encoded_info_from_origin_or_uri_or_referer_or_cache( if let Ok(hex) = parse_from_origin(origin) { if let Ok(info) = process_hex_encoded_json(&hex) { cache_extension_info(&info); // Cache setzen - println!("Parsed und gecached aus Origin: {}", hex); + println!("Parsed und gecached aus Origin: {hex}"); return Ok(info); } } @@ -507,17 +505,17 @@ fn parse_encoded_info_from_origin_or_uri_or_referer_or_cache( if let Ok(hex) = parse_from_uri_path(uri_ref) { if let Ok(info) = process_hex_encoded_json(&hex) { cache_extension_info(&info); // Cache setzen - println!("Parsed und gecached aus URI: {}", hex); + println!("Parsed und gecached aus URI: {hex}"); return Ok(info); } } - println!("Fallback zu Referer-Parsing: {}", referer); + println!("Fallback zu Referer-Parsing: {referer}"); if !referer.is_empty() && referer != "null" { if let Ok(hex) = parse_from_uri_string(referer) { if let Ok(info) = process_hex_encoded_json(&hex) { cache_extension_info(&info); // Cache setzen - println!("Parsed und gecached aus Referer: {}", hex); + println!("Parsed und gecached aus Referer: {hex}"); return Ok(info); } } @@ -609,11 +607,6 @@ fn validate_and_return_hex(segment: &str) -> Result Ok(segment.to_string()) } -fn encode_hex_for_log(info: &ExtensionInfo) -> String { - let json_str = serde_json::to_string(info).unwrap_or_default(); - hex::encode(json_str.as_bytes()) -} - // Helper function to parse extension info from path segments fn parse_extension_info_from_path( path_str: &str, @@ -627,11 +620,11 @@ fn parse_extension_info_from_path( match (segments_iter.next(), segments_iter.next(), segments_iter.next()) { (Some(public_key), Some(name), Some(version)) => { println!("=== Extension Protocol Handler (path-based) ==="); - println!("Full URI: {}", uri_ref); + println!("Full URI: {uri_ref}"); println!("Parsed from path segments:"); - println!(" PublicKey: {}", public_key); - println!(" Name: {}", name); - println!(" Version: {}", version); + println!(" PublicKey: {public_key}"); + println!(" Name: {name}"); + println!(" Version: {version}"); let info = ExtensionInfo { public_key: public_key.to_string(), @@ -653,7 +646,7 @@ fn parse_extension_info_from_path( ) { Ok(decoded) => { println!("=== Extension Protocol Handler (legacy hex format) ==="); - println!("Full URI: {}", uri_ref); + println!("Full URI: {uri_ref}"); println!("Decoded info:"); println!(" PublicKey: {}", decoded.public_key); println!(" Name: {}", decoded.name); @@ -670,8 +663,8 @@ fn parse_extension_info_from_path( Ok((decoded, segments)) } Err(e) => { - eprintln!("Fehler beim Parsen (alle Fallbacks): {}", e); - Err(format!("Ungültige Anfrage: {}", e).into()) + eprintln!("Fehler beim Parsen (alle Fallbacks): {e}"); + Err(format!("Ungültige Anfrage: {e}").into()) } } } diff --git a/src-tauri/src/extension/core/types.rs b/src-tauri/src/extension/core/types.rs index 340ca02..b0c1560 100644 --- a/src-tauri/src/extension/core/types.rs +++ b/src-tauri/src/extension/core/types.rs @@ -70,8 +70,7 @@ pub fn copy_directory( use std::path::PathBuf; println!( - "Kopiere Verzeichnis von '{}' nach '{}'", - source, destination + "Kopiere Verzeichnis von '{source}' nach '{destination}'" ); let source_path = PathBuf::from(&source); @@ -81,7 +80,7 @@ pub fn copy_directory( return Err(ExtensionError::Filesystem { source: std::io::Error::new( std::io::ErrorKind::NotFound, - format!("Source directory '{}' not found", source), + format!("Source directory '{source}' not found"), ), }); } @@ -93,7 +92,7 @@ pub fn copy_directory( fs_extra::dir::copy(&source_path, &destination_path, &options).map_err(|e| { ExtensionError::Filesystem { - source: std::io::Error::new(std::io::ErrorKind::Other, e.to_string()), + source: std::io::Error::other(e.to_string()), } })?; Ok(()) diff --git a/src-tauri/src/extension/crypto.rs b/src-tauri/src/extension/crypto.rs index a2197c2..9b002f7 100644 --- a/src-tauri/src/extension/crypto.rs +++ b/src-tauri/src/extension/crypto.rs @@ -18,20 +18,20 @@ impl ExtensionCrypto { signature_hex: &str, ) -> Result<(), String> { let public_key_bytes = - hex::decode(public_key_hex).map_err(|e| format!("Invalid public key: {}", e))?; + hex::decode(public_key_hex).map_err(|e| format!("Invalid public key: {e}"))?; let public_key = VerifyingKey::from_bytes(&public_key_bytes.try_into().unwrap()) - .map_err(|e| format!("Invalid public key: {}", e))?; + .map_err(|e| format!("Invalid public key: {e}"))?; let signature_bytes = - hex::decode(signature_hex).map_err(|e| format!("Invalid signature: {}", e))?; + hex::decode(signature_hex).map_err(|e| format!("Invalid signature: {e}"))?; let signature = Signature::from_bytes(&signature_bytes.try_into().unwrap()); let content_hash = - hex::decode(content_hash_hex).map_err(|e| format!("Invalid content hash: {}", e))?; + hex::decode(content_hash_hex).map_err(|e| format!("Invalid content hash: {e}"))?; public_key .verify(&content_hash, &signature) - .map_err(|e| format!("Signature verification failed: {}", e)) + .map_err(|e| format!("Signature verification failed: {e}")) } /// Berechnet Hash eines Verzeichnisses (für Verifikation) @@ -71,7 +71,7 @@ impl ExtensionCrypto { if !canonical_manifest_path.starts_with(&canonical_dir) { return Err(ExtensionError::ManifestError { - reason: format!("Manifest path resolves outside of extension directory (potential path traversal)"), + reason: "Manifest path resolves outside of extension directory (potential path traversal)".to_string(), }); } @@ -90,7 +90,7 @@ impl ExtensionCrypto { let mut manifest: serde_json::Value = serde_json::from_str(&content_str).map_err(|e| { ExtensionError::ManifestError { - reason: format!("Cannot parse manifest JSON: {}", e), + reason: format!("Cannot parse manifest JSON: {e}"), } })?; @@ -107,7 +107,7 @@ impl ExtensionCrypto { let canonical_manifest_content = serde_json::to_string_pretty(&manifest).map_err(|e| { ExtensionError::ManifestError { - reason: format!("Failed to serialize manifest: {}", e), + reason: format!("Failed to serialize manifest: {e}"), } })?; diff --git a/src-tauri/src/extension/database/executor.rs b/src-tauri/src/extension/database/executor.rs index 9e6a599..d6e005c 100644 --- a/src-tauri/src/extension/database/executor.rs +++ b/src-tauri/src/extension/database/executor.rs @@ -3,7 +3,7 @@ use crate::crdt::hlc::HlcService; use crate::crdt::transformer::CrdtTransformer; use crate::crdt::trigger; -use crate::database::core::{convert_value_ref_to_json, parse_sql_statements, ValueConverter}; +use crate::database::core::{convert_value_ref_to_json, parse_sql_statements}; use crate::database::error::DatabaseError; use rusqlite::{params_from_iter, types::Value as SqliteValue, ToSql, Transaction}; use serde_json::Value as JsonValue; @@ -52,14 +52,14 @@ impl SqlExecutor { } let sql_str = statement.to_string(); - eprintln!("DEBUG: Transformed execute SQL: {}", sql_str); + eprintln!("DEBUG: Transformed execute SQL: {sql_str}"); // Führe Statement aus tx.execute(&sql_str, params) .map_err(|e| DatabaseError::ExecutionError { sql: sql_str.clone(), table: None, - reason: format!("Execute failed: {}", e), + reason: format!("Execute failed: {e}"), })?; // Trigger-Logik für CREATE TABLE @@ -70,7 +70,7 @@ impl SqlExecutor { .trim_matches('"') .trim_matches('`') .to_string(); - eprintln!("DEBUG: Setting up triggers for table: {}", table_name_str); + eprintln!("DEBUG: Setting up triggers for table: {table_name_str}"); trigger::setup_triggers_for_table(tx, &table_name_str, false)?; } @@ -115,7 +115,7 @@ impl SqlExecutor { } let sql_str = statement.to_string(); - eprintln!("DEBUG: Transformed SQL (with RETURNING): {}", sql_str); + eprintln!("DEBUG: Transformed SQL (with RETURNING): {sql_str}"); // Prepare und query ausführen let mut stmt = tx @@ -170,7 +170,7 @@ impl SqlExecutor { .trim_matches('"') .trim_matches('`') .to_string(); - eprintln!("DEBUG: Setting up triggers for table (RETURNING): {}", table_name_str); + eprintln!("DEBUG: Setting up triggers for table (RETURNING): {table_name_str}"); trigger::setup_triggers_for_table(tx, &table_name_str, false)?; } @@ -186,7 +186,7 @@ impl SqlExecutor { ) -> Result, DatabaseError> { let sql_params: Vec = params .iter() - .map(|v| crate::database::core::ValueConverter::json_to_rusqlite_value(v)) + .map(crate::database::core::ValueConverter::json_to_rusqlite_value) .collect::, _>>()?; let param_refs: Vec<&dyn ToSql> = sql_params.iter().map(|p| p as &dyn ToSql).collect(); Self::execute_internal_typed(tx, hlc_service, sql, ¶m_refs) @@ -201,7 +201,7 @@ impl SqlExecutor { ) -> Result<(HashSet, Vec>), DatabaseError> { let sql_params: Vec = params .iter() - .map(|v| crate::database::core::ValueConverter::json_to_rusqlite_value(v)) + .map(crate::database::core::ValueConverter::json_to_rusqlite_value) .collect::, _>>()?; let param_refs: Vec<&dyn ToSql> = sql_params.iter().map(|p| p as &dyn ToSql).collect(); Self::query_internal_typed(tx, hlc_service, sql, ¶m_refs) @@ -252,12 +252,12 @@ impl SqlExecutor { let stmt_to_execute = ast_vec.pop().unwrap(); let transformed_sql = stmt_to_execute.to_string(); - eprintln!("DEBUG: SELECT (no transformation): {}", transformed_sql); + eprintln!("DEBUG: SELECT (no transformation): {transformed_sql}"); // Convert JSON params to SQLite values let sql_params: Vec = params .iter() - .map(|v| crate::database::core::ValueConverter::json_to_rusqlite_value(v)) + .map(crate::database::core::ValueConverter::json_to_rusqlite_value) .collect::, _>>()?; let mut prepared_stmt = conn.prepare(&transformed_sql)?; diff --git a/src-tauri/src/extension/database/mod.rs b/src-tauri/src/extension/database/mod.rs index bfd7efb..0032c8a 100644 --- a/src-tauri/src/extension/database/mod.rs +++ b/src-tauri/src/extension/database/mod.rs @@ -13,10 +13,8 @@ use crate::AppState; use rusqlite::params_from_iter; use rusqlite::types::Value as SqlValue; use rusqlite::Transaction; -use serde_json::json; use serde_json::Value as JsonValue; use sqlparser::ast::{Statement, TableFactor, TableObject}; -use std::collections::HashSet; use tauri::State; /// Führt Statements mit korrekter Parameter-Bindung aus @@ -185,7 +183,7 @@ pub async fn extension_sql_execute( if let Statement::CreateTable(ref create_table_details) = statement { // Extract table name and remove quotes (both " and `) let raw_name = create_table_details.name.to_string(); - println!("DEBUG: Raw table name from AST: {:?}", raw_name); + println!("DEBUG: Raw table name from AST: {raw_name:?}"); println!("DEBUG: Raw table name chars: {:?}", raw_name.chars().collect::>()); let table_name_str = raw_name @@ -193,17 +191,15 @@ pub async fn extension_sql_execute( .trim_matches('`') .to_string(); - println!("DEBUG: Cleaned table name: {:?}", table_name_str); + println!("DEBUG: Cleaned table name: {table_name_str:?}"); println!("DEBUG: Cleaned table name chars: {:?}", table_name_str.chars().collect::>()); println!( - "Table '{}' created by extension, setting up CRDT triggers...", - table_name_str + "Table '{table_name_str}' created by extension, setting up CRDT triggers..." ); trigger::setup_triggers_for_table(&tx, &table_name_str, false)?; println!( - "Triggers for table '{}' successfully created.", - table_name_str + "Triggers for table '{table_name_str}' successfully created." ); } diff --git a/src-tauri/src/extension/error.rs b/src-tauri/src/extension/error.rs index d54088e..7516049 100644 --- a/src-tauri/src/extension/error.rs +++ b/src-tauri/src/extension/error.rs @@ -174,7 +174,7 @@ impl serde::Serialize for ExtensionError { let mut state = serializer.serialize_struct("ExtensionError", 4)?; state.serialize_field("code", &self.code())?; - state.serialize_field("type", &format!("{:?}", self))?; + state.serialize_field("type", &format!("{self:?}"))?; state.serialize_field("message", &self.to_string())?; if let Some(ext_id) = self.extension_id() { diff --git a/src-tauri/src/extension/filesystem/core.rs b/src-tauri/src/extension/filesystem/core.rs index 90c93e1..5a9c57d 100644 --- a/src-tauri/src/extension/filesystem/core.rs +++ b/src-tauri/src/extension/filesystem/core.rs @@ -133,7 +133,7 @@ fn validate_path_pattern(pattern: &str) -> Result<(), ExtensionError> { // Check for path traversal attempts if pattern.contains("../") || pattern.contains("..\\") { return Err(ExtensionError::SecurityViolation { - reason: format!("Path traversal detected in pattern: {}", pattern), + reason: format!("Path traversal detected in pattern: {pattern}"), }); } @@ -177,7 +177,7 @@ pub fn resolve_path_pattern( "$TEMP" => "Temp", _ => { return Err(ExtensionError::ValidationError { - reason: format!("Unknown base directory variable: {}", base_var), + reason: format!("Unknown base directory variable: {base_var}"), }); } }; diff --git a/src-tauri/src/extension/mod.rs b/src-tauri/src/extension/mod.rs index 2f607f6..6fec940 100644 --- a/src-tauri/src/extension/mod.rs +++ b/src-tauri/src/extension/mod.rs @@ -52,7 +52,7 @@ pub async fn get_all_extensions( .extension_manager .load_installed_extensions(&app_handle, &state) .await - .map_err(|e| format!("Failed to load extensions: {:?}", e))?; + .map_err(|e| format!("Failed to load extensions: {e:?}"))?; /* } */ let mut extensions = Vec::new(); @@ -292,12 +292,12 @@ pub async fn load_dev_extension( let (host, port, haextension_dir) = if config_path.exists() { let config_content = std::fs::read_to_string(&config_path).map_err(|e| ExtensionError::ValidationError { - reason: format!("Failed to read haextension.config.json: {}", e), + reason: format!("Failed to read haextension.config.json: {e}"), })?; let config: HaextensionConfig = serde_json::from_str(&config_content).map_err(|e| ExtensionError::ValidationError { - reason: format!("Failed to parse haextension.config.json: {}", e), + reason: format!("Failed to parse haextension.config.json: {e}"), })?; (config.dev.host, config.dev.port, config.dev.haextension_dir) @@ -306,23 +306,22 @@ pub async fn load_dev_extension( (default_host(), default_port(), default_haextension_dir()) }; - let dev_server_url = format!("http://{}:{}", host, port); - eprintln!("📡 Dev server URL: {}", dev_server_url); - eprintln!("📁 Haextension directory: {}", haextension_dir); + let dev_server_url = format!("http://{host}:{port}"); + eprintln!("📡 Dev server URL: {dev_server_url}"); + eprintln!("📁 Haextension directory: {haextension_dir}"); // 1.5. Check if dev server is running if !check_dev_server_health(&dev_server_url).await { return Err(ExtensionError::ValidationError { reason: format!( - "Dev server at {} is not reachable. Please start your dev server first (e.g., 'npm run dev')", - dev_server_url + "Dev server at {dev_server_url} is not reachable. Please start your dev server first (e.g., 'npm run dev')" ), }); } eprintln!("✅ Dev server is reachable"); // 2. Validate and build path to manifest: //manifest.json - let manifest_relative_path = format!("{}/manifest.json", haextension_dir); + let manifest_relative_path = format!("{haextension_dir}/manifest.json"); let manifest_path = ExtensionManager::validate_path_in_directory( &extension_path_buf, &manifest_relative_path, @@ -330,15 +329,14 @@ pub async fn load_dev_extension( )? .ok_or_else(|| ExtensionError::ManifestError { reason: format!( - "Manifest not found at: {}/manifest.json. Make sure you run 'npx @haexhub/sdk init' first.", - haextension_dir + "Manifest not found at: {haextension_dir}/manifest.json. Make sure you run 'npx @haexhub/sdk init' first." ), })?; // 3. Read and parse manifest let manifest_content = std::fs::read_to_string(&manifest_path).map_err(|e| ExtensionError::ManifestError { - reason: format!("Failed to read manifest: {}", e), + reason: format!("Failed to read manifest: {e}"), })?; let manifest: ExtensionManifest = serde_json::from_str(&manifest_content)?; @@ -406,7 +404,7 @@ pub fn remove_dev_extension( if let Some(id) = to_remove { dev_exts.remove(&id); - eprintln!("✅ Dev extension removed: {}", name); + eprintln!("✅ Dev extension removed: {name}"); Ok(()) } else { Err(ExtensionError::NotFound { public_key, name }) diff --git a/src-tauri/src/extension/permissions/manager.rs b/src-tauri/src/extension/permissions/manager.rs index 66a9918..d0462c9 100644 --- a/src-tauri/src/extension/permissions/manager.rs +++ b/src-tauri/src/extension/permissions/manager.rs @@ -28,8 +28,7 @@ impl PermissionManager { })?; let sql = format!( - "INSERT INTO {} (id, extension_id, resource_type, action, target, constraints, status) VALUES (?, ?, ?, ?, ?, ?, ?)", - TABLE_EXTENSION_PERMISSIONS + "INSERT INTO {TABLE_EXTENSION_PERMISSIONS} (id, extension_id, resource_type, action, target, constraints, status) VALUES (?, ?, ?, ?, ?, ?, ?)" ); for perm in permissions { @@ -76,8 +75,7 @@ impl PermissionManager { let db_perm: HaexExtensionPermissions = permission.into(); let sql = format!( - "UPDATE {} SET resource_type = ?, action = ?, target = ?, constraints = ?, status = ? WHERE id = ?", - TABLE_EXTENSION_PERMISSIONS + "UPDATE {TABLE_EXTENSION_PERMISSIONS} SET resource_type = ?, action = ?, target = ?, constraints = ?, status = ? WHERE id = ?" ); let params = params![ @@ -111,7 +109,7 @@ impl PermissionManager { reason: "Failed to lock HLC service".to_string(), })?; - let sql = format!("UPDATE {} SET status = ? WHERE id = ?", TABLE_EXTENSION_PERMISSIONS); + let sql = format!("UPDATE {TABLE_EXTENSION_PERMISSIONS} SET status = ? WHERE id = ?"); let params = params![new_status.as_str(), permission_id]; SqlExecutor::execute_internal_typed(&tx, &hlc_service, &sql, params)?; tx.commit().map_err(DatabaseError::from) @@ -133,7 +131,7 @@ impl PermissionManager { })?; // Echtes DELETE - wird vom CrdtTransformer zu UPDATE umgewandelt - let sql = format!("DELETE FROM {} WHERE id = ?", TABLE_EXTENSION_PERMISSIONS); + let sql = format!("DELETE FROM {TABLE_EXTENSION_PERMISSIONS} WHERE id = ?"); SqlExecutor::execute_internal_typed(&tx, &hlc_service, &sql, params![permission_id])?; tx.commit().map_err(DatabaseError::from) }).map_err(ExtensionError::from) @@ -152,7 +150,7 @@ impl PermissionManager { reason: "Failed to lock HLC service".to_string(), })?; - let sql = format!("DELETE FROM {} WHERE extension_id = ?", TABLE_EXTENSION_PERMISSIONS); + let sql = format!("DELETE FROM {TABLE_EXTENSION_PERMISSIONS} WHERE extension_id = ?"); SqlExecutor::execute_internal_typed(&tx, &hlc_service, &sql, params![extension_id])?; tx.commit().map_err(DatabaseError::from) }).map_err(ExtensionError::from) @@ -164,7 +162,7 @@ impl PermissionManager { hlc_service: &crate::crdt::hlc::HlcService, extension_id: &str, ) -> Result<(), DatabaseError> { - let sql = format!("DELETE FROM {} WHERE extension_id = ?", TABLE_EXTENSION_PERMISSIONS); + let sql = format!("DELETE FROM {TABLE_EXTENSION_PERMISSIONS} WHERE extension_id = ?"); SqlExecutor::execute_internal_typed(tx, hlc_service, &sql, params![extension_id])?; Ok(()) } @@ -174,7 +172,7 @@ impl PermissionManager { extension_id: &str, ) -> Result, ExtensionError> { with_connection(&app_state.db, |conn| { - let sql = format!("SELECT * FROM {} WHERE extension_id = ?", TABLE_EXTENSION_PERMISSIONS); + let sql = format!("SELECT * FROM {TABLE_EXTENSION_PERMISSIONS} WHERE extension_id = ?"); let mut stmt = conn.prepare(&sql).map_err(DatabaseError::from)?; let perms_iter = stmt.query_map(params![extension_id], |row| { @@ -209,7 +207,7 @@ impl PermissionManager { .extension_manager .get_extension(extension_id) .ok_or_else(|| ExtensionError::ValidationError { - reason: format!("Extension with ID {} not found", extension_id), + reason: format!("Extension with ID {extension_id} not found"), })?; // Build expected table prefix: {publicKey}__{extensionName}__ @@ -238,8 +236,8 @@ impl PermissionManager { if !has_permission { return Err(ExtensionError::permission_denied( extension_id, - &format!("{:?}", action), - &format!("database table '{}'", table_name), + &format!("{action:?}"), + &format!("database table '{table_name}'"), )); } @@ -415,7 +413,7 @@ impl PermissionManager { "db" => Ok(ResourceType::Db), "shell" => Ok(ResourceType::Shell), _ => Err(DatabaseError::SerializationError { - reason: format!("Unknown resource type: {}", s), + reason: format!("Unknown resource type: {s}"), }), } } @@ -423,8 +421,7 @@ impl PermissionManager { fn matches_path_pattern(pattern: &str, path: &str) -> bool { - if pattern.ends_with("/*") { - let prefix = &pattern[..pattern.len() - 2]; + if let Some(prefix) = pattern.strip_suffix("/*") { return path.starts_with(prefix); } diff --git a/src-tauri/src/extension/permissions/types.rs b/src-tauri/src/extension/permissions/types.rs index c68d040..27a1bbc 100644 --- a/src-tauri/src/extension/permissions/types.rs +++ b/src-tauri/src/extension/permissions/types.rs @@ -267,7 +267,7 @@ impl ResourceType { "db" => Ok(ResourceType::Db), "shell" => Ok(ResourceType::Shell), _ => Err(ExtensionError::ValidationError { - reason: format!("Unknown resource type: {}", s), + reason: format!("Unknown resource type: {s}"), }), } } @@ -301,7 +301,7 @@ impl Action { ResourceType::Fs => Ok(Action::Filesystem(FsAction::from_str(s)?)), ResourceType::Http => { let action: HttpAction = - serde_json::from_str(&format!("\"{}\"", s)).map_err(|_| { + serde_json::from_str(&format!("\"{s}\"")).map_err(|_| { ExtensionError::InvalidActionString { input: s.to_string(), resource_type: "http".to_string(), @@ -329,7 +329,7 @@ impl PermissionStatus { "granted" => Ok(PermissionStatus::Granted), "denied" => Ok(PermissionStatus::Denied), _ => Err(ExtensionError::ValidationError { - reason: format!("Unknown permission status: {}", s), + reason: format!("Unknown permission status: {s}"), }), } } diff --git a/src-tauri/src/extension/permissions/validator.rs b/src-tauri/src/extension/permissions/validator.rs index 009137c..b7e7572 100644 --- a/src-tauri/src/extension/permissions/validator.rs +++ b/src-tauri/src/extension/permissions/validator.rs @@ -17,7 +17,7 @@ impl SqlPermissionValidator { fn is_own_table(extension_id: &str, table_name: &str) -> bool { // Tabellennamen sind im Format: {keyHash}_{extensionName}_{tableName} // extension_id ist der keyHash der Extension - table_name.starts_with(&format!("{}_", extension_id)) + table_name.starts_with(&format!("{extension_id}_")) } /// Validiert ein SQL-Statement gegen die Permissions einer Extension @@ -45,7 +45,7 @@ impl SqlPermissionValidator { Self::validate_schema_statement(app_state, extension_id, &statement).await } _ => Err(ExtensionError::ValidationError { - reason: format!("Statement type not allowed: {}", sql), + reason: format!("Statement type not allowed: {sql}"), }), } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 4bf202a..86f9b36 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -26,7 +26,7 @@ pub fn run() { let state = app_handle.state::(); // Rufe den Handler mit allen benötigten Parametern auf - match extension::core::extension_protocol_handler(state, &app_handle, &request) { + match extension::core::extension_protocol_handler(state, app_handle, &request) { Ok(response) => response, Err(e) => { eprintln!( @@ -38,11 +38,10 @@ pub fn run() { .status(500) .header("Content-Type", "text/plain") .body(Vec::from(format!( - "Interner Serverfehler im Protokollhandler: {}", - e + "Interner Serverfehler im Protokollhandler: {e}" ))) .unwrap_or_else(|build_err| { - eprintln!("Konnte Fehler-Response nicht erstellen: {}", build_err); + eprintln!("Konnte Fehler-Response nicht erstellen: {build_err}"); tauri::http::Response::builder() .status(500) .body(Vec::new())