Skip to content

Commit

Permalink
Add remaining pages. Add CI.
Browse files Browse the repository at this point in the history
  • Loading branch information
mleonhard committed Nov 4, 2023
1 parent fc01ff0 commit 8599c55
Show file tree
Hide file tree
Showing 9 changed files with 509 additions and 23 deletions.
36 changes: 36 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

env:
CARGO_TERM_COLOR: always
CARGO_HOME: cargo/

jobs:
check:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/cache@v3
with:
path: |
cargo/
target/
key: ${{ runner.os }}-gen3
- run: du -sh cargo/ target/ || true
- name: Version
run: rustc --version && cargo --version
- name: Install rustfmt
run: cargo fmt --version || rustup component add rustfmt
- name: Install clippy
run: cargo clippy --version || rustup component add clippy
- run: cargo install cargo-readme
- name: Check
run: ./check.sh
- run: du -sh cargo/ target/ || true
4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ version = "0.1.0"
[dependencies]
applin = { version = "0.1.0", features = ["servlin"], path = "../applin-rust" }
dotenv = "^0.15.0"
servlin = { version = "^0.4.0", features = ["json"] }
include_dir = "^0.7.3"
servlin = { version = "^0.4.0", features = ["include_dir", "json"] }
#nanorand = { version = "^0.7.0", features = ["alloc", "chacha", "std"] }
#once_cell = "1"
safe-regex = "^0.2.5"
serde = { version = "1", features = ["derive"] }
temp-dir = "^0.1.11"
14 changes: 14 additions & 0 deletions check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
echo PWD=$PWD
set -e
set -x
time cargo check
time cargo check --all-targets --all-features
time cargo build
time cargo build --all-targets --all-features
time cargo fmt -- --check
time cargo clippy -- -D clippy::pedantic
time cargo clippy --all-targets --all-features -- -D clippy::pedantic
time cargo test --tests
time cargo test --all-targets --all-features
echo "$0 finished"
Binary file added public/placeholder-200x200.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 48 additions & 0 deletions src/account.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#![allow(clippy::module_name_repetitions)]
use crate::HOME_PAGE_KEY;
use applin::{
applin_response, checkbox, form, form_button, nav_button, nav_page, push, replace_all, rpc,
scroll, textfield, user_error,
};
use safe_regex::{regex, Matcher0};
use serde::Deserialize;
use servlin::{Error, Request, Response};

pub const CREATE_ACCOUNT_KEY: &str = "/create_account";
pub fn create_account(req: &Request) -> Result<Response, Error> {
#[derive(Deserialize)]
struct Input {
#[serde(default)]
username: String,
#[serde(default)]
agree: bool,
}
let input: Input = req.json()?;
if !input.agree {
return Ok(user_error("You must agree to the terms"));
}
let username = input.username.trim();
let matcher: Matcher0<_> = regex!(br"[a-z0-9]+");
if !matcher.is_match(username.as_bytes()) {
return Ok(user_error("Please enter letters and numbers"));
}
Ok(Response::ok_200())
}

pub const NEW_ACCOUNT_PAGE_KEY: &str = "/new_account_page";
pub fn new_account_page() -> Response {
applin_response(nav_page(
"New Account",
scroll(form((
textfield("username").with_label("Username"),
nav_button("Terms", [push("/terms")]),
nav_button("Privacy", [push("/privacy")]),
checkbox("agree").with_text("I agree"),
form_button(
"Create Account",
[rpc("/create_account"), replace_all(HOME_PAGE_KEY)],
),
))),
))
.unwrap()
}
42 changes: 26 additions & 16 deletions src/index.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
#![allow(clippy::module_name_repetitions)]

use applin::{
applin_response, column, form_section, launch_url, nav_button, nav_page, push, scroll, text,
};
use servlin::Response;

use crate::account::NEW_ACCOUNT_PAGE_KEY;
use crate::pages::{INERT_PAGE_KEY, NAV_PAGE_PAGE_KEY, PLAIN_PAGE_PAGE_KEY, POLLED_PAGE_KEY};
use crate::widgets::{
BACK_BUTTON_PAGE_KEY, BUTTON_PAGE_KEY, CHECKBOX_PAGE_KEY, ERROR_TEXT_PAGE_KEY,
FORM_BUTTON_PAGE_KEY, FORM_SECTION_PAGE_KEY, GROUPED_ROW_TABLE_PAGE_KEY, IMAGE_PAGE_KEY,
NAV_BUTTON_PAGE_KEY, TEXTFIELD_PAGE_KEY, TEXT_PAGE_KEY,
};

