Rustのfactorybot的なライブラリ「factori」とsqlxでモックデータを生成する【1】

pythonの単体テストでよく使っていたfactoryboyというツールがあるのですが、これが本当に使いやすい! factory_boy — Factory Boy stable documentation これとfixtureを使えば、大抵のテストケースに必要なデータを引数から取得することができます。 で、これと同じようなことをRustでもやりたいな〜と思っていたんですが、「factori」というライブラリがそれに相当するということで、備忘録を兼ねて触った感想などをここに書いていこうと思います。 使い方 とりあえずCargoパッケージに追加します。 [dependencies] factori = { version = "1.1.0" } fake = "2.8.0" uuid = { version = "1.4.1", features = [ "serde", "v4" ] } 使い方はメチャクチャ簡単で、定義された構造体にfactori!マクロを定義してあげれば、create!マクロから呼び出すことができます。 fakeというライブラリと組み合わせてテストデータを作る実装は以下のようになります。 use factori::{factori, create}; use fake::Fake; use fake::faker::name::raw::*; use fake::locales::JA_JP; use uuid::Uuid; #[derive(Debug)] pub struct User { id: Uuid, name: String, } factori!(User, { default { id = Uuid::new_v4(), name = Name(JA_JP).fake::<String>(), } }); fn main() { // Userを10人作成する let user: Vec<User> = [0; 10].iter().map(|_| create!(User)).collect(); println!("{:?}", user); } これだけでUserをちゃちゃっと作る実装ができました。 ...

10月 7, 2023 · にあえん

次世代DBマイグレーションツール「sqldef」

先々月くらいにPrismaを知り、関連の記事をいろいろ書いていました。 prisma-client-pythonでpythonからORMを楽にやる prisma client pythonが実行しているNodeはどこにあるのか prismaでfactoryboyみたいなやつが作りたい ただ、最近Rustを触ることが多くてPrismaの使いどころがマイグレーションぐらいしかないのでちょっとしょんぼりしてたんですが、関連してちょっと気になるツールを見つけたので勉強がてら紹介します。 GitHub - sqldef/sqldef: Idempotent schema management for MySQL, PostgreSQL, and more このツールを使うと、Prismaでいうschema.prismaをSQLファイルで管理できるようになるとのこと! すごい柔軟性!!!!! つまり、1つのSQLファイルと現在のDBを比較して、追加・変更・削除されたテーブルや列などの差分情報を読み取り、マイグレーションを行ってくれるという感じです。 ちなみに、sqlx関連で調べて見ていた参考にしていたブログはこちら。 [Rust] sqlxを使ってみる #Database - Qiita インストール まずは使用しているUbuntuにダウンロードしてみます。 私は普段使いがPostgresなので、以下のコマンドでpsqldefをインストールします。 curl -OL https://github.com/k0kubun/sqldef/releases/download/v0.16.9/psqldef_linux_amd64.tar.gz sudo tar xf psqldef_linux_amd64.tar.gz -C /usr/local/bin/ インストールできたか確認してみます。 % psqldef --help Usage: psqldef [option...] db_name Application Options: -U, --user=username PostgreSQL user name (default: postgres) -W, --password=password PostgreSQL user password, overridden by $PGPASSWORD -h, --host=hostname Host or socket directory to connect to the PostgreSQL server (default: 127.0.0.1) -p, --port=port Port used for the connection (default: 5432) --password-prompt Force PostgreSQL user password prompt -f, --file=filename Read schema SQL from the file, rather than stdin (default: -) --dry-run Don't run DDLs but just show them --export Just dump the current schema to stdout --enable-drop-table Enable destructive changes such as DROP (enable only table drops) --before-apply= Execute the given string before applying the regular DDLs --config= YAML file to specify: target_tables, skip_tables --help Show this help --version Show this version ヘルプコマンドが表示されたので、ちゃんとインストールできましたね。 ...

10月 6, 2023 · にあえん

RustでAsync Dropができないか調べた

Rust、最近使い出しました。 テストを書くにあたって、テスト用のデータベース作成と削除をうまく実装できないか考えていて、調べるとDropというトレイトが使えるとわかりました。 ただ、Dropはasyncに対応していないので、どういう代替案があるかな〜と気になったので調べてみました。 そもそもDropトレイトとは? dropメソッドのみが定義されており、ここに実装を入れるとオブジェクトがメモリから開放される際に勝手に呼び出されるという仕組みです。 メモリ解放 - Rust By Example 日本語版 async-dropper 調べてみたら一応メジャーリリースしているasyncに対応したDropトレイトがありました。 async-dropper ちょっと使ってみましょう。 以下のようにパッケージを追加してあげます。 [dependencies] async-dropper = { version = "0.2.3", features = [ "tokio" ] } async-trait = "0.1.73" sqlx = { version = "0.7", features = [ "runtime-tokio", "postgres" ] } 普段からtokioを使っているので、そのfeaturesも入れてあげます。 tokioの対応バージョンは^1.29.1からとなっているので、比較的新しいバージョンじゃないと動かないようになってますね。 async-traitはトレイトにasyncメソッドを定義できるようになるマクロで、async-dropperを使用する際に役立つので入れておきます。 データベースの作成・削除に使用するためSQLXも入れておきます。 ついでにdocker-composeで使えるpostgresサーバーも作っておきましょう。 version: '3' services: db: image: postgres:15 container_name: postgres ports: - 5432:5432 environment: - POSTGRES_PASSWORD=example 次に実装です。 とりあえずデータベース作成をランダム文字列で行うような実装をしました。 use sqlx::postgres::{PgPool, PgPoolOptions}; use rand::{Rng}; const CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyz"; const DBNAME_LEN: usize = 30; struct MockDBServer { pool: PgPool, } impl MockDBServer { async fn new() -> Self { let mut rng = rand::thread_rng(); let dbname: String = (0..DBNAME_LEN) .map(|_| { let idx = rng.gen_range(0..CHARSET.len()); char::from(unsafe { *CHARSET.get_unchecked(idx) }) }) .collect(); let pool = PgPoolOptions::new() .max_connections(5) .connect("postgres://postgres:example@localhost/postgres").await.unwrap(); sqlx::query(format!("CREATE DATABASE {};", dbname).as_str()) .bind(dbname) .execute(&pool).await.unwrap(); Self { pool } } } #[tokio::main] async fn main() { MockDBServer::new().await; } これを実行するとデータベースが作成されます。ただ作成されっぱなしになってしまいます。 ...

10月 4, 2023 · にあえん

Windows11でRust使ってたら「error failed to run custom build command for `openssl sys v0.9.93`」がでたとき

新しく使いはじめたWindows11でcargo buildしたらこんなエラーが出ました。 error: failed to run custom build command for `openssl-sys v0.9.93` Caused by: process didn't exit successfully: `C:\Users\Nanao\Project\tools\sync-zaim\target\debug\build\openssl-sys-18a1f426c8f5f296\build-script-main` (exit code: 101) --- stdout cargo:rerun-if-env-changed=X86_64_PC_WINDOWS_MSVC_OPENSSL_LIB_DIR X86_64_PC_WINDOWS_MSVC_OPENSSL_LIB_DIR unset cargo:rerun-if-env-changed=OPENSSL_LIB_DIR OPENSSL_LIB_DIR unset cargo:rerun-if-env-changed=X86_64_PC_WINDOWS_MSVC_OPENSSL_INCLUDE_DIR X86_64_PC_WINDOWS_MSVC_OPENSSL_INCLUDE_DIR unset cargo:rerun-if-env-changed=OPENSSL_INCLUDE_DIR OPENSSL_INCLUDE_DIR unset cargo:rerun-if-env-changed=X86_64_PC_WINDOWS_MSVC_OPENSSL_DIR X86_64_PC_WINDOWS_MSVC_OPENSSL_DIR unset cargo:rerun-if-env-changed=OPENSSL_DIR OPENSSL_DIR unset note: vcpkg did not find openssl: Could not look up details of packages in vcpkg tree could not read status file updates dir: 指定されたパスが見つかりません。 (os error 3) --- stderr thread 'main' panicked at ' Could not find directory of OpenSSL installation, and this `-sys` crate cannot proceed without this knowledge. If OpenSSL is installed and this crate had trouble finding it, you can set the `OPENSSL_DIR` environment variable for the compilation process. Make sure you also have the development packages of openssl installed. For example, `libssl-dev` on Ubuntu or `openssl-devel` on Fedora. If you're in a situation where you think the directory *should* be found automatically, please open a bug at https://github.com/sfackler/rust-openssl and include information about your system as well as this message. $HOST = x86_64-pc-windows-msvc $TARGET = x86_64-pc-windows-msvc openssl-sys = 0.9.93 It looks like you're compiling for MSVC but we couldn't detect an OpenSSL installation. If there isn't one installed then you can try the rust-openssl README for more information about how to download precompiled binaries of OpenSSL: https://github.com/sfackler/rust-openssl#windows ', C:\Users\Nanao\.cargo\registry\src\index.crates.io-6f17d22bba15001f\openssl-sys-0.9.93\build\find_normal.rs:190:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace warning: build failed, waiting for other jobs to finish... OpenSSLが存在しないから出てるエラーっぽい? ...

10月 2, 2023 · にあえん

Windows11にwingetを使ってAWS CLIをインストールする

Windows11でのAWS CLIのセットアップの備忘録です。 あまりにも簡単すぎますが、wingetコマンドを使うだけです。 winget install Amazon.AWSCLI あとは再起動して、いつも通りaws configureとかで設定を追加します。

10月 1, 2023 · にあえん

cargo buildしたら「error: linker `link.exe` not found」が出たとき

そのまんまなタイトルですが、備忘録のために解決方法を記載しておきます。 Windows11でRustの開発中、cargo buildしたらこんな感じでエラーが出ました。 error: linker `link.exe` not found | = note: program not found note: the msvc targets depend on the msvc linker but `link.exe` was not found note: please ensure that Visual Studio 2017 or later, or Build Tools for Visual Studio were installed with the Visual C++ option. note: VS Code is a different product, and is not sufficient. wingetコマンドでビルドツールをインストールしてあげましょう。 winget install Microsoft.VisualStudio.2022.BuildTools これが終わったら、Visual Studio Installerを起動します。 インストール済みのところに先ほどインストールしたビルドツールが表示されています。 ここの変更を押します。 ...

10月 1, 2023 · にあえん

一般的なエンジニアがやるであろうWindowsインストール後のセットアップについて

最近、デスクトップPCのUbuntuがめちゃくちゃ調子が悪くなってしまったのと、確定申告を行うにあたって電子申告にLinuxが対応していないようなので、「ええい!もうUbuntuなんてこりごりでい!」ということでWindowsに載せ替えました。 まっさらな状態からセットアップを行っていくにあたって、備忘録も兼ねて文章に残しておこうと思います。 Braveのインストール Edgeがいくら使いやすくなったとて、今まで使っていたブラウザがBraveで同期などもしたかったのでインストールしました。 標準ブラウザじゃ話にならんので 開発環境周りの整備 ここら辺はパッケージマネージャーを活用しようと思い、Windows用のパッケージマネージャーならChocolateyとかかな…と思ってたんですが、いつの間にやら標準でパッケージマネージャーが搭載されるようになっていたようです。 Windowsのパッケージマネージャー「winget」を使ってみた | DevelopersIO これがまーーーーー素晴らしい。 例えば、VSCodeなら以下のコマンドで一発で導入できます。 winget install "Microsoft Visual Studio Code" ね?簡単でしょう? Rustのインストールも楽々できます。 winget install "Rustlang.Rustup" あまりにも素晴らしすぎる。 ちなみに、wingetからChocolateyのインストールもできます。 winget install Chocolatey Chocolateyのインストール後は再起動が必要なため、すぐには使用できません。 再起動後、コマンドが有効になっているか確認します。 > choco Chocolatey v2.2.2 Please run 'choco -?' or 'choco <command> -?' for help menu. うーん、なんかここまで楽だと逆に手ごたえがないですね。もちろんいい意味で。 そしてDockerもインストールしておきます。何でもできるなこれ。 winget install Docker.DockerDesktop こちらもインストール後は再起動が必要です。 WSLのセットアップ WSLはMicrosoft StoreからUbuntuをダウンロードしました。 wingetからでもダウンロードできます。 Ubuntu 22.04をインストールしたければ以下のコマンドで可能です。 (ほんとなんでもできるなこいつ) winget install Canonical.Ubuntu.2204 このインストールには結構時間がかかりますが、のんびり行きましょう。 あとターミナルですが、これもWindowsが提供してるやつがあるので使いましょう。 前にも使ったことがありますが、WSL、CMD、PowerShellを横断的に使えるので便利ですよ。(あと一応オフィシャルだから安心感ある) ほんでこれもwingetが使えます。 winget install Microsoft.WindowsTerminal ターミナルからWSLの軌道ができたら普段zshを使っているので、ここを参考にしてzshの環境構築を行いました。 WSL2でWeb開発環境の構築メモ (zsh, node, dockerなど) ...

9月 25, 2023 · にあえん

RustでZaimと東京ガスを連携させてみた【1】

最近、昨年の医療費控除を受けるために確定申告の準備などをしているのですが、家計簿ソフトで利用しているZaimにガスや水道代が勝手に計上されてくれると便利だな〜と思い、Zaim APIとSeleniumを使って実現してみようと思います。 Zaim Developers Center 東京ガスのCSVファイルをダウンロードする Rustのheadless-chromeを使って構築します。 とりあえずCargoパッケージ作ってライブラリを追加します。 cargo new sync-zaim cargo add headless-chrome 追加できました。 使い勝手を知るために、まずはGoogleにアクセスしてスクショをとってみます。 use headless_chrome::{protocol::cdp::Page::CaptureScreenshotFormatOption, Browser}; use std::fs; fn main() { let browser = Browser::default().unwrap(); let tab = browser.new_tab().unwrap(); tab.set_default_timeout(std::time::Duration::from_secs(200)); tab.navigate_to("https://www.google.com/").unwrap(); tab.wait_until_navigated().unwrap(); let jpeg_data = tab.capture_screenshot( CaptureScreenshotFormatOption::Jpeg, None, None, true, ).unwrap(); fs::write("screenshot.jpg", jpeg_data).unwrap(); } 実行すると、Googleの画面のスクショが保存されました。 この調子で東京ガスのCSVを取得できるようにしましょう。 コードはpythonですがここを参考にします。 Python(Selenium)で東京ガスのcsvをダウンロード|gadgetking 実行すると少しエラーは出ますが、ファイルダウンロードは可能になりました。 use headless_chrome::{protocol::cdp::Page::CaptureScreenshotFormatOption, Browser}; use headless_chrome::protocol::cdp::types::Method; use std::sync::Arc; use std::{env, thread, time, error::Error}; use std::{fs, path}; use serde::Serialize; const TOKYO_GAS_LOGIN_URL: &str = "https://members.tokyo-gas.co.jp/login.html"; const GAS_ID: &str = "東京ガスのログインID"; const GAS_PASSWD: &str = "東京ガスのログインパスワード"; // https://github.com/rust-headless-chrome/rust-headless-chrome/issues/187 #[derive(Serialize, Debug)] struct Command { behavior: &'static str, downloadPath: &'static str, } impl Method for Command { const NAME: &'static str = "Page.setDownloadBehavior"; type ReturnObject = serde_json::Value; } fn main() -> Result<(), Box<dyn Error>> { let browser = Browser::default().unwrap(); let tab = browser.new_tab().unwrap(); tab.set_default_timeout(std::time::Duration::from_secs(200)); // https://github.com/rust-headless-chrome/rust-headless-chrome/issues/280 tab.enable_request_interception(Arc::new(|transport, session_id, params| { println!("request!"); println!("transport: {:?}", transport); println!("session_id: {:?}", session_id); println!("params: {:?}", params); headless_chrome::browser::tab::RequestPausedDecision::Continue(None) }))?; tab.navigate_to(TOKYO_GAS_LOGIN_URL).unwrap(); tab.wait_until_navigated().unwrap(); let jpeg_data = tab.capture_screenshot( CaptureScreenshotFormatOption::Jpeg, None, None, true, ).unwrap(); fs::write("screenshot.jpg", jpeg_data).unwrap(); tab.wait_for_element("#loginId").unwrap().type_into(GAS_ID).unwrap(); tab.wait_for_element("#password").unwrap().type_into(GAS_PASSWD).unwrap(); tab.wait_for_element("#submit-btn").unwrap().click().unwrap(); // tab.wait_until_navigated().unwrap(); thread::sleep(time::Duration::from_secs(5)); let jpeg_data = tab.capture_screenshot( CaptureScreenshotFormatOption::Jpeg, None, None, true, ).unwrap(); fs::write("screenshot.jpg", jpeg_data).unwrap(); let path_string = env::current_dir()?.into_os_string().into_string().unwrap(); // println!("pwd: {}", path_string); let static_path_str: &'static str = Box::leak(path_string.into_boxed_str()); let command = Command { behavior: "allow", downloadPath: static_path_str, }; tab.call_method(command)?; // CSVファイルをダウンロード tab.navigate_to("https://members.tokyo-gas.co.jp/api/mieru/chargeAmountCsv.jsp?no=0&target=total") .unwrap(); thread::sleep(time::Duration::from_secs(30)); Ok(()) } 実行すると以下のようなエラーが出ます。 ...

9月 10, 2023 · にあえん

prismaでfactoryboyみたいなやつが作りたい

作りたい。 factoryboyをご存知ですか? factory_boy — Factory Boy stable documentation pythonでテスト実装する際、ダミーデータの実装に役に立つライブラリなのですが、これがまー使いやすい。 ので、prismaでシード作るときにも同じような感じで使いたいなーと思い、実現方法を考えてみました。 factory-botを使ってみる 調べてみると、factory-botというのがありました。 Git これを使ってみたいと思います。 とりあえずディレクトリを作って初期化します。 mkdir prisma-factory-bot cd prisma-factory-bot npm init 次に、typescriptの実行基盤とお目当てのfactory-botをインストールします。 npm i -D typescript ts-node @types/node npm i factory-bot これで準備できました。 最も簡易的な形の実装は以下のとおりです。 import { factory } from "factory-bot"; class User { username: string; score: number; constructor(attrs: { username: string, score: number }) { this.username = attrs.username; this.score = attrs.score; } } factory.define('user', User, { username: 'Bob', score: 50, }); (async () => { const user = await factory.build<User>('user'); console.log(user); })(); とりあえず動く形になりました。 実行すると、生成されたUserが表示されます。 User { username: 'Bob', score: 50 } bulidMany関数を使うと、複数のモデルをいっぺんに作ることができます。 ...

8月 23, 2023 · にあえん

OS自作入門【1】

wasmに入門したりprismaにハマったりいろいろありますが、以前から興味のあった自作OSというものに手を出してみます。 この書籍を参考にしています。 ゼロからのOS自作入門 | 内田 公太 |本 | 通販 | Amazon 機械語でHello World! まずはバイナリの編集に使用するエディターをダウンロードします。 sudo apt install okteta エディターがダウンロードできたら、ひたすらHello Worldを実装します。 以下に上がっていますが、本見ながら写経しました。チカレタ。。 mikanos-build/day01/bin/hello.efi at master · uchan-nos/mikanos-build · GitHub 実際はこんな感じです。痺れますね。 これをBOOTX64.efiとして保存します。 ちゃんとコーディングできていれば、sumコマンドでチェックサムが12430となっています。 % sum ../../study/custom_os/BOOTX64.efi 12430 2 ではこれをエミュレーターで実行します。 QEMUをダウンロードしましょう。 Download QEMU - QEMU 公式にダウンロードのためのshがあったので使ってみます。 wget https://download.qemu.org/qemu-8.1.0-rc4.tar.xz tar xvJf qemu-8.1.0-rc4.tar.xz cd qemu-8.1.0-rc4 ./configure make しかし./configureのあたりでエラーになってしまいました。。 % ./configure Using './build' as the directory for build output python determined to be '/home/username/.anyenv/envs/pyenv/shims/python3' python version: Python 3.8.12 mkvenv: Creating non-isolated virtual environment at 'pyvenv' mkvenv: checking for meson>=0.63.0 mkvenv: installing meson>=0.63.0 mkvenv: checking for sphinx>=1.6.0, sphinx-rtd-theme>=0.5.0 'sphinx>=1.6.0' not found: • Python package 'sphinx' was not found nor installed. • mkvenv was configured to operate offline and did not check PyPI. Sphinx not found/usable, disabling docs. ERROR: Cannot find Ninja なんかpython使っているっぽいです。 ...

8月 21, 2023 · にあえん