drteeth 0.2.1

Low-complexity web technology user interface library for desktop apps
Documentation
// Copyright 2022 Daniel Arbuckle
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Run with `cargo run --example tide --no-default-features --features "asyncstd examples"`

use std::net::TcpListener;
use std::sync::{Arc, Mutex};

use async_std::prelude::FutureExt;
use tide::prelude::*;
use tide::Request;

struct State {
    shutdown: Option<async_std::channel::Sender<()>>,
}

async fn index(_req: Request<Arc<Mutex<State>>>) -> tide::Result {
    let mut resp: tide::Response = r#"Hello World. <form method="post" action="/shutdown"><input type="submit" value="Shut Down"></form>"#.into();
    resp.set_content_type("text/html");
    Ok(resp)
}

async fn shutdown(req: Request<Arc<Mutex<State>>>) -> tide::Result {
    if let Some(shutdown) = req.state().lock().unwrap().shutdown.take() {
        let _ = shutdown.try_send(());
        Ok("Shutting down".into())
    } else {
        Ok("Shutdown already initiated".into())
    }
}

fn main() {
    // Non-async set up. We can do any server configuration that
    // doesn't need an async context here, parse the command line,
    // etc.

    let (shutdown_tx, shutdown_rx) = async_std::channel::bounded::<()>(1);

    let state = Arc::new(Mutex::new(State {
        shutdown: Some(shutdown_tx),
    }));

    let mut app = tide::with_state(state);
    app.at("/").get(index);
    app.at("/shutdown").post(shutdown);

    // Pick a random available port on the loopback interface. Not
    // actually required, but usually a good idea.

    let ear = TcpListener::bind("127.0.0.1:0").expect("bind port");
    let addr = ear.local_addr().expect("retrieve port");

    // Here we start up the webview and actually run the server. The
    // async block is the asynchronous entry point for the program
    // (like tokio::main or async_std::main), so any server set up
    // code that needs to run in an async context should be there, as
    // well as the code to actually run the server. When the async
    // block finishes executing, the program will close.

    drteeth::launch(
        "Tide Demo",
        addr,
        async move {
            app.listen(ear).await.unwrap();
        }
        .race(async move {
            shutdown_rx.recv().await.unwrap();
        }),
    )
    .unwrap();
}