From 743ae22168b1fcdf2e1e54bcadbb1bb3fce3370d Mon Sep 17 00:00:00 2001
From: "Adam T. Carpenter" <atc@53hor.net>
Date: Mon, 5 Oct 2020 22:05:58 -0400
Subject: started work on schema, models, and repos

---
 dichroism/src/bin/dichroismd.rs | 44 +++++++++++++++++++++++++++++++++++++++++
 dichroism/src/config.rs         | 32 +++++++++++++++++++++++++++++-
 dichroism/src/constants.rs      |  3 +++
 dichroism/src/error.rs          |  1 +
 dichroism/src/handlers.rs       | 31 +++++++++++++++++++++++++----
 dichroism/src/image_api.rs      | 11 ++++++-----
 dichroism/src/image_repo.rs     | 32 ++++++++++++++++++++++++++++++
 dichroism/src/lib.rs            | 16 +++++++++++++++
 dichroism/src/main.rs           | 35 --------------------------------
 dichroism/src/models.rs         |  5 +++++
 dichroism/src/product_api.rs    |  0
 dichroism/src/product_repo.rs   | 18 +++++++++++++++++
 dichroism/src/schema.rs         |  6 ++++++
 dichroism/src/types.rs          |  5 +++++
 14 files changed, 194 insertions(+), 45 deletions(-)
 create mode 100644 dichroism/src/bin/dichroismd.rs
 create mode 100644 dichroism/src/constants.rs
 create mode 100644 dichroism/src/image_repo.rs
 create mode 100644 dichroism/src/lib.rs
 delete mode 100644 dichroism/src/main.rs
 create mode 100644 dichroism/src/models.rs
 create mode 100644 dichroism/src/product_api.rs
 create mode 100644 dichroism/src/product_repo.rs
 create mode 100644 dichroism/src/schema.rs
 create mode 100644 dichroism/src/types.rs

(limited to 'dichroism/src')

diff --git a/dichroism/src/bin/dichroismd.rs b/dichroism/src/bin/dichroismd.rs
new file mode 100644
index 0000000..2dc059d
--- /dev/null
+++ b/dichroism/src/bin/dichroismd.rs
@@ -0,0 +1,44 @@
+use actix_web::{App, HttpServer};
+use dichroism::config;
+use dichroism::handlers;
+use dichroism::result::Result;
+use diesel::prelude::SqliteConnection;
+use diesel::r2d2::ConnectionManager;
+use diesel::r2d2::Pool;
+use listenfd::ListenFd;
+
+#[actix_web::main]
+async fn main() -> Result<()> {
+    // Gather config.
+    let config = config::Config::new_from_env().await?;
+    let bind_addr = config.bind_addr;
+
+    // Initialize DB connection pool.
+    //let manager = ConnectionManager::<SqliteConnection>::new(config.db_url);
+    let manager = ConnectionManager::<SqliteConnection>::new(&config.db_url);
+    let pool = Pool::builder().build(manager)?;
+
+    // Initialize application server.
+    let mut server = HttpServer::new(move || {
+        App::new()
+            .data(config.clone())
+            .data(pool.clone())
+            .service(handlers::hello)
+            .service(handlers::create_image)
+            .service(handlers::get_products)
+    });
+
+    let mut listenfd = ListenFd::from_env();
+    server = if let Some(l) = listenfd
+        .take_tcp_listener(0)
+        .expect("Unable to grab TCP listener!")
+    {
+        // If using listenfd, use it to allow for cargo watch auto-reloading.
+        server.listen(l)?
+    } else {
+        // Bind to config for release.
+        server.bind(bind_addr)?
+    };
+
+    Ok(server.run().await?)
+}
diff --git a/dichroism/src/config.rs b/dichroism/src/config.rs
index e5b4046..c3dec51 100644
--- a/dichroism/src/config.rs
+++ b/dichroism/src/config.rs
@@ -1 +1,31 @@
-struct Config {}
+use crate::constants::*;
+use crate::error::DichroismError;
+use crate::result::Result;
+use async_std::fs::metadata;
+use async_std::path::PathBuf;
+use std::env;
+use std::net::SocketAddr;
+
+#[derive(Debug, Clone)]
+pub struct Config {
+    pub db_url: String,
+    pub img_root: PathBuf,
+    pub bind_addr: SocketAddr,
+}
+
+impl Config {
+    pub async fn new_from_env() -> Result<Self> {
+        let img_root = PathBuf::from(env::var(ENV_IMG_ROOT)?);
+        let meta = metadata(&img_root).await?;
+
+        if !meta.is_dir() || meta.permissions().readonly() {
+            return Err(Box::new(DichroismError::InvalidImageRoot));
+        }
+
+        Ok(Config {
+            db_url: env::var(ENV_DB_URL)?,
+            img_root,
+            bind_addr: env::var(ENV_BIND_ADDR)?.parse()?,
+        })
+    }
+}
diff --git a/dichroism/src/constants.rs b/dichroism/src/constants.rs
new file mode 100644
index 0000000..fe45c1e
--- /dev/null
+++ b/dichroism/src/constants.rs
@@ -0,0 +1,3 @@
+pub const ENV_IMG_ROOT: &str = "DICHROISM_IMG_ROOT";
+pub const ENV_BIND_ADDR: &str = "DICHROISM_BIND_ADDR";
+pub const ENV_DB_URL: &str = "DICHROISM_DB_URL";
diff --git a/dichroism/src/error.rs b/dichroism/src/error.rs
index 2f01381..5e00abc 100644
--- a/dichroism/src/error.rs
+++ b/dichroism/src/error.rs
@@ -1,6 +1,7 @@
 #[derive(Debug)]
 pub enum DichroismError {
     UriDataExtract,
+    InvalidImageRoot,
 }
 
 impl std::error::Error for DichroismError {}
