【Rust】reqwestのresponseでstatusコードによって切り分ける

error_for_status() で、エラーレスポンスの場合を取得できる。
https://docs.rs/reqwest/latest/reqwest/struct.Response.html
200の場合は通過する。

pub async fn handle_health() -> Response {
    (StatusCode::OK, format!("I'm ACTIVE")).into_response()
    // (StatusCode::BAD_REQUEST, format!("Something wrong happened")).into_response()
}

async fn handle_post_test()-> Response {
    let res = reqwest::get("http://192.168.33.10:3000/health").await;
    match res.unwrap().error_for_status() {
        Ok(_res) => {
            println!("health check is OK");
        },
        Err(_err) => {
            println!("got bad request response. something wrong");
        }
    }
    //
}

なるほど〜

レスポンスで、statusコードをStatusCode::OKと返却しない場合でも、.error_for_status()は通過する。

// OK
(StatusCode::OK, format!(“I’m ACTIVE”)).into_response()
// OK
“All good”.into_response()
// Err
(StatusCode::BAD_REQUEST, format!(“Something wrong happened”)).into_response()

【Rust】【アルゴリズム】くじ引き

4回くじを引くという条件から、nパターンの4回分を全通り試して、一致する組み合わせを探している。
全通りを探す場合、ループの中でループをするという書き方が多い。

static MAX_N: usize = 50;

fn main() {
    let mut n: i32;
    let mut m: i32;
    let mut k: Vec<i32> = Vec::new();

    println!("紙の枚数 n を入力してください。");
    let mut line = String::new();
    std::io::stdin().read_line(&mut line).ok();
    let n = line.trim().parse::<i32>().unwrap();
    println!("期待する数字の和 m を入力してください。");
    let mut line = String::new();
    std::io::stdin().read_line(&mut line).ok();
    let m = line.trim().parse::<i32>().unwrap();

    println!("紙に書かれている数字をそれぞれ入力してください。");
    for i in 0..n {
        println!("{}番目の数字:", i + 1);
        let mut line = String::new();
        std::io::stdin().read_line(&mut line).ok();
        let n = line.trim().parse::<i32>().unwrap();
        k.push(n);
    }

    let mut flg = false;

    for a in 0..n {
        for b in 0..n {
            for c in 0..n {
                for d in 0..n {
                    if k[a as usize] + k[b as usize] + k + k[d as usize] == m {
                        flg = true;
                        println!("発見! 組み合わせは{},{},{},{}", k[a as usize], k[b as usize], k, k[d as usize]);
                    }
                }
            }
        }
    }

    if !flg {
        println!("そのような組み合わせはありません。");
    }
}

紙の枚数 n を入力してください。
3
期待する数字の和 m を入力してください。
10
紙に書かれている数字をそれぞれ入力してください。
1番目の数字:
3
2番目の数字:
3
3番目の数字:
2
発見! 組み合わせは3,3,2,2
発見! 組み合わせは3,3,2,2
発見! 組み合わせは3,2,3,2
発見! 組み合わせは3,2,3,2
発見! 組み合わせは3,2,2,3
発見! 組み合わせは3,2,2,3
発見! 組み合わせは3,3,2,2
発見! 組み合わせは3,3,2,2
発見! 組み合わせは3,2,3,2
発見! 組み合わせは3,2,3,2
発見! 組み合わせは3,2,2,3
発見! 組み合わせは3,2,2,3
発見! 組み合わせは2,3,3,2
発見! 組み合わせは2,3,3,2
発見! 組み合わせは2,3,2,3
発見! 組み合わせは2,3,2,3
発見! 組み合わせは2,3,3,2
発見! 組み合わせは2,3,3,2
発見! 組み合わせは2,3,2,3
発見! 組み合わせは2,3,2,3
発見! 組み合わせは2,2,3,3
発見! 組み合わせは2,2,3,3
発見! 組み合わせは2,2,3,3
発見! 組み合わせは2,2,3,3

symbol-sdk

import { RepositoryFactoryHttp, Account } from "symbol-sdk";
import { firstValueFrom } from 'rxjs';

const example = async (): Promise<void> => {
    const nodeUrl = "http://sym-test-01.opening-line.jp:3000";
    const respositoryFactory = new RepositoryFactoryHttp(nodeUrl);
    const networkType = await firstValueFrom(respositoryFactory.getNetworkType());
    const alice = Account.generateNewAccount(networkType!);
    console.log(alice);
    console.log(alice.address);
    console.log(alice.privateKey);
    console.log(alice.publicKey);
};
example().then();

