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
// lib.rs
// application related functions are implemented here

pub mod nav;
pub mod db;

use chrono::prelude::*;
use log::*;
use std::collections::HashMap;
use lazy_static::*;
use std::sync::RwLock;
use config::*;

lazy_static! {
    // pull-in app specific settings from Settings.toml and 
    // Enviroment variables that start with APP, eg: APP_DEBUG=1
    static ref SETTINGS: RwLock<Config> = RwLock::new({
        let mut settings = config::Config::default();
        settings
            .merge(config::File::with_name("Settings")).unwrap()
            .merge(config::Environment::with_prefix("APP")).unwrap();
        settings
    });
}

pub fn app_config(key: &str) -> String {
    let app_config = SETTINGS.read().unwrap().clone().try_into::<HashMap<String, String>>().unwrap();
    app_config.get(key).unwrap().to_string()
}

// starting point of finding latest nav
pub fn process(inputs: &str) -> Vec<nav::Record> {
    let (scheme_code_numbers, isin_codes) = extract(inputs);
    let today = Local::today();
    let conn = db::open_connection(&today.year()); // database is built on yearly basis; hence year is passed
    let status = db::get_retrieval_status(&today, &conn);

    if status == 0 {
        debug!("Latest NAV NOT FOUND...");
        nav::backup_latest_nav(&conn);
        db::delete_latest_nav(&conn);
        nav::retrieve_csv_data(&conn).unwrap();
        db::update_retrieval_status(&today, &conn);
    }
    let net_asset_values: Vec<nav::Record> = nav::find_latest_for(scheme_code_numbers, isin_codes, &conn);
    debug!("END: finding latest nav....with results: {:?}", net_asset_values);
    net_asset_values
}

fn extract(codes: &str) -> (Vec<u32>, Vec<&str>) {
    debug!("START: extract ....");
    let split_codes: Vec<&str> = codes.split(|c| c == ',' || c == ' ' || c ==';' || c == '+' || c == '\r' || c == '\n').collect();
    let mut trimmed_codes = vec![]; 
    for code in split_codes {
        trimmed_codes.push( code.trim() )
    }
    let mut scheme_codes = vec![];
    let mut isin_codes = vec![];
    for code in trimmed_codes {
        match code.len() {
            6 => { scheme_codes.push(code); },
            12 => { isin_codes.push(code); },
            _ => { debug!("invalid code {:?}", code); }
        }
    }
    let mut scheme_code_numbers = vec![];
    for code in &scheme_codes {
        scheme_code_numbers.push( code.parse::<u32>().unwrap() );
    }
    debug!("END: extract ....returns ({:?}, {:?})", scheme_code_numbers, isin_codes);
    (scheme_code_numbers, isin_codes)
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_extract_scheme_number() {
        let user_input_string = "119550"; // Scenario 1: user entered only one scheme number in the input field
        let (actual_scheme_numbers, actual_isin_codes) = extract(user_input_string);
        let expected_scheme_numbers: Vec<u32> = vec![119550];
        let expected_isin_codes: Vec<&str> = [].to_vec();
        assert_eq!(actual_scheme_numbers, expected_scheme_numbers);
        assert_eq!(actual_isin_codes.len(), expected_isin_codes.len());
    }

    #[test]
    fn test_extract_scheme_numbers() {
        let user_input_string = "119550, 119551"; // Scenario 2: user entered two scheme numbers in the input field
        let (actual_scheme_numbers, actual_isin_codes) = extract(user_input_string);
        let expected_scheme_numbers: Vec<u32> = vec![119550, 119551];
        let expected_isin_codes: Vec<&str> = [].to_vec();
        assert_eq!(actual_scheme_numbers, expected_scheme_numbers);
        assert_eq!(actual_isin_codes.len(), expected_isin_codes.len());
    }   
    
    #[test]
    fn test_extract_isin_code() {
        let user_input_string = "INF209K01YM2"; // Scenario 3: user entered only one isin code in the input field
        let (actual_scheme_numbers, actual_isin_codes) = extract(user_input_string);
        let expected_scheme_numbers: Vec<u32> = [].to_vec();
        let expected_isin_codes: Vec<&str> = vec!["INF209K01YM2"]; 
        assert_eq!(actual_scheme_numbers.len(), expected_scheme_numbers.len());
        assert_eq!(actual_isin_codes, expected_isin_codes);
    }

    #[test]
    fn test_extract_isin_codes() {
        let user_input_string = "INF209K01YM2, INF209K01YO8"; // Scenario 4: user entered two isin codes in the input field
        let (actual_scheme_numbers, actual_isin_codes) = extract(user_input_string);
        let expected_scheme_numbers: Vec<u32> = [].to_vec();
        let expected_isin_codes: Vec<&str> = vec!["INF209K01YM2", "INF209K01YO8"];
        assert_eq!(actual_scheme_numbers.len(), expected_scheme_numbers.len());
        assert_eq!(actual_isin_codes, expected_isin_codes);
    }

    #[test]
    fn test_extract_isin_and_scheme() {
        let user_input_string = "INF209K01YM2, 119550, INF209K01YO8, 119551"; // Scenario 5: user entered both isin codes and scheme numbers in the input field
        let (actual_scheme_numbers, actual_isin_codes) = extract(user_input_string);
        let expected_scheme_numbers: Vec<u32> = vec![119550, 119551];
        let expected_isin_codes: Vec<&str> = vec!["INF209K01YM2", "INF209K01YO8"];
        assert_eq!(actual_scheme_numbers, expected_scheme_numbers);
        assert_eq!(actual_isin_codes, expected_isin_codes);
    }

    #[test]
    fn test_extract_on_code_separators() {
        let user_input_string = "INF209K01YM2, 119550 INF209K01YO8; 119551 + INF209K01LX6"; // Scenario 6: user entered codes separated by , space ; +
        let (actual_scheme_numbers, actual_isin_codes) = extract(user_input_string);
        let expected_scheme_numbers: Vec<u32> = vec![119550, 119551];
        let expected_isin_codes: Vec<&str> = vec!["INF209K01YM2", "INF209K01YO8", "INF209K01LX6"];
        assert_eq!(actual_scheme_numbers, expected_scheme_numbers);
        assert_eq!(actual_isin_codes, expected_isin_codes);
    }

}
// end of lib.rs