diff --git a/dichroism/src/handlers.rs b/dichroism/src/handlers.rs
index 1e0ae28..e4ea6a0 100644
--- a/dichroism/src/handlers.rs
+++ b/dichroism/src/handlers.rs
@@ -1,13 +1,29 @@
+use super::image_repo;
+use super::product_repo;
+use super::types::DbPool;
+use crate::config::Config;
 use crate::image_api;
-use actix_web::{get, post, HttpResponse, Responder};
+use actix_web::{get, post, web, Error, HttpResponse, Responder};
 
 #[get("/")]
 async fn hello() -> impl Responder {
     HttpResponse::Ok().body("Hey, this is an API!")
 }
 
+#[get("/images")]
+async fn get_images(pool: web::Data<DbPool>) -> Result<HttpResponse, Error> {
+    let conn = pool.get().expect("Couldn't get DB connection from pool.");
+    let images = web::block(move || image_repo::read_images(&conn))
+        .await
+        .map_err(|e| {
+            eprintln!("{}", e);
+            HttpResponse::InternalServerError().finish()
+        })?;
+    Ok(HttpResponse::Ok().json(images))
+}
+
 #[post("/images")]
-async fn create_image(req_body: String) -> impl Responder {
+async fn create_image(_config: web::Data<Config>, req_body: String) -> impl Responder {
     let data = match image_api::extract_data(&req_body) {
         Err(e) => return HttpResponse::BadRequest().body(format!("fail: {}", e.to_string())),
         Ok(d) => d,
@@ -24,6 +40,13 @@ async fn create_image(req_body: String) -> impl Responder {
 }
 
 #[get("/products")]
-async fn get_products(_req_body: String) -> impl Responder {
-    HttpResponse::Ok().body("got products!")
+async fn get_products(pool: web::Data<DbPool>) -> Result<HttpResponse, Error> {
+    let conn = pool.get().expect("Couldn't get DB connection from pool.");
+    let products = web::block(move || product_repo::read_products(&conn))
+        .await
+        .map_err(|e| {
+            eprintln!("{}", e);
+            HttpResponse::InternalServerError().finish()
+        })?;
+    Ok(HttpResponse::Ok().json(products))
 }
diff --git a/dichroism/src/image_api.rs b/dichroism/src/image_api.rs
index 44effe4..97faead 100644
--- a/dichroism/src/image_api.rs
+++ b/dichroism/src/image_api.rs
@@ -3,10 +3,11 @@ use crate::result::Result;
 use base64::decode;
 use regex::Regex;
 
-lazy_static! {
-    static ref DATA_URI_RE: Regex =
-        Regex::new("^data:image/(png|jpeg);base64,(?P<data>.+)").expect("Couldn't parse Regex!");
-}
+use once_cell::sync::Lazy;
+
+static DATA_URI_RE: Lazy<Regex> = Lazy::new(|| {
+    Regex::new("^data:image/(png|jpeg);base64,(?P<data>.+)").expect("Couldn't parse Regex.")
+});
 
 pub fn generate_images(data: &str) -> Result<()> {
     let bytes = decode(data)?;
@@ -24,7 +25,7 @@ pub fn extract_data(uri: &str) -> Result<&str> {
 
     Ok(caps
         .name("data")
-        .expect("Should never fail if regex succeeded")
+        .expect("Should never fail if regex succeeded.")
         .as_str())
 }
 
diff --git a/dichroism/src/image_repo.rs b/dichroism/src/image_repo.rs
new file mode 100644
index 0000000..c36f94e
--- /dev/null
+++ b/dichroism/src/image_repo.rs
@@ -0,0 +1,32 @@
+use super::models::ProductImg;
+use diesel::prelude::*;
+use diesel::result::Error;
+
+type DBConn = SqliteConnection;
+
+pub fn read_images(conn: &DBConn) -> Result<Vec<ProductImg>, Error> {
+    use crate::schema::images::dsl::*;
+    let results = images.load::<ProductImg>(conn)?;
+    Ok(results)
+}
+
+pub fn create_image() {
+    todo!()
+}
+
+pub fn update_image() {
+    todo!()
+}
+
+pub fn delete_image() {
+    todo!()
+}
+
+#[cfg(test)]
+mod tests {
+
+    #[test]
+    fn test() -> std::result::Result<(), Box<dyn std::error::Error>> {
+        Ok(())
+    }
+}
diff --git a/dichroism/src/lib.rs b/dichroism/src/lib.rs
new file mode 100644
index 0000000..e3842d3
--- /dev/null
+++ b/dichroism/src/lib.rs
@@ -0,0 +1,16 @@
+#[macro_use]
+extern crate serde;
+#[macro_use]
+extern crate diesel;
+
+pub mod config;
+mod constants;
+mod error;
+pub mod handlers;
+mod image_api;
+mod image_repo;
+mod models;
+mod product_repo;
+pub mod result;
+mod schema;
+pub mod types;
diff --git a/dichroism/src/main.rs b/dichroism/src/main.rs
deleted file mode 100644
index 442b92d..0000000
--- a/dichroism/src/main.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-#[macro_use]
-extern crate lazy_static;
-
-use actix_web::{App, HttpServer};
-use listenfd::ListenFd;
-
-mod config;
-mod error;
-mod handlers;
-mod image_api;
-mod result;
-
-#[actix_web::main]
-async fn main() -> std::io::Result<()> {
-    let mut listenfd = ListenFd::from_env();
-    let mut server = HttpServer::new(|| {
-        App::new()
-            .service(handlers::hello)
-            .service(handlers::create_image)
-            .service(handlers::get_products)
-    });
-
-    server = if let Some(l) = listenfd
-        .take_tcp_listener(0)
-        .expect("Unable to grab TCP listener!")
-    {
-        // "Debug mode" with cargo watch auto-reloading
-        server.listen(l)?
-    } else {
-        // "Release mode"
-        server.bind("127.0.0.1:8000")?
-    };
-
-    server.run().await
-}
diff --git a/dichroism/src/models.rs b/dichroism/src/models.rs
new file mode 100644
index 0000000..5516688
--- /dev/null
+++ b/dichroism/src/models.rs
@@ -0,0 +1,5 @@
+#[derive(Debug, Queryable, Serialize)]
+pub struct ProductImg {
+    pub id: i32,
+    pub path: String,
+}
diff --git a/dichroism/src/product_api.rs b/dichroism/src/product_api.rs
new file mode 100644
index 0000000..e69de29
diff --git a/dichroism/src/product_repo.rs b/dichroism/src/product_repo.rs
new file mode 100644
index 0000000..67c5d3e
--- /dev/null
+++ b/dichroism/src/product_repo.rs
@@ -0,0 +1,18 @@
+use super::models::ProductImg;
+use diesel::prelude::*;
+use diesel::result::Error;
+
+type DBConn = SqliteConnection;
+
+pub fn read_products(_conn: &DBConn) -> Result<Vec<ProductImg>, Error> {
+    todo!()
+}
+
+#[cfg(test)]
+mod tests {
+
+    #[test]
+    fn test() -> std::result::Result<(), Box<dyn std::error::Error>> {
+        Ok(())
+    }
+}
diff --git a/dichroism/src/schema.rs b/dichroism/src/schema.rs
new file mode 100644
index 0000000..e1c4408
--- /dev/null
+++ b/dichroism/src/schema.rs
@@ -0,0 +1,6 @@
+table! {
+    images (id) {
+        id -> Integer,
+        path -> Text,
+    }
+}
diff --git a/dichroism/src/types.rs b/dichroism/src/types.rs
new file mode 100644
index 0000000..91aef95
--- /dev/null
+++ b/dichroism/src/types.rs
@@ -0,0 +1,5 @@
+use diesel::r2d2::ConnectionManager;
+use diesel::r2d2::Pool;
+use diesel::SqliteConnection;
+
+pub type DbPool = Pool<ConnectionManager<SqliteConnection>>;
-- 
cgit v1.2.3