#web #button #axum #website #host #focus #user

btnify

托管带按钮的网站,让您可以专注于重要的事情!

5个版本 (稳定)

2.0.2 2023年11月19日
1.0.0 2023年11月10日
0.3.0 2023年10月29日

945HTTP服务器

每月46次下载

MIT 许可证

27KB
386

██████╗ ████████╗███╗   ██╗██╗███████╗██╗   ██╗
██╔══██╗╚══██╔══╝████╗  ██║██║██╔════╝╚██╗ ██╔╝
██████╔╝   ██║   ██╔██╗ ██║██║█████╗   ╚████╔╝ 
██╔══██╗   ██║   ██║╚██╗██║██║██╔══╝    ╚██╔╝  
██████╔╝   ██║   ██║ ╚████║██║██║        ██║   
╚═════╝    ╚═╝   ╚═╝  ╚═══╝╚═╝╚═╝        ╚═╝   

rust library to simplify allowing user input over the web

License: MIT GitHub Workflow Status (with event) docs.rs

托管带按钮的网站,让您可以专注于重要的事情!

Btnify是一个小型库,可以让您托管一个带有按钮的网站,这些按钮在点击时会调用函数或闭包。在底层,Btnify使用Axum。这个库很简单,但是它确实工作,并且它是开源的!请提交任何改进的拉取请求:我会非常感激的。

安装

运行 cargo add btnify

或者

btnify = "2.0.2" 添加到您的 Cargo.toml

如何使用

文档在这里

示例

Hello World

use btnify::button::{Button, ButtonResponse};

fn greet_handler() -> ButtonResponse {
    ButtonResponse::from("hello world!")
}

// this button doesn't use any state so we will mark the state generic as unit
let greet_button: Button<()> = Button::create_basic_button("Greet!", Box::new(greet_handler));

Hello World 2.0

use btnify::button::{Button, ButtonResponse};

fn better_greet_handler(responses: Vec<Option<String>>) -> ButtonResponse {
    // responses is guaranteed to be the same length as the number of extra prompts
    // specified when creating a button
    let name = &responses[0];
    match name {
        Some(name) => format!("Hello, {name}").into(),
        None => format!("You didn't provide a name! :(").into()
    }
}

let better_greet_button: Button<()> = Button::create_button_with_prompts(
    "Greet 2.0",
    Box::new(better_greet_handler),
    vec!["What's your name?".to_string()]
);

计数器应用程序

use std::sync::Mutex;
use tokio::sync::oneshot;
use btnify::bind_server;
use btnify::ShutdownConfig;
use btnify::button::{Button, ButtonResponse};

struct Counter {
    // must use Mutex for interior mutability and thread-safety
    count: Mutex<i32>,
    end_server_tx: Mutex<Option<oneshot::Sender<()>>>,
}

impl Counter {
    fn new(tx: oneshot::Sender<()>) -> Counter {
        Counter {
            count: Mutex::new(0),
            end_server_tx: Mutex::new(Some(tx)),
        }
    }

    fn end_server(&self) {
        // Acquire the Mutex to modify
        let mut tx = self.end_server_tx.lock().unwrap();

        // Take the sender
        let tx = tx.take().unwrap();

        // Send the signal to end the server
        tx.send(()).unwrap();
    }
}

fn count_handler(state: &Counter) -> ButtonResponse {
    let count  = state.count.lock().unwrap();
    format!("The count is: {count}").into()
}

fn plus_handler(state: &Counter, responses: Vec<Option<String>>) -> ButtonResponse {
    match &responses[0] {
        Some(response_str) => {
            if let Ok(amount) = response_str.parse::<i32>() {
                let mut count = state.count.lock().unwrap();
                *count += amount;
                format!("The count now is: {}", *count).into()
            } else {
                "You did not provide a number.".into()
            }
        }
        None => "You didn't provide any input.".into(),
    }
}

fn end_button_handler(state: &Counter) -> ButtonResponse {
    state.end_server();
    "Server is ending. Goodbye!".into()
}

fn server_end(state: &Counter) {
    println!("goodbye world. ;(");
}

let count_button = Button::create_button_with_state("Counter", Box::new(count_handler));

let plus_button = Button::create_button_with_state_and_prompts(
    "add to counter",
    Box::new(plus_handler),
    vec!["How much do you want to add?".to_string()]
);

let end_button = Button::create_button_with_state("End Server", Box::new(end_button_handler));

let buttons = vec![count_button, plus_button, end_button];

let (tx, rx) = oneshot::channel();

let shutdown_config = ShutdownConfig::new(Some(rx), Some(Box::new(server_end)));

bind_server(&"0.0.0.0:3000".parse().unwrap(), buttons, Counter::new(tx), None);
// uncomment to actually run the server:
//    .await
//    .unwrap();

依赖关系

~6–16MB
~194K SLoC