rust_unique_pass/password/generate_pass.rs
1/* Copyright 2023-2025 Neuron Grid
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7 https://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License. */
14
15use crate::cli::RupassArgs;
16use crate::cli::UserInterface;
17use crate::core::app_errors::{GenerationError, Result};
18use crate::core::utils::fallback_translation;
19use crate::password::character_set::assemble_character_set;
20use crate::password::password_generation::produce_secure_password;
21use crate::password::password_length::get_password_length;
22use fluent::{FluentBundle, FluentResource};
23use tokio::task;
24
25#[doc(alias = "generate")]
26/// # Overview
27/// パスワード生成の主要なフローを実行します。
28/// ユーザーインターフェースを通じてパスワード長と使用文字セットを取得し、
29/// 安全なパスワードを生成して表示します。
30///
31/// # Arguments
32/// * `ui`: ユーザーとの対話に使用する [`UserInterface`] トレイトオブジェクト。
33/// * `bundle`: 国際化対応に使用する [`FluentBundle`] オブジェクト。
34/// * `args`: コマンドライン引数を格納した [`RupassArgs`] 構造体。
35///
36/// # Returns
37/// パスワード生成フローが成功した場合は `Ok(())` を返します。
38///
39/// # Errors
40/// パスワード長の取得、文字セットの組み立て、またはパスワード生成中にエラーが発生した場合、
41/// [`GenerationError`] を含む [`Result`] を返します。
42///
43/// # Notes
44/// パスワード生成処理は計算コストが高いため、ブロッキングスレッドプール
45/// (`tokio::task::spawn_blocking`) で実行されます。
46/// 生成されたパスワードは [`Zeroizing<String>`] でラップされ、スコープを離れる際に自動的にゼロクリアされます。
47// パスワード生成フローのエントリポイント
48pub async fn generate_password_flow(
49 ui: &mut dyn UserInterface,
50 bundle: &FluentBundle<FluentResource>,
51 args: &RupassArgs,
52) -> Result<()> {
53 let gen_msg = fallback_translation(bundle, "generated_password", "Generated password:", None);
54 let length = get_password_length(ui, bundle, args).await?;
55 let (all_chars, req_sets) = assemble_character_set(ui, bundle, args).await?;
56
57 let all_vec: Vec<char> = all_chars.chars().collect();
58 let req_vec: Vec<Vec<char>> = req_sets.iter().map(|s| s.chars().collect()).collect();
59
60 let pwd = task::spawn_blocking(move || produce_secure_password(&all_vec, length, &req_vec))
61 .await
62 .map_err(|_join_err| GenerationError::GenerationFailed)??;
63 let output_msg = format!("{gen_msg}\n{}\n", pwd.as_str());
64 ui.print(&output_msg).await?;
65
66 // スコープ離脱で自動zeroize
67 drop(pwd);
68 Ok(())
69}