1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use crate::manager::pool::{Pool, PoolError};
use crate::sql::parser::{Parser, ParserError};
use crate::storage::diskinterface::{DiskError, DiskInterface};
use crate::Response;
use std::fmt;

use std::sync::{Arc, Mutex};

#[derive(Debug)]
pub struct Request {
    pub username: String,
    pub addr: String,
    pub key: i32,
}

#[derive(Debug)]
pub enum RequestError {
    PoolError(PoolError),
    CauseByParser(ParserError),
    DiskError(DiskError),
    UserNotExist(String),
    CreateDBBeforeCmd,
    BadRequest,
    InvalidKey,
}

impl fmt::Display for RequestError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            RequestError::PoolError(ref e) => write!(f, "error caused by pool: {}", e),
            RequestError::CauseByParser(ref e) => write!(f, "error caused by parser: {}", e),
            RequestError::DiskError(ref e) => write!(f, "error caused by file: {}", e),
            RequestError::UserNotExist(ref s) => write!(f, "user: {} not found", s),
            RequestError::CreateDBBeforeCmd => write!(f, "please create a database before any other commands"),
            RequestError::BadRequest => write!(f, "BadRequest, invalid request format"),
            RequestError::InvalidKey => write!(f, "invalid key format"),
        }
    }
}

impl Request {
    pub fn new(new_addr: String) -> Request {
        Request {
            username: "".to_string(),
            addr: new_addr,
            key: 0,
        }
    }
    pub fn parse(input: &str, mutex: &Arc<Mutex<Pool>>, req: &mut Request) -> Result<Response, RequestError> {
        /*
         * request format
         * case0: init (must be first request in each connection)
         * username||||||key
         * case1: normal
         * username||dbname||command;
         * case2: user without a database
         * username||||create database dbname;
         *
         */
        let split_str: Vec<&str> = input.split("||").collect();

        // first connection
        if req.username == "" {
            if split_str.len() != 4 || split_str[1] != "" || split_str[2] != "" {
                return Err(RequestError::BadRequest);
            }
            let username = split_str[0];

            // type check, save key
            let key: i32 = match split_str[3].parse() {
                Ok(_key) => _key,
                Err(_) => return Err(RequestError::InvalidKey),
            };
            req.key = key;

            // initialize username
            match Request::user_verify(username) {
                Ok(()) => req.username = username.to_string(),
                Err(ret) => return Err(ret),
            }
            return Ok(Response::OK {
                msg: "Login OK!".to_string(),
            });
        }

        if split_str.len() != 3 {
            return Err(RequestError::BadRequest);
        }

        let username = split_str[0];
        let dbname = split_str[1];
        let cmd = format!("{};", split_str[2]);

        // load sql object from memory pool
        let mut pool = mutex.lock().unwrap();
        let mut sql = match pool.get(username, dbname, req.addr.clone()) {
            Ok(tsql) => tsql,
            Err(ret) => return Err(RequestError::PoolError(ret)),
        };
        // initialize public key
        if sql.user.key == 0 {
            sql.user.key = req.key;
        }
        // check dbname
        if dbname != "" {
            let parser = match Parser::new(&cmd) {
                Ok(_parser) => _parser,
                Err(ret) => return Err(RequestError::CauseByParser(ret)),
            };
            match parser.parse(&mut sql) {
                Err(ret) => return Err(RequestError::CauseByParser(ret)),
                Ok(_) => {}
            }
        } else {
            // check cmd if it is "create database dbname;"
            let mut iter = cmd.split_whitespace();
            if iter.next() != Some("create") || iter.next() != Some("database") {
                return Err(RequestError::CreateDBBeforeCmd);
            }
            let parser = match Parser::new(&cmd) {
                Ok(_parser) => _parser,
                Err(ret) => return Err(RequestError::CauseByParser(ret)),
            };
            match parser.parse(&mut sql) {
                Err(ret) => return Err(RequestError::CauseByParser(ret)),
                Ok(_) => {}
            }
        }
        if !sql.result_json.is_empty() {
            let return_json = sql.result_json.clone();
            sql.result_json.clear();
            return Ok(Response::OK {
                msg: return_json.to_string(),
            });
        }
        Ok(Response::OK {
            msg: "Query OK!".to_string(),
        })
        //Ok(Response::OK { msg: format!("{}, user:{}",input, sql.username) })
    }
    fn user_verify(name: &str) -> Result<(), RequestError> {
        // auto create new users for now
        if name == "" {
            return Err(RequestError::UserNotExist(name.to_string()));
        } else {
            let users = match DiskInterface::get_usernames(Some(dotenv!("FILE_BASE_PATH"))) {
                Ok(us) => us,
                Err(ret) => return Err(RequestError::DiskError(ret)),
            };
            if !users.contains(&name.to_string()) {
                match DiskInterface::create_username(name, Some(dotenv!("FILE_BASE_PATH"))) {
                    Ok(_) => {}
                    Err(ret) => return Err(RequestError::DiskError(ret)),
                }
            }
        }
        Ok(())
    }
}