Rust · 3535 bytes Raw Blame History
1 //! wanda scan - Scan for Steam games
2
3 use clap::Args;
4 use console::style;
5 use std::path::PathBuf;
6 use wanda_core::{config::WandaConfig, steam::SteamInstallation, Result};
7
8 #[derive(Args)]
9 pub struct ScanArgs {
10 /// Filter games by name
11 #[arg(short, long)]
12 filter: Option<String>,
13
14 /// Show all games, including non-Proton
15 #[arg(long)]
16 all: bool,
17
18 /// Show detailed information
19 #[arg(short, long)]
20 long: bool,
21 }
22
23 pub async fn run(args: ScanArgs, config_path: Option<PathBuf>) -> Result<()> {
24 let config = match &config_path {
25 Some(path) => WandaConfig::load_from(path)?,
26 None => WandaConfig::load()?,
27 };
28
29 let steam = SteamInstallation::discover(&config)?;
30 let mut games: Vec<_> = steam.get_all_games();
31
32 // Filter by name if specified
33 if let Some(ref filter) = args.filter {
34 let filter_lower = filter.to_lowercase();
35 games.retain(|g| g.name.to_lowercase().contains(&filter_lower));
36 }
37
38 // Filter to Proton games unless --all
39 if !args.all {
40 games.retain(|g| g.uses_proton());
41 }
42
43 // Sort by name
44 games.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
45
46 if games.is_empty() {
47 println!("No games found.");
48 if !args.all {
49 println!("(Use --all to show non-Proton games)");
50 }
51 return Ok(());
52 }
53
54 println!(
55 "Found {} games{}:\n",
56 games.len(),
57 if !args.all {
58 " (Proton/Windows)"
59 } else {
60 ""
61 }
62 );
63
64 if args.long {
65 // Detailed view
66 for game in &games {
67 let proton_indicator = if game.uses_proton() {
68 style("PROTON").green()
69 } else {
70 style("NATIVE").blue()
71 };
72
73 println!(
74 "{} {} {}",
75 style(format!("[{}]", game.app_id)).dim(),
76 style(&game.name).bold(),
77 proton_indicator
78 );
79 println!(" Path: {}", game.install_path.display());
80 println!(" Size: {}", game.size_human());
81 if let Some(ref compat) = game.compat_data_path {
82 println!(" Prefix: {}", compat.display());
83 }
84 println!();
85 }
86 } else {
87 // Compact view
88 let max_name_len = games
89 .iter()
90 .map(|g| g.name.len())
91 .max()
92 .unwrap_or(20)
93 .min(50);
94
95 println!(
96 "{:>8} {:<width$} {:>10} {}",
97 "App ID",
98 "Name",
99 "Size",
100 "Type",
101 width = max_name_len
102 );
103 println!("{}", "-".repeat(8 + 2 + max_name_len + 2 + 10 + 2 + 8));
104
105 for game in &games {
106 let name = if game.name.len() > max_name_len {
107 format!("{}...", &game.name[..max_name_len - 3])
108 } else {
109 game.name.clone()
110 };
111
112 let game_type = if game.uses_proton() {
113 style("Proton").green()
114 } else {
115 style("Native").blue()
116 };
117
118 println!(
119 "{:>8} {:<width$} {:>10} {}",
120 game.app_id,
121 name,
122 game.size_human(),
123 game_type,
124 width = max_name_len
125 );
126 }
127 }
128
129 println!();
130 println!(
131 "Launch a game with: {} <name or app_id>",
132 style("wanda launch").cyan()
133 );
134
135 Ok(())
136 }
137