mirror of
https://gitlab.com/comunic/comunicapiv3
synced 2024-11-26 07:19:22 +00:00
Create query method
This commit is contained in:
parent
ab5df69ee5
commit
a67f988e1b
28
src/data/error.rs
Normal file
28
src/data/error.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use core::fmt;
|
||||
use serde::export::Formatter;
|
||||
use std::error;
|
||||
|
||||
/// Simple rust error
|
||||
///
|
||||
/// @author Pierre Hubert
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExecError(pub String);
|
||||
|
||||
impl ExecError {
|
||||
pub fn new(msg: &str) -> ExecError {
|
||||
ExecError(msg.to_string())
|
||||
}
|
||||
|
||||
pub fn boxed_new(msg: &str) -> Box<ExecError> {
|
||||
Box::new(ExecError(msg.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ExecError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Encountered error: {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for ExecError {}
|
@ -1,3 +1,5 @@
|
||||
pub mod error;
|
||||
pub mod config;
|
||||
|
||||
pub mod http_error;
|
||||
pub mod http_request_handler;
|
@ -1,14 +1,20 @@
|
||||
use std::collections;
|
||||
use std::error::Error;
|
||||
use std::ops::Add;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use mysql::Pool;
|
||||
use mysql::{Binary, Pool, ResultSet};
|
||||
use mysql::prelude::Queryable;
|
||||
|
||||
use crate::data::config::DatabaseConfig;
|
||||
use crate::data::error::ExecError;
|
||||
|
||||
/// Database access helper
|
||||
///
|
||||
/// @author Pierre Hubert
|
||||
|
||||
pub type ProcessRowResult<E> = Result<E, Box<dyn Error>>;
|
||||
|
||||
// Pool shared across threads
|
||||
static mut POOL: Option<Arc<Mutex<mysql::Pool>>> = None;
|
||||
|
||||
@ -40,3 +46,155 @@ pub fn get_connection() -> Result<mysql::PooledConn, Box<dyn Error>> {
|
||||
|
||||
Ok(pool.get_conn()?)
|
||||
}
|
||||
|
||||
|
||||
pub struct QueryInfo {
|
||||
/// Fetched table
|
||||
pub table: String,
|
||||
|
||||
/// Query limits
|
||||
pub conditions: collections::HashMap<String, String>,
|
||||
|
||||
/// Limit of the query (0 = no limit)
|
||||
pub limit: u64,
|
||||
|
||||
/// Queried arguments
|
||||
pub fields: Vec<String>,
|
||||
}
|
||||
|
||||
impl QueryInfo {
|
||||
/// Initialize a new query on the database
|
||||
pub fn new(table: &str) -> QueryInfo {
|
||||
QueryInfo {
|
||||
table: table.to_string(),
|
||||
conditions: collections::HashMap::new(),
|
||||
limit: 0,
|
||||
fields: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cond(&mut self, key: &str, val: &str) {
|
||||
self.conditions.insert(key.to_string(), val.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
/// Struct used to read the result of a request
|
||||
pub struct RowResult<'a> {
|
||||
row: &'a mysql::Row
|
||||
}
|
||||
|
||||
impl<'a> RowResult<'a> {
|
||||
pub fn new(row: &mysql::Row) -> RowResult {
|
||||
RowResult {
|
||||
row
|
||||
}
|
||||
}
|
||||
|
||||
/// Find a column in result set
|
||||
fn find_col(&self, name: &str) -> Result<usize, ExecError> {
|
||||
let name_bytes = name.as_bytes();
|
||||
let mut index = 0;
|
||||
for c in self.row.columns_ref() {
|
||||
if c.name_ref().eq(name_bytes) {
|
||||
return Ok(index);
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
Err(ExecError(format!("Column {} not found in database result set!", name)))
|
||||
}
|
||||
|
||||
/// Find an integer included in the request
|
||||
pub fn get_int64(&self, name: &str) -> Result<i64, ExecError> {
|
||||
let value: Option<i64> = self.row.get(self.find_col(name)?);
|
||||
|
||||
match value {
|
||||
None => Err(ExecError(format!("Could not extract integer field {} !", name))),
|
||||
Some(s) => Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Find a string included in the request
|
||||
pub fn get_str(&self, name: &str) -> Result<String, ExecError> {
|
||||
let value: Option<String> = self.row.get(self.find_col(name)?);
|
||||
|
||||
match value {
|
||||
None => Err(ExecError(format!("Could not extract string field {} !", name))),
|
||||
Some(s) => Ok(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Query a single row of the database
|
||||
pub fn query_row<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(mut info: QueryInfo,
|
||||
process_function: F) -> Result<E, Box<dyn Error>> {
|
||||
info.limit = 1;
|
||||
let mut list = query(info, process_function)?;
|
||||
|
||||
match list.len() {
|
||||
0 => Err(ExecError::boxed_new("Request did not return a result!")),
|
||||
_ => Ok(list.remove(0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Make a simple query
|
||||
pub fn query<E, F: Fn(&RowResult) -> ProcessRowResult<E>>(info: QueryInfo, process_function: F)
|
||||
-> Result<Vec<E>, Box<dyn Error>> {
|
||||
let mut params = vec![];
|
||||
|
||||
let select_elements = match info.fields.len() {
|
||||
0 => "*".to_string(),
|
||||
_ => info.fields.join(", ")
|
||||
};
|
||||
|
||||
// Build query
|
||||
let mut query = format!("SELECT {} FROM {} ", select_elements, info.table);
|
||||
|
||||
|
||||
// WHERE clause
|
||||
if !info.conditions.is_empty() {
|
||||
let mut where_args = vec![];
|
||||
|
||||
for (k, v) in info.conditions {
|
||||
where_args.push(format!("{} = ?", k));
|
||||
params.push(v)
|
||||
}
|
||||
|
||||
let where_args = format!(" WHERE {} ", where_args.join(" AND "));
|
||||
query = query.add(&where_args);
|
||||
}
|
||||
|
||||
// LIMIT clause
|
||||
if info.limit > 0 {
|
||||
query = query.add(&format!(" LIMIT {}", info.limit));
|
||||
}
|
||||
|
||||
// Execute query
|
||||
let mut con = get_connection()?;
|
||||
let stmt = con.prep(&query)?;
|
||||
|
||||
let mut res = con.exec_iter(stmt, params)?;
|
||||
|
||||
// This system is made to support only one dataset
|
||||
let result_set = res.next_set();
|
||||
if let None = result_set {
|
||||
return Err(Box::new(ExecError::new("No result set in a query!")));
|
||||
}
|
||||
let result_set: ResultSet<Binary> = result_set.unwrap()?;
|
||||
|
||||
|
||||
// Parse each result of the dataset
|
||||
let mut res: Vec<E> = vec![];
|
||||
for row in result_set {
|
||||
let row = row?;
|
||||
let result = RowResult::new(&row);
|
||||
|
||||
let parsed = process_function(&result)?;
|
||||
res.push(parsed);
|
||||
}
|
||||
|
||||
|
||||
Ok(res)
|
||||
}
|
17
src/main.rs
17
src/main.rs
@ -1,6 +1,13 @@
|
||||
use comunic_server::data::config::{conf, Config};
|
||||
use comunic_server::helpers::database;
|
||||
use comunic_server::controllers::server;
|
||||
use comunic_server::helpers::database::QueryInfo;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct User {
|
||||
id : i64,
|
||||
name: String,
|
||||
}
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
@ -11,6 +18,16 @@ async fn main() -> std::io::Result<()> {
|
||||
// Connect to the database
|
||||
database::connect(&conf().database).expect("Could not connect to database!");
|
||||
|
||||
let mut query = QueryInfo::new("user");
|
||||
query.cond("age", "190");
|
||||
//query.cond("id", "1");
|
||||
let res = database::query_row(query, |res| Ok(User {
|
||||
id: res.get_int64("id")?,
|
||||
name: res.get_str("name")?,
|
||||
})).unwrap();
|
||||
|
||||
println!("{:#?}", res);
|
||||
|
||||
// Start the server
|
||||
server::start_server(conf()).await
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user