pub fn index_page() -> Response {
applin_response(nav_page("Applin Rust Demo", scroll(column((
form_section("About", (
Expand All @@ -13,25 +23,25 @@ pub fn index_page() -> Response {
).with_sub_text("github.com/leonhard-llc/applin-rails-demo"),
)),
form_section("Pages", (
nav_button("Nav Page", [push("/nav_page_page")]),
nav_button("Plain Page", [push("/plain_page_page")]),
nav_button("Nav Page", [push(NAV_PAGE_PAGE_KEY)]),
nav_button("Plain Page", [push(PLAIN_PAGE_PAGE_KEY)]),
)),
form_section("Widgets", (
nav_button("Back Button", [push("/back_button_page")]),
nav_button("Button", [push("/button_page")]),
nav_button("Checkbox", [push("/checkbox_page")]),
nav_button("Error Text", [push("/error_text_page")]),
nav_button("Form Button", [push("/form_button_page")]),
nav_button("Form Section", [push("/form_section_page")]),
nav_button("Grouped Row Table", [push("/grouped_row_table_page")]),
nav_button("Image", [push("/image_page")]),
nav_button("Nav Button", [push("/nav_button_page")]),
nav_button("Text", [push("/text_page")]),
nav_button("Textfield", [push("/textfield_page")]),
nav_button("Back Button", [push(BACK_BUTTON_PAGE_KEY)]),
nav_button("Button", [push(BUTTON_PAGE_KEY)]),
nav_button("Checkbox", [push(CHECKBOX_PAGE_KEY)]),
nav_button("Error Text", [push(ERROR_TEXT_PAGE_KEY)]),
nav_button("Form Button", [push(FORM_BUTTON_PAGE_KEY)]),
nav_button("Form Section", [push(FORM_SECTION_PAGE_KEY)]),
nav_button("Grouped Row Table", [push(GROUPED_ROW_TABLE_PAGE_KEY)]),
nav_button("Image", [push(IMAGE_PAGE_KEY)]),
nav_button("Nav Button", [push(NAV_BUTTON_PAGE_KEY)]),
nav_button("Text", [push(TEXT_PAGE_KEY)]),
nav_button("Textfield", [push(TEXTFIELD_PAGE_KEY)]),
)),
form_section("Page Update Modes", (
nav_button("Inert", [push("/inert_page")]),
nav_button("Polled", [push("/polled_page")]),
nav_button("Inert", [push(INERT_PAGE_KEY)]),
nav_button("Polled", [push(POLLED_PAGE_KEY)]),
)),
form_section("Error Pages", (
nav_button("App Error", [push("/applin_app_error")]),
Expand All @@ -43,7 +53,7 @@ pub fn index_page() -> Response {
nav_button("Error Details", [push("/error_details")]),
)),
form_section("Example Pages", (
nav_button("New Account", [push("/new_account")]),
nav_button("New Account", [push(NEW_ACCOUNT_PAGE_KEY)]),
)),
)))).with_poll(30)).unwrap()
}
48 changes: 42 additions & 6 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
use std::sync::Arc;

use applin::user_error;
use include_dir::include_dir;
use servlin::log::log_request_and_response;
use servlin::reexport::{safina_executor, safina_timer};
use servlin::{socket_addr_all_interfaces, Error, HttpServerBuilder, Request, Response};
use temp_dir::TempDir;

mod account;
mod index;
mod pages;
mod widgets;

struct State {}

const PUBLIC_DIR: include_dir::Dir = include_dir!("public/");

// fn index(_state: Arc<State>, req: Request) -> Result<Response, Error> {
// #[derive(Deserialize)]
// struct Input {
Expand All @@ -21,16 +26,47 @@ struct State {}
// .unwrap())
// }

pub const OK_KEY: &str = "/ok";
pub const USER_ERROR_KEY: &str = "/user_error";
pub const SERVER_ERROR_KEY: &str = "/server_error";
pub const HOME_PAGE_KEY: &str = "/";
pub const PLACEHOLDER_IMAGE_KEY: &str = "/placeholder-200x200.png";

#[allow(clippy::needless_pass_by_value)]
fn handle_req(_state: Arc<State>, req: Request) -> Result<Response, Error> {
match (req.method(), req.url().path()) {
("GET", "/healthz") => Ok(Response::text(200, "success")),
("GET", "/ok") => Ok(Response::new(200)),
("GET", "/user_error") => Ok(user_error("example error")),
("GET", "/server_error") => Err(Error::server_error("server error")),
("GET", "/") => Ok(index::index_page()),
("GET", "/nav_page_page") => Ok(pages::nav_page_page()),
("GET", "/plain_page_page") => Ok(pages::plain_page_page()),
("GET" | "POST", OK_KEY) => Ok(Response::new(200)),
("GET" | "POST", USER_ERROR_KEY) => Ok(user_error("example error")),
("GET" | "POST", SERVER_ERROR_KEY) => Err(Error::server_error("server error")),
("POST", account::CREATE_ACCOUNT_KEY) => account::create_account(&req),
("GET" | "POST", account::NEW_ACCOUNT_PAGE_KEY) => Ok(account::new_account_page()),
("GET", HOME_PAGE_KEY) => Ok(index::index_page()),
("GET", pages::NAV_PAGE_PAGE_KEY) => Ok(pages::nav_page_page()),
("GET", pages::PLAIN_PAGE_PAGE_KEY) => Ok(pages::plain_page_page()),
("GET", pages::INERT_PAGE_KEY) => Ok(pages::inert_page()),
("GET", pages::POLLED_PAGE_KEY) => Ok(pages::polled_page()),
("GET", widgets::BACK_BUTTON_DEFAULT_PAGE_KEY) => Ok(widgets::back_button_default_page()),
("GET", widgets::BACK_BUTTON_DISABLED_PAGE_KEY) => Ok(widgets::back_button_disabled_page()),
("GET", widgets::BACK_BUTTON_MISSING_PAGE_KEY) => Ok(widgets::back_button_missing_page()),
("GET", widgets::BACK_BUTTON_PAGE_KEY) => Ok(widgets::back_button_page()),
("GET", widgets::BACK_BUTTON_RPC_ERROR_PAGE_KEY) => {
Ok(widgets::back_button_rpc_error_page())
}
("GET", widgets::BACK_BUTTON_RPC_OK_PAGE_KEY) => Ok(widgets::back_button_rpc_ok_page()),
("GET", widgets::BUTTON_PAGE_KEY) => Ok(widgets::button_page()),
("GET", widgets::BUTTON_PRESSED_PAGE_KEY) => Ok(widgets::button_pressed_page()),
("GET" | "POST", widgets::CHECKBOX_PAGE_KEY) => Ok(widgets::checkbox_page()),
("GET", widgets::ERROR_TEXT_PAGE_KEY) => Ok(widgets::error_text_page()),
("GET", widgets::FORM_BUTTON_PAGE_KEY) => Ok(widgets::form_button_page()),
("GET", widgets::FORM_SECTION_PAGE_KEY) => Ok(widgets::form_section_page()),
("GET", widgets::GROUPED_ROW_TABLE_PAGE_KEY) => Ok(widgets::grouped_row_table_page()),
("GET", widgets::IMAGE_PAGE_KEY) => Ok(widgets::image_page()),
("GET", widgets::NAV_BUTTON_PAGE_KEY) => Ok(widgets::nav_button_page()),
("GET", widgets::TABLE_PAGE_KEY) => Ok(widgets::table_page()),
("GET", widgets::TEXT_PAGE_KEY) => Ok(widgets::text_page()),
("GET" | "POST", widgets::TEXTFIELD_PAGE_KEY) => Ok(widgets::textfield_page()),
("GET", _) => Response::include_dir(&req, &PUBLIC_DIR),
_ => Ok(Response::text(404, "Not found")),
}
}
Expand Down
31 changes: 31 additions & 0 deletions src/pages.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use applin::{applin_response, button, form, nav_page, plain_page, pop, scroll, text};
use servlin::internal::FormatTime;
use servlin::Response;
use std::time::SystemTime;

pub const NAV_PAGE_PAGE_KEY: &str = "/nav_page_page";
pub fn nav_page_page() -> Response {
applin_response(nav_page(
"Nav Page",
Expand All @@ -9,6 +12,7 @@ pub fn nav_page_page() -> Response {
.unwrap()
}

pub const PLAIN_PAGE_PAGE_KEY: &str = "/plain_page_page";
pub fn plain_page_page() -> Response {
applin_response(plain_page(
"Nav Page",
Expand All @@ -20,3 +24,30 @@ pub fn plain_page_page() -> Response {
))
.unwrap()
}

pub const INERT_PAGE_KEY: &str = "/inert_page";
pub fn inert_page() -> Response {
applin_response(nav_page(
"Inert",
scroll(form((
text("This page updates when you load or refresh it (pull to refresh)."),
text(SystemTime::now().iso8601_utc()),
))),
))
.unwrap()
}

pub const POLLED_PAGE_KEY: &str = "/polled_page";
pub fn polled_page() -> Response {
applin_response(
nav_page(
"Polled",
scroll(form((
text("This page updates automatically every 2 seconds."),
text(SystemTime::now().iso8601_utc()),
))),
)
.with_poll(2),
)
.unwrap()
}
Loading

0 comments on commit 8599c55

Please sign in to comment.