【Rust】ファイルの拡張子を取得して、任意のファイル名に変更

正規表現(regex)もあるけど、単純にreplaceを使う。

let tmp_path = "./tmp/test.png";
    let str = &tmp_path.replace("./tmp/", "");
    let pos = str.find('.').unwrap();
    let file_extension = &str[(pos+1)..].to_string();
    let time = Utc::now().format("%Y%m%d%H%M%S").to_string();
    let file_key = format!("{}.{}", time, file_extension);
    println!("{}", file_key);

Finished `dev` profile [unoptimized + debuginfo] target(s) in 7.22s
Running `target/debug/axum`
20250217071013.png

【Rust】base64エンコード/デコード(2回目)

これ、何回やっても忘れてしまうな…

base64= “0.22.1”

use base64::prelude::*;

fn main() {
    let ip = "192.168.33.10".to_string();
    println!("{}", base64_str(&ip));
    let str = "MTkyLjE2OC4zMy4xMA==".to_string();
    println!("{}", base64_decode(&str));
}

fn base64_str(str: &String) -> String {
    BASE64_STANDARD.encode(&str)
}

fn base64_decode_bytes(b64str: &str) -> Vec<u8> {
    let t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    let mut table: [u8; 256] = [0; 256]; 
    for (i, v) in t.as_bytes().iter().enumerate() {
        table[*v as usize] = i as u8; 
    }
    let b64 = String::from(b64str).replace("\r", "").replace("\n", "");
    let b64bytes = b64.as_bytes();
    let mut result: Vec<u8> = vec![];
    let cnt = b64bytes.len() / 4;
    for i in 0..cnt {
        let i0 = b64bytes[i*4+0];
        let i1 = b64bytes[i*4+1];
        let i2 = b64bytes[i*4+2];
        let i3 = b64bytes[i*4+3];
        let c0 = table[i0 as usize] as usize;
        let c1 = table[i1 as usize] as usize;
        let c2 = table[i2 as usize] as usize;
        let c3 = table[i3 as usize] as usize;
        let b24 = (c0 << 18) | (c1 << 12) | (c2 <<  6) | (c3 <<  0);
        let b0 = ((b24 >> 16) & 0xFF) as u8;
        let b1 = ((b24 >>  8) & 0xFF) as u8;
        let b2 = ((b24 >>  0) & 0xFF) as u8;
        result.push(b0);
        if i2 as char != '=' { result.push(b1); }
        if i3 as char != '=' { result.push(b2); }
    }
    result
}

fn base64_decode(b64str: &str) -> String {
    String::from_utf8(base64_decode_bytes(b64str)).unwrap()
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.51s
Running `target/debug/app`
MTkyLjE2OC4zMy4xMA==
192.168.33.10

【Rust】Subモジュールで定義したMutexの使い方

mutexの場合でも、基本的にはstaticと同じようにモジュール名から呼び出せば使えるようになる。

sub file

use once_cell::sync::Lazy;
use std::sync::Mutex;

pub static BOX: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(vec![]));

main

mod sub1;

fn main() {
    sub1::BOX.lock().unwrap().push("text1".to_string());
    println!("{:?}", sub1::BOX);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.67s
Running `target/debug/app`
Lazy { cell: OnceCell(Mutex { data: [“text1”], poisoned: false, .. }), init: “..” }

なるほど、そういうことか…

【Rust】IPv4のポートを除いたアドレスをスライスする

IPv4: 10進数表記で 「3桁:3桁:3桁:3桁」
*3桁の数字は、0 〜 255 の範囲
例:192.168.1.10

fn main() {
    let str = "192.168.33.10:53218".to_string();
    let pos = str.find(':').unwrap();
    println!("{}", &str[..pos]);

}

Compiling app v0.1.0 (/home/vagrant/dev/rust/app)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.46s
Running `target/debug/app`
192.168.33.10

“:”があるかないかを判定してから、スライスをした方が良さそう。 ただ、これ、IPv4だけなんだよな… IPv6だったらどうしよう…

pub fn slice_ipv4(str: &String) -> String {
    if str.find(':').is_some() {
        let pos = str.find(':').unwrap();
        return (&str[..pos]).to_string()
    } 
    return str.to_string()
}

【Rust】構造体にDateTime型を使用する

構造体に時間を入れて、取り出す際には”2025-02-15 05:49:42″など秒までで使いたい。
RustでDatetimeを使用すると、2025-02-15T05:49:42.648829007Zというような値になる。
datetimeをString型に変えて[0..19]で切り抜いて使おうかと思ったが、、、、formatを使えば、自由に取り出せることができる。

#[derive(Debug)]
struct TimeS {
    time: DateTime<Utc>,
}

fn main() {
    let utc_datetime: DateTime<Utc> = Utc::now();
    let t: String = utc_datetime.to_string();
    println!("{}", &t[0..19]);

    let t = TimeS { time: utc_datetime};
    println!("{:?}", t);
    println!("{}", t.time.format("%Y年%m月%d日 %H時%M分%S秒 %Z"));
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.55s
Running `target/debug/app`
2025-02-15 05:49:42
TimeS { time: 2025-02-15T05:49:42.648829007Z }
2025年02月15日 05時49分42秒 UTC

こう考えると、timeはString型ではなくDateTimeとした方が良いですね。。

【Rust】Log出力を実装する

log = “0.4.25”
env_logger = “0.11.6”

use env_logger;
use log::{error, warn, info, debug};
use std::env;

fn main() {
    env::set_var("RUST_LOG", "info");
    env_logger::init();
    
    debug!("debugです");
    info!("infoです");
    warn!("warnです");
    error!("errorです");
}

[2025-02-14T07:29:59Z INFO app] infoです
[2025-02-14T07:29:59Z WARN app] warnです
[2025-02-14T07:29:59Z ERROR app] errorです

上記でも良いんだけど、log、env_logger使わずに、普通にchronoとFileだけで良い気がする。

use std::path::Path;
use chrono::{Utc, DateTime};
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Write;

fn main() {
    log_output("this is test".to_string());
}

fn log_output(message: String) {
    let utc_datetime: DateTime<Utc> = Utc::now();
    let t: String = utc_datetime.to_string();
    let filename = format!("./log/{}.txt",&t[0..7]);

    let log_message = format!("[{}] {}", &t[0..19], message);
    println!("{}", log_message);

    if !Path::new(&filename).is_file() {
        let _ = File::create(&filename);
    }
    let mut file_ref = OpenOptions::new()
                        .append(true)
                        .open(filename)
                        .expect("Unable to open file");

    file_ref.write_all(&log_message.as_bytes()).expect("write failed");
    file_ref.write_all(b"\n").expect("write failed");
}

Compiling app v0.1.0 (/home/vagrant/dev/rust/app)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.54s
Running `target/debug/app`
[2025-02-14 07:58:05] this is test

【Rust】モジュール間で別のモジュールの関数を利用する

mainで利用するモジュールを定義し、使用する方のモジュールで、 use crate::${modName} と定義する。
main.rsで両方のモジュールを定義していないとエラーになるので注意が必要

main.rs

mod account;
mod crypt;

account.rs

use crate::crypt;
//
println!("{}", crypt::password_verify(&loginform.password, &password_hash));

crypt.rs

use pwhash::bcrypt;

pub fn password_verify(password: &String, hash: &String) -> bool {
    bcrypt::verify(password, hash)
}

なるほどなるほど!

【Rust】最短経路問題

M, N = 6, 5

route = [[0 for i in range(N + 1)]for j in range(M + 1)]

print(route)

$ python3 test.py
[[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]

fn main() {
    let M = 6;
    let N = 5;

    let mut route: Vec<Vec<usize>> = Vec::new();
    
    for _ in 0..(M+1) {
        let mut L: Vec<usize> = Vec::new();
        for _ in 0..(N+1) {
            L.push(0);
        } 
        route.push(L);
    }
    println!("{:?}", route);
}

[[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]

fn main() {
    let M = 6;
    let N = 5;

    let mut route: Vec<Vec<usize>> = Vec::new();
    
    for _ in 0..(M+1) {
        let mut L: Vec<usize> = Vec::new();
        for _ in 0..(N+1) {
            L.push(0);
        } 
        route.push(L);
    }

    for i in 0..(M+1){
        route[i][0] = 1;
    }
    
    for i in 1..(N+1) {
        route[0][i] = 1;
        for j in 1..(M+1) {
            route[j][i] = route[j - 1][i] + route[j][i - 1];
        }
    }
    println!("{:?}", route);
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/rust`
[[1, 1, 1, 1, 1, 1], [1, 2, 3, 4, 5, 6], [1, 3, 6, 10, 15, 21], [1, 4, 10, 20, 35, 56], [1, 5, 15, 35, 70, 126], [1, 6, 21, 56, 126, 252], [1, 7, 28, 84, 210, 462]]

おおお、なるほど、これは凄い。

実はこう書ける

fn search(m: usize, n: usize) -> usize {
    if (m == 0) || (n == 0) {
        return 1
    }

    return search(m-1, n) + search(m, n - 1);
}

fn main() {
    let M = 6;
    let N = 5;

    println!("{}", search(M, N));
}

なるほど、これは凄い。
経路の問題も基本は、位置を数字で表現するのね。

【Rust】ユニットテスト

テストを実行するには、
$ cargo test
を実行する

fn main() {
    println!("This is test code");
}

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2+2, 4);
    }
}

Running unittests src/main.rs (target/debug/deps/sample-a78b7dbded83e75d)

running 1 test
test tests::it_works … ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

作成したfunctionのテストを行う

#[cfg(test)]
mod tests {
    use {
        super::*,
    };

    #[test]
    fn test_quick_sort() {
        let data = vec![6, 15, 4, 2, 8, 5, 11, 9, 7, 13];
        let expected = vec![2, 4, 5, 6, 7, 8, 9, 11, 13, 15];
        assert_eq!(expected, quick_sort(data));
    }
}

note: to see what the problems were, use the option `–future-incompat-report`, or run `cargo report future-incompatibilities –id 1`
Running unittests src/main.rs (target/debug/deps/sample-a78b7dbded83e75d)

running 1 test
test tests::test_quick_sort … ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

assert!, assert_eq!, assert_ne!がある。

うおおおおおおおおおお、これは超絶勉強になるな…

【Rust】AxumでWeb Socketの処理を行う

ws モジュールを使用する
https://docs.rs/axum/latest/axum/extract/ws/index.html

#[tokio::main]
async fn main() {
    let app = Router::new().route("/ws", any(handler));
    // Start the server
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app.into_make_service_with_connect_info::<SocketAddr>()).await.unwrap();
}
async fn handler(ConnectInfo(addr): ConnectInfo<SocketAddr>, ws: WebSocketUpgrade) -> Response {
    println!("{}", addr);
    ws.on_upgrade(handle_socket)
}

async fn handle_socket(mut socket: WebSocket) {
    while let Some(msg) = socket.recv().await {
        let mut msg = if let Ok(msg) = msg {
            msg
        } else {
            return;
        };
        if socket.send(msg).await.is_err() {
            // client disconnected
            return;
        }
    }
}

クライアントサイド

    <textarea id="output" cols="50" rows="10" readonly></textarea><br>
    <input type="text" id="input" placeholder="Type a message" />
    <button onclick="sendMessage()">Send</button>
<script>
        const ws = new WebSocket("ws://192.168.33.10:3000/ws");
        const output = document.getElementById("output");
        const input = document.getElementById("input");
        input.value = "hello";
        
        ws.onmessage = (event) => {
            output.value += `\n${event.data}`;
        };
        function sendMessage() {
            const message = input.value;
            ws.send(message);
            input.value = "";
        }
    </script>

axumでも問題なくできることがわかりました。
なるほど、ちょっと理解しました。