🐈⬛🐈🐈🐈🐈🐈⬛
This commit is contained in:
commit
f312b82671
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
/target
|
||||
cats.db
|
||||
.DS_Store
|
2512
Cargo.lock
generated
Normal file
2512
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
Cargo.toml
Normal file
18
Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "loyalty_rust"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
sqlx = { version = "0.6", features = [ "runtime-async-std-native-tls" ] }
|
||||
actix-web = "4"
|
||||
config = "0.13.1"
|
||||
serde = "1.0.8"
|
||||
deadpool-postgres = { version = "0.10.2", features = ["serde"] }
|
||||
tokio-postgres = "0.7.6"
|
||||
rusqlite = { version = "0.28.0", features = ["bundled"] }
|
||||
actix-files = "0.6"
|
||||
env_logger = "0.9.0"
|
||||
log = "0.4"
|
12
settings.yml
Normal file
12
settings.yml
Normal file
@ -0,0 +1,12 @@
|
||||
debug: false
|
||||
host: 127.0.0.1
|
||||
port: 8080
|
||||
|
||||
postgres:
|
||||
db: pay
|
||||
host: ekb-pay01
|
||||
user: postgres
|
||||
password: hdgKXR4BEFCb27vb
|
||||
port: "5432"
|
||||
schema: loyalty
|
||||
prefix: ""
|
21
src/cfg/mod.rs
Normal file
21
src/cfg/mod.rs
Normal file
@ -0,0 +1,21 @@
|
||||
|
||||
|
||||
#[derive(Debug, Default, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct AppConfig {
|
||||
pub debug: bool,
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub postgres: PostgresSettings,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, serde::Deserialize, PartialEq, Eq)]
|
||||
pub struct PostgresSettings {
|
||||
db: String,
|
||||
host: String,
|
||||
user: String,
|
||||
password: String,
|
||||
port: String,
|
||||
schema: String,
|
||||
prefix: String,
|
||||
}
|
||||
|
133
src/loyalty/db/mod.rs
Normal file
133
src/loyalty/db/mod.rs
Normal file
@ -0,0 +1,133 @@
|
||||
use rusqlite::{Connection, Error, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Cat {
|
||||
id: i32,
|
||||
name: String,
|
||||
color: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Color {
|
||||
id: i32,
|
||||
name: String,
|
||||
}
|
||||
|
||||
pub type CatsResult = Result<Vec<Cat>, Error>;
|
||||
pub type ColorsResult = Result<Vec<Color>, Error>;
|
||||
pub type CatResult = Result<Cat, Error>;
|
||||
pub type ColorResult = Result<Color, Error>;
|
||||
|
||||
// Сервис базы данных
|
||||
|
||||
pub struct DbService {
|
||||
db: rusqlite::Connection,
|
||||
}
|
||||
impl DbService {
|
||||
// Создание сервиса
|
||||
pub fn new() -> DbService {
|
||||
let conn = Connection::open("cats.db");
|
||||
let db_con = match conn {
|
||||
Ok(conn) => conn,
|
||||
Err(error) => panic!("Problem opening the db: {:?}", error),
|
||||
};
|
||||
db_con
|
||||
.execute(
|
||||
"create table if not exists cat_colors (
|
||||
id integer primary key,
|
||||
name text not null unique
|
||||
)",
|
||||
[],
|
||||
)
|
||||
.ok();
|
||||
db_con
|
||||
.execute(
|
||||
"create table if not exists cats (
|
||||
id integer primary key,
|
||||
name text not null,
|
||||
color_id integer not null references cat_colors(id)
|
||||
)",
|
||||
[],
|
||||
)
|
||||
.ok();
|
||||
let mut cat_colors = HashMap::new();
|
||||
cat_colors.insert(String::from("Blue"), vec!["Tigger", "Sammy"]);
|
||||
cat_colors.insert(String::from("Black"), vec!["Oreo", "Biscuit"]);
|
||||
for (color, catnames) in &cat_colors {
|
||||
db_con
|
||||
.execute(
|
||||
"INSERT INTO cat_colors (name) values (?1)",
|
||||
&[&color.to_string()],
|
||||
)
|
||||
.ok();
|
||||
let last_id: String = db_con.last_insert_rowid().to_string();
|
||||
for cat in catnames {
|
||||
db_con
|
||||
.execute(
|
||||
"INSERT INTO cats (name, color_id) values (?1, ?2)",
|
||||
&[&cat.to_string(), &last_id],
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
return DbService { db: db_con };
|
||||
}
|
||||
// Получение кошек
|
||||
pub fn get_cats(&self) -> CatsResult {
|
||||
let mut stmt = self.db.prepare(
|
||||
"SELECT c.id, c.name, cc.name from cats c INNER JOIN cat_colors cc ON cc.id = c.color_id;",
|
||||
)?;
|
||||
return stmt
|
||||
.query_map([], |row| {
|
||||
Ok(Cat {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
color: row.get(2)?,
|
||||
})
|
||||
})
|
||||
.and_then(Iterator::collect);
|
||||
}
|
||||
|
||||
// Получение цветов
|
||||
pub fn get_colors(&self) -> ColorsResult {
|
||||
let mut stmt = self.db.prepare("SELECT id, name from cat_colors")?;
|
||||
return stmt
|
||||
.query_map([], |row| {
|
||||
Ok(Color {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
})
|
||||
})
|
||||
.and_then(Iterator::collect);
|
||||
}
|
||||
|
||||
// Добавление кошки
|
||||
pub fn add_cat(&self, cat_name: String, cat_color: String) -> CatResult {
|
||||
return self.db.query_row("INSERT INTO cats (name, color_id) SELECT ?1, id FROM cat_colors cc WHERE name = ?2 RETURNING id, name, ?2",
|
||||
[cat_name, cat_color],
|
||||
|row| {
|
||||
Ok(Cat {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
color: row.get(2)?,
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
// Добавление цвета
|
||||
pub fn add_color(&self, color_name: String) -> ColorResult {
|
||||
return self.db.query_row(
|
||||
"INSERT INTO cat_colors (name) VALUES (?1) RETURNING id, name",
|
||||
[color_name],
|
||||
|row| {
|
||||
Ok(Color {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
})
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
74
src/loyalty/mod.rs
Normal file
74
src/loyalty/mod.rs
Normal file
@ -0,0 +1,74 @@
|
||||
pub mod db;
|
||||
use actix_web::{get,post, web, HttpResponse, Responder, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub struct LoyaltyService {
|
||||
pub db: db::DbService,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ArrayResponse<T> {
|
||||
result: Vec<T>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AddColorRequest {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[get("/cats")]
|
||||
pub async fn get_cats(ctx: web::Data<LoyaltyService>) -> Result<impl Responder> {
|
||||
let cats = ctx.db.get_cats();
|
||||
let res = match cats {
|
||||
Ok(v) => ArrayResponse{ result: v },
|
||||
Err(_err) => ArrayResponse { result: vec![] },
|
||||
};
|
||||
return Ok(HttpResponse::Ok().json(res));
|
||||
}
|
||||
|
||||
#[get("/colors")]
|
||||
pub async fn get_colors(ctx: web::Data<LoyaltyService>) -> Result<impl Responder> {
|
||||
let colors = ctx.db.get_colors();
|
||||
let res = match colors {
|
||||
Ok(v) => ArrayResponse { result: v },
|
||||
Err(_err) => ArrayResponse { result: vec![] },
|
||||
};
|
||||
return Ok(HttpResponse::Ok().json(res));
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AddCatRequest {
|
||||
pub name: String,
|
||||
pub color: String,
|
||||
}
|
||||
|
||||
|
||||
#[post("/add/cat")]
|
||||
pub async fn add_cat(
|
||||
ctx: web::Data<LoyaltyService>,
|
||||
cat: web::Json<AddCatRequest>,
|
||||
) -> Result<impl Responder> {
|
||||
let _cat = cat.into_inner();
|
||||
let cat = ctx.db.add_cat(_cat.name, _cat.color);
|
||||
let res = match cat {
|
||||
Ok(v) => ArrayResponse { result: vec![v] },
|
||||
Err(_err) => panic!("{:?}", _err),
|
||||
};
|
||||
return Ok(HttpResponse::Ok().json(res));
|
||||
}
|
||||
|
||||
#[post("/add/color")]
|
||||
pub async fn add_color(
|
||||
ctx: web::Data<LoyaltyService>,
|
||||
cat: web::Json<AddColorRequest>,
|
||||
) -> Result<impl Responder> {
|
||||
let _color = cat.into_inner();
|
||||
let cat = ctx.db.add_color(_color.name);
|
||||
let res = match cat {
|
||||
Ok(v) => ArrayResponse { result: vec![v] },
|
||||
Err(_err) => panic!("{:?}", _err),
|
||||
};
|
||||
return Ok(HttpResponse::Ok().json(res));
|
||||
}
|
||||
|
38
src/main.rs
Normal file
38
src/main.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use config::Config;
|
||||
mod cfg;
|
||||
mod loyalty;
|
||||
use actix_web::{middleware::Logger, web, App, HttpServer};
|
||||
use actix_files::Files;
|
||||
|
||||
|
||||
#[actix_web::main]
|
||||
pub async fn main() -> std::io::Result<()> {
|
||||
|
||||
let settings = Config::builder()
|
||||
.add_source(config::File::with_name("settings.yml"))
|
||||
.build()
|
||||
.unwrap();
|
||||
let app: cfg::AppConfig = settings.try_deserialize().unwrap();
|
||||
println!(
|
||||
"Запуск приложения в режиме {} {}:{}",
|
||||
app.debug, app.host, app.port
|
||||
);
|
||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||||
log::info!("starting HTTP server at http://{}:{}", app.host, app.port);
|
||||
|
||||
HttpServer::new(|| {
|
||||
App::new()
|
||||
.app_data(web::Data::new(loyalty::LoyaltyService {
|
||||
db: loyalty::db::DbService::new(),
|
||||
}))
|
||||
.service(Files::new("/", "./static/").index_file("index.html"))
|
||||
|
||||
.service(loyalty::get_cats)
|
||||
.service(loyalty::get_colors)
|
||||
.service(loyalty::add_cat)
|
||||
.service(loyalty::add_color)
|
||||
})
|
||||
.bind((app.host, app.port))?
|
||||
.run()
|
||||
.await
|
||||
}
|
15
static/index.html
Normal file
15
static/index.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1> Hello cats!</h1>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user