尝试使用rust实现个小应用,语法在实际应用中确实难懂,难整

159 阅读2分钟

cargo.toml

name = "learn"
version = "0.1.0"
edition = "2021"

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

[dependencies]
otpauth = "0.4.1"
chrono = "0.4"
axum = "0.5"
serde_derive = "1.0"
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }

main.rs

#[derive(Deserialize)]
struct QueryParams {
    user_otp: Option<String>, // 修改字段类型,
}

use otpauth::TOTP;
use chrono::Local;

use axum::{
    http::StatusCode,
    routing::get,
    Router,
    response::{IntoResponse,Html},
    extract::Query,
};

use std::net::SocketAddr;
use tokio;
use serde::Deserialize;

async fn generate_otp(Query(params): Query<QueryParams>) -> impl IntoResponse{
// 检查 user_otp 是否存在且不为空,as_ref() 方法将 Option<T> 转换为 Option<&T>,即从包含值的 Option 中获取一个值的引用,而不是获取值本身。这样做的好处是避免了所有权的转移,允许在之后的链式调用中继续使用原始的 Option。
// map_or(default, f) 方法是 Option 类型的一个方法,它接受两个参数:default 和一个函数 f。如果 Option 是 Some,它会应用函数 f 到 Option 中包含的值上,并返回函数的结果。如果 Option 是 None,它会返回 default 值。
// default 是当 Option 为 None 时返回的默认值。
// f 是一个函数,当 Option 为 Some 时,这个函数会被应用到 Some 中的值上。

    if params.user_otp.as_ref().map_or(true, String::is_empty) {
        return (
            StatusCode::BAD_REQUEST,
            Html("<h1>user_opt is required</h1>".to_string())
        )
            .into_response();
    }

    let totp = TOTP::new("google");
    // 假设手动构建 Key URL(根据实际情况调整)
    let issuer = "baidu.com";
    let account_name = "toms@baidu.com";
    let key_url = format!("otpauth://totp/{}:{}?secret={}&issuer={}", issuer, account_name, "google", issuer);
    println!("Key URL: {}", key_url);

    // 调整 generate 方法的调用(根据实际方法签名调整)
    let now = Local::now().timestamp();
    let mut otps_html = String::new();
    let passcode = totp.generate( 6,now as u64); // 假设这是正确的调用方式
    let passcode_six_digits = ensure_six_digits(passcode.to_string());
    println!("OTP: {}", passcode_six_digits);
    for i in 0..10 {
        let passcode = totp.generate( 6,now as u64+i); // 假设这是正确的调用方式
        let passcode_six_digits = ensure_six_digits(passcode.to_string());
        otps_html.push_str(&format!("<p>i:{}, OTP: {}</p>", i, passcode_six_digits));
    }
    (StatusCode::OK, Html(format!("<h4>Generated OTPs</h4>{},user_otp:{},key_url:{}", otps_html, params.user_otp.as_ref().unwrap_or(&"".to_string()), key_url))).into_response()
}

fn ensure_six_digits(mut otp: String) -> String {
    while otp.len() < 6 {
        otp.insert(0, '0');
    }
    otp
}

#[tokio::main]
async fn main() {
    let app = Router::new().route("/generate_otp", get(generate_otp));

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    println!("Listening on {}", addr);
     // 绑定地址并启动服务器
     axum::Server::bind(&addr).serve(app.into_make_service())
     .await
     .unwrap();
}