ホームページ  >  記事  >  バックエンド開発  >  Rust で遊ぶ: より安全な rm を構築し、途中で楽しむ

Rust で遊ぶ: より安全な rm を構築し、途中で楽しむ

Mary-Kate Olsen
Mary-Kate Olsenオリジナル
2024-10-21 06:07:02431ブラウズ

Playing with Rust: Building a Safer rm and Having Fun Along the Way

私の YOLO シリーズへようこそ。ここでは私が構築した単純なツールとプロジェクトを紹介します。時には楽しみのために、時には特定の問題を解決するために、あるいは単に純粋な好奇心からの場合もあります。ここでの目標は、ツールを提供することだけではありません。また、技術的な洞察や、これらの小さな実験を作成する際に学んだ教訓など、プロセスに関連した興味深い内容についても詳しく説明します。

rrm の紹介: 誰も求めなかったコマンドライン ツール

誰もそれを求めていませんし、誰もそれを望んでいません—しかし、とにかくそれはここにあります。 rrm をご紹介します。これは、私だけが抱えていると思われる問題を解決するツールです (ただし、これはレイヤー 8 の問題である可能性があります。あるいは、スキルの問題である可能性が高いです!)。

rrm は、ファイルを完全に削除するのではなく、ゴミ箱に移動することにより、コマンドライン エクスペリエンスに安全層を追加します。カスタマイズ可能な猶予期間を使用すると、手遅れになる前に 「ああ、これが本当に必要だったんだ!」 と気づく機会が得られます。

さらに、rrm は削除されたファイルを管理するために外部設定ファイルや追跡システムに依存しません。代わりに、ファイルシステムの拡張属性を利用して、元のファイルのパスや削除時刻などの重要なメタデータをゴミ箱に入れられたアイテム内に直接保存します。

あなたは疑問に思っているかもしれません。「類似した、おそらくより優れたツールが存在するのに、なぜこのツールを作成するのでしょうか?」 答えは簡単です:

  • Rust で遊びたかったのです。 目的を持った小さなツールを構築することは、言語を探索し、スキルを磨くための素晴らしい方法です。
  • 精神的なフレームワークを作成する方法として独自の CLI ツールを開発するのと似ています。 これは、特定のテクノロジーに合わせてコマンドライン ユーティリティを構築する方法に一貫してアプローチするのに役立ちます。これらのツールを構築することで、どの依存関係を使用するか、コードを編成する方法、各ツールを言語のエコシステムに適応させる方法についての理解が深まります。これは、自分のニーズに合った CLI ツールを作成するためのメンタル プレイブックを構築する方法です。
  • YOLO です。 私は、解決したい問題や興味のあることに関する簡単なツールや概念実証を作成するのが好きです。時には、学習のために実験することもあります。

面白いメモ: std::Path を使って作業しているときに、Rust 標準ライブラリで laputa

という名前のフォルダーを使用する例を見つけました。これが天空の城ラピュタを引用していることはわかっていますが、スペイン語話者にとっては呪いの言葉でもあり、私にとっては面白い瞬間でした!<script> // Detect dark theme var iframe = document.getElementById('tweet-1844834987184410735-190'); if (document.body.className.includes('dark-theme')) { iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1844834987184410735&theme=dark" } </script>

拡張属性: ファイルを変更せずにメタデータを保存する

私が rrm の構築を開始したとき、削除されたファイルの元のパスとそれらが永久に削除されるべき時間を追跡する方法が必要でした。特に後でさらに多くのデータを保存したい場合には、JSON ファイルを使用したり、この情報を含む奇妙な命名形式を実装したくありませんでした。このような小さなタスクに対してデータベースは過剰に感じられました。

そのとき、拡張属性を発見しました。

拡張属性とは何ですか?

皆さんはどうか知りませんが、ファイルにカスタム メタデータを追加できる組み込みメカニズムがあるとは知りませんでした。これは、ほとんどの Linux ファイルシステムと macOS などの Unix 系システムでサポートされています。 。この機能は拡張ファイル属性と呼ばれます。システムごとに、追加できるデータの量や使用される特定の名前空間など、独自の制限がありますが、ユーザー定義のメタデータを保存できます

