init
This commit is contained in:
1
.env
Normal file
1
.env
Normal file
@@ -0,0 +1 @@
|
||||
DATABASE_URL=postgres://user:password@localhost/diesel_demo
|
||||
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
2060
Cargo.lock
generated
Normal file
2060
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
Cargo.toml
Normal file
14
Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "photos"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
description = " "
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[workspace]
|
||||
members = ["frontend", "backend", "common"]
|
||||
default-members = ["backend"]
|
||||
21
Dockerfile
Normal file
21
Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
FROM rust:latest as build
|
||||
|
||||
RUN rustup target add wasm32-unknown-unknown
|
||||
RUN cargo install trunk wasm-bindgen-cli
|
||||
|
||||
RUN apt-get update && apt-get install -y npm binaryen && npm install -g sass
|
||||
|
||||
WORKDIR /app/src
|
||||
COPY . .
|
||||
|
||||
RUN cd frontend && trunk build --release
|
||||
RUN cargo build --release
|
||||
|
||||
FROM gcr.io/distroless/cc-debian10
|
||||
RUN apt-get update && apt-get install -y libpq5
|
||||
|
||||
COPY --from=build /app/src/target/release/backend /app/backend
|
||||
COPY --from=build /app/src/frontend/dist /app/dist
|
||||
|
||||
WORKDIR /app
|
||||
CMD ["./backend"]
|
||||
1396
backend/Cargo.lock
generated
Normal file
1396
backend/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
23
backend/Cargo.toml
Normal file
23
backend/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "backend"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
description = " "
|
||||
default-run = "backend"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
actix-web = "4"
|
||||
actix-web-lab = "^0"
|
||||
actix-files = "0.6"
|
||||
env_logger = "0.9.0"
|
||||
log = "0.4"
|
||||
diesel = { version = "1.4.8", features = ["postgres", "r2d2"] }
|
||||
diesel_migrations = "1.4"
|
||||
dotenv = "0.15.0"
|
||||
walkdir = "2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
common = { path = "../common" }
|
||||
5
backend/diesel.toml
Normal file
5
backend/diesel.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
# For documentation on how to configure this file,
|
||||
# see diesel.rs/guides/configuring-diesel-cli
|
||||
|
||||
[print_schema]
|
||||
file = "src/schema.rs"
|
||||
@@ -0,0 +1,6 @@
|
||||
-- This file was automatically created by Diesel to setup helper functions
|
||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||
-- changes will be added to existing projects as new migrations.
|
||||
|
||||
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
|
||||
DROP FUNCTION IF EXISTS diesel_set_updated_at();
|
||||
@@ -0,0 +1,36 @@
|
||||
-- This file was automatically created by Diesel to setup helper functions
|
||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||
-- changes will be added to existing projects as new migrations.
|
||||
|
||||
|
||||
|
||||
|
||||
-- Sets up a trigger for the given table to automatically set a column called
|
||||
-- `updated_at` whenever the row is modified (unless `updated_at` was included
|
||||
-- in the modified columns)
|
||||
--
|
||||
-- # Example
|
||||
--
|
||||
-- ```sql
|
||||
-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW());
|
||||
--
|
||||
-- SELECT diesel_manage_updated_at('users');
|
||||
-- ```
|
||||
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
|
||||
BEGIN
|
||||
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
|
||||
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
IF (
|
||||
NEW IS DISTINCT FROM OLD AND
|
||||
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
|
||||
) THEN
|
||||
NEW.updated_at := current_timestamp;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
@@ -0,0 +1 @@
|
||||
DROP TABLE pictures
|
||||
18
backend/migrations/2022-04-12-165834_create_photos/up.sql
Normal file
18
backend/migrations/2022-04-12-165834_create_photos/up.sql
Normal file
@@ -0,0 +1,18 @@
|
||||
CREATE TABLE pictures (
|
||||
id SERIAL PRIMARY KEY,
|
||||
filepath VARCHAR NOT NULL,
|
||||
created_at INTEGER,
|
||||
focal_length VARCHAR,
|
||||
shutter_speed VARCHAR,
|
||||
width INTEGER NOT NULL,
|
||||
height INTEGER NOT NULL,
|
||||
make VARCHAR,
|
||||
model VARCHAR,
|
||||
lens VARCHAR,
|
||||
orientation VARCHAR,
|
||||
fnumber FLOAT,
|
||||
iso INTEGER,
|
||||
exposure_program VARCHAR,
|
||||
exposure_compensation VARCHAR,
|
||||
thumbnail VARCHAR
|
||||
)
|
||||
16
backend/src/actions.rs
Normal file
16
backend/src/actions.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
extern crate diesel;
|
||||
|
||||
use backend::models::*;
|
||||
use diesel::prelude::*;
|
||||
use backend::*;
|
||||
|
||||
type DbError = Box<dyn std::error::Error + Send + Sync>;
|
||||
|
||||
pub fn list_pictures(conn: &PgConnection) -> Result<Vec<Picture>, DbError> {
|
||||
use self::schema::pictures::dsl::*;
|
||||
|
||||
Ok(pictures
|
||||
.limit(50)
|
||||
.order_by(created_at.desc())
|
||||
.load::<Picture>(conn)?)
|
||||
}
|
||||
238
backend/src/bin/add_picture.rs
Normal file
238
backend/src/bin/add_picture.rs
Normal file
@@ -0,0 +1,238 @@
|
||||
extern crate diesel;
|
||||
use backend::create_picture;
|
||||
use backend::models::NewPicture;
|
||||
use backend::establish_connection;
|
||||
|
||||
// use backend::*;
|
||||
use std::ffi::OsStr;
|
||||
use std::path::Path;
|
||||
use std::time::UNIX_EPOCH;
|
||||
use std::{fs, process::Command};
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct PhotoExif {
|
||||
#[serde(default, alias = "FocalLength")]
|
||||
focal_length: Option<String>,
|
||||
#[serde(default, alias = "ShutterSpeed")]
|
||||
shutter_speed: Option<String>,
|
||||
#[serde(alias = "ImageWidth")]
|
||||
width: i32,
|
||||
#[serde(alias = "ImageHeight")]
|
||||
height: i32,
|
||||
#[serde(default, alias = "Make")]
|
||||
make: Option<String>,
|
||||
#[serde(default, alias = "Model")]
|
||||
model: Option<String>,
|
||||
#[serde(default, alias = "LensID")]
|
||||
lens: Option<String>,
|
||||
#[serde(default, alias = "Orientation")]
|
||||
orientation: Option<String>,
|
||||
#[serde(default, alias = "FNumber")]
|
||||
fnumber: Option<f64>,
|
||||
#[serde(default, alias = "ExposureProgram")]
|
||||
exposure_program: Option<String>,
|
||||
#[serde(default, alias = "CreateDate")]
|
||||
created_at: Option<i32>,
|
||||
#[serde(default, alias = "ISO")]
|
||||
iso: Option<i32>,
|
||||
#[serde(default = "MaybeString::default", alias = "ExposureCompensation")]
|
||||
exposure_compensation: MaybeString,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum MaybeString {
|
||||
Number(i32),
|
||||
Str(String),
|
||||
None,
|
||||
}
|
||||
|
||||
impl MaybeString {
|
||||
fn default() -> MaybeString {
|
||||
MaybeString::None
|
||||
}
|
||||
fn to_opt_string(&self) -> Option<String> {
|
||||
if let MaybeString::Str(exp_comp) = &self {
|
||||
Some(exp_comp.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_hidden(entry: &DirEntry) -> bool {
|
||||
entry
|
||||
.file_name()
|
||||
.to_str()
|
||||
.map(|s| s.starts_with('.'))
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn is_image(entry: &DirEntry) -> bool {
|
||||
let allowed_extensions = ["cr2", "cr3", "jpg", "jpeg"];
|
||||
|
||||
let extension = if let Some(ext) = entry.path().extension() {
|
||||
ext
|
||||
} else {
|
||||
OsStr::new("")
|
||||
};
|
||||
|
||||
if allowed_extensions
|
||||
.iter()
|
||||
.all(|&v| v != extension.to_ascii_lowercase())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
static PICTURE_PATH: &str = "./pictures";
|
||||
static LIBRARY_PATH: &str = "./examples";
|
||||
|
||||
fn main() {
|
||||
let connection = establish_connection();
|
||||
|
||||
WalkDir::new(LIBRARY_PATH)
|
||||
.into_iter()
|
||||
.filter_map(Result::ok)
|
||||
.filter(|e| !e.file_type().is_dir())
|
||||
.filter(|e| !is_hidden(e))
|
||||
.filter(is_image)
|
||||
.into_iter()
|
||||
.for_each(|path| {
|
||||
let thumbnail = if let Ok(t) = extract_preview(path.path()) {
|
||||
t
|
||||
} else {
|
||||
println!("Could not create thumbnail");
|
||||
return;
|
||||
};
|
||||
let thumbnail = std::path::PathBuf::from(thumbnail.strip_prefix(PICTURE_PATH).unwrap());
|
||||
|
||||
let output = Command::new("exiftool")
|
||||
.arg("-j")
|
||||
.arg("-d")
|
||||
.arg("%s")
|
||||
.arg(path.path())
|
||||
.output()
|
||||
.expect("failed to execute exiftool");
|
||||
let pel: Vec<PhotoExif> = serde_json::from_slice(&output.stdout).unwrap();
|
||||
let pe = &pel[0];
|
||||
|
||||
println!("pe = {}", serde_json::to_string_pretty(pe).unwrap());
|
||||
|
||||
|
||||
let created_at: Option<i32> = if let Some(c) = pe.created_at {
|
||||
Some(c)
|
||||
} else {
|
||||
let metadata = fs::metadata(&path.path()).unwrap();
|
||||
if let Ok(time) = metadata.created() {
|
||||
Some(time.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
.try_into()
|
||||
.unwrap())
|
||||
} else {
|
||||
println!("Not supported on this platform or filesystem");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let filepath = path.path().to_string_lossy().into_owned();
|
||||
|
||||
let new_picture = NewPicture {
|
||||
filepath: filepath.clone(),
|
||||
created_at: created_at.clone(),
|
||||
focal_length: pe.focal_length.clone(),
|
||||
shutter_speed: pe.shutter_speed.clone(),
|
||||
width: pe.width,
|
||||
height: pe.height,
|
||||
make: pe.make.clone(),
|
||||
model: pe.model.clone(),
|
||||
lens: pe.lens.clone(),
|
||||
orientation: pe.orientation.clone(),
|
||||
fnumber: pe.fnumber,
|
||||
iso: pe.iso,
|
||||
exposure_program: pe.exposure_program.clone(),
|
||||
exposure_compensation: pe.exposure_compensation.to_opt_string(),
|
||||
thumbnail: Some(thumbnail.into_os_string().into_string().unwrap()),
|
||||
};
|
||||
|
||||
let pic = create_picture(&connection, new_picture);
|
||||
println!(
|
||||
"Created picture with filepath={} and id={}",
|
||||
filepath, pic
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn extract_preview(path: &Path) -> Result<std::path::PathBuf, Box<dyn std::error::Error>> {
|
||||
let file_name = if let Some(p) = path.file_name() {
|
||||
p
|
||||
} else {
|
||||
OsStr::new("")
|
||||
};
|
||||
let parent = if let Some(p) = path.parent() {
|
||||
p
|
||||
} else {
|
||||
Path::new(LIBRARY_PATH)
|
||||
};
|
||||
let relative_parent = parent
|
||||
.strip_prefix(LIBRARY_PATH)
|
||||
.expect("Could not remove prefix");
|
||||
|
||||
let thumb_path = Path::new(PICTURE_PATH).join(relative_parent);
|
||||
|
||||
if !thumb_path.exists() {
|
||||
fs::create_dir_all(&thumb_path).unwrap_or_else(|e| {
|
||||
panic!("Could not create directory {}: {}", thumb_path.display(), e)
|
||||
});
|
||||
}
|
||||
|
||||
let mut thumbnail = thumb_path.join(file_name);
|
||||
thumbnail.set_extension("jpg");
|
||||
|
||||
let extension = path.extension().unwrap();
|
||||
let jpegs = ["jpg", "jpeg"];
|
||||
if jpegs.iter().any(|&x| x == extension.to_ascii_lowercase()) {
|
||||
match fs::copy(path, &thumbnail) {
|
||||
Ok(_it) => return Ok(thumbnail),
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
}
|
||||
let _output_thumb = Command::new("exiftool")
|
||||
.arg("-if")
|
||||
.arg("$jpgfromraw")
|
||||
.arg("-b")
|
||||
.arg("-jpgfromraw")
|
||||
.arg("-w")
|
||||
.arg(thumb_path.join("%f.jpg"))
|
||||
.arg("-execute")
|
||||
.arg("-if")
|
||||
.arg("$previewimage")
|
||||
.arg("-b")
|
||||
.arg("-previewimage")
|
||||
.arg("-w")
|
||||
.arg(thumb_path.join("%f.jpg"))
|
||||
.arg("-execute")
|
||||
.arg("-tagsfromfile")
|
||||
.arg("@")
|
||||
.arg("-srcfile")
|
||||
.arg(thumb_path.join("%f.jpg"))
|
||||
.arg("-overwrite_original")
|
||||
.arg("-common_args")
|
||||
.arg("--ext")
|
||||
.arg("jpg")
|
||||
.arg(path)
|
||||
.output()
|
||||
.expect("failed to execute exiftool to extract thumbnail");
|
||||
// println!("{:?}", _output_thumb);
|
||||
|
||||
if thumbnail.exists() {
|
||||
Ok(thumbnail)
|
||||
} else {
|
||||
Err("Could not create thumbnail".into())
|
||||
}
|
||||
}
|
||||
21
backend/src/bin/show_pictures.rs
Normal file
21
backend/src/bin/show_pictures.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
extern crate diesel;
|
||||
|
||||
use self::models::*;
|
||||
use diesel::prelude::*;
|
||||
use backend::*;
|
||||
|
||||
fn main() {
|
||||
use self::schema::pictures::dsl::*;
|
||||
|
||||
let connection = establish_connection();
|
||||
let results = pictures
|
||||
.limit(5)
|
||||
.load::<Picture>(&connection)
|
||||
.expect("Error loading pictures");
|
||||
|
||||
println!("Displaying {} pictures", results.len());
|
||||
for picture in results {
|
||||
println!("filepath: {}", picture.filepath);
|
||||
println!("\tid: {}", picture.id);
|
||||
}
|
||||
}
|
||||
30
backend/src/lib.rs
Normal file
30
backend/src/lib.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
extern crate dotenv;
|
||||
|
||||
pub mod models;
|
||||
pub mod schema;
|
||||
|
||||
use self::models::{NewPicture};
|
||||
use diesel::prelude::*;
|
||||
use diesel::pg::PgConnection;
|
||||
use dotenv::dotenv;
|
||||
use std::env;
|
||||
|
||||
pub fn establish_connection() -> PgConnection {
|
||||
dotenv().ok();
|
||||
|
||||
let database_url = env::var("DATABASE_URL")
|
||||
.expect("DATABASE_URL must be set");
|
||||
PgConnection::establish(&database_url)
|
||||
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
|
||||
}
|
||||
|
||||
pub fn create_picture<'a>(conn: &PgConnection, new_picture: NewPicture) -> usize {
|
||||
use schema::pictures;
|
||||
|
||||
diesel::insert_into(pictures::table)
|
||||
.values(&new_picture)
|
||||
.execute(conn)
|
||||
.expect("Error saving new picture")
|
||||
}
|
||||
132
backend/src/main.rs
Normal file
132
backend/src/main.rs
Normal file
@@ -0,0 +1,132 @@
|
||||
#[macro_use]
|
||||
extern crate diesel;
|
||||
|
||||
#[macro_use]
|
||||
extern crate diesel_migrations;
|
||||
|
||||
use actix_files as fs;
|
||||
use actix_web::{Responder, Result};
|
||||
use actix_web_lab::web::spa;
|
||||
use backend::establish_connection;
|
||||
use actix_web::{
|
||||
// get, middleware, post, web, App, Error, HttpRequest, HttpResponse, HttpServer,
|
||||
get,
|
||||
middleware,
|
||||
web,
|
||||
App,
|
||||
HttpServer,
|
||||
};
|
||||
use diesel::prelude::*;
|
||||
use diesel::r2d2::{self, ConnectionManager};
|
||||
|
||||
// use uuid::Uuid;
|
||||
|
||||
mod actions;
|
||||
mod models;
|
||||
mod schema;
|
||||
|
||||
use common::OutputPicture;
|
||||
|
||||
type DbPool = r2d2::Pool<ConnectionManager<PgConnection>>;
|
||||
|
||||
// type DbError = Box<dyn std::error::Error + Send + Sync>;
|
||||
|
||||
#[get("/api/pictures/")]
|
||||
async fn get_pictures(pool: web::Data<DbPool>) -> Result<impl Responder> {
|
||||
let conn = pool.get().unwrap();
|
||||
let pics = if let Ok(p) = actions::list_pictures(&conn) {
|
||||
p
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
let pics: Vec<OutputPicture> = pics
|
||||
.iter()
|
||||
.map(|x| OutputPicture {
|
||||
thumbnail: x.thumbnail.clone(),
|
||||
width: x.width.try_into().unwrap(),
|
||||
height: x.height.try_into().unwrap(),
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(web::Json(pics))
|
||||
}
|
||||
|
||||
embed_migrations!("migrations");
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
dotenv::dotenv().ok();
|
||||
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
|
||||
|
||||
let connection = establish_connection();
|
||||
|
||||
// // This will run the necessary migrations.
|
||||
// embedded_migrations::run(&connection).expect("Could not migrate database.");
|
||||
|
||||
// By default the output is thrown out. If you want to redirect it to stdout, you
|
||||
// should call embedded_migrations::run_with_output.
|
||||
embedded_migrations::run_with_output(&connection, &mut std::io::stdout())
|
||||
.expect("Could not migrate database.");
|
||||
|
||||
// set up database connection pool
|
||||
let conn_spec = std::env::var("DATABASE_URL").expect("DATABASE_URL");
|
||||
let manager = ConnectionManager::<PgConnection>::new(conn_spec);
|
||||
let pool = r2d2::Pool::builder()
|
||||
.build(manager)
|
||||
.expect("Failed to create pool.");
|
||||
|
||||
let host = "0.0.0.0";
|
||||
let port = 8081;
|
||||
log::info!("starting HTTP server at http://{}:{}", host, port);
|
||||
|
||||
// Start HTTP server
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
// set up DB pool to be used with web::Data<Pool> extractor
|
||||
.app_data(web::Data::new(pool.clone()))
|
||||
.wrap(middleware::Logger::default())
|
||||
.service(get_pictures)
|
||||
.service(fs::Files::new("/api/pictures/", "./pictures/"))
|
||||
.service(
|
||||
spa()
|
||||
.index_file("./dist/index.html")
|
||||
.static_resources_mount("/")
|
||||
.static_resources_location("./dist")
|
||||
.finish(),
|
||||
)
|
||||
// .service(fs::Files::new("/", STATIC_FILES_PATH).index_file("index.html"))
|
||||
// .service(get_user)
|
||||
// .service(add_user)
|
||||
})
|
||||
.bind((host, port))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use actix_web::test;
|
||||
|
||||
#[actix_web::test]
|
||||
async fn user_routes() {
|
||||
std::env::set_var("RUST_LOG", "actix_web=debug");
|
||||
env_logger::init();
|
||||
dotenv::dotenv().ok();
|
||||
|
||||
let connspec = std::env::var("DATABASE_URL").expect("DATABASE_URL");
|
||||
let manager = ConnectionManager::<PgConnection>::new(connspec);
|
||||
let pool = r2d2::Pool::builder()
|
||||
.build(manager)
|
||||
.expect("Failed to create pool.");
|
||||
|
||||
let _app = test::init_service(
|
||||
App::new()
|
||||
.app_data(web::Data::new(pool.clone()))
|
||||
.wrap(middleware::Logger::default()), // .service(get_user)
|
||||
// .service(add_user),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
43
backend/src/models.rs
Normal file
43
backend/src/models.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use serde::Serialize;
|
||||
|
||||
use super::schema::pictures;
|
||||
|
||||
#[derive(Queryable, Serialize)]
|
||||
pub struct Picture {
|
||||
pub id: i32,
|
||||
pub filepath: String,
|
||||
pub created_at: Option<i32>,
|
||||
pub focal_length: Option<String>,
|
||||
pub shutter_speed: Option<String>,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub make: Option<String>,
|
||||
pub model: Option<String>,
|
||||
pub lens: Option<String>,
|
||||
pub orientation: Option<String>,
|
||||
pub fnumber: Option<f64>,
|
||||
pub iso: Option<i32>,
|
||||
pub exposure_program: Option<String>,
|
||||
pub exposure_compensation: Option<String>,
|
||||
pub thumbnail: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[table_name = "pictures"]
|
||||
pub struct NewPicture {
|
||||
pub filepath: String,
|
||||
pub created_at: Option<i32>,
|
||||
pub focal_length: Option<String>,
|
||||
pub shutter_speed: Option<String>,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub make: Option<String>,
|
||||
pub model: Option<String>,
|
||||
pub lens: Option<String>,
|
||||
pub orientation: Option<String>,
|
||||
pub fnumber: Option<f64>,
|
||||
pub iso: Option<i32>,
|
||||
pub exposure_program: Option<String>,
|
||||
pub exposure_compensation: Option<String>,
|
||||
pub thumbnail: Option<String>,
|
||||
}
|
||||
20
backend/src/schema.rs
Normal file
20
backend/src/schema.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
table! {
|
||||
pictures (id) {
|
||||
id -> Int4,
|
||||
filepath -> Varchar,
|
||||
created_at -> Nullable<Int4>,
|
||||
focal_length -> Nullable<Varchar>,
|
||||
shutter_speed -> Nullable<Varchar>,
|
||||
width -> Int4,
|
||||
height -> Int4,
|
||||
make -> Nullable<Varchar>,
|
||||
model -> Nullable<Varchar>,
|
||||
lens -> Nullable<Varchar>,
|
||||
orientation -> Nullable<Varchar>,
|
||||
fnumber -> Nullable<Float8>,
|
||||
iso -> Nullable<Int4>,
|
||||
exposure_program -> Nullable<Varchar>,
|
||||
exposure_compensation -> Nullable<Varchar>,
|
||||
thumbnail -> Nullable<Varchar>,
|
||||
}
|
||||
}
|
||||
10
common/Cargo.toml
Normal file
10
common/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "common"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
8
common/src/lib.rs
Normal file
8
common/src/lib.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize,Deserialize)]
|
||||
pub struct OutputPicture {
|
||||
pub thumbnail: Option<String>,
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
26
docker-compose.yml
Normal file
26
docker-compose.yml
Normal file
@@ -0,0 +1,26 @@
|
||||
version: '3.1'
|
||||
|
||||
services:
|
||||
|
||||
db:
|
||||
image: postgres
|
||||
#restart: always
|
||||
ports:
|
||||
- 5432:5432
|
||||
environment:
|
||||
POSTGRES_USER: user
|
||||
POSTGRES_PASSWORD: password
|
||||
|
||||
adminer:
|
||||
image: adminer
|
||||
#restart: always
|
||||
ports:
|
||||
- 3000:8080
|
||||
|
||||
# photos:
|
||||
# build: .
|
||||
# environment:
|
||||
# - DATABASE_URL="postgres://user:password@db/diesel_demo"
|
||||
# ports:
|
||||
# - 8081:8081
|
||||
|
||||
1
frontend/.gitignore
vendored
Normal file
1
frontend/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
dist/
|
||||
711
frontend/Cargo.lock
generated
Normal file
711
frontend/Cargo.lock
generated
Normal file
@@ -0,0 +1,711 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
|
||||
|
||||
[[package]]
|
||||
name = "anymap"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33954243bd79057c2de7338850b85983a44588021f8a5fee574a8888c6de4344"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "boolinator"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfa8873f51c92e232f9bac4065cddef41b714152812bfc5f7672ba16d6ef8cd9"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-match"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8100e46ff92eb85bf6dc2930c73f2a4f7176393c84a9446b3d501e1b354e7b34"
|
||||
|
||||
[[package]]
|
||||
name = "console_error_panic_hook"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8d976903543e0c48546a91908f21588a680a8c8f984df9a5d69feccb2b2a211"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "convert_case"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustc_version",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "279fb028e20b3c4c320317955b77c5e0c9701f05a1d309905d6fc702cdc5053e"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "gloo"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68ce6f2dfa9f57f15b848efa2aade5e1850dc72986b87a2b0752d44ca08f4967"
|
||||
dependencies = [
|
||||
"gloo-console-timer",
|
||||
"gloo-events",
|
||||
"gloo-file",
|
||||
"gloo-timers",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-console-timer"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b48675544b29ac03402c6dffc31a912f716e38d19f7e74b78b7e900ec3c941ea"
|
||||
dependencies = [
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-events"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "088514ec8ef284891c762c88a66b639b3a730134714692ee31829765c5bc814f"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-file"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f9fecfe46b5dc3cc46f58e98ba580cc714f2c93860796d002eb3527a465ef49"
|
||||
dependencies = [
|
||||
"gloo-events",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "integer-sqrt"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "276ec31bcb4a9ee45f58bec6f9ec700ae4cf4f4f8f2fa7e06cb406bd5ffdd770"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6607c62aa161d23d17a9072cc5da0be67cdfc89d3afb1e8d9c842bebc2525ffe"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
|
||||
|
||||
[[package]]
|
||||
name = "memory_units"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
dependencies = [
|
||||
"lexical-core",
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathfinding"
|
||||
version = "3.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84da3eab6c7638931f4876ebb03455be74db8eab1e344cd5a90daba8b3ad2f22"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
"integer-sqrt",
|
||||
"itertools",
|
||||
"num-traits",
|
||||
"rustc-hash",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||
dependencies = [
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7ed8b8c7b886ea3ed7dde405212185f423ab44682667c8c6dd14aa1d9f6612"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||
dependencies = [
|
||||
"pest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1873d832550d4588c3dbc20f01361ab00bfe741048f71e3fecf145a7cc18b29c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5491a68ab4500fa6b4d726bd67408630c3dbe9c4fe7bda16d5c82a1fd8c7340a"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fba7978c679d53ce2d0ac80c8c175840feb849a161664365d1287b41f2e67f1"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c441e177922bc58f1e12c022624b6216378e5febc2f0533e41ba443d505b80aa"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d94ac45fcf608c1f45ef53e748d35660f168490c10b23704c7779ab8f5c3048"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.81"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a89911bd99e5f3659ec4acf9c4d93b0a90fe4a2a11f15328472058edc5261be"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.51"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wee_alloc"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb3b5a6b2bb17cb6ad44a2e68a43e8d2722c997da10e928665c72ec6c0a0b8e"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"libc",
|
||||
"memory_units",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "ybc"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c409d21870c31cc3beb3b5ba8447306ecfac198876fa73bdce861b23299121"
|
||||
dependencies = [
|
||||
"derive_more",
|
||||
"web-sys",
|
||||
"yew",
|
||||
"yew-router",
|
||||
"yewtil",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yew"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4d5154faef86dddd2eb333d4755ea5643787d20aca683e58759b0e53351409f"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"anymap",
|
||||
"bincode",
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-match",
|
||||
"console_error_panic_hook",
|
||||
"gloo",
|
||||
"http",
|
||||
"indexmap",
|
||||
"js-sys",
|
||||
"log",
|
||||
"ryu",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"slab",
|
||||
"thiserror",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"yew-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yew-example"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"console_error_panic_hook",
|
||||
"pathfinding",
|
||||
"wasm-bindgen",
|
||||
"wee_alloc",
|
||||
"ybc",
|
||||
"yew",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yew-macro"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6e23bfe3dc3933fbe9592d149c9985f3047d08c637a884b9344c21e56e092ef"
|
||||
dependencies = [
|
||||
"boolinator",
|
||||
"lazy_static",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yew-router"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27666236d9597eac9be560e841e415e20ba67020bc8cd081076be178e159c8bc"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"cfg-match",
|
||||
"gloo",
|
||||
"js-sys",
|
||||
"log",
|
||||
"nom",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
"yew",
|
||||
"yew-router-macro",
|
||||
"yew-router-route-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yew-router-macro"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c0ace2924b7a175e2d1c0e62ee7022a5ad840040dcd52414ce5f410ab322dba"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"yew-router-route-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yew-router-route-parser"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de4a67208fb46b900af18a7397938b01f379dfc18da34799cfa8347eec715697"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yewtil"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8543663ac49cd613df079282a1d8bdbdebdad6e02bac229f870fd4237b5d9aaa"
|
||||
dependencies = [
|
||||
"log",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"yew",
|
||||
]
|
||||
22
frontend/Cargo.toml
Normal file
22
frontend/Cargo.toml
Normal file
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "yew-example"
|
||||
version = "0.1.0"
|
||||
authors = ["Anthony Dodd <dodd.anthonyjosiah@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1.6"
|
||||
wasm-bindgen = "=0.2.81"
|
||||
wee_alloc = "0.4.5"
|
||||
ybc = "0.2"
|
||||
yew = "0.18"
|
||||
pathfinding = "3.0.13"
|
||||
common = { path = "../common" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
anyhow = "1.0.58"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
demo-abc = []
|
||||
demo-xyz = []
|
||||
7
frontend/Trunk.toml
Normal file
7
frontend/Trunk.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[build]
|
||||
target = "index.html"
|
||||
dist = "dist"
|
||||
port = 3000
|
||||
|
||||
[[proxy]]
|
||||
backend = "http://localhost:8081/api"
|
||||
19
frontend/index.html
Normal file
19
frontend/index.html
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>Trunk | Yew | YBC</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.3/css/bulma.min.css"/>
|
||||
|
||||
<link data-trunk rel="scss" href="src/index.scss"/>
|
||||
<link data-trunk rel="scss" data-inline href="src/inline-scss.scss"/>
|
||||
<link data-trunk rel="css" href="src/app.css"/>
|
||||
<link data-trunk rel="icon" href="src/yew.svg"/>
|
||||
<link data-trunk rel="copy-file" href="src/yew.svg"/>
|
||||
<base data-trunk-public-url/>
|
||||
</head>
|
||||
<body>
|
||||
<link data-trunk rel="rust" href="Cargo.toml" data-wasm-opt="z" data-bin="yew-example" data-cargo-features="demo-abc,demo-xyz"/>
|
||||
</body>
|
||||
</html>
|
||||
1
frontend/src/app.css
Normal file
1
frontend/src/app.css
Normal file
@@ -0,0 +1 @@
|
||||
body {}
|
||||
3
frontend/src/index.scss
Normal file
3
frontend/src/index.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
@charset "utf-8";
|
||||
|
||||
html {}
|
||||
4
frontend/src/inline-scss.scss
Normal file
4
frontend/src/inline-scss.scss
Normal file
@@ -0,0 +1,4 @@
|
||||
.trunk_yew_example_fancy_green {
|
||||
$nice_color: green;
|
||||
background-color: $nice_color;
|
||||
}
|
||||
196
frontend/src/main.rs
Normal file
196
frontend/src/main.rs
Normal file
@@ -0,0 +1,196 @@
|
||||
#![recursion_limit = "1024"]
|
||||
|
||||
#[global_allocator]
|
||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||
|
||||
use common::OutputPicture;
|
||||
use console_error_panic_hook::set_once as set_panic_hook;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use ybc::TileCtx::{Ancestor, Child, Parent};
|
||||
use yew::prelude::*;
|
||||
// use pathfinding::directed::dijkstra;
|
||||
use yew::format::Json;
|
||||
use yew::format::Nothing;
|
||||
|
||||
use yew::services::fetch::FetchService;
|
||||
use yew::services::ConsoleService;
|
||||
use yew::services::fetch::FetchTask;
|
||||
use yew::services::fetch::Request;
|
||||
use yew::services::fetch::Response;
|
||||
|
||||
pub enum Msg {
|
||||
GetPictures,
|
||||
ReceiveResponse(Result<Vec<OutputPicture>, anyhow::Error>),
|
||||
}
|
||||
|
||||
struct App {
|
||||
pictures: Option<Vec<OutputPicture>>,
|
||||
fetch_task: Option<FetchTask>,
|
||||
link: ComponentLink<Self>,
|
||||
error: Option<String>,
|
||||
}
|
||||
|
||||
impl App {
|
||||
fn view_fetching(&self) -> Html {
|
||||
if self.fetch_task.is_some() {
|
||||
html! { <p>{ "Fetching data..." }</p> }
|
||||
} else {
|
||||
html! { <p></p> }
|
||||
}
|
||||
}
|
||||
fn view_error(&self) -> Html {
|
||||
if let Some(ref error) = self.error {
|
||||
html! { <p>{ error.clone() }</p> }
|
||||
} else {
|
||||
html! {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Component for App {
|
||||
type Message = Msg;
|
||||
type Properties = ();
|
||||
|
||||
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||
Self {
|
||||
fetch_task: None,
|
||||
link,
|
||||
pictures: None,
|
||||
error: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Message) -> bool {
|
||||
match msg {
|
||||
Msg::GetPictures => {
|
||||
let request = Request::get("http://localhost/api/pictures/")
|
||||
.body(Nothing)
|
||||
.expect("Could not build that request");
|
||||
let callback =
|
||||
self.link
|
||||
.callback(|response: Response<Json<Result<Vec<OutputPicture>, anyhow::Error>>>| {
|
||||
let Json(data) = response.into_body();
|
||||
Msg::ReceiveResponse(data)
|
||||
});
|
||||
let task = FetchService::fetch(request, callback).expect("failed to start request");
|
||||
self.fetch_task = Some(task);
|
||||
true
|
||||
}
|
||||
Msg::ReceiveResponse(response) => {
|
||||
match response {
|
||||
Ok(pictures) => {
|
||||
self.pictures = Some(pictures);
|
||||
}
|
||||
Err(error) => {
|
||||
self.error = Some(error.to_string());
|
||||
}
|
||||
}
|
||||
self.fetch_task = None;
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn change(&mut self, _: Self::Properties) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn view(&self) -> Html {
|
||||
html! {
|
||||
<>
|
||||
<ybc::Navbar
|
||||
classes=classes!("is-success")
|
||||
padded=true
|
||||
navbrand=html!{
|
||||
<ybc::NavbarItem>
|
||||
<ybc::Title classes=classes!("has-text-white") size=ybc::HeaderSize::Is4>{"Trunk | Yew | YBC"}</ybc::Title>
|
||||
</ybc::NavbarItem>
|
||||
}
|
||||
navstart=html!{}
|
||||
navend=html!{
|
||||
<>
|
||||
<ybc::NavbarItem>
|
||||
<ybc::ButtonAnchor classes=classes!("is-black", "is-outlined") rel=String::from("noopener noreferrer") target=String::from("_blank") href="https://github.com/thedodd/trunk">
|
||||
{"Trunk"}
|
||||
</ybc::ButtonAnchor>
|
||||
</ybc::NavbarItem>
|
||||
<ybc::NavbarItem>
|
||||
<ybc::ButtonAnchor classes=classes!("is-black", "is-outlined") rel=String::from("noopener noreferrer") target=String::from("_blank") href="https://yew.rs">
|
||||
{"Yew"}
|
||||
</ybc::ButtonAnchor>
|
||||
</ybc::NavbarItem>
|
||||
<ybc::NavbarItem>
|
||||
<ybc::ButtonAnchor classes=classes!("is-black", "is-outlined") rel=String::from("noopener noreferrer") target=String::from("_blank") href="https://github.com/thedodd/ybc">
|
||||
{"YBC"}
|
||||
</ybc::ButtonAnchor>
|
||||
</ybc::NavbarItem>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
<ybc::Hero
|
||||
classes=classes!("is-light")
|
||||
size=ybc::HeroSize::FullheightWithNavbar
|
||||
body=html!{
|
||||
<ybc::Container classes=classes!("is-centered")>
|
||||
<ybc::Tile ctx=Ancestor>
|
||||
<>
|
||||
<button onclick=self.link.callback(|_| Msg::GetPictures)>
|
||||
{ "Load pictures" }
|
||||
</button>
|
||||
{ self.view_fetching() }
|
||||
{ self.view_error() }
|
||||
</>
|
||||
<ybc::Tile ctx=Parent size=ybc::TileSize::Twelve>
|
||||
<ybc::Tile ctx=Parent>
|
||||
<ybc::Tile ctx=Child classes=classes!("notification", "is-success")>
|
||||
<ybc::Subtitle size=ybc::HeaderSize::Is3 classes=classes!("has-text-white")>{"Trunk"}</ybc::Subtitle>
|
||||
<p>{"Trunk is a WASM web application bundler for Rust."}</p>
|
||||
</ybc::Tile>
|
||||
</ybc::Tile>
|
||||
<ybc::Tile ctx=Parent>
|
||||
<ybc::Tile ctx=Child classes=classes!("notification", "is-success")>
|
||||
<ybc::Icon size=ybc::Size::Large classes=classes!("is-pulled-right")><img src="yew.svg"/></ybc::Icon>
|
||||
<ybc::Subtitle size=ybc::HeaderSize::Is3 classes=classes!("has-text-white")>
|
||||
{"Yew"}
|
||||
</ybc::Subtitle>
|
||||
<p>{"Yew is a modern Rust framework for creating multi-threaded front-end web apps with WebAssembly."}</p>
|
||||
</ybc::Tile>
|
||||
</ybc::Tile>
|
||||
<ybc::Tile ctx=Parent>
|
||||
<ybc::Tile ctx=Child classes=classes!("notification", "is-success")>
|
||||
<ybc::Subtitle size=ybc::HeaderSize::Is3 classes=classes!("has-text-white")>{"YBC"}</ybc::Subtitle>
|
||||
<p>{"A Yew component library based on the Bulma CSS framework."}</p>
|
||||
</ybc::Tile>
|
||||
</ybc::Tile>
|
||||
</ybc::Tile>
|
||||
</ybc::Tile>
|
||||
</ybc::Container>
|
||||
}>
|
||||
</ybc::Hero>
|
||||
</>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen(inline_js = "export function snippetTest() { console.log('Hello from JS FFI!'); }")]
|
||||
extern "C" {
|
||||
fn snippetTest();
|
||||
}
|
||||
|
||||
fn main() {
|
||||
set_panic_hook();
|
||||
snippetTest();
|
||||
|
||||
// Show off some feature flag enabling patterns.
|
||||
#[cfg(feature = "demo-abc")]
|
||||
{
|
||||
ConsoleService::log("feature `demo-abc` enabled");
|
||||
}
|
||||
#[cfg(feature = "demo-xyz")]
|
||||
{
|
||||
ConsoleService::log("feature `demo-xyz` enabled");
|
||||
}
|
||||
|
||||
yew::start_app::<App>();
|
||||
}
|
||||
7
frontend/src/yew.svg
Normal file
7
frontend/src/yew.svg
Normal file
@@ -0,0 +1,7 @@
|
||||
<svg width="75" height="82" viewBox="0 0 75 82" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="38" cy="40" r="25" fill="#FFEFB8"/>
|
||||
<path d="M38.2373 41.0339L14 14" stroke="#444444" stroke-width="6" stroke-linecap="round"/>
|
||||
<path d="M38.2373 41.0339L62.4746 14" stroke="#444444" stroke-width="6" stroke-linecap="round"/>
|
||||
<path d="M38.2373 41.0339L38.2373 69" stroke="#444444" stroke-width="6" stroke-linecap="round"/>
|
||||
<circle cx="38" cy="41" r="7" fill="#FFD707" stroke="#444444" stroke-width="4"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 518 B |
3
src/main.rs
Normal file
3
src/main.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
println!("Hello world!")
|
||||
}
|
||||
Reference in New Issue
Block a user