🐈⬛🐈🐈🐈🐈🐈⬛
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