拡張属性は本質的に、ファイルとディレクトリに永続的に関連付けられる名前:値のペアです。前に述べたように、これを処理する方法はシステムによって異なります。たとえば、Linux では、名前は名前空間識別子で始まります。このような名前空間には、セキュリティ、システム、信頼済み、ユーザーの 4 つがあります。 Linux では、名前はこれらのいずれかで始まり、その後にドット (「.」)、そして NULL で終わる文字列が続きます。 macOS では、状況が少し異なります。 macOS は、統合メタデータ アプローチ のおかげで、名前空間をまったく必要としません。これは、拡張属性を、分類することなくファイルに直接関連付けられた追加のメタデータとして扱います。

この小さな CLI では、Linux と macOS の両方をサポートするクレート xattr を使用しています。 Linux について前述した名前空間については、ユーザー名前空間に焦点を当てます。これらの属性はユーザーが使用することを目的としているためです。したがって、コードには次のような内容が表示されます:

/// Namespace for extended attributes (xattrs) on macOS and other operating systems.
/// On macOS, this is an empty string, while on other operating systems, it is "user.".
#[cfg(target_os = "macos")]
const XATTR_NAMESPACE: &str = "";
#[cfg(not(target_os = "macos"))]
const XATTR_NAMESPACE: &str = "user.";

...

    fn set_attr(&self, path: &Path, attr: &str, value: &str) -> Result<()> {
        let attr_name = format!("{}{}", XATTR_NAMESPACE, attr);
        ...
    }

Rust の #[cfg(target_os = "macos")] 属性は、ターゲット オペレーティング システムに基づいてコードを条件付きでコンパイルするために使用されます。この場合、macOS 用にコンパイルする場合にのみコード ブロックが含まれることが保証されます。これが関係するのは、前述したように、macOS では拡張属性に名前空間が必要ないため、XATTR_NAMESPACE は空の文字列に設定されるからです。他のオペレーティング システムの場合、名前空間は「user.」に設定されます。この条件付きコンパイルにより、コードがさまざまなプラットフォーム間でシームレスに適応できるようになり、CLI が Linux と macOS の両方と相互互換性を持つようになります。

拡張属性について非常に優れていると感じた点の 1 つは、拡張属性がファイル自体を変更しないことです。メタデータは、inode によって参照される別のディスク領域に存在します。これは、ファイルの実際の内容が変更されないことを意味します。たとえば、単純なファイルを作成し、shasum を使用してそのチェックサムを取得するとします。

inode (インデックス ノード) は、ファイルやディレクトリなどのファイル システム オブジェクトを記述する Unix スタイルのファイル システムのデータ構造です。リンク

/// Namespace for extended attributes (xattrs) on macOS and other operating systems.
/// On macOS, this is an empty string, while on other operating systems, it is "user.".
#[cfg(target_os = "macos")]
const XATTR_NAMESPACE: &str = "";
#[cfg(not(target_os = "macos"))]
const XATTR_NAMESPACE: &str = "user.";

...

    fn set_attr(&self, path: &Path, attr: &str, value: &str) -> Result<()> {
        let attr_name = format!("{}{}", XATTR_NAMESPACE, attr);
        ...
    }

rrm を使用してファイルを削除した後、削除されたファイルを一覧表示すると、ファイルがメタデータをそのままにしてゴミ箱に移動されたことが確認できます。

$ cat a.txt
https://www.kungfudev.com/

$ shasum a.txt
e4c51607d5e7494143ffa5a20b73aedd4bc5ceb5  a.txt

ご覧のとおり、ファイル名が UUID に変更されます。これは、同じ名前のファイルを削除するときに名前の衝突を避けるために行われます。 rrm は、各ファイルに一意の識別子を割り当てることにより、削除されたすべてのファイル(名前が同じであっても)を問題なく追跡および復元できるようにします。