$ npm i -D tsx
$ npx tsx sample.ts
Account {
address: Address {
address: ‘TDKBPLRUAJLQLW7GTWIBXR5C35OLDTPOYPTGCQA’,
networkType: 152
},
keyPair: {
privateKey: Uint8Array(32) [
154, 6, 107, 15, 31, 11, 53, 21,
239, 158, 217, 230, 149, 43, 210, 175,
9, 46, 66, 202, 37, 169, 113, 38,
220, 27, 134, 214, 214, 187, 146, 181
],
publicKey: Uint8Array(32) [
30, 210, 66, 23, 95, 217, 178, 237,
101, 141, 64, 213, 241, 27, 146, 146,
169, 63, 253, 50, 218, 183, 250, 51,
52, 42, 249, 118, 210, 118, 62, 229
]
}
}
Address {
address: ‘TDKBPLRUAJLQLW7GTWIBXR5C35OLDTPOYPTGCQA’,
networkType: 152
}
9A066B0F1F0B3515EF9ED9E6952BD2AF092E42CA25A97126DC1B86D6D6BB92B5
1ED242175FD9B2ED658D40D5F11B9292A93FFD32DAB7FA33342AF976D2763EE5

どちらかというと、最初はフォーマットを覚えるのに時間がかかりそうやな…

【Rust】mutexのvectorから指定した複数の値を削除する

普通のvectorから1つだけ削除

    let target = 2;
    let mut vec = vec![1, 2, 3, 4, 5];
    if let Some(remove_index) = vec.iter().position(|x| *x == target){
        vec.remove(remove_index);
    }
    println!("{:?}", vec);

[1, 3, 4, 5]

vectorから複数削除する

    let targets = [2, 4];
    let mut vec = vec![1, 2, 3, 4, 5];
    for target in targets {
        if let Some(remove_index) = vec.iter().position(|x| *x == target){
            vec.remove(remove_index);
        }
    }
    println!("{:?}", vec);

[1, 3, 5]

vectorの中身が構造体の時
=> 基本同じ処理

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
struct Person {
    last: String,
    first: String,
    age: i32
}
//
    let mut vec: Vec<Person> = Vec::new();
    let person1 = Person{last:"yamada".to_string(), first:"taro".to_string(), age: 20};
    let person2 = Person{last:"yoshida".to_string(), first:"akira".to_string(), age: 15};
    let person3 = Person{last:"takahashi".to_string(), first:"kazumi".to_string(), age: 21};
    let person4 = Person{last:"ichiba".to_string(), first:"kumi".to_string(), age: 22};
    let person5 = Person{last:"okamoto".to_string(), first:"hajime".to_string(), age: 17};
    vec.push(person1.clone());
    vec.push(person2.clone());
    vec.push(person3.clone());
    vec.push(person4.clone());
    vec.push(person5.clone());
    

    let mut engineers:Vec<Person> = Vec::new();
    engineers.push(person1);
    engineers.push(person2);

    for engineer in engineers {
        if let Some(remove_index) = vec.iter().position(|x| *x == engineer){
            vec.remove(remove_index);
        }
    }
    println!("{:?}", vec);

[Person { last: “takahashi”, first: “kazumi”, age: 21 }, Person { last: “ichiba”, first: “kumi”, age: 22 }, Person { last: “okamoto”, first: “hajime”, age: 17 }]

### mutexのvectorの場合
=> うまくいかない

static VECT: Lazy<Mutex<Vec<Person>>> = Lazy::new(|| Mutex::new(vec![]));

