Skip to content

Commit ed3bc33

Browse files
committed
advancedコマンドをリファクタリング
- `load_character_data()`関数を追加し、キャラクターデータ読み込み処理を分離 - `find_move_and_images()`関数を導入し、技情報と画像データの読み込みロジックを抽出 - `create_advanced_embeds()`関数を作成し、埋め込みメッセージ生成処理を分離 - エラーハンドリングを改善し、関数の責務を明確に分割 - コードの可読性と保守性を向上
1 parent 6b4b693 commit ed3bc33

1 file changed

Lines changed: 135 additions & 73 deletions

File tree

src/commands/frames/advanced.rs

Lines changed: 135 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -12,118 +12,103 @@ use std::{fs, string::String};
1212
/// デフォルト画像URL
1313
const IMAGE_DEFAULT: &str = "https://www.dustloop.com/wiki/images/5/54/GGST_Logo_Sparkly.png";
1414

15-
/// 指定されたキャラクターの技データを詳細に表示する非同期コマンド
16-
///
17-
/// # 概要
18-
/// 入力されたキャラクター名(または愛称)と技名(入力またはエイリアス)から、
19-
/// キャラクター情報ファイルおよび画像リンクファイルを読み込み、
20-
/// 対象の技データを抽出し、Discord用の埋め込みメッセージとして表示する。
15+
/// キャラクターデータを読み込む関数
2116
///
2217
/// # 引数
23-
/// * `ctx` - コマンド実行コンテキスト(Discordとの通信に利用)
24-
/// * `character` - キャラクター名または愛称(2文字以上必須)
25-
/// * `character_move` - 技名、入力、またはエイリアス(2文字以上必須)
18+
/// * `character` - ユーザーが入力したキャラクター名
19+
/// * `ctx` - コマンドコンテキスト
2620
///
2721
/// # 戻り値
28-
/// 正常終了時は `Ok(())` を返し、エラー発生時は `Err(Error)` を返す。
29-
///
30-
/// # 例
31-
/// ```rust,no_run
32-
/// # async fn example(ctx: Context<'_>) -> Result<(), Error> {
33-
/// advanced(ctx, "Sol".to_string(), "236H".to_string()).await?;
34-
/// # Ok(()) }
35-
/// ```
36-
///
37-
/// # 注意
38-
/// この関数は非同期関数であるため、呼び出し時に `.await` が必要。
39-
#[poise::command(prefix_command, slash_command)]
40-
pub async fn advanced(
41-
ctx: Context<'_>,
42-
#[min_length = 2]
43-
#[description = "キャラクター名または愛称"]
44-
character: String,
45-
#[min_length = 2]
46-
#[rename = "move"]
47-
#[description = "技名、入力、またはエイリアス"]
48-
character_move: String,
49-
) -> Result<(), AppError> {
50-
// コマンド引数の表示 引数確認用
51-
println!(
52-
"{}",
53-
("Command Args: '".to_owned() + &character + ", " + &character_move + "'").purple()
54-
);
55-
56-
// 埋め込み画像用変数の初期化 初期値はデフォルト画像URL
57-
let mut embed_image = IMAGE_DEFAULT.to_string();
58-
59-
// 入力チェックの実施 各種前提条件チェック
60-
if (check::adaptive_check(
61-
ctx,
62-
check::CheckOptions {
63-
data_folder: true,
64-
nicknames_json: true,
65-
character_folders: true,
66-
character_jsons: true,
67-
character_images: true,
68-
},
69-
)
70-
.await)
71-
.is_err()
72-
{
73-
return Ok(());
74-
}
75-
22+
/// 成功時は正式なキャラクター名、失敗時はエラー
23+
async fn load_character_data(character: &str, ctx: &Context<'_>) -> Result<String, AppError> {
7624
// キャラクター名の正規化 入力に基づく正式名称の取得
77-
let character_arg_altered = match find::find_character(&character).await {
25+
let character_arg_altered = match find::find_character(&character.to_string()).await {
7826
Ok(character_arg_altered) => character_arg_altered, // 正式名称取得
7927
Err(err) => {
8028
ctx.say(err.to_string()).await?; // エラーメッセージ送信
8129
println!("{}", ("Error: ".to_owned() + &err.to_string()).red());
82-
return Ok(()); // 処理中断
30+
return Err(AppError::CharacterNotFound(err.to_string())); // 処理中断
8331
}
8432
};
8533

34+
Ok(character_arg_altered)
35+
}
36+
37+
/// 技情報と画像データを読み込む関数
38+
///
39+
/// # 引数
40+
/// * `character_arg_altered` - 正式なキャラクター名
41+
/// * `character_move` - ユーザーが入力した技名
42+
/// * `ctx` - コマンドコンテキスト
43+
///
44+
/// # 戻り値
45+
/// 成功時は (技情報, 画像URL) のタプル、失敗時はエラー
46+
async fn find_move_and_images(
47+
character_arg_altered: &str,
48+
character_move: &str,
49+
ctx: &Context<'_>,
50+
) -> Result<(MoveInfo, String), AppError> {
8651
// JSONファイルパスの組み立て キャラクター情報ファイルのパス生成
8752
let char_file_path =
88-
"data/".to_owned() + &character_arg_altered + "/" + &character_arg_altered + ".json";
53+
"data/".to_owned() + character_arg_altered + "/" + character_arg_altered + ".json";
8954
// JSONファイルの読み込み キャラクター情報の取得
9055
let char_file_data = fs::read_to_string(char_file_path)
91-
.expect(&("\nFailed to read '".to_owned() + &character_arg_altered + ".json" + "' file."));
56+
.expect(&("\nFailed to read '".to_owned() + character_arg_altered + ".json" + "' file."));
9257

9358
// JSON文字列のデシリアライズ 技情報の構造体へ変換
9459
let moves_info = serde_json::from_str::<Vec<MoveInfo>>(&char_file_data).unwrap();
9560

9661
// JSON読み込み成功の表示 確認メッセージ出力
9762
println!(
9863
"{}",
99-
("Successfully read '".to_owned() + &character_arg_altered + ".json' file.").green()
64+
("Successfully read '".to_owned() + character_arg_altered + ".json' file.").green()
10065
);
10166

10267
// 画像リンク用JSONファイルのパス組み立て 画像情報ファイルの指定
10368
let image_links = fs::read_to_string(
104-
"data/".to_owned() + &character_arg_altered + "/images.json",
69+
"data/".to_owned() + character_arg_altered + "/images.json",
10570
)
10671
.expect(
107-
&("\nFailed to read 'data/".to_owned() + &character_arg_altered + "'/images.json' file."),
72+
&("\nFailed to read 'data/".to_owned() + character_arg_altered + "'/images.json' file."),
10873
);
10974

11075
// 画像リンクJSONのデシリアライズ 画像リンク情報の構造体へ変換
11176
let image_links = serde_json::from_str::<Vec<ImageLinks>>(&image_links).unwrap();
77+
78+
// 技インデックス検索
79+
let move_index = match find::find_move_index(
80+
&character_arg_altered.to_string(),
81+
character_move.to_string(),
82+
&moves_info,
83+
)
84+
.await
85+
{
86+
Ok(index) => index,
87+
Err(err) => {
88+
ctx.say(err.to_string() + "\nView the moves of a character by executing `/moves`.")
89+
.await?;
90+
println!("{}", ("Error: ".to_owned() + &err.to_string()).red());
91+
return Err(AppError::MoveNotFound(err.to_string()));
92+
}
93+
};
94+
11295
// 対象技情報の取得 入力に対応する技データの抽出
113-
let move_info = &moves_info
114-
[find::find_move_index(&character_arg_altered, character_move, &moves_info).await?];
96+
let move_info = moves_info[move_index].clone();
11597

11698
// 対象技の読み込み成功の表示 確認メッセージ出力
11799
println!(
118100
"{}",
119101
("Successfully read move '".to_owned()
120-
+ &move_info.input.to_string()
102+
+ &move_info.input
121103
+ "' in '"
122-
+ &character_arg_altered
104+
+ character_arg_altered
123105
+ ".json' file.")
124106
.green()
125107
);
126108

109+
// デフォルト画像URL設定
110+
let mut embed_image = IMAGE_DEFAULT.to_string();
111+
127112
// 画像リンクの探索 対象技の画像リンクを検索
128113
for img_links in image_links {
129114
// 対象技の入力と画像情報の入力が一致し、画像リンクが存在する場合
@@ -133,22 +118,38 @@ pub async fn advanced(
133118
}
134119
}
135120

121+
Ok((move_info, embed_image))
122+
}
123+
124+
/// 技情報の詳細な埋め込みメッセージを作成する関数
125+
///
126+
/// # 引数
127+
/// * `move_info` - 技情報
128+
/// * `embed_image` - 埋め込む画像のURL
129+
/// * `character_arg_altered` - 正式なキャラクター名
130+
///
131+
/// # 戻り値
132+
/// 埋め込みメッセージのベクター
133+
fn create_advanced_embeds(
134+
move_info: &MoveInfo,
135+
embed_image: &str,
136+
character_arg_altered: &str,
137+
) -> Vec<CreateEmbed> {
136138
// 埋め込みメッセージ群生成用ベクターの初期化
137139
let mut vec_embeds = Vec::new();
138140
// 埋め込みタイトルの作成 キャラクター名と技名を組み合わせたタイトル
139141
let embed_title = "__**".to_owned() + &move_info.input + "**__";
140142
// 埋め込みURLの作成 Dustloop Wiki のキャラクター概要ページURL生成
141-
let embed_url =
142-
"https://dustloop.com/w/GGST/".to_owned() + &character_arg_altered + "#Overview";
143+
let embed_url = "https://dustloop.com/w/GGST/".to_owned() + character_arg_altered + "#Overview";
143144
// 埋め込みフッターの作成 技に関するキャプションを利用
144145
let embed_footer = CreateEmbedFooter::new(&move_info.caption);
145146

146147
// 埋め込みメッセージの生成 技データの各パラメータをフィールドとして追加
147148
let embed = CreateEmbed::new()
148149
.color(EMBED_COLOR) // 埋め込みカラー設定
149-
.title(&embed_title) // タイトル設定
150-
.url(&embed_url) // URL設定
151-
.image(&embed_image) // 画像リンク設定
150+
.title(embed_title) // タイトル設定
151+
.url(embed_url) // URL設定
152+
.image(embed_image) // 画像リンク設定
152153
.fields(vec![
153154
(
154155
"ダメージ",
@@ -235,6 +236,67 @@ pub async fn advanced(
235236
vec_embeds.push(embed2); // ベクターに追加
236237
}
237238

239+
vec_embeds
240+
}
241+
242+
/// 技の詳細情報を埋め込みメッセージで表示するコマンド
243+
///
244+
/// # 引数
245+
/// * `ctx` - コマンドコンテキスト
246+
/// * `character` - キャラクター名または愛称
247+
/// * `character_move` - 技名、入力、またはエイリアス
248+
///
249+
/// # 戻り値
250+
/// 処理結果 `Result<(), AppError>`
251+
#[poise::command(prefix_command, slash_command)]
252+
pub async fn advanced(
253+
ctx: Context<'_>,
254+
#[min_length = 2]
255+
#[description = "キャラクター名または愛称"]
256+
character: String,
257+
#[min_length = 2]
258+
#[rename = "move"]
259+
#[description = "技名、入力、またはエイリアス"]
260+
character_move: String,
261+
) -> Result<(), AppError> {
262+
// コマンド引数の表示 引数確認用
263+
println!(
264+
"{}",
265+
("Command Args: '".to_owned() + &character + ", " + &character_move + "'").purple()
266+
);
267+
268+
// 入力チェックの実施 各種前提条件チェック
269+
if (check::adaptive_check(
270+
ctx,
271+
check::CheckOptions {
272+
data_folder: true,
273+
nicknames_json: true,
274+
character_folders: true,
275+
character_jsons: true,
276+
character_images: true,
277+
},
278+
)
279+
.await)
280+
.is_err()
281+
{
282+
return Ok(());
283+
}
284+
285+
// キャラクターデータ読み込み
286+
let Ok(character_arg_altered) = load_character_data(&character, &ctx).await else {
287+
return Ok(());
288+
};
289+
290+
// 技情報と画像データ読み込み
291+
let Ok((move_info, embed_image)) =
292+
find_move_and_images(&character_arg_altered, &character_move, &ctx).await
293+
else {
294+
return Ok(());
295+
};
296+
297+
// 埋め込みメッセージ作成
298+
let vec_embeds = create_advanced_embeds(&move_info, &embed_image, &character_arg_altered);
299+
238300
// 返信メッセージ用オブジェクト生成 送信用オブジェクトの初期化
239301
let mut reply = poise::CreateReply::default();
240302
// 生成した埋め込みメッセージ群を返信オブジェクトに追加

0 commit comments

Comments
 (0)