[[Rustでテキストをside-by-side diff表示]] で作成した機能を[[クレート]]にして公開してみる。 ## プロジェクト作成 libプロジェクトとしてまずは作る。 ```console cargo new --lib side-by-side-diff ``` 依存関係として[[Similar]]と[[unicode-diff]]を追加。 ```console cd side-by-side-diff cargo add similiar unicode-width ``` ## 実装 `lib.rs`にコードを書く。ついでにテストコードも。 ```rust use similar::{ChangeTag, TextDiff}; use unicode_width::UnicodeWidthStr; pub fn create_side_by_side_diff(text1: &str, text2: &str, max_width: usize) -> String { TextDiff::from_lines(text1, text2) .iter_all_changes() .map(|change| { let content = change.to_string().trim_end_matches('\n').to_string(); let width = max_width - (content.width() - content.chars().count()); match change.tag() { ChangeTag::Delete => format!( "{:>6} | {:<width$} | {:>6} | {:<blank_width$} |", change.old_index().unwrap() + 1, content, "", "", blank_width = max_width, width = width, ), ChangeTag::Insert => format!( "{:>6} | {:<blank_width$} | {:>6} | {:<width$} |", "", "", change.new_index().unwrap() + 1, content, blank_width = max_width, width = width ), ChangeTag::Equal => format!( "{:>6} | {token:<width$} | {:>6} | {token:<width$} |", change.old_index().unwrap() + 1, change.new_index().unwrap() + 1, token = content, width = width ), } }) .collect::<Vec<_>>() .join("\n") } #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { let actual = create_side_by_side_diff( " あああ bbb ccc ddd いいいいいーeee かかお FFFFFFFFFF ggg" .trim_matches('\n'), " あああ bbb いいいいいーeee かかか ffffffffff ggg" .trim_matches('\n'), 20, ); let expected = " 1 | あああ | 1 | あああ | 2 | bbb | 2 | bbb | 3 | ccc | | | 4 | ddd | | | 5 | いいいいいーeee | 3 | いいいいいーeee | 6 | かかお | | | 7 | FFFFFFFFFF | | | | | 4 | かかか | | | 5 | ffffffffff | 8 | ggg | 6 | ggg | " .trim_matches('\n'); assert_eq!(expected, actual); } } ``` ## 公開 実装は不十分だが、今回の目的は[[クレート]]の公開実績を解除すること。なので、まずはこの状態でトライする。 <div class="link-card"> <div class="link-card-header"> <img src="https://doc.rust-lang.org/cargo/favicon.png" class="link-card-site-icon"/> <span class="link-card-site-name">doc.rust-lang.org</span> </div> <div class="link-card-body"> <div class="link-card-content"> <div> <p class="link-card-title">Publishing on crates.io - The Cargo Book</p> </div> <div class="link-card-description"> </div> </div> </div> <a href="https://doc.rust-lang.org/cargo/reference/publishing.html"></a> </div> ### アカウントとAPIトークンの作成 [[crates.io]]から[[GitHub]]のアカウント経由でログインする。APIトークンを作成しコピーする。 次に、コマンドラインからログインする。APIトークンを入力する。 ```console cargo login ``` `${CARGO_HOME}/.cargo/credentials`にAPIトークンが書き込まれる。 ```toml [registry] token = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ``` ### メタデータの入力 [[Cargo.toml]]に必要な情報を入力する。 - [x] licenseまたlicense-file - [x] description - [*] homepage - [x] documentation - [x] repository - [x] readme ```toml authors = ["tadashi-aikawa <[email protected]>"] description = "Create side-by-side diff text." license = "MIT" homepage = "https://github.com/tadashi-aikawa/side-by-side-diff" documentation = "https://docs.rs/side-by-side-diff" repository = "https://github.com/tadashi-aikawa/side-by-side-diff" readme = "README.md" ``` ### dry-run まずはdry runをする。 ```console cargo publish --dry-run ``` ### アップロード 問題なければ本実行する。 ```console cargo publish ``` エラーになった。 ``` Caused by: the remote server responded with an error: A verified email address is required to publish crates to crates.io. Visit https://crates.io/me to set and verify your email address. ``` メールアドレスからverifiedが必要らしい。 ![[Pasted image 20230218195606.png]] もう一度実行したら無事に公開された。 https://crates.io/crates/side-by-side-diff ## 利用 アップロードした[[クレート]]を利用できるか確かめてみる。 ```console cargo new --bin side-by-side-diff-use cd side-by-side-diff-use cargo add side-by-side-diff ``` コードを書く。 ```rust use side_by_side_diff::create_side_by_side_diff; fn main() { let diff = create_side_by_side_diff("aaa\niii\nuuu", "aaa\nii\nuuu", 20); println!("{diff}"); } ``` 実行する。 ```console $ cargo run 1 | aaa | 1 | aaa | 2 | iii | | | | | 2 | ii | 3 | uuu | 3 | uuu | ``` ## バージョンアップを自動化する ローカルで以下の作業を自動化する。 - テスト - ビルド - バージョンのインクリメント - タグの追加 - 変更点のコミット/プッシュ [[Task]]を使う。[[Taskfile.yml]]を作成する。また、[[cargo-release]]もインストールされている前提。 ```yaml version: "3" tasks: default: - task: help help: silent: true cmds: - task -l ci: desc: For CI cmds: - cargo test - cargo build --release release: desc: | Build ∟ [Ex] task release VERSION=1.2.3 ∟ [Ex] task release VERSION=1.2.3-beta deps: - ci cmds: - cargo release version {{.VERSION}} -x --no-confirm - task: ci - git add . - git commit -m "version {{.VERSION}}" - git tag v{{.VERSION}} -m v{{.VERSION}} - git push --tags - git push preconditions: - sh: "[ {{.VERSION}} != '' ]" msg: "VERSION is required." ``` ## [[GitHub Actions]]でリリース タグがpushされたら自動でリリースされるようにしたい。必要な作業は以下の通り。 - ログイン or 認証を通す何か - [[cargo publishでAPIトークンを指定]] - テスト - ビルド - 公開 - [[GitHub]]のReleaseページ更新 [[GitHub Actions]]のファイルを作成する。 `.github/workflows/release.yaml` ```yaml name: "Release" on: push: tags: - "*" jobs: release: runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: stable - run: cargo test - run: cargo build --release - run: cargo publish env: CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - name: Create Release id: create_release uses: softprops/action-gh-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ```