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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#![allow(dead_code)]

extern crate postgres;
extern crate r2d2;
extern crate r2d2_postgres;
extern crate rustc_serialize;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate log;

use std::sync::RwLock;
use postgres::rows::Row;
use postgres::types::{ToSql, FromSql};
use r2d2::{Config, Pool, PooledConnection};
use r2d2_postgres::{PostgresConnectionManager, SslMode};

pub trait Entity {
    fn convert(row: Row) -> Self;
    fn get(_: i32) -> Option<Self> where Self: Sized { unimplemented!(); }
    fn count() -> i32 { unimplemented!(); }
    fn list() -> Vec<Self> where Self: Sized { unimplemented!(); }
    fn page_list(_: i32, _: i32, _:&str) -> Vec<Self> where Self: Sized { unimplemented!(); }
    fn delete(_: i32) -> bool { unimplemented!(); }
    fn new(&self) -> bool { unimplemented!(); }
    fn update(&self) -> bool { unimplemented!(); }
}

impl<T: FromSql> Entity for T {
    fn convert(row: Row) -> Self {
        row.get(0)
    }
}

#[macro_export]
macro_rules! reposable {
    (struct $name:ident($table_name:ident) {
        $($field_name:ident: $field_type:ty,)*
    }) => {
        #[derive(Default,Debug,RustcEncodable, RustcDecodable,Clone, PartialEq,Eq)]
        pub struct $name {
            pub id: i32,
            $(pub $field_name: $field_type,)*
        }

        impl Entity for $name{
            fn convert(row: ::postgres::rows::Row)->Self{
               let mut m=$name::default();
               m.id=row.get("id");
               $(m.$field_name=row.get(stringify!($field_name));)*
               m
            }
            fn get(id: i32) -> Option<Self> {
                let sql=format!("select * from {} where id={}",stringify!($table_name),id );
                find_one(&sql,&[])
            }
            fn delete(id: i32) -> bool {
                let sql=format!("delete from {} where id={}",stringify!($table_name),id );
                execute(&sql,&[])>0
            }
            fn new(&self) -> bool {
                let fields=vec![$(stringify!($field_name)),*];
                let values: &[&::postgres::types::ToSql]= &[$(&self.$field_name),*];
                let params =(0..fields.len()).map(|i| format!("${}",i+1)).collect::<Vec<String>>().join(",");
                let sql=format!("insert into {} ({}) values ({})",stringify!($table_name),fields.join(","),params);
                execute(&sql,values)>0
            }
            fn update(&self) -> bool {
                let fields=vec![$(stringify!($field_name)),*];
                let values: &[&::postgres::types::ToSql]= &[$(&self.$field_name),*];
                let params=fields.iter().enumerate().map(|(i,name)| format!("{}=${}",name,i+1)).collect::<Vec<String>>().join(",");
                let sql=format!("update {} set {} where id={}",stringify!($table_name),params,&self.id);
                execute(&sql,values)>0
            }

            fn list()->Vec<Self>{
                let sql=format!("select * from {} order by id",stringify!($table_name));
                find_list(&sql,&[])
            }

            fn page_list(page_size: i32, page_num: i32,order_field: &str) -> Vec<Self>{
                let mut order_field=String::from(order_field.trim());
                let mut desc=String::new();
                if order_field.starts_with("-") {
                    desc.push_str("desc");
                    order_field=order_field.trim_left_matches('-').into();
                }
                let sql = format!("select * from {} order by {} {} limit {} offset {}",stringify!($table_name),order_field, desc, page_size, page_num * page_size);
                find_list(&sql,&[])
            }

            fn count() -> i32 {
                let sql=format!("select count(1) from {}",stringify!($table_name));
                find_one::<i64>(&sql,&[]).unwrap_or(0) as i32
            }

        }

    }
}

pub struct PostgresConfig {
    pub host: String,
    pub port: String,
    pub user_name: String,
    pub password: String,
    pub db_name: String,
}

lazy_static! {
    static ref POSTGRES_POOL: RwLock<Option<Pool<PostgresConnectionManager>>>  = RwLock::new(None);
}

pub fn init(c: &PostgresConfig) {
    let connect_str = format!("postgres://{}:{}@{}:{}/{}", c.user_name, c.password, c.host, c.port, c.db_name);
    info!("Connecting to postgres:{}", connect_str);
    let manager = PostgresConnectionManager::new(connect_str.as_str(), SslMode::None).unwrap();
    let config = Config::builder().pool_size(10).build();
    match Pool::new(config, manager) {
        Ok(pool) => {
            info!("Connected to postgres with pool: {:?}", pool);
            let mut w = POSTGRES_POOL.write().unwrap();
            *w = Some(pool);
        }
        Err(err) => {
            panic!("error occurs when connect to postgres {}.Error info:{}", connect_str, err);
        }
    };
}

fn get_conn() -> PooledConnection<PostgresConnectionManager> {
    match *POSTGRES_POOL.read().unwrap() {
        Some(ref pool) => {
            match pool.get() {
                Ok(conn) => conn,
                Err(err) => panic!("error in get_conn():{}", err),
            }
        },
        None => panic!("repository not initialized"),
    }
}


pub fn find_one<T: Entity>(query: &str, params: &[&ToSql]) -> Option<T> {
    info!("find_one: {}, params:{:?}", query, params);
    let conn = get_conn();
    match conn.query(query, params) {
        Ok(rows) => {
            return rows.into_iter().next().map(T::convert);
        }
        Err(err) => {
            panic!("error occur when execute query:{},params:{:?},error:{}", query, params, err)
        }
    }
    None
}

pub fn find_list<T: Entity>(query: &str, params: &[&ToSql]) -> Vec<T> {
    info!("find_list: {}, params:{:?}", query, params);
    let conn = get_conn();
    match conn.query(query, params) {
        Ok(rows) => {
            return rows.into_iter().map(T::convert).collect();
        }
        Err(err) => {
            panic!("error occur when execute query:{},params:{:?},error:{}", query, params, err)
        }
    }
    vec![]
}

// return: the number of rows modified
pub fn execute(query: &str, params: &[&ToSql]) -> u64 {
    info!("execute :{}, params:{:?}", query, params);
    let conn = get_conn();
    // conn.execute(query,params).unwrap()
    match conn.execute(query, params) {
        Ok(count) => count,
        Err(err) => panic!("error occur when execute query:{},params:{:?},error:{}", query, params, err),
    }
}

pub fn batch_execute(query: &str) -> postgres::Result<()> {
    info!("batch_execute :{}", query);
    let conn = get_conn();
    conn.batch_execute(query)
}