- Added sharing feature for table owners to share their tables with other registered users. - Fixed a bug where the wrong entries would be deleted or modified when searching or filtering.
582 lines
19 KiB
Rust
582 lines
19 KiB
Rust
/* Simple web app using rocket to help maintain inventory data.
|
|
* Copyright (C) 2024 Johannes Randerath
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/
|
|
*/
|
|
|
|
//! Library providing a spreadsheet-like table system.
|
|
//! Adapter to a Mysql DB.
|
|
//! This file exposes public methods and datastructures to interface the system.
|
|
|
|
mod models;
|
|
mod schema;
|
|
mod jrtables;
|
|
mod jrcolumns;
|
|
mod jrentries;
|
|
mod jrcells;
|
|
mod users;
|
|
mod shares;
|
|
|
|
use dotenvy::dotenv;
|
|
use std::env;
|
|
use diesel::mysql::MysqlConnection;
|
|
use diesel::prelude::*;
|
|
use std::hash::{Hash, DefaultHasher};
|
|
use std::collections::HashSet;
|
|
use diesel::deserialize::FromSql;
|
|
use diesel::deserialize;
|
|
use diesel::backend::Backend;
|
|
use diesel::sql_types::Integer;
|
|
use diesel::expression::Expression;
|
|
use models::Share;
|
|
|
|
/// Every column has a type.
|
|
/// The default is text, currently number (isize) exists too.
|
|
/// Types are mainly used for sorting and can be helpful in client applications.
|
|
#[derive(PartialEq, Clone, Copy, diesel::FromSqlRow, Debug)]
|
|
#[repr(i32)]
|
|
pub enum FIELDTYPE {
|
|
TEXT = 0,
|
|
NUMBER = 1,
|
|
}
|
|
|
|
impl Expression for FIELDTYPE {
|
|
type SqlType = Integer;
|
|
}
|
|
|
|
impl<DB> FromSql<Integer, DB> for FIELDTYPE
|
|
where DB: Backend, i32: FromSql<Integer,DB> {
|
|
fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
|
|
match i32::from_sql(bytes)? {
|
|
0 => Ok(FIELDTYPE::TEXT),
|
|
1 => Ok(FIELDTYPE::NUMBER),
|
|
x => Err(format!("Unknown field type {}.", x).into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<isize> for FIELDTYPE {
|
|
fn from(value: isize) -> Self {
|
|
match value {
|
|
1 => FIELDTYPE::NUMBER,
|
|
_ => FIELDTYPE::TEXT,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// represents and summarised all relevant data of a table.
|
|
/// Standard return type if whole tables should be returned
|
|
#[derive(Clone, Debug)]
|
|
pub struct Tbl {
|
|
/// table id of the represented object
|
|
pub tblid: i32,
|
|
pub name: String,
|
|
pub column_names: Vec<String>,
|
|
pub column_types: Vec<FIELDTYPE>,
|
|
pub rows: Vec<TRow>,
|
|
}
|
|
|
|
/// Represents a table's row.
|
|
/// Internal data structure is abstracted until strings.
|
|
#[derive(Hash, Clone, Debug)]
|
|
pub struct TRow {
|
|
pub row_pos: i32,
|
|
pub cells: Vec<String>,
|
|
}
|
|
|
|
impl PartialEq for TRow {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
let mut hasher = DefaultHasher::new();
|
|
self.hash(&mut hasher) == other.hash(&mut hasher)
|
|
}
|
|
}
|
|
impl Eq for TRow {}
|
|
|
|
/// Identifies a user
|
|
/// All fields are unique within the db at any given point.
|
|
pub struct User {
|
|
/// User's unique id.
|
|
pub uid: i32,
|
|
pub uname: String,
|
|
pub email: String,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct ShareCard {
|
|
pub tblid: i32,
|
|
pub sharee_id: i32,
|
|
pub sharee_name: String,
|
|
pub readonly: bool,
|
|
}
|
|
|
|
/// Connect to database.
|
|
/// Return value must be passed to most of the crate's functions.
|
|
pub fn establish_connection() -> MysqlConnection {
|
|
dotenv().ok();
|
|
let db_url = env::var("DATABASE_URL").expect("No database url set.");
|
|
MysqlConnection::establish(&db_url)
|
|
.unwrap_or_else(|_| panic!("Couldn't open database."))
|
|
}
|
|
|
|
/// Fetch one whole table from the database. Identified by it's ID.
|
|
/// Needs the id of the requesting user.
|
|
/// Returns:
|
|
/// A Tbl struct
|
|
/// Or:
|
|
/// None
|
|
/// if the table was not found or
|
|
/// if it could not be read or
|
|
/// if the requesting user is not the table's owner or
|
|
/// if there has been another error
|
|
///
|
|
pub fn get_table(conn: &mut MysqlConnection, tblid: i32, uid: i32) -> Option<Tbl> {
|
|
let tbl = fetch_table(conn, tblid);
|
|
if tbl.is_none() ||
|
|
tbl.clone().unwrap().1 != uid
|
|
{
|
|
return None;
|
|
}
|
|
Some(tbl.unwrap().0)
|
|
}
|
|
|
|
fn fetch_table(conn: &mut MysqlConnection, tblid: i32) -> Option<(Tbl, i32)>{
|
|
let tbl = jrtables::get_tbl(conn, tblid);
|
|
if tbl.is_err() {
|
|
return None;
|
|
}
|
|
let tbl = tbl.unwrap();
|
|
let clmns = jrcolumns::get_clmns_of(conn, tblid);
|
|
if clmns.is_err() {
|
|
return None;
|
|
}
|
|
let clmns = clmns.unwrap();
|
|
let mut clmn_nms = Vec::new();
|
|
for clmn in &clmns {
|
|
clmn_nms.push(clmn.name.clone());
|
|
}
|
|
let clmn_nms = clmn_nms;
|
|
let mut clmn_tps = Vec::new();
|
|
for clmn in &clmns {
|
|
clmn_tps.push(clmn.column_type);
|
|
}
|
|
let clmn_tps = clmn_tps;
|
|
let rowids = jrentries::get_entries_of(conn, tblid);
|
|
if rowids.is_err() {
|
|
return None;
|
|
}
|
|
let rowids = rowids.unwrap();
|
|
let mut rows = Vec::new();
|
|
for rowid in rowids {
|
|
let row = jrcells::get_entry_cells(conn, rowid.id);
|
|
if row.is_err() {
|
|
return None;
|
|
}
|
|
let row = row.unwrap();
|
|
let mut data = Vec::new();
|
|
for cell in row {
|
|
data.push(cell.cell_value);
|
|
}
|
|
let data = data;
|
|
rows.push(TRow { row_pos: rowid.row_pos, cells: data });
|
|
}
|
|
Some((Tbl { tblid: tbl.id, name: tbl.name, column_names: clmn_nms, column_types: clmn_tps,rows: rows }, tbl.owner_id))
|
|
}
|
|
|
|
pub fn get_table_shared(conn: &mut MysqlConnection, tblid: i32, uid: i32) -> Option<Tbl> {
|
|
let tbl = fetch_table(conn, tblid);
|
|
let share = shares::is_shared(conn, tblid, uid);
|
|
if !share {
|
|
return None;
|
|
}
|
|
let tbl = tbl;
|
|
if tbl.is_none() {
|
|
return None;
|
|
}
|
|
Some(tbl.unwrap().0)
|
|
}
|
|
|
|
/// Take a Tbl object, sort it according to parameters and return a sorted Tbl object.
|
|
/// sort_field is the index of the column to sort by.
|
|
/// 0 is the position of the row in the table.
|
|
/// then 1-indexed column index
|
|
pub fn sort_table(tbl: Tbl, sort_field: usize, sort_dir: u8) -> Tbl {
|
|
let mut rows = tbl.rows;
|
|
if sort_field == 0 {
|
|
rows.sort_unstable_by_key(|a: &TRow| a.row_pos);
|
|
if sort_dir == 1 {
|
|
rows.reverse();
|
|
}
|
|
}else {
|
|
if tbl.column_types[sort_field-1] == FIELDTYPE::NUMBER {
|
|
rows.sort_by_key(|a: &TRow| a.cells[sort_field - 1].trim().parse::<i32>().expect("Number not a number."));
|
|
}else {
|
|
rows.sort_by_key(|a: &TRow| a.cells[sort_field - 1].clone());
|
|
}
|
|
if sort_dir == 1 {
|
|
rows.reverse();
|
|
}
|
|
}
|
|
let rows = rows;
|
|
Tbl { tblid: tbl.tblid, name: tbl.name, column_names: tbl.column_names, column_types: tbl.column_types, rows: rows.clone() }
|
|
}
|
|
|
|
/// Take a Tbl object, find all rows matching the search queries and return a new Tbl object containing only those.
|
|
/// search_fields contains a Vec of (1-indexed) column positions to be included in the search
|
|
/// search_value is a string to search for in the search_fields.
|
|
/// Returned Tbl is not sorted and should be according to user preferences before being displayed.
|
|
pub fn search_table(tbl: Tbl, search_fields: Vec<i32>, search_value: String) -> Tbl {
|
|
let rows = tbl.rows;
|
|
let mut field_sets = HashSet::new();
|
|
for field in search_fields {
|
|
for row in &rows {
|
|
if row.cells[field as usize].to_lowercase().contains(&search_value.to_lowercase()) {
|
|
field_sets.insert(row.clone());
|
|
}
|
|
}
|
|
}
|
|
Tbl { tblid: tbl.tblid, name: tbl.name, column_names: tbl.column_names, column_types: tbl.column_types, rows: Vec::from_iter(field_sets) }
|
|
}
|
|
|
|
/// For a Vec a database ids of tables, return a Vec of their names in the same order.
|
|
pub fn get_tblnames(conn: &mut MysqlConnection, tblids: Vec<i32>) -> Option<Vec<String>> {
|
|
let mut tblnames = Vec::new();
|
|
for tblid in tblids {
|
|
let tblname = jrtables::get_tbl(conn, tblid);
|
|
if tblname.is_err() {
|
|
return None;
|
|
}
|
|
tblnames.push(tblname.unwrap().name);
|
|
}
|
|
Some(tblnames)
|
|
}
|
|
|
|
/// Returns a Vec of the ids of a user's table if the user exists, else None.
|
|
/// User is identified by its user ID.
|
|
pub fn get_user_tblids(conn: &mut MysqlConnection, uid: i32) -> Option<Vec<i32>> {
|
|
let tblids = jrtables::get_tblids_uid(conn, uid);
|
|
if tblids.is_err() {
|
|
return None;
|
|
}
|
|
Some(tblids.unwrap())
|
|
}
|
|
|
|
/// Change the name of a table.
|
|
/// Needs the user ID of the requesting user.
|
|
/// Returns Some(true) if the action seems to have been successful and the requesting user is the
|
|
/// table's owner.
|
|
/// None otherwise.
|
|
pub fn rename_table(conn: &mut MysqlConnection, tblid: i32, new_name: String, uid: i32) -> Option<bool> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
if owner.is_err() || owner.unwrap() != uid {
|
|
return None;
|
|
}
|
|
let rnm = jrtables::rename_jrtable(conn, tblid, &new_name);
|
|
if rnm.is_err() {
|
|
return None;
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
|
|
/// Delete a table.
|
|
/// Needs the user ID of the requesting user.
|
|
/// Returns Some(true) if the action seems to have been successful and the requesting user is the
|
|
/// table's owner.
|
|
/// None otherwise.
|
|
pub fn delete_table(conn: &mut MysqlConnection, tblid: i32, uid: i32) -> Option<bool> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
if owner.is_err() || owner.unwrap() != uid {
|
|
return None;
|
|
}
|
|
let dlt = jrtables::delete_jrtable(conn, tblid);
|
|
if dlt.is_err() {
|
|
return None;
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
|
|
/// Create a new table.
|
|
/// Needs the user ID of the requesting user.
|
|
/// Returns the id of the newly created table if the action seems to have been successful and the requesting user is the
|
|
/// table's owner.
|
|
/// None otherwise.
|
|
pub fn create_table(conn: &mut MysqlConnection, name: String, field_names: Vec<String>, field_types: Vec<crate::FIELDTYPE>, uid: i32) -> Option<i32> {
|
|
if jrtables::get_tbl_by_name_uid(conn, &name, uid).is_ok() {
|
|
return None;
|
|
}
|
|
if jrtables::create_jrtable(conn, &name, field_names, field_types, uid).is_err() {
|
|
return None;
|
|
}
|
|
let tblid = jrtables::get_tbl_by_name_uid(conn, &name, uid);
|
|
if tblid.is_err() {
|
|
return None;
|
|
}
|
|
Some(tblid.unwrap())
|
|
}
|
|
|
|
/// Returns a User struct of a requested user if the combination of username and email are
|
|
/// consistent or both do not exist yet, in which case the user is newly created.
|
|
pub fn register_or_login(conn: &mut MysqlConnection, uname: String, mail: String) -> Option<User> {
|
|
let mut uid = users::get_uid_email(conn, &mail);
|
|
if uid.is_err() {
|
|
if users::get_uid_uname(conn, &uname).is_ok() || users::create_user(conn, &uname, &mail).is_err() {
|
|
return None;
|
|
}
|
|
uid = users::get_uid_email(conn, &mail);
|
|
if uid.is_err() {
|
|
return None;
|
|
}
|
|
}
|
|
let uid = uid.unwrap();
|
|
let nm = users::get_uname(conn, uid);
|
|
if nm.is_err() || nm.unwrap() != uname {
|
|
return None;
|
|
}
|
|
Some(User { uid: uid, uname: uname, email: mail })
|
|
}
|
|
|
|
/// Take a user id (database representation, not username) and delete it from the database.
|
|
/// All its tables are cascadingly deleted.
|
|
/// Return Some(true) if successful, None otherwise
|
|
pub fn delete_user(conn: &mut MysqlConnection, uid: i32) -> Option<bool> {
|
|
if users::delete_user(conn, uid).is_err() {
|
|
return None;
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
/// For a given id of a table and a vec of string values, create a new jrentry (table row) and all its jrcells (table cells) filled with the string values.
|
|
/// Only works if uid corresponds to the user id of the table's owner.
|
|
/// Return the id of the newly created row if successful, None otherwise.
|
|
pub fn add_row(conn: &mut MysqlConnection, tblid: i32, values: Vec<String>, uid: i32) -> Option<i32> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
let nrows = jrtables::get_nrows(conn, tblid);
|
|
if nrows.is_err() || owner.is_err() || owner.unwrap() != uid {
|
|
return None;
|
|
}
|
|
let nrows = nrows.unwrap() as i32;
|
|
if jrentries::create_jrentry(conn, tblid).is_err() {
|
|
return None;
|
|
}
|
|
let entryid = jrentries::get_jrentry_id(conn, tblid, nrows+1);
|
|
if entryid.is_err() {
|
|
return None;
|
|
}
|
|
let entryid = entryid.unwrap();
|
|
let cols = jrcolumns::get_clmns_of(conn, tblid);
|
|
if cols.is_err() {
|
|
return None;
|
|
}
|
|
let cols = cols.unwrap();
|
|
for (i,col) in cols.iter().enumerate() {
|
|
if jrcells::create_jrcell(conn, entryid, col.id, &values[i]).is_err() {
|
|
return None;
|
|
}
|
|
}
|
|
Some(entryid)
|
|
}
|
|
|
|
/// Delete a jrentry (table row) identified by the id of its table and the current position wihthin it
|
|
/// Return Some(true) if successful, None otherwise
|
|
pub fn delete_row(conn: &mut MysqlConnection, tblid: i32, row_pos: i32, uid: i32) -> Option<bool> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
if owner.is_err() || owner.unwrap() != uid || jrentries::delete_jrentry_relative(conn, tblid, row_pos).is_err() {
|
|
return None;
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
/// Change a jrentry's (table row's) current position within its table, given the table's id, the current position and the new position it should be moved to.
|
|
/// Only works if uid equals to the owner's user id.
|
|
/// Returns Some(true) if successful, None otherwise.
|
|
pub fn move_row(conn: &mut MysqlConnection, tblid: i32, rowpos: i32, newpos: i32, uid: i32) -> Option<bool> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
if owner.is_err() || owner.unwrap() != uid || jrentries::move_jrentry(conn, tblid, rowpos, newpos).is_err() {
|
|
return None;
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
/// Update a jrentry (table row) with a new set of string values, given those new values and the row identified by its table and its position within the table.
|
|
/// Only works if uid equals to the table's owner's user id.
|
|
/// Returns Some(true) if successful, None otherwise.
|
|
pub fn edit_row(conn: &mut MysqlConnection, tblid: i32, cells: Vec<String>, row_pos: i32, uid: i32) -> Option<bool> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
if owner.is_err() || owner.unwrap() != uid {
|
|
return None;
|
|
}
|
|
for (i, cell) in cells.iter().enumerate() {
|
|
if jrcells::change_jrcell_value_relative(conn, tblid, row_pos, (i+1) as i32, cell).is_err() {
|
|
return None;
|
|
}
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
/// Edit the content of a single jrcell (table cell), given its table id, the position of its row and column within the table and the new value.
|
|
/// Only works if uid equals to the table's owner's user id.
|
|
/// Returns Some(true) if successful, None otherwise.
|
|
pub fn edit_cell(conn: &mut MysqlConnection, tblid: i32, row_pos: i32, column_pos: i32, new_value: &String, uid: i32) -> Option<bool> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
if owner.is_err() ||
|
|
owner.unwrap() != uid ||
|
|
jrcells::change_jrcell_value_relative(conn, tblid, row_pos, column_pos, new_value).is_err()
|
|
{
|
|
return None;
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
/// Change a jrcolumn's (table column's) current position within its table, given the table's id, the current position and the new position it should be moved to.
|
|
/// Only works if uid equals to the owner's user id.
|
|
/// Returns Some(true) if successful, None otherwise.
|
|
pub fn move_column(conn: &mut MysqlConnection, tblid:i32, column_pos: i32, new_column_pos: i32, uid: i32) -> Option<bool> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
if owner.is_err() ||
|
|
owner.unwrap() != uid ||
|
|
jrcolumns::move_column_relative(conn, tblid, column_pos, new_column_pos).is_err()
|
|
{
|
|
return None;
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
/// Add a new jrcolumn (table column) to an existing table.
|
|
/// Required are the table's id, the column's name, the column's FIELDTYPE and the id of the logged in user.
|
|
/// Only works if uid equals to the table's owner's user id.
|
|
/// Returns the id of the newly created column if successful, None otherwise.
|
|
pub fn add_column(conn: &mut MysqlConnection, tblid: i32, name: String, clmtype: FIELDTYPE, uid: i32) -> Option<i32> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
if owner.is_err() ||
|
|
owner.unwrap() != uid ||
|
|
jrcolumns::create_jrcolumn(conn, tblid, name, clmtype).is_err()
|
|
{
|
|
return None;
|
|
}
|
|
let clmid = jrcolumns::get_last_column_id_of(conn, tblid);
|
|
if clmid.is_err() {
|
|
return None;
|
|
}
|
|
Some(clmid.unwrap())
|
|
}
|
|
|
|
/// Delete a column identified by its table's id and its position within the table.
|
|
/// Only works if uid equals to the table's owner's id.
|
|
/// Returns Some(true) if successful, None otherwise.
|
|
pub fn delete_column(conn: &mut MysqlConnection, tblid: i32, column_pos: i32, uid: i32) -> Option<bool> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
if owner.is_err() ||
|
|
owner.unwrap() != uid ||
|
|
jrcolumns::delete_column_relative(conn, tblid, column_pos).is_err()
|
|
{
|
|
return None;
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
/// Update a jrcolumn (table column) with a given new name, and new type. The column is identified by its table's id and its position within it.
|
|
/// Only works if uid equals to the table's owner's id.
|
|
/// Returns Some(true) if successful, None otherwise.
|
|
pub fn edit_column(conn: &mut MysqlConnection, tblid: i32, column_pos: i32, new_name: &String, new_type: FIELDTYPE, uid: i32) -> Option<bool> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
if owner.is_err() ||
|
|
owner.unwrap() != uid ||
|
|
jrcolumns::edit_column_relative(conn, tblid, column_pos, new_name, new_type).is_err()
|
|
{
|
|
return None;
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
|
|
pub fn share(conn: &mut MysqlConnection, tblid: i32, sharee_id: &i32, readonly: &bool, uid: i32) -> Option<bool> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
println!(":)");
|
|
if owner.is_err() ||
|
|
owner.unwrap() != uid ||
|
|
shares::edit_or_create(conn, tblid, sharee_id, readonly).is_err()
|
|
{
|
|
return None;
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
pub fn unshare(conn: &mut MysqlConnection, tblid: i32, sharee_id: &i32, uid: i32) -> Option<bool> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
if owner.is_err() ||
|
|
owner.unwrap() != uid ||
|
|
shares::delete(conn, tblid, sharee_id).is_err()
|
|
{
|
|
return None;
|
|
}
|
|
Some(true)
|
|
}
|
|
|
|
pub fn get_sharees(conn: &mut MysqlConnection, tblid: i32, uid: i32) -> Option<Vec<ShareCard>> {
|
|
let owner = jrtables::get_owner_id(conn, tblid);
|
|
if owner.is_err() ||
|
|
owner.unwrap() != uid
|
|
{
|
|
return None;
|
|
}
|
|
let result = shares::get_shares_tbl(conn, tblid);
|
|
if result.is_err() {
|
|
return None;
|
|
}
|
|
let result = result.unwrap();
|
|
let mut sharecards = Vec::new();
|
|
for share in result {
|
|
let uname = users::get_uname(conn, uid);
|
|
if uname.is_err() {
|
|
continue;
|
|
}
|
|
sharecards.push(ShareCard { tblid: tblid, sharee_id: share.sharee_id, sharee_name: uname.unwrap(), readonly: share.readonly });
|
|
}
|
|
return Some(sharecards)
|
|
}
|
|
|
|
pub fn get_shared_tbls(conn: &mut MysqlConnection, uid: i32) -> Option<Vec<ShareCard>> {
|
|
let result = shares::get_shares_usr(conn, uid);
|
|
if result.is_err() {
|
|
return None;
|
|
}
|
|
let result = result.unwrap();
|
|
let uname = users::get_uname(conn, uid);
|
|
if uname.is_err() {
|
|
return None;
|
|
}
|
|
let uname = uname.unwrap();
|
|
let mut sharecards = Vec::new();
|
|
for share in result {
|
|
sharecards.push(ShareCard { tblid: share.tblid, sharee_id: uid, sharee_name: uname.clone(), readonly: share.readonly});
|
|
}
|
|
Some(sharecards)
|
|
}
|
|
|
|
pub fn query_users(conn: &mut MysqlConnection, query: String, uid: i32) -> Option<Vec<User>> {
|
|
let result = users::query_users(conn, query);
|
|
if users::get_uname(conn, uid).is_err() ||
|
|
result.is_err()
|
|
{
|
|
return None;
|
|
}
|
|
let result = result.unwrap();
|
|
let mut pubusers: Vec<User> = Vec::new();
|
|
for user in result {
|
|
pubusers.push( User { uid: user.id, uname: user.username, email: user.email } );
|
|
}
|
|
Some(pubusers)
|
|
}
|