Skip to content

Commit

Permalink
Merge branch 'main' into htmlx-example
Browse files Browse the repository at this point in the history
  • Loading branch information
fundon committed Dec 1, 2023
2 parents a2d31a2 + 2869844 commit 56f258b
Show file tree
Hide file tree
Showing 42 changed files with 553 additions and 69 deletions.
8 changes: 1 addition & 7 deletions .github/workflows/CI.yml → .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ env:
CARGO_NET_RETRY: 10
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
RUSTFLAGS: -Dwarnings
RUSTUP_MAX_RETRIES: 10

jobs:
Expand All @@ -29,7 +28,6 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- uses: Swatinem/rust-cache@v2
- run: cargo test --workspace --verbose

coverage:
Expand All @@ -39,7 +37,6 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@cargo-llvm-cov
- uses: Swatinem/rust-cache@v2
- name: Generate code coverage
run: cargo llvm-cov --all-features --workspace --lcov --output-path lcov.info
- name: Upload coverage to Codecov
Expand All @@ -54,16 +51,14 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@clippy
- uses: Swatinem/rust-cache@v2
- run: cargo clippy --tests -- -Dclippy::all -Dclippy::pedantic
- run: cargo clippy --tests

fmt:
name: Fmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo fmt --all -- --check

docs:
Expand All @@ -74,5 +69,4 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- uses: Swatinem/rust-cache@v2
- run: cargo doc --workspace --all-features --no-deps
22 changes: 20 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ members = [
"examples/unix-socket",
"examples/static-files/embed",
"examples/static-files/serve",
"examples/static-files/static-embed",
"examples/limits",
"examples/forms/form",
"examples/forms/multipart",
Expand All @@ -28,6 +29,7 @@ members = [
"examples/templates/*",
"examples/tracing",
"examples/graceful-shutdown",
"examples/databases/*",
]
resolver = "2"

Expand All @@ -39,6 +41,7 @@ homepage = "https://viz.rs"
documentation = "https://docs.rs/viz"
repository = "https://github.com/viz-rs/viz"
license = "MIT"
rust-version = "1.63" # follows `tokio` and `hyper`

[workspace.dependencies]
viz = { version = "0.5.0-rc.2", path = "viz" }
Expand All @@ -61,8 +64,7 @@ thiserror = "1.0"
path-tree = "0.7"

# http
# TODO: wait headers-v1.0
headers = { git = "https://github.com/hyperium/headers.git", rev = "4400aa9" }
headers = "0.4"
http = "1"
http-body = "1"
http-body-util = "0.1"
Expand Down Expand Up @@ -110,3 +112,19 @@ split-debuginfo = "unpacked"
[profile.dev.package."*"]
opt-level = 3
debug = false

[workspace.lints.rust]
unsafe_code = "forbid"
rust_2018_idioms = "warn"
single_use_lifetimes = "warn"
non_ascii_idents = "warn"
unreachable_pub = "warn"
missing_debug_implementations = "warn"
missing_docs = "warn"

[workspace.lints.clippy]
all = "deny"
pedantic = "deny"
module_name_repetitions = { level = "allow", priority = 1 }
too_many_lines = { level = "allow", priority = 1 }
type_complexity = { level = "allow", priority = 1 }
3 changes: 2 additions & 1 deletion clippy.toml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
msrv = "1.64"
# Clippy configuration
# https://doc.rust-lang.org/nightly/clippy/lint_configuration.html
4 changes: 4 additions & 0 deletions examples/databases/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Databases

## Examples
- [sea-orm](./sea-orm/README.md)
17 changes: 17 additions & 0 deletions examples/databases/sea-orm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "sea-orm-example"
version = "0.1.0"
edition.workspace = true
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
viz = { workspace = true, features = ["serve"] }
serde.workspace = true

tokio = { workspace = true, features = [ "rt-multi-thread", "macros" ] }
sea-orm = { version = "0.12.7", features = ["runtime-tokio-rustls", "sqlx-sqlite"] }

[lints]
workspace = true
25 changes: 25 additions & 0 deletions examples/databases/sea-orm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Viz SeaOrm example

UI inspired by: https://github.com/HapticX/happyx/blob/master/examples/todo/README.md

## USAGE
sqlite use `in-memory`` mode,every time run the app, content reset!
```base
carog run
```

## FUNCTION IMPL

- [x] list
- [x] create
- [x] update
- [ ] delete

## SCREENSHOT

![SeaOrm Demo](./sea-orm-demo.gif)

## FAQ
- libsqlite3 error: you need install libsqlite3 for your system

- sea-orm doc: https://www.sea-ql.org/sea-orm-tutorial/ch01-00-build-backend-getting-started.html
154 changes: 154 additions & 0 deletions examples/databases/sea-orm/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.tailwindcss.com"></script>
<title>Viz SeaOrm Todo Demo</title>
</head>

<body>
<div class="flex justify-center items-center w-screen h-screen bg-gray-100">
<div class="flex flex-col gap-4 px-8 py-4 bg-white rounded-2xl drop-shadow-xl">
<div class="flex justify-between gap-2 items-center">
<input id="input" maxlength="20" placeholder="Enter task..." type="text"
class="rounded-full bg-gray-100 px-4 py-2 outline-0 border-0">
<button
class="flex text-xl font-semibold w-10 h-10 justify-center items-center rounded-full cursor-pointer bg-green-300"
id="submit">+</button>
</div>

<div id="todo-container" class="flex flex-col gap-2">
</div>
</div>
</div>
</body>
<script>

var checked_tpl = `
<div
class="flex gap-2 bg-green-400 rounded-xl px-4 py-2 w-full cursor-pointer select-none transition-all">
<div class="flex justify-center items-center w-6 h-6 rounded-md outline outline-1 outline-black">
</div>
</div>
`;
var unchecked_tpl = `
<div
class="flex gap-2 bg-red-400 rounded-xl px-4 py-2 w-full cursor-pointer select-none transition-all">
<div class="flex justify-center items-center w-6 h-6 rounded-md outline outline-1 outline-black">
</div>
</div>
`

var template = {
update: (todos) => {
var todo_container = document.getElementById("todo-container")
todo_container.innerHTML = "";
todos.forEach(todo => {
if (todo['completed']) {
template.create_div(todo, checked_tpl, todo_container);
} else {
template.create_div(todo, unchecked_tpl, todo_container);

}
});
},
create_div: (todo, html, parent) => {
var div = document.createElement("div");
div.setAttribute("id", todo['id']);
div.innerHTML = html;
parent.appendChild(div);
var child = document.createElement("div")
child.classList = "flex-1"
child.innerHTML = `
${todo['text']}
`

var close_div = document.createElement("div");
close_div.classList = "text-xs rounded-full bg-gray-100 p-1"
close_div.innerHTML = "❌";
close_div.addEventListener("click", (event) => {
event.stopPropagation();
event.preventDefault();
service.delete(todo['id']);

})
var new_el = document.getElementById(todo['id']);
new_el.getElementsByTagName("div")[0]
.appendChild(child);

new_el.getElementsByTagName("div")[0].appendChild(close_div)
new_el.addEventListener("click", () => {
todo['completed'] = !todo['completed'];
service.update(todo);
})
}

}

var service = {
load: () => {
fetch("/todos")
.then(response => response.json())
.then(json => {
template.update(json);
})
.catch(err => console.log('Request Failed', err));
},
create: (task) => {
fetch("/todos", { method: "POST", headers: { "Content-Type": "application/json; charset=utf-8" }, body: JSON.stringify(task) })
.then(response => response.json())
.then(json => {
service.load();
})
.catch(err => console.log('Request Failed', err));
},
update: (task) => {
fetch(`/todos/${task['id']}`, { method: "PUT", headers: { "Content-Type": "application/json; charset=utf-8" }, body: JSON.stringify(task) })
.then(response => response.json())
.then(json => {
service.load();
})
.catch(err => console.log('Request Failed', err));
},
delete: (id) => {
fetch(`/todos/${id}`, { method: "DELETE", headers: { "Content-Type": "application/json; charset=utf-8" } })
.then(response => response.json())
.then(json => {
service.load();
})
.catch(err => console.log('Request Failed', err));
}
}
document.addEventListener("DOMContentLoaded", () => {
service.load();
var input = document.getElementById("input");

var create_task = () => {
var text = input.value;
if (!text) {
alert("task is empty");
return;

}
service.create({ "text": text, "completed": false });
input.value = ""
}
input.addEventListener("keypress", () => {
if (event.key === "Enter") {
event.preventDefault();
create_task();
}
})

document.getElementById("submit").addEventListener("click", () => {
create_task()

})
})
</script>

</html>
Binary file added examples/databases/sea-orm/sea-orm-demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 78 additions & 0 deletions examples/databases/sea-orm/src/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//! web api mod
use crate::entities::todo::{ActiveModel, Entity as todoEntity, Model as Todo};

use sea_orm::{
ActiveModelTrait, ActiveValue::NotSet, DatabaseConnection, EntityTrait, Set, TryIntoModel,
};
use viz::{
types::{Json, Params, State},
IntoResponse, Request, RequestExt, Response, ResponseExt, Result,
};

/// list todos
/// # Errors
/// - `viz::Error`
pub async fn list(mut req: Request) -> Result<Response> {
let State(db) = req.extract::<State<DatabaseConnection>>().await?;
let todos = todoEntity::find()
.all(&db)
.await
.map_err(|err| err.to_string().into_error())?;
Ok(Response::json(todos)?)
}

/// create todos
/// # Errors
/// - `viz::Error`
pub async fn create(mut req: Request) -> Result<Response> {
let (State(db), Json(todo)) = req
.extract::<(State<DatabaseConnection>, Json<Todo>)>()
.await?;

let mut todo_am: ActiveModel = todo.into();
todo_am.id = NotSet;
let result = todo_am
.insert(&db)
.await
.map_err(|err| err.to_string().into_error())?;
let todo_new: Todo = result
.try_into_model()
.map_err(|err| err.to_string().into_error())?;
Ok(Response::json(todo_new)?)
}

/// update todos
/// PUT /todos/:id
/// # Errors
/// - `viz::Error`
pub async fn update(mut req: Request) -> Result<Response> {
let (State(db), Params(id), Json(todo)) = req
.extract::<(State<DatabaseConnection>, Params<i32>, Json<Todo>)>()
.await?;
let mut todo_am: ActiveModel = todo.clone().into();
todo_am.id = Set(id);
todo_am.completed = Set(todo.completed);
let model = todo_am
.update(&db)
.await
.map_err(|err| err.to_string().into_error())?;

Ok(Response::json(model)?)
}

/// delete todos
/// DELETE /todos/:id
/// # Errors
/// - `viz::Error`
pub async fn delete(mut req: Request) -> Result<Response> {
let (State(db), Params(id)) = req
.extract::<(State<DatabaseConnection>, Params<i32>)>()
.await?;
let delete_result = todoEntity::delete_by_id(id)
.exec(&db)
.await
.map_err(|err| err.to_string().into_error())?;
let rows_affected = delete_result.rows_affected;
Ok(Response::json(rows_affected)?)
}
Loading

0 comments on commit 56f258b

Please sign in to comment.