#[tokio::main]
async fn main() {

    let mut vec: Vec<Person> = Vec::new();
    let person1 = Person{last:"yamada".to_string(), first:"taro".to_string(), age: 20};
    let person2 = Person{last:"yoshida".to_string(), first:"akira".to_string(), age: 15};
    let person3 = Person{last:"takahashi".to_string(), first:"kazumi".to_string(), age: 21};
    let person4 = Person{last:"ichiba".to_string(), first:"kumi".to_string(), age: 22};
    let person5 = Person{last:"okamoto".to_string(), first:"hajime".to_string(), age: 17};
    VECT.lock().unwrap().push(person1.clone());
    VECT.lock().unwrap().push(person2.clone());
    VECT.lock().unwrap().push(person3.clone());
    VECT.lock().unwrap().push(person4.clone());
    VECT.lock().unwrap().push(person5.clone());
    

    let mut engineers:Vec<Person> = Vec::new();
    engineers.push(person1);
    engineers.push(person2);

    for engineer in engineers {
        if let Some(remove_index) = VECT.lock().unwrap().iter().position(|x| *x == engineer){
            VECT.lock().unwrap().remove(remove_index);
        }
    }
    println!("{:?}", VECT.lock().unwrap());
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.94s
Running `target/debug/app`

処理が止まったままになる…

ちょっとコードが冗長になるが、別の関数に切り出して、削除後のデータを再度空にしたmutexに入れるという処理を行うと上手くいく。

#[tokio::main]
async fn main() {
    // mutex vectorの準備
    let mut vec: Vec<Person> = Vec::new();
    let person1 = Person{last:"yamada".to_string(), first:"taro".to_string(), age: 20};
    let person2 = Person{last:"yoshida".to_string(), first:"akira".to_string(), age: 15};
    let person3 = Person{last:"takahashi".to_string(), first:"kazumi".to_string(), age: 21};
    let person4 = Person{last:"ichiba".to_string(), first:"kumi".to_string(), age: 22};
    let person5 = Person{last:"okamoto".to_string(), first:"hajime".to_string(), age: 17};
    VECT.lock().unwrap().push(person1.clone());
    VECT.lock().unwrap().push(person2.clone());
    VECT.lock().unwrap().push(person3.clone());
    VECT.lock().unwrap().push(person4.clone());
    VECT.lock().unwrap().push(person5.clone());
    
    println!("{:?}", VECT.lock().unwrap());

    // mutex vectorから削除したい値
    let mut engineers:Vec<Person> = Vec::new();
    engineers.push(person1);
    engineers.push(person2);

    let objs = processing_mutex_vector(engineers).await;

    // 一度全て削除し、残したいデータを挿入する
    VECT.lock().unwrap().clear();
    for obj in objs {
        VECT.lock().unwrap().push(obj);
    }
    println!("{:?}", VECT.lock().unwrap());

}

async fn processing_mutex_vector(engineers:Vec<Person>) -> Vec<Person> {

    let mut binding = VECT.lock().unwrap();
    let objs = binding.deref_mut();

    for engineer in engineers {
        if let Some(remove_index) = objs.iter().position(|x| *x == engineer){
            objs.remove(remove_index);
        }
    }
    return objs.to_vec();
}

[Person { last: “yamada”, first: “taro”, age: 20 }, Person { last: “yoshida”, first: “akira”, age: 15 }, Person { last: “takahashi”, first: “kazumi”, age: 21 }, Person { last: “ichiba”, first: “kumi”, age: 22 }, Person { last: “okamoto”, first: “hajime”, age: 17 }]
[Person { last: “takahashi”, first: “kazumi”, age: 21 }, Person { last: “ichiba”, first: “kumi”, age: 22 }, Person { last: “okamoto”, first: “hajime”, age: 17 }]

【Rust】hex::encodeとhex::decode

hex::encodeした値をhex::decodeした場合、元のStringを .to_owned().into_bytes() としないといけない。

pub static TEST_STRING : &'static str = "hogehoge";

#[tokio::main]
async fn main() {
    let hex_string = hex::encode(TEST_STRING);
    println!("{}", hex_string);

    let hex_string_decode = hex::decode(hex_string).unwrap();
    assert_eq!(TEST_STRING.to_owned().into_bytes(), hex_string_decode);
}

assert_eq!(TEST_STRING, hex_string_decode); ではないので、注意が必要。

【Rust】ipv6へのreqwestのURLの指定

async fn post_test() -> Result<(), Box<dyn std::error::Error>> {
    let result = reqwest::get("http://[::ffff:c0a8:210a]:3000/health").await;
    println!("{:?}", result);
    Ok(())
}

Ok(Response { url: Url { scheme: “http”, cannot_be_a_base: false, username: “”, password: None, host: Some(Ipv6(::ffff:192.168.33.10)), port: Some(3000), path: “/health”, query: None, fragment: None }, status: 200, headers: {“content-type”: “text/plain; charset=utf-8”, “content-length”: “10”, “date”: “Wed, 05 Mar 2025 11:29:56 GMT”} })

ちなみに、ipv4だと 以下のようになる。
let result = reqwest::get(“http://192.168.33.10:3000/health”).await;

【Rust】reqwest::Client::new() をforループで実行できるのか?

tokio::spawnのthreadで、reqwest::Clientをループで回す。結論から言うとできる。

//
    let handle = tokio::spawn(async move {
        let _ = post_test().await;
    });
//

async fn post_test() {
    for i in 0..3 {
        let n = Name { family: "tanaka".to_string(), first:"taro".to_string(), age: i as i32};
        let json = json!(n);
        let client = reqwest::Client::new();
        let resp = client.post("http://httpbin.org/post")
            .json(&json)
            .send()
            .await;
        let body = resp.expect("REASON").text().await.unwrap();   
        println!("{}", body);
    }
}

//
“json”: {
“age”: 2,
“family”: “tanaka”,
“first”: “taro”
},
//

存在しないipなど、異常系の場合は、reqwest::getでErrで返ってくるので、成功した時だけ送信する。

async fn post_test() -> Result<(), Box<dyn std::error::Error>> {

    let url = ["https://hogehogehogehogehoge.org/".to_string(), "http://httpbin.org/post".to_string()];
    for i in 0..url.len() {
        let result = reqwest::get(url[i].clone()).await;
        match result {
            Err(error) => {
                println!("error, {}", url[i].clone());
            }
            Ok(_) => {
                let n = Name { family: "tanaka".to_string(), first:"taro".to_string(), age: i as i32};
                let json = json!(n);
                let client = reqwest::Client::new();
                let resp = client.post(url[i].clone())
                    .json(&json)
                    .send()
                    .await?;
                let body = resp.text().await.unwrap();   
                println!("{}", body);
            }
        }
    }
    Ok(())
}

一番しっくりするが、なんかベストプラクティスではなさそうな感じがする…

【Rust】SocketAddrによるipv4とipv6の判定

SocketAddrには、is_ipv4、is_ipv6があるが、SocketAddrV6にはない。
socket.ip() で取得した場合、ipv6は [ip]:port としなければならないので、reqwestで送る際も、ipv4/ipv6の判定処理が必要。

use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV6};

#[tokio::main]
async fn main() {
    let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192,168,33,10)), 3000);
    println!("{}", socket);
    println!("{}", socket.port());
    println!("{}", socket.ip());
    println!("{}", socket.is_ipv4());
    println!("{}", socket.is_ipv6());

    let socket = SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)), 8080);
    println!("{}", socket);
    println!("{}", socket.port());
    println!("{}", socket.ip());
    println!("{}", socket.is_ipv4());
    println!("{}", socket.is_ipv6());

    // let socket_v6 = SocketAddrV6::new(Ipv6Addr::new(0000,0000,0000,0000,0000,ffff,c0a8,210a), 3000, 0, 0);

    let socket_v6 = SocketAddrV6::new(Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 1), 8080, 0, 0);

    println!("{}", socket_v6);
    println!("{}", socket_v6.port());
    println!("{}", socket_v6.ip());
}

###ipv4
192.168.33.10:3000
3000
192.168.33.10
true
false

###ipv6
[::1]:8080
8080
::1
false
true

###ipv6
[2001:db8::1]:8080
8080
2001:db8::1

axumの場合も同様

async fn handle_index(ConnectInfo(addr): ConnectInfo<SocketAddr>) -> axum::response::Html<String> {
    println!("{}", addr);
    println!("{}", addr.port());
    println!("{}", addr.ip());

【Rust】axumでページ遷移してもtokio::spawnは実行される?

axumで重い処理を tokio::spawn で別スレッドにしてページのレンダリングを先にした場合、ユーザが別ページに飛んだ場合でも、別スレッドの関数は止まらない。

async fn handle_index() -> axum::response::Html<String> {

    let handle = tokio::spawn(async move {
        let _ = delay().await;
    });
    println!("main thread");
    
    let tera = tera::Tera::new("templates/*").unwrap();
    let mut context = tera::Context::new();
    context.insert("title", "Index page");
    let output = tera.render("app.html", &context);
    return axum::response::Html(output.unwrap());
}

async fn delay() {
    thread::sleep(Duration::from_secs(10));
    println!("sub thread");
}

async fn handle_sub() -> axum::response::Html<String> {
    
    let tera = tera::Tera::new("templates/*").unwrap();

    let data = 100;
    let mut context = tera::Context::new();
    context.insert("title", "Index page");
    let output = tera.render("app.html", &context);
    return axum::response::Html(output.unwrap());
}

Finished `dev` profile [unoptimized + debuginfo] target(s) in 9.57s
Running `target/debug/axum`
main thread
sub thread

そりゃそうだと思っていても、実際にテストしないと安心できませんね。

【Rust】ファイルの拡張子の取得

### 駄目な例
ファイル名の”.”以降の値を取得する

let pos = file_name.find('.').unwrap();
let file_extension = &file_name[(pos+1)..].to_string();

これだと、img.test.png という画像ファイルだった場合、拡張子が test.png になってしまう。
一度、ファイルを保存して、use std::path::PathBuf; を使って、ちゃんと取得する。

                let pathbuf = PathBuf::from(format!("./tmp/{}", file_name));
                let ext_string = pathbuf
                    .extension()
                    .unwrap()
                    .to_string_lossy()
                    .into_owned();
                println!("{}", ext_string);
                let ex_filename = format!("./tmp/{}", file_name);
                let new_filename = format!("./tmp/nft.{}", ext_string);
                fs::rename(ex_filename, new_filename).unwrap();

ファイル名に、”.”が入らないなんてのは勝手な思い込みだ。