ゴミ箱フォルダーに移動し、ファイルを検査して、その内容が変更されていないことを確認できます。

$ rrm rm a.txt

$ rrm list
╭──────────────────────────────────────────────────────┬──────────────────────────────────────┬──────┬─────────────────────╮
│ Original Path                                        ┆ ID                                   ┆ Kind ┆ Deletion Date       │
╞══════════════════════════════════════════════════════╪══════════════════════════════════════╪══════╪═════════════════════╡
│ /Users/douglasmakey/workdir/personal/kungfudev/a.txt ┆ 3f566788-75dc-4674-b069-0faeaa86aa55 ┆ File ┆ 2024-10-27 04:10:19 │
╰──────────────────────────────────────────────────────┴──────────────────────────────────────┴──────┴─────────────────────╯

さらに、macOS で xattr を使用すると、ファイルに削除日や元のパスなどのメタデータがあることを確認できます。

$ shasum 3f566788-75dc-4674-b069-0faeaa86aa55
e4c51607d5e7494143ffa5a20b73aedd4bc5ceb5  3f566788-75dc-4674-b069-0faeaa86aa55

このメタデータを使用した単純な検証やアクションの潜在的なユースケースの範囲を想像できます。拡張属性はファイル自体を変更せずに機能するため、元のコンテンツに影響を与えることなく、ファイルの整合性をチェックしたり、その他の操作を実行したりできます。

これは、拡張属性とそれがこのプロジェクトでどのように使用されるかを簡単に紹介したものです。詳細な説明を目的としたものではありませんが、さらに詳しく知りたい場合は、詳細なリソースがたくさんあります。このトピックに関する最も有用でよく説明されたリソースへのリンクをいくつか示します:

  • https://wiki.archlinux.org/title/Extended_attributes
  • https://man7.org/linux/man-pages/man7/xattr.7.html
  • https://en.wikipedia.org/wiki/Extended_file_attributes

Rust でのモッキング: テスト用のモックルの探索

私は Go を使って数年間仕事をしてきましたが、特定のパターンが好きになり、そのパターンの 1 つであることを嘲笑しています。 Go では、不必要なインポートを回避したり、柔軟性を高めたりする場合は、通常、自分で実装します。私はこのアプローチに慣れているため、Rust でテストを書き始めたとき、特性のモック実装の作成など、特定のものを手動でモックすることを好む自分に気づきました。

たとえば、この小さな CLI では、ゴミ箱マネージャーを拡張属性と対話する方法から切り離す特性を作成しました。 ExtendedAttributes という名前のこのトレイトは、当初はテスト目的でしたが、xattr を使用するか別の実装を使用するか確信がなかったためでもありました。そこで、次の特性を定義しました:

$ xattr -l 3f566788-75dc-4674-b069-0faeaa86aa55
deletion_date: 2024-10-27T04:10:19.875614+00:00
original_path: /Users/douglasmakey/workdir/personal/kungfudev/a.txt

Go では、前述のインターフェイスの簡単な実装を提供する次のようなものを作成します。以下のコードは単純であり、単なる例として、あまり考慮せずに生成されています。

/// Namespace for extended attributes (xattrs) on macOS and other operating systems.
/// On macOS, this is an empty string, while on other operating systems, it is "user.".
#[cfg(target_os = "macos")]
const XATTR_NAMESPACE: &str = "";
#[cfg(not(target_os = "macos"))]
const XATTR_NAMESPACE: &str = "user.";

...

    fn set_attr(&self, path: &Path, attr: &str, value: &str) -> Result<()> {
        let attr_name = format!("{}{}", XATTR_NAMESPACE, attr);
        ...
    }

次に、モックを使用して、各テストに必要な特定の動作を挿入します。繰り返しますが、これは例を目的とした単純なコードです:

$ cat a.txt
https://www.kungfudev.com/

$ shasum a.txt
e4c51607d5e7494143ffa5a20b73aedd4bc5ceb5  a.txt

Go のこのパターンには慣れてきたので、今後も使用していくつもりです。しかし、私はRustでも同様のことを行ってきました。このプロジェクトでは、モコール クレートを試してみることにしました。これが非常に便利であることがわかりました。

まずはモックを使ってみました!私の構造を手動でモックするマクロ。モックコールにオートモック機能があることは知っていますが、使用されるテストでモック構造体を直接定義することを好みます。 これが一般的なことなのか、それともコミュニティでこれとは異なる基準があるのか​​を教えてください。

$ rrm rm a.txt

$ rrm list
╭──────────────────────────────────────────────────────┬──────────────────────────────────────┬──────┬─────────────────────╮
│ Original Path                                        ┆ ID                                   ┆ Kind ┆ Deletion Date       │
╞══════════════════════════════════════════════════════╪══════════════════════════════════════╪══════╪═════════════════════╡
│ /Users/douglasmakey/workdir/personal/kungfudev/a.txt ┆ 3f566788-75dc-4674-b069-0faeaa86aa55 ┆ File ┆ 2024-10-27 04:10:19 │
╰──────────────────────────────────────────────────────┴──────────────────────────────────────┴──────┴─────────────────────╯

モックコールは非常に便利で、古いパターンの冗長さを省いて特定の動作をテストに挿入できることがわかりました。

$ shasum 3f566788-75dc-4674-b069-0faeaa86aa55
e4c51607d5e7494143ffa5a20b73aedd4bc5ceb5  3f566788-75dc-4674-b069-0faeaa86aa55

ご覧のとおり、モックコールでは、モック メソッドを使用してテストの特定の動作を定義する機能が提供されます。

  • MockXattrManager::new() これにより、モック オブジェクト MockXattrManager の新しいインスタンスが作成され、テスト用に XattrManager の動作をモックするために使用されます。
  • xattr_manager.expect_set_attr() これにより、テスト中に set_attr メソッドが呼び出されることが期待されます。次に、このメソッドの予期される動作を定義します。
  • with(...) with メソッドは、set_attr が呼び出されたときに予期される引数を指定します。この場合、3 つの引数が想定されており、in_iter を使用して、各引数が提供されたベクトルの値の 1 つと一致する必要があることを示します。これにより、引数が単一の完全一致ではなく、渡されたベクトルの値の 1 つであるかどうかをチェックするため、テストに柔軟性が得られます。
  • 回(4) これは、set_attr メソッドがテスト中にちょうど 4 回呼び出されることが予期されることを指定します。
  • 戻ります(|_, _, _| OK(())) これにより、set_attr が呼び出されたときに何を返すかをモックに指示します。この場合、引数に関係なく Ok(()) を返します (|_, _, _| は引数が無視されることを意味します)。これは、set_attr.
  • の正常な実行をシミュレートします。

これは超基本的な内容だったり、それほど面白くないと思う人もいるかもしれませんが、前述したように、この YOLO シリーズでは、私が面白いと思うものや話したいことを共有しています。私は Go の制約もあって、この種のライブラリを Go で使用することはあまり好きではありませんでしたが、Rust ではモコールが非常に便利であることがわかりました。 Python を使っていた昔のことさえ思い出しました。

繰り返しになりますが、このセクションは Rust またはモコールでのモックを説明することを目的としたものではありません。詳細をカバーする素晴らしいリソースがたくさんあると思います。簡単に触れておきたかっただけです。

結論としては

この投稿では、rrm の構築の背後にある理由の一部と、その過程で使用したツールを共有しました。メタデータの処理を簡素化するための拡張属性の使用から、Rust でのテスト用のモックオール クレートの実験まで、これらは私の興味をそそるものでした。

この YOLO シリーズの目標は、たとえ単純なツールでも構築することに伴う楽しさと学習を強調することです。ここで何か役に立つことがあれば幸いです。今後の投稿でさらに多くのプロジェクトや洞察を共有できることを楽しみにしています。いつものように、フィードバックは大歓迎です!

コーディングを楽しんでください!

以上がRust で遊ぶ: より安全な rm を構築し、途中で楽しむの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。