lastfm_edit/commands/
mod.rs

1pub mod delete;
2pub mod edit;
3pub mod show;
4pub mod utils;
5
6use crate::LastFmEditClientImpl;
7use clap::{arg, Subcommand};
8
9#[derive(Subcommand)]
10pub enum Commands {
11    /// Edit scrobble metadata
12    ///
13    /// This command allows you to edit scrobble metadata by specifying what to search for
14    /// and what to change it to. You can specify any combination of fields to search for,
15    /// and any combination of new values to change them to.
16    ///
17    /// Usage examples:
18    /// # Discover variations for an artist (dry run by default)
19    /// lastfm-edit edit --artist "Jimi Hendrix"
20    ///
21    /// # Discover variations with optional track name
22    /// lastfm-edit edit --artist "Radiohead" --track "Creep"
23    ///
24    /// # Actually apply edits (change artist name)
25    /// lastfm-edit edit --artist "The Beatles" --new-artist "Beatles, The" --apply
26    ///
27    /// # Change track name for specific track
28    /// lastfm-edit edit --artist "Jimi Hendrix" --track "Lover Man" --new-track "Lover Man (Live)" --apply
29    Edit {
30        /// Artist name (required)
31        #[arg(long)]
32        artist: String,
33
34        /// Track name (optional)
35        #[arg(long)]
36        track: Option<String>,
37
38        /// Album name (optional)
39        #[arg(long)]
40        album: Option<String>,
41
42        /// Album artist name (optional)
43        #[arg(long)]
44        album_artist: Option<String>,
45
46        /// New track name (optional)
47        #[arg(long)]
48        new_track: Option<String>,
49
50        /// New album name (optional)
51        #[arg(long)]
52        new_album: Option<String>,
53
54        /// New artist name (optional)
55        #[arg(long)]
56        new_artist: Option<String>,
57
58        /// New album artist name (optional)
59        #[arg(long)]
60        new_album_artist: Option<String>,
61
62        /// Timestamp for specific scrobble (optional)
63        #[arg(long)]
64        timestamp: Option<u64>,
65
66        /// Whether to edit all instances (optional, defaults to false)
67        #[arg(long)]
68        edit_all: bool,
69
70        /// Actually apply the edits (default is dry-run mode)
71        #[arg(long)]
72        apply: bool,
73
74        /// Perform a dry run without actually submitting edits (default behavior)
75        #[arg(long)]
76        dry_run: bool,
77    },
78    /// Delete scrobbles in a range
79    ///
80    /// This command allows you to delete scrobbles from your library. You can specify
81    /// timestamp ranges, delete recent scrobbles from specific pages, or use offsets
82    /// from the most recent scrobble.
83    ///
84    /// Usage examples:
85    /// # Show recent scrobbles that would be deleted (dry run)
86    /// lastfm-edit delete --recent-pages 1-3
87    ///
88    /// # Delete scrobbles from timestamp range
89    /// lastfm-edit delete --timestamp-range 1640995200-1641000000 --apply
90    ///
91    /// # Delete scrobbles by offset from most recent (0-indexed)
92    /// lastfm-edit delete --recent-offset 0-4 --apply
93    Delete {
94        /// Delete scrobbles from recent pages (format: start-end, 0-indexed)
95        #[arg(long, conflicts_with_all = ["timestamp_range", "recent_offset"])]
96        recent_pages: Option<String>,
97
98        /// Delete scrobbles from timestamp range (format: start_ts-end_ts)
99        #[arg(long, conflicts_with_all = ["recent_pages", "recent_offset"])]
100        timestamp_range: Option<String>,
101
102        /// Delete scrobbles by offset from most recent (format: start-end, 0-indexed)
103        #[arg(long, conflicts_with_all = ["recent_pages", "timestamp_range"])]
104        recent_offset: Option<String>,
105
106        /// Actually perform the deletions (default is dry-run mode)
107        #[arg(long)]
108        apply: bool,
109
110        /// Perform a dry run without actually deleting (default behavior)
111        #[arg(long)]
112        dry_run: bool,
113    },
114    /// Show scrobble details for specific offsets
115    ///
116    /// This command displays detailed information for scrobbles at the specified
117    /// offsets from your most recent scrobbles.
118    ///
119    /// Usage examples:
120    /// # Show details for the most recent scrobble (offset 0)
121    /// lastfm-edit show 0
122    ///
123    /// # Show details for multiple scrobbles (0-indexed)
124    /// lastfm-edit show 0 1 2 5 10
125    Show {
126        /// Offsets of scrobbles to show (0-indexed, 0 = most recent)
127        offsets: Vec<u64>,
128    },
129}
130
131/// Execute the appropriate command handler based on the parsed command
132pub async fn execute_command(
133    command: Commands,
134    client: &LastFmEditClientImpl,
135) -> Result<(), Box<dyn std::error::Error>> {
136    match command {
137        Commands::Edit {
138            artist,
139            track,
140            album,
141            album_artist,
142            new_track,
143            new_album,
144            new_artist,
145            new_album_artist,
146            timestamp,
147            edit_all,
148            apply,
149            dry_run,
150        } => {
151            // Determine whether this is a dry run or actual edit
152            let is_dry_run = dry_run || !apply;
153
154            let edit = edit::create_scrobble_edit_from_args(
155                &artist,
156                track.as_deref(),
157                album.as_deref(),
158                album_artist.as_deref(),
159                new_track.as_deref(),
160                new_album.as_deref(),
161                new_artist.as_deref(),
162                new_album_artist.as_deref(),
163                timestamp,
164                edit_all,
165            );
166
167            edit::handle_edit_command(client, &edit, is_dry_run).await
168        }
169
170        Commands::Delete {
171            recent_pages,
172            timestamp_range,
173            recent_offset,
174            apply,
175            dry_run,
176        } => {
177            // Determine whether this is a dry run or actual deletion
178            let is_dry_run = dry_run || !apply;
179
180            if let Some(pages_range) = recent_pages {
181                delete::handle_delete_recent_pages(client, &pages_range, is_dry_run).await
182            } else if let Some(ts_range) = timestamp_range {
183                delete::handle_delete_timestamp_range(client, &ts_range, is_dry_run).await
184            } else if let Some(offset_range) = recent_offset {
185                delete::handle_delete_recent_offset(client, &offset_range, is_dry_run).await
186            } else {
187                Err(
188                    "Must specify one of: --recent-pages, --timestamp-range, or --recent-offset"
189                        .into(),
190                )
191            }
192        }
193
194        Commands::Show { offsets } => {
195            if offsets.is_empty() {
196                return Err("Must specify at least one offset to show".into());
197            }
198
199            show::handle_show_scrobbles(client, &offsets).await
200        }
201    }
202}