Zenn
Open24

dotenv コードリーディング

a-yossya-yossy

Cargo.toml[[bin]] をコメントアウトしても cargo run --bin dotenv --features="cli" で呼び出せる

# [[bin]]
# name = "dotenv"
# required-features = ["cli"]
a-yossya-yossy

https://doc.rust-lang.org/cargo/reference/cargo-targets.html#binaries

A binary’s source can be src/main.rs and/or stored in the src/bin/ directory. For src/main.rs, the default binary name is the package name. The settings for each binary can be customized in the[[bin]] tables in Cargo.toml.

[[bin]] を使うことで実行するバイナリの設定をカスタマイズできる
今回の場合だと cargo run --bin dotenv を実行した時の動作は下記のようになる

[[bin]] なし

cargo run --bin dotenv
   Compiling dotenv v0.15.0 (/xxxx/oss_reading/dotenv_reading/dotenv/dotenv)
error[E0463]: can't find crate for `clap`
 --> dotenv/src/bin/dotenv.rs:1:1
  |
1 | extern crate clap;
  | ^^^^^^^^^^^^^^^^^^ can't find crate

For more information about this error, try `rustc --explain E0463`.
error: could not compile `dotenv` (bin "dotenv") due to 1 previous error

[[bin]] あり

cargo run --bin dotenv
error: target `dotenv` in package `dotenv` requires the features: `cli`
Consider enabling them by passing, e.g., `--features="cli"`
a-yossya-yossy

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/Cargo.toml#L25-L26

dependenciesclap があるが、自動でインストールされないのは optional = true があるから

https://doc.rust-lang.org/cargo/reference/features.html#optional-dependencies

Dependencies can be marked “optional”, which means they will not be compiled by default.

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/Cargo.toml#L31-L32

[features] に設定を追加することでインストールしたい依存関係を明示できる

https://doc.rust-lang.org/cargo/reference/features.html#the-features-section

Features are defined in the [features] table in Cargo.toml. Each feature specifies an array of other features or optional dependencies that it enables.

a-yossya-yossy

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/lib.rs

[dependencies] に追加すると src/lib.rs を利用出来るようになる

https://doc.rust-jp.rs/book-ja/ch07-01-packages-and-crates.html#パッケージとクレート

Cargo.toml の中身を見ても、src/main.rs については何も書いてありません。これは、Cargoは src/main.rs が、パッケージと同じ名前を持つバイナリクレートのクレートルートであるという慣習に従っているためです。 同じように、Cargoはパッケージディレクトリに src/lib.rs が含まれていたら、パッケージにはパッケージと同じ名前のライブラリクレートが含まれており、src/lib.rs がそのクレートルートなのだと判断します。 Cargoはクレートルートファイルを rustcに渡し、ライブラリやバイナリをビルドします。

a-yossya-yossy

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/lib.rs#L23

call_once に渡されたクロージャーは一度しか実行されなくなる
初期化処理などに使用される

https://doc.rust-lang.org/std/sync/struct.Once.html#method.call_once

Performs an initialization routine once and only once. The given closure will be executed if this is the first time call_once has been called, and otherwise the routine will not be invoked.

動作例

use std::sync::Once;

static INIT: Once = Once::new();

fn init() {
    INIT.call_once(|| {
        println!("init");
    });
}

fn once_example() {
    init();
    init();
    init();
}

fn main() {
    once_example();
}
init
a-yossya-yossy

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/lib.rs#L161

PathBuf はファイルシステムのパスを表すことが出来る
ファイル操作の時に使用する

https://doc.rust-lang.org/std/path/struct.PathBuf.html

An owned, mutable path (akin to String).
This type provides methods like push and set_extension that mutate the path in place.

https://doc.rust-jp.rs/rust-by-example-ja/std_misc/path.html

構造体Pathは、ファイルシステム中のパスを表します。
Pathはイミュータブルです。Pathの所有権ありのバージョンがPathBufです。

a-yossya-yossy

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/lib.rs#L162

Finder は別ファイルで定義されている

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/find.rs#L8-L10

構造体のフィールドに参照を指定した場合、<'a> のようにライフタイムの指定が必須になる

https://doc.rust-jp.rs/book-ja/ch05-01-defining-structs.html#構造体データの所有権

構造体に、他の何かに所有されたデータへの参照を保持させることもできますが、 そうするにはライフタイムという第10章で議論するRustの機能を使用しなければなりません。 ライフタイムのおかげで構造体に参照されたデータが、構造体自体が有効な間、ずっと有効であることを保証してくれるのです。 ライフタイムを指定せずに構造体に参照を保持させようとしたとしましょう。以下の通りですが、これは動きません:

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/find.rs#L13-L17

filename フィールドを Path::new(".env") で初期化

a-yossya-yossy

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/find.rs#L25

env::current_dir で現在のディレクトリ名を取得できる
アクセス権限がない場合などには Err が返る

https://doc.rust-lang.org/std/env/fn.current_dir.html

Returns the current working directory as a PathBuf
Returns an Err if the current working directory value is invalid. Possible cases:

  • Current directory does not exist.
  • There are insufficient permissions to access the current directory.

map_err メソッドにより Result 型が返した Err を任意の Err に変換することが出来る
これの Ok バージョンとして map が存在する

https://doc.rust-lang.org/std/result/enum.Result.html#method.map_err

Maps a Result<T, E> to Result<T, F> by applying a function to a contained Err value, leaving an Ok value untouched.

find メソッドは下記で定義されている

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/find.rs#L32-L57

a-yossya-yossy

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/find.rs#L34

ルートディレクトリ(ビルド結果である target ディレクトリが作成されるディレクトリ)にある .env への絶対パスが candidate 変数に束縛される

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/find.rs#L36

fs::metadata メソッドにより引数のファイルが存在するかなどの処理を行える

https://doc.rust-lang.org/std/fs/fn.metadata.html

Given a path, queries the file system to get information about a file, directory, etc.
This function will return an error in the following situations, but is not limited to just these cases:

  • The user lacks permissions to perform metadata call on path.
  • path does not exist.

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/find.rs#L37-L46

ファイルが存在する場合はファイルの絶対パスを返す
ファイルが存在しないかつ NotFound エラー以外の場合はエラーを返す

std::io::Errorkind メソッドによりエラーの詳細を取得できる

https://doc.rust-lang.org/std/io/struct.Error.html#method.kind

Returns the corresponding ErrorKind for this error.

a-yossya-yossy

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/find.rs#L25-L28

path 変数には .env ファイルへの絶対パスが束縛されている
file 変数を引数にして Iter 構造体を初期化する

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/iter.rs#L15-L20

lines メソッドによりファイルの行単位での操作が可能になる

https://doc.rust-lang.org/std/io/trait.BufRead.html#method.lines

Returns an iterator over the lines of this reader.

(path, iter) によって .env ファイルへの絶対パスと Iter 構造体のインスタンスが返る

a-yossya-yossy

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/iter.rs#L23

for in 構文を呼び出している

https://doc.rust-jp.rs/rust-by-example-ja/flow_control/for.html#forとイテレータ

for in構文はIteratorとさまざまな方法でやり取りできます。

https://github.com/dotenv-rs/dotenv/blob/3c1a77bc95821777e5ceb996c5e0b082f2a3ea38/dotenv/src/iter.rs#L34-L52

Rust では Iterator トレイトを実装することで繰り返し処理を行えるようになる

https://doc.rust-jp.rs/rust-by-example-ja/trait/iter.html#イテレータ

Iteratorトレイトは、例えば配列のような、要素の集合に対してイテレータを実装するためのトレイトです。
このトレイトはnextの要素に相当するものを決定するためのメソッドのみを要求します。このメソッドはimplブロック内で手動で実装するか、あるいは(配列やrangeのように)自動で定義されます。

ログインするとコメントできます