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 · にあえん

ファンドって何?NISAで扱えるファンドの種類について調べた

はやいものでもう9月ですね。 今年の終わりも近いですが、最近つみたてNISAを始めるにあたっていろいろ金融知識を調べているので、雑にまとめてみます。 そもそもファンドって何? ファンドとは、金融用語で資金を募って株や債権を専門家が運用するパッケージのことです。 ファンド | 証券用語集 | 東海東京証券株式会社 ファンドに投資する人は投資家と呼ばれるので、NISAに投資した私は投資家です(どや〜) そもそもファンドは「基金」という意味で、基金は「なにかの目的を達成するために集められたお金」という意味です。 基金 - Wikipedia 今回の場合では、「株や債権を運用して利益を出す」という目的のために集められるお金の入金先のことがファンドとなります。 つみたてNISAで扱うファンドとは めちゃめちゃわかりやすい表があるサイトがあります。 本音が飛び出す! つみたてNISA座談会 これによるとファンドの種類は大きく以下の2種類に分けられます。 インデックスファンド アクティブファンド それぞれの違いですが、その前に指数について理解しておく必要があります。 金融用語において指数とは、市場の値動きを示す数値のことです。 ファンドによって異なりますが、主な指数の一覧は以下のサイトにまとめてあります。 主な指数一覧 | ファンズアイ まずインデックスファンドは、この指数がそのまま利益と連動するファンドのことをいいます。 インデックスファンドは既にある指数を元にしているため、運用にかかるコストが少なく、信託報酬と呼ばれる手数料が抑えられています。 ただし、指数以上の利益を出すことは難しいです。 一方アクティブファンドは指数を上回る利益を目指すファンドのことで、より多くの利益を求める場合やユニークなファンドを選びたい場合はこちらになります。 ただし運用コストはインデックスファンドよりも高く設定されています。 このサイトにわかりやすい表があったので参照します。 アクティブファンドとインデックスファンドの違いとは? ファンドの比較 このインデックスファンドの中でも更に投資対象が株式だけかそうでないかで2種類あり、以下のような種類に分けられています。 株式を対象 国内 日本の株のみを対象とし、更に以下のような分類による指数を取る 東証一部に上場する株式のうち代表する225銘柄(日経平均株価を指数とする) 銘柄一覧 東証一部に上場する株式すべての銘柄(約2100銘柄)(TOPIXを指数とする) 全世界 全世界47ヶ国の2800銘柄(MSCI ACWIを指数とする) 全世界47ヶ国の7800銘柄(FTSE Global All Capを指数とする) 海外 米国 米国株式市場に上場する500銘柄(S&P500を指数とする) 米国株式市場に上場するほぼすべての銘柄(約3600銘柄、CRSP U.S.Total Marketを指数とする) 先進国 日本を除く先進国22ヶ国の約1300銘柄(MSCI コクサイを指数とする) 新興国 新興国24ヶ国の約1100銘柄(ESCI Emerging Markets) バランスファンド(株式とそれ以外も対象) 4資産 日本・先進国の株式・債権 6資産 日本・先進国・新興国の株式・債権 8資産 日本・先進国の株式・債権・不動産+新興国の株式・債権 etc.. 以下の様なランキングなど調べると無限にファンドが出てきて迷ってしまいますが、こういったジャンルごとに分けるとなんか分かった気になってきますね。 ...

9月 6, 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 · にあえん