737 lines
42 KiB
Rust
737 lines
42 KiB
Rust
use rusqlite::{params, Connection, OptionalExtension};
|
|
|
|
use crate::error::{AppError, AppResult};
|
|
use crate::shared::types::Lang;
|
|
|
|
pub struct LocationQueryResult {
|
|
pub loc_id: String,
|
|
pub loc_name: String,
|
|
pub element_id: Option<String>,
|
|
pub element_name: Option<String>,
|
|
pub element_description: Option<String>,
|
|
pub sub_element_id: Option<String>,
|
|
pub sub_elem_name: Option<String>,
|
|
pub sub_elem_description: Option<String>,
|
|
pub series_location_id: Option<String>,
|
|
}
|
|
|
|
pub struct LocationElementQueryResult {
|
|
pub sub_element_id: Option<String>,
|
|
pub sub_elem_name: Option<String>,
|
|
pub sub_elem_description: Option<String>,
|
|
pub element_id: String,
|
|
pub element_name: String,
|
|
pub element_description: Option<String>,
|
|
}
|
|
|
|
pub struct BookLocationTable {
|
|
pub loc_id: String,
|
|
pub book_id: String,
|
|
pub user_id: String,
|
|
pub loc_name: String,
|
|
pub loc_original_name: String,
|
|
pub last_update: i64,
|
|
}
|
|
|
|
pub struct LocationElementTable {
|
|
pub element_id: String,
|
|
pub location: String,
|
|
pub user_id: String,
|
|
pub element_name: String,
|
|
pub original_name: String,
|
|
pub element_description: Option<String>,
|
|
pub last_update: i64,
|
|
}
|
|
|
|
pub struct LocationSubElementTable {
|
|
pub sub_element_id: String,
|
|
pub element_id: String,
|
|
pub user_id: String,
|
|
pub sub_elem_name: String,
|
|
pub original_name: String,
|
|
pub sub_elem_description: Option<String>,
|
|
pub last_update: i64,
|
|
}
|
|
|
|
pub struct SyncedLocationResult {
|
|
pub loc_id: String,
|
|
pub book_id: String,
|
|
pub loc_name: String,
|
|
pub last_update: i64,
|
|
}
|
|
|
|
pub struct SyncedLocationElementResult {
|
|
pub element_id: String,
|
|
pub location: String,
|
|
pub element_name: String,
|
|
pub last_update: i64,
|
|
}
|
|
|
|
pub struct SyncedLocationSubElementResult {
|
|
pub sub_element_id: String,
|
|
pub element_id: String,
|
|
pub sub_elem_name: String,
|
|
pub last_update: i64,
|
|
}
|
|
|
|
/// Retrieves all locations with their elements and sub-elements for a specific book.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `book_id` - The book's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns an array of location query results with nested elements.
|
|
pub fn get_location(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<LocationQueryResult>> {
|
|
let mut statement = conn
|
|
.prepare("SELECT loc_id, loc_name, element.element_id AS element_id, element.element_name, element.element_description, sub_elem.sub_element_id AS sub_element_id, sub_elem.sub_elem_name, sub_elem.sub_elem_description, location.series_location_id FROM book_location AS location LEFT JOIN location_element AS element ON location.loc_id = element.location LEFT JOIN location_sub_element AS sub_elem ON element.element_id = sub_elem.element_id WHERE location.user_id = ?1 AND location.book_id = ?2")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les emplacements.".to_string() } else { "Unable to retrieve locations.".to_string() }))?;
|
|
|
|
let locations = statement
|
|
.query_map(params![user_id, book_id], |query_row| {
|
|
Ok(LocationQueryResult {
|
|
loc_id: query_row.get(0)?,
|
|
loc_name: query_row.get(1)?,
|
|
element_id: query_row.get(2)?,
|
|
element_name: query_row.get(3)?,
|
|
element_description: query_row.get(4)?,
|
|
sub_element_id: query_row.get(5)?,
|
|
sub_elem_name: query_row.get(6)?,
|
|
sub_elem_description: query_row.get(7)?,
|
|
series_location_id: query_row.get(8)?,
|
|
})
|
|
})
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les emplacements.".to_string() } else { "Unable to retrieve locations.".to_string() }))?
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les emplacements.".to_string() } else { "Unable to retrieve locations.".to_string() }))?;
|
|
|
|
Ok(locations)
|
|
}
|
|
|
|
/// Inserts a new location section for a book.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `location_id` - The new location's unique identifier
|
|
/// * `book_id` - The book's unique identifier
|
|
/// * `encrypted_name` - The encrypted location name
|
|
/// * `original_name` - The original (unencrypted) location name
|
|
/// * `last_update` - The timestamp of the last update
|
|
/// * `series_location_id` - The series location ID (optional)
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns the location ID if insertion was successful.
|
|
pub fn insert_location(
|
|
conn: &Connection, user_id: &str, location_id: &str, book_id: &str, encrypted_name: &str,
|
|
original_name: &str, last_update: i64, series_location_id: Option<&str>, lang: Lang,
|
|
) -> AppResult<String> {
|
|
let insert_result = match series_location_id {
|
|
Some(series_id) => conn.execute(
|
|
"INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, series_location_id, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
|
params![location_id, book_id, user_id, encrypted_name, original_name, series_id, last_update],
|
|
),
|
|
None => conn.execute(
|
|
"INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
|
params![location_id, book_id, user_id, encrypted_name, original_name, last_update],
|
|
),
|
|
}
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ajouter la section d'emplacement.".to_string() } else { "Unable to add location section.".to_string() }))?;
|
|
|
|
if insert_result > 0 {
|
|
Ok(location_id.to_string())
|
|
} else {
|
|
Err(AppError::Internal(if lang == Lang::Fr { "Une erreur s'est produite lors de l'ajout de la section d'emplacement.".to_string() } else { "Error adding location section.".to_string() }))
|
|
}
|
|
}
|
|
|
|
/// Inserts a new location element within a location section.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `element_id` - The new element's unique identifier
|
|
/// * `location_id` - The parent location's unique identifier
|
|
/// * `encrypted_name` - The encrypted element name
|
|
/// * `original_name` - The original (unencrypted) element name
|
|
/// * `last_update` - The timestamp of the last update
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns the element ID if insertion was successful.
|
|
pub fn insert_location_element(
|
|
conn: &Connection, user_id: &str, element_id: &str, location_id: &str,
|
|
encrypted_name: &str, original_name: &str, last_update: i64, lang: Lang,
|
|
) -> AppResult<String> {
|
|
let insert_result = conn
|
|
.execute(
|
|
"INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
|
params![element_id, location_id, user_id, encrypted_name, original_name, "", last_update],
|
|
)
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ajouter l'élément d'emplacement.".to_string() } else { "Unable to add location element.".to_string() }))?;
|
|
|
|
if insert_result > 0 {
|
|
Ok(element_id.to_string())
|
|
} else {
|
|
Err(AppError::Internal(if lang == Lang::Fr { "Une erreur s'est produite lors de l'ajout de l'élément d'emplacement.".to_string() } else { "Error adding location element.".to_string() }))
|
|
}
|
|
}
|
|
|
|
/// Inserts a new sub-element within a location element.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `sub_element_id` - The new sub-element's unique identifier
|
|
/// * `element_id` - The parent element's unique identifier
|
|
/// * `encrypted_name` - The encrypted sub-element name
|
|
/// * `original_name` - The original (unencrypted) sub-element name
|
|
/// * `last_update` - The timestamp of the last update
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns the sub-element ID if insertion was successful.
|
|
pub fn insert_location_sub_element(
|
|
conn: &Connection, user_id: &str, sub_element_id: &str, element_id: &str,
|
|
encrypted_name: &str, original_name: &str, last_update: i64, lang: Lang,
|
|
) -> AppResult<String> {
|
|
let insert_result = conn
|
|
.execute(
|
|
"INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
|
params![sub_element_id, element_id, user_id, encrypted_name, original_name, "", last_update],
|
|
)
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'ajouter le sous-élément d'emplacement.".to_string() } else { "Unable to add location sub-element.".to_string() }))?;
|
|
|
|
if insert_result > 0 {
|
|
Ok(sub_element_id.to_string())
|
|
} else {
|
|
Err(AppError::Internal(if lang == Lang::Fr { "Une erreur s'est produite lors de l'ajout du sous-élément d'emplacement.".to_string() } else { "Error adding location sub-element.".to_string() }))
|
|
}
|
|
}
|
|
|
|
/// Updates an existing location sub-element's name and description.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `id` - The sub-element's unique identifier
|
|
/// * `encrypted_name` - The new encrypted sub-element name
|
|
/// * `original_name` - The new original (unencrypted) sub-element name
|
|
/// * `encrypt_description` - The new encrypted description
|
|
/// * `last_update` - The timestamp of the last update
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the update affected at least one row.
|
|
pub fn update_location_sub_element(
|
|
conn: &Connection, user_id: &str, id: &str, encrypted_name: &str,
|
|
original_name: &str, encrypt_description: &str, last_update: i64, lang: Lang,
|
|
) -> AppResult<bool> {
|
|
let update_result = conn
|
|
.execute(
|
|
"UPDATE location_sub_element SET sub_elem_name = ?1, original_name = ?2, sub_elem_description = ?3, last_update = ?4 WHERE sub_element_id = ?5 AND user_id = ?6",
|
|
params![encrypted_name, original_name, encrypt_description, last_update, id, user_id],
|
|
)
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour le sous-élément d'emplacement.".to_string() } else { "Unable to update location sub-element.".to_string() }))?;
|
|
|
|
Ok(update_result > 0)
|
|
}
|
|
|
|
/// Updates an existing location element's name and description.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `id` - The element's unique identifier
|
|
/// * `encrypted_name` - The new encrypted element name
|
|
/// * `original_name` - The new original (unencrypted) element name
|
|
/// * `encrypted_description` - The new encrypted description
|
|
/// * `last_update` - The timestamp of the last update
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the update affected at least one row.
|
|
pub fn update_location_element(
|
|
conn: &Connection, user_id: &str, id: &str, encrypted_name: &str,
|
|
original_name: &str, encrypted_description: &str, last_update: i64, lang: Lang,
|
|
) -> AppResult<bool> {
|
|
let update_result = conn
|
|
.execute(
|
|
"UPDATE location_element SET element_name = ?1, original_name = ?2, element_description = ?3, last_update = ?4 WHERE element_id = ?5 AND user_id = ?6",
|
|
params![encrypted_name, original_name, encrypted_description, last_update, id, user_id],
|
|
)
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour l'élément d'emplacement.".to_string() } else { "Unable to update location element.".to_string() }))?;
|
|
|
|
Ok(update_result > 0)
|
|
}
|
|
|
|
/// Updates an existing location section's name.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `id` - The location section's unique identifier
|
|
/// * `encrypted_name` - The new encrypted location name
|
|
/// * `original_name` - The new original (unencrypted) location name
|
|
/// * `last_update` - The timestamp of the last update
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the update affected at least one row.
|
|
pub fn update_location_section(
|
|
conn: &Connection, user_id: &str, id: &str, encrypted_name: &str,
|
|
original_name: &str, last_update: i64, lang: Lang,
|
|
) -> AppResult<bool> {
|
|
let update_result = conn
|
|
.execute(
|
|
"UPDATE book_location SET loc_name = ?1, loc_original_name = ?2, last_update = ?3 WHERE loc_id = ?4 AND user_id = ?5",
|
|
params![encrypted_name, original_name, last_update, id, user_id],
|
|
)
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour la section d'emplacement.".to_string() } else { "Unable to update location section.".to_string() }))?;
|
|
|
|
Ok(update_result > 0)
|
|
}
|
|
|
|
/// Deletes a location section by its ID.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `location_id` - The location section's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the deletion affected at least one row.
|
|
pub fn delete_location_section(conn: &Connection, user_id: &str, location_id: &str, lang: Lang) -> AppResult<bool> {
|
|
let delete_result = conn
|
|
.execute("DELETE FROM book_location WHERE loc_id = ?1 AND user_id = ?2", params![location_id, user_id])
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer la section d'emplacement.".to_string() } else { "Unable to delete location section.".to_string() }))?;
|
|
|
|
Ok(delete_result > 0)
|
|
}
|
|
|
|
/// Deletes a location element by its ID.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `element_id` - The element's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the deletion affected at least one row.
|
|
pub fn delete_location_element(conn: &Connection, user_id: &str, element_id: &str, lang: Lang) -> AppResult<bool> {
|
|
let delete_result = conn
|
|
.execute("DELETE FROM location_element WHERE element_id = ?1 AND user_id = ?2", params![element_id, user_id])
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer l'élément d'emplacement.".to_string() } else { "Unable to delete location element.".to_string() }))?;
|
|
|
|
Ok(delete_result > 0)
|
|
}
|
|
|
|
/// Deletes a location sub-element by its ID.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `sub_element_id` - The sub-element's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the deletion affected at least one row.
|
|
pub fn delete_location_sub_element(conn: &Connection, user_id: &str, sub_element_id: &str, lang: Lang) -> AppResult<bool> {
|
|
let delete_result = conn
|
|
.execute("DELETE FROM location_sub_element WHERE sub_element_id = ?1 AND user_id = ?2", params![sub_element_id, user_id])
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de supprimer le sous-élément d'emplacement.".to_string() } else { "Unable to delete location sub-element.".to_string() }))?;
|
|
|
|
Ok(delete_result > 0)
|
|
}
|
|
|
|
/// Fetches all location elements and sub-elements for tagging purposes.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `book_id` - The book's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns an array of location elements with their sub-elements.
|
|
pub fn fetch_location_tags(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<LocationElementQueryResult>> {
|
|
let mut statement = conn
|
|
.prepare("SELECT se.sub_element_id AS sub_element_id, se.sub_elem_name, se.sub_elem_description, el.element_id AS element_id, el.element_name, el.element_description FROM location_sub_element AS se RIGHT JOIN location_element AS el ON se.element_id = el.element_id LEFT JOIN book_location AS lo ON el.location = lo.loc_id WHERE lo.book_id = ?1 AND lo.user_id = ?2")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags d'emplacement.".to_string() } else { "Unable to retrieve location tags.".to_string() }))?;
|
|
|
|
let location_tags = statement
|
|
.query_map(params![book_id, user_id], |query_row| {
|
|
Ok(LocationElementQueryResult {
|
|
sub_element_id: query_row.get(0)?,
|
|
sub_elem_name: query_row.get(1)?,
|
|
sub_elem_description: query_row.get(2)?,
|
|
element_id: query_row.get(3)?,
|
|
element_name: query_row.get(4)?,
|
|
element_description: query_row.get(5)?,
|
|
})
|
|
})
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags d'emplacement.".to_string() } else { "Unable to retrieve location tags.".to_string() }))?
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les tags d'emplacement.".to_string() } else { "Unable to retrieve location tags.".to_string() }))?;
|
|
|
|
Ok(location_tags)
|
|
}
|
|
|
|
/// Fetches locations by their tag IDs (element or sub-element IDs).
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `locations` - An array of location tag IDs to search for
|
|
/// Checks if a location exists in the database.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `loc_id` - The location's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the location exists, false otherwise.
|
|
pub fn is_location_exist(conn: &Connection, user_id: &str, loc_id: &str, lang: Lang) -> AppResult<bool> {
|
|
let mut statement = conn
|
|
.prepare("SELECT 1 FROM book_location WHERE loc_id = ?1 AND user_id = ?2")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'emplacement.".to_string() } else { "Unable to check location existence.".to_string() }))?;
|
|
|
|
let existing_location = statement
|
|
.query_row(params![loc_id, user_id], |query_row| query_row.get::<_, i32>(0))
|
|
.optional()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'emplacement.".to_string() } else { "Unable to check location existence.".to_string() }))?;
|
|
|
|
Ok(existing_location.is_some())
|
|
}
|
|
|
|
/// Checks if a location element exists in the database.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `element_id` - The element's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the location element exists, false otherwise.
|
|
pub fn is_location_element_exist(conn: &Connection, user_id: &str, element_id: &str, lang: Lang) -> AppResult<bool> {
|
|
let mut statement = conn
|
|
.prepare("SELECT 1 FROM location_element WHERE element_id = ?1 AND user_id = ?2")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'élément d'emplacement.".to_string() } else { "Unable to check location element existence.".to_string() }))?;
|
|
|
|
let existing_element = statement
|
|
.query_row(params![element_id, user_id], |query_row| query_row.get::<_, i32>(0))
|
|
.optional()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence de l'élément d'emplacement.".to_string() } else { "Unable to check location element existence.".to_string() }))?;
|
|
|
|
Ok(existing_element.is_some())
|
|
}
|
|
|
|
/// Checks if a location sub-element exists in the database.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `sub_element_id` - The sub-element's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the location sub-element exists, false otherwise.
|
|
pub fn is_location_sub_element_exist(conn: &Connection, user_id: &str, sub_element_id: &str, lang: Lang) -> AppResult<bool> {
|
|
let mut statement = conn
|
|
.prepare("SELECT 1 FROM location_sub_element WHERE sub_element_id = ?1 AND user_id = ?2")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du sous-élément d'emplacement.".to_string() } else { "Unable to check location sub-element existence.".to_string() }))?;
|
|
|
|
let existing_sub_element = statement
|
|
.query_row(params![sub_element_id, user_id], |query_row| query_row.get::<_, i32>(0))
|
|
.optional()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de vérifier l'existence du sous-élément d'emplacement.".to_string() } else { "Unable to check location sub-element existence.".to_string() }))?;
|
|
|
|
Ok(existing_sub_element.is_some())
|
|
}
|
|
|
|
/// Fetches all locations for a specific book.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `book_id` - The book's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns an array of book location records.
|
|
pub fn fetch_book_locations(conn: &Connection, user_id: &str, book_id: &str, lang: Lang) -> AppResult<Vec<BookLocationTable>> {
|
|
let mut statement = conn
|
|
.prepare("SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update FROM book_location WHERE user_id = ?1 AND book_id = ?2")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux.".to_string() } else { "Unable to retrieve locations.".to_string() }))?;
|
|
|
|
let book_locations = statement
|
|
.query_map(params![user_id, book_id], |query_row| {
|
|
Ok(BookLocationTable {
|
|
loc_id: query_row.get(0)?, book_id: query_row.get(1)?,
|
|
user_id: query_row.get(2)?, loc_name: query_row.get(3)?,
|
|
loc_original_name: query_row.get(4)?, last_update: query_row.get(5)?,
|
|
})
|
|
})
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux.".to_string() } else { "Unable to retrieve locations.".to_string() }))?
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux.".to_string() } else { "Unable to retrieve locations.".to_string() }))?;
|
|
|
|
Ok(book_locations)
|
|
}
|
|
|
|
/// Fetches all elements for a specific location.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `location_id` - The location's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns an array of location element records.
|
|
pub fn fetch_location_elements(conn: &Connection, user_id: &str, location_id: &str, lang: Lang) -> AppResult<Vec<LocationElementTable>> {
|
|
let mut statement = conn
|
|
.prepare("SELECT element_id, location, user_id, element_name, original_name, element_description, last_update FROM location_element WHERE user_id = ?1 AND location = ?2")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu.".to_string() } else { "Unable to retrieve location elements.".to_string() }))?;
|
|
|
|
let location_elements = statement
|
|
.query_map(params![user_id, location_id], |query_row| {
|
|
Ok(LocationElementTable {
|
|
element_id: query_row.get(0)?, location: query_row.get(1)?,
|
|
user_id: query_row.get(2)?, element_name: query_row.get(3)?,
|
|
original_name: query_row.get(4)?, element_description: query_row.get(5)?,
|
|
last_update: query_row.get(6)?,
|
|
})
|
|
})
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu.".to_string() } else { "Unable to retrieve location elements.".to_string() }))?
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu.".to_string() } else { "Unable to retrieve location elements.".to_string() }))?;
|
|
|
|
Ok(location_elements)
|
|
}
|
|
|
|
/// Fetches all sub-elements for a specific location element.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `element_id` - The element's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns an array of location sub-element records.
|
|
pub fn fetch_location_sub_elements(conn: &Connection, user_id: &str, element_id: &str, lang: Lang) -> AppResult<Vec<LocationSubElementTable>> {
|
|
let mut statement = conn
|
|
.prepare("SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update FROM location_sub_element WHERE user_id = ?1 AND element_id = ?2")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu.".to_string() } else { "Unable to retrieve location sub-elements.".to_string() }))?;
|
|
|
|
let location_sub_elements = statement
|
|
.query_map(params![user_id, element_id], |query_row| {
|
|
Ok(LocationSubElementTable {
|
|
sub_element_id: query_row.get(0)?, element_id: query_row.get(1)?,
|
|
user_id: query_row.get(2)?, sub_elem_name: query_row.get(3)?,
|
|
original_name: query_row.get(4)?, sub_elem_description: query_row.get(5)?,
|
|
last_update: query_row.get(6)?,
|
|
})
|
|
})
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu.".to_string() } else { "Unable to retrieve location sub-elements.".to_string() }))?
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu.".to_string() } else { "Unable to retrieve location sub-elements.".to_string() }))?;
|
|
|
|
Ok(location_sub_elements)
|
|
}
|
|
|
|
/// Fetches all synced locations for a user (used for synchronization).
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns an array of synced location records.
|
|
pub fn fetch_synced_locations(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedLocationResult>> {
|
|
let mut statement = conn
|
|
.prepare("SELECT loc_id, book_id, loc_name, last_update FROM book_location WHERE user_id = ?1")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux synchronisés.".to_string() } else { "Unable to retrieve synced locations.".to_string() }))?;
|
|
|
|
let synced_locations = statement
|
|
.query_map(params![user_id], |query_row| {
|
|
Ok(SyncedLocationResult { loc_id: query_row.get(0)?, book_id: query_row.get(1)?, loc_name: query_row.get(2)?, last_update: query_row.get(3)? })
|
|
})
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux synchronisés.".to_string() } else { "Unable to retrieve synced locations.".to_string() }))?
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les lieux synchronisés.".to_string() } else { "Unable to retrieve synced locations.".to_string() }))?;
|
|
|
|
Ok(synced_locations)
|
|
}
|
|
|
|
/// Fetches all synced location elements for a user (used for synchronization).
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns an array of synced location element records.
|
|
pub fn fetch_synced_location_elements(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedLocationElementResult>> {
|
|
let mut statement = conn
|
|
.prepare("SELECT element_id, location, element_name, last_update FROM location_element WHERE user_id = ?1")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu synchronisés.".to_string() } else { "Unable to retrieve synced location elements.".to_string() }))?;
|
|
|
|
let synced_location_elements = statement
|
|
.query_map(params![user_id], |query_row| {
|
|
Ok(SyncedLocationElementResult { element_id: query_row.get(0)?, location: query_row.get(1)?, element_name: query_row.get(2)?, last_update: query_row.get(3)? })
|
|
})
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu synchronisés.".to_string() } else { "Unable to retrieve synced location elements.".to_string() }))?
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les éléments de lieu synchronisés.".to_string() } else { "Unable to retrieve synced location elements.".to_string() }))?;
|
|
|
|
Ok(synced_location_elements)
|
|
}
|
|
|
|
/// Fetches all synced location sub-elements for a user (used for synchronization).
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns an array of synced location sub-element records.
|
|
pub fn fetch_synced_location_sub_elements(conn: &Connection, user_id: &str, lang: Lang) -> AppResult<Vec<SyncedLocationSubElementResult>> {
|
|
let mut statement = conn
|
|
.prepare("SELECT sub_element_id, element_id, sub_elem_name, last_update FROM location_sub_element WHERE user_id = ?1")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu synchronisés.".to_string() } else { "Unable to retrieve synced location sub-elements.".to_string() }))?;
|
|
|
|
let synced_location_sub_elements = statement
|
|
.query_map(params![user_id], |query_row| {
|
|
Ok(SyncedLocationSubElementResult { sub_element_id: query_row.get(0)?, element_id: query_row.get(1)?, sub_elem_name: query_row.get(2)?, last_update: query_row.get(3)? })
|
|
})
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu synchronisés.".to_string() } else { "Unable to retrieve synced location sub-elements.".to_string() }))?
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer les sous-éléments de lieu synchronisés.".to_string() } else { "Unable to retrieve synced location sub-elements.".to_string() }))?;
|
|
|
|
Ok(synced_location_sub_elements)
|
|
}
|
|
|
|
/// Inserts a synced location from the remote server.
|
|
/// * `conn` - Database connection
|
|
/// * `loc_id` - The location's unique identifier
|
|
/// * `book_id` - The book's unique identifier
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `loc_name` - The encrypted location name
|
|
/// * `loc_original_name` - The original (unencrypted) location name
|
|
/// * `last_update` - The timestamp of the last update
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the insertion affected at least one row.
|
|
pub fn insert_sync_location(
|
|
conn: &Connection, loc_id: &str, book_id: &str, user_id: &str,
|
|
loc_name: &str, loc_original_name: &str, last_update: i64, lang: Lang,
|
|
) -> AppResult<bool> {
|
|
let insert_result = conn
|
|
.execute(
|
|
"INSERT INTO book_location (loc_id, book_id, user_id, loc_name, loc_original_name, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6)",
|
|
params![loc_id, book_id, user_id, loc_name, loc_original_name, last_update],
|
|
)
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer le lieu.".to_string() } else { "Unable to insert location.".to_string() }))?;
|
|
|
|
Ok(insert_result > 0)
|
|
}
|
|
|
|
/// Inserts a synced location element from the remote server.
|
|
/// * `conn` - Database connection
|
|
/// * `element_id` - The element's unique identifier
|
|
/// * `location` - The parent location's unique identifier
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `element_name` - The encrypted element name
|
|
/// * `original_name` - The original (unencrypted) element name
|
|
/// * `element_description` - The encrypted element description (can be null)
|
|
/// * `last_update` - The timestamp of the last update
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the insertion affected at least one row.
|
|
pub fn insert_sync_location_element(
|
|
conn: &Connection, element_id: &str, location: &str, user_id: &str, element_name: &str,
|
|
original_name: &str, element_description: Option<&str>, last_update: i64, lang: Lang,
|
|
) -> AppResult<bool> {
|
|
let insert_result = conn
|
|
.execute(
|
|
"INSERT INTO location_element (element_id, location, user_id, element_name, original_name, element_description, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
|
params![element_id, location, user_id, element_name, original_name, element_description, last_update],
|
|
)
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer l'élément du lieu.".to_string() } else { "Unable to insert location element.".to_string() }))?;
|
|
|
|
Ok(insert_result > 0)
|
|
}
|
|
|
|
/// Inserts a synced location sub-element from the remote server.
|
|
/// * `conn` - Database connection
|
|
/// * `sub_element_id` - The sub-element's unique identifier
|
|
/// * `element_id` - The parent element's unique identifier
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `sub_elem_name` - The encrypted sub-element name
|
|
/// * `original_name` - The original (unencrypted) sub-element name
|
|
/// * `sub_elem_description` - The encrypted sub-element description (can be null)
|
|
/// * `last_update` - The timestamp of the last update
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the insertion affected at least one row.
|
|
pub fn insert_sync_location_sub_element(
|
|
conn: &Connection, sub_element_id: &str, element_id: &str, user_id: &str, sub_elem_name: &str,
|
|
original_name: &str, sub_elem_description: Option<&str>, last_update: i64, lang: Lang,
|
|
) -> AppResult<bool> {
|
|
let insert_result = conn
|
|
.execute(
|
|
"INSERT INTO location_sub_element (sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
|
params![sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update],
|
|
)
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible d'insérer le sous-élément du lieu.".to_string() } else { "Unable to insert location sub-element.".to_string() }))?;
|
|
|
|
Ok(insert_result > 0)
|
|
}
|
|
|
|
/// Fetches complete location data by its ID (without user filtering).
|
|
/// * `conn` - Database connection
|
|
/// * `id` - The location's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns an array of book location records.
|
|
pub fn fetch_complete_location_by_id(conn: &Connection, id: &str, lang: Lang) -> AppResult<Vec<BookLocationTable>> {
|
|
let mut statement = conn
|
|
.prepare("SELECT loc_id, book_id, user_id, loc_name, loc_original_name, last_update FROM book_location WHERE loc_id = ?1")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lieu complet.".to_string() } else { "Unable to retrieve complete location.".to_string() }))?;
|
|
|
|
let complete_location = statement
|
|
.query_map(params![id], |query_row| {
|
|
Ok(BookLocationTable {
|
|
loc_id: query_row.get(0)?, book_id: query_row.get(1)?,
|
|
user_id: query_row.get(2)?, loc_name: query_row.get(3)?,
|
|
loc_original_name: query_row.get(4)?, last_update: query_row.get(5)?,
|
|
})
|
|
})
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lieu complet.".to_string() } else { "Unable to retrieve complete location.".to_string() }))?
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le lieu complet.".to_string() } else { "Unable to retrieve complete location.".to_string() }))?;
|
|
|
|
Ok(complete_location)
|
|
}
|
|
|
|
/// Fetches complete location element data by its ID (without user filtering).
|
|
/// * `conn` - Database connection
|
|
/// * `id` - The element's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns an array of location element records.
|
|
pub fn fetch_complete_location_element_by_id(conn: &Connection, id: &str, lang: Lang) -> AppResult<Vec<LocationElementTable>> {
|
|
let mut statement = conn
|
|
.prepare("SELECT element_id, location, user_id, element_name, original_name, element_description, last_update FROM location_element WHERE element_id = ?1")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer l'élément de lieu complet.".to_string() } else { "Unable to retrieve complete location element.".to_string() }))?;
|
|
|
|
let complete_location_element = statement
|
|
.query_map(params![id], |query_row| {
|
|
Ok(LocationElementTable {
|
|
element_id: query_row.get(0)?, location: query_row.get(1)?,
|
|
user_id: query_row.get(2)?, element_name: query_row.get(3)?,
|
|
original_name: query_row.get(4)?, element_description: query_row.get(5)?,
|
|
last_update: query_row.get(6)?,
|
|
})
|
|
})
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer l'élément de lieu complet.".to_string() } else { "Unable to retrieve complete location element.".to_string() }))?
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer l'élément de lieu complet.".to_string() } else { "Unable to retrieve complete location element.".to_string() }))?;
|
|
|
|
Ok(complete_location_element)
|
|
}
|
|
|
|
/// Fetches complete location sub-element data by its ID (without user filtering).
|
|
/// * `conn` - Database connection
|
|
/// * `id` - The sub-element's unique identifier
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns an array of location sub-element records.
|
|
pub fn fetch_complete_location_sub_element_by_id(conn: &Connection, id: &str, lang: Lang) -> AppResult<Vec<LocationSubElementTable>> {
|
|
let mut statement = conn
|
|
.prepare("SELECT sub_element_id, element_id, user_id, sub_elem_name, original_name, sub_elem_description, last_update FROM location_sub_element WHERE sub_element_id = ?1")
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le sous-élément de lieu complet.".to_string() } else { "Unable to retrieve complete location sub-element.".to_string() }))?;
|
|
|
|
let complete_location_sub_element = statement
|
|
.query_map(params![id], |query_row| {
|
|
Ok(LocationSubElementTable {
|
|
sub_element_id: query_row.get(0)?, element_id: query_row.get(1)?,
|
|
user_id: query_row.get(2)?, sub_elem_name: query_row.get(3)?,
|
|
original_name: query_row.get(4)?, sub_elem_description: query_row.get(5)?,
|
|
last_update: query_row.get(6)?,
|
|
})
|
|
})
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le sous-élément de lieu complet.".to_string() } else { "Unable to retrieve complete location sub-element.".to_string() }))?
|
|
.collect::<Result<Vec<_>, _>>()
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de récupérer le sous-élément de lieu complet.".to_string() } else { "Unable to retrieve complete location sub-element.".to_string() }))?;
|
|
|
|
Ok(complete_location_sub_element)
|
|
}
|
|
|
|
/// Updates a location section with optional name change and series link.
|
|
/// * `conn` - Database connection
|
|
/// * `user_id` - The user's unique identifier
|
|
/// * `section_id` - The section's unique identifier
|
|
/// * `encrypted_name` - The new encrypted name (optional)
|
|
/// * `original_name` - The new original name (optional)
|
|
/// * `series_location_id` - The series location ID to link (optional, None to unlink)
|
|
/// * `last_update` - The timestamp of the last update
|
|
/// * `lang` - The language for error messages ("fr" or "en")
|
|
/// Returns true if the update was successful.
|
|
pub fn update_section_with_series_link(
|
|
conn: &Connection, user_id: &str, section_id: &str, encrypted_name: Option<&str>,
|
|
original_name: Option<&str>, series_location_id: Option<&str>, last_update: i64, lang: Lang,
|
|
) -> AppResult<bool> {
|
|
let mut set_clauses: Vec<String> = vec![format!("last_update={}", last_update)];
|
|
let mut param_values: Vec<Box<dyn rusqlite::types::ToSql>> = Vec::new();
|
|
|
|
if let (Some(enc_name), Some(orig_name)) = (encrypted_name, original_name) {
|
|
set_clauses.push("loc_name=?".to_string());
|
|
set_clauses.push("loc_original_name=?".to_string());
|
|
param_values.push(Box::new(enc_name.to_string()));
|
|
param_values.push(Box::new(orig_name.to_string()));
|
|
}
|
|
|
|
if let Some(series_id) = series_location_id {
|
|
set_clauses.push("series_location_id=?".to_string());
|
|
param_values.push(Box::new(series_id.to_string()));
|
|
}
|
|
|
|
param_values.push(Box::new(section_id.to_string()));
|
|
param_values.push(Box::new(user_id.to_string()));
|
|
|
|
let query = format!("UPDATE book_location SET {} WHERE loc_id=? AND user_id=?", set_clauses.join(", "));
|
|
let param_refs: Vec<&dyn rusqlite::types::ToSql> = param_values.iter().map(|parameter| parameter.as_ref() as &dyn rusqlite::types::ToSql).collect();
|
|
|
|
let update_result = conn
|
|
.execute(&query, param_refs.as_slice())
|
|
.map_err(|_| AppError::Internal(if lang == Lang::Fr { "Impossible de mettre à jour la section d'emplacement.".to_string() } else { "Unable to update location section.".to_string() }))?;
|
|
|
|
Ok(update_result > 0)
|
|
}
|