lastfm_edit/discovery/
exact_match.rs

1use super::common::filter_by_original_album_artist;
2use crate::{
3    AsyncDiscoveryIterator, ExactScrobbleEdit, LastFmEditClientImpl, LastFmError, Result,
4    ScrobbleEdit,
5};
6use async_trait::async_trait;
7
8/// Case 1: Exact match discovery (track + album specified)
9///
10/// This discovers the specific scrobble that matches both the track and album,
11/// yielding at most one result.
12pub struct ExactMatchDiscovery {
13    client: LastFmEditClientImpl,
14    edit: ScrobbleEdit,
15    track_name: String,
16    album_name: String,
17    result: Option<ExactScrobbleEdit>,
18    completed: bool,
19}
20
21impl ExactMatchDiscovery {
22    pub fn new(
23        client: LastFmEditClientImpl,
24        edit: ScrobbleEdit,
25        track_name: String,
26        album_name: String,
27    ) -> Self {
28        Self {
29            client,
30            edit,
31            track_name,
32            album_name,
33            result: None,
34            completed: false,
35        }
36    }
37}
38
39#[async_trait(?Send)]
40impl AsyncDiscoveryIterator<ExactScrobbleEdit> for ExactMatchDiscovery {
41    async fn next(&mut self) -> Result<Option<ExactScrobbleEdit>> {
42        if self.completed {
43            return Ok(None);
44        }
45
46        if self.result.is_none() {
47            // Perform the lookup inline (previously discover_track_album_exact_match)
48            log::debug!(
49                "Looking up missing metadata for track '{}' on album '{}' by '{}'",
50                self.track_name,
51                self.album_name,
52                self.edit.artist_name_original
53            );
54
55            match self
56                .client
57                .load_edit_form_values_internal(&self.track_name, &self.edit.artist_name_original)
58                .await
59            {
60                Ok(all_variations) => {
61                    // Filter by album artist first if specified, then find the variation that matches the specific album
62                    let filtered_variations =
63                        filter_by_original_album_artist(all_variations, &self.edit);
64
65                    if let Some(exact_edit) = filtered_variations
66                        .iter()
67                        .find(|variation| variation.album_name_original == self.album_name)
68                    {
69                        // Apply the user's desired changes to this exact variation
70                        let mut modified_edit = exact_edit.clone();
71                        if let Some(new_track_name) = &self.edit.track_name {
72                            modified_edit.track_name = new_track_name.clone();
73                        }
74                        if let Some(new_album_name) = &self.edit.album_name {
75                            modified_edit.album_name = new_album_name.clone();
76                        }
77                        modified_edit.artist_name = self.edit.artist_name.clone();
78                        if let Some(new_album_artist_name) = &self.edit.album_artist_name {
79                            modified_edit.album_artist_name = new_album_artist_name.clone();
80                        }
81                        modified_edit.edit_all = self.edit.edit_all;
82
83                        self.result = Some(modified_edit);
84                    } else {
85                        let album_artist_filter = if self.edit.album_artist_name_original.is_some()
86                        {
87                            format!(
88                                " with album artist '{}'",
89                                self.edit.album_artist_name_original.as_ref().unwrap()
90                            )
91                        } else {
92                            String::new()
93                        };
94                        self.completed = true;
95                        return Err(LastFmError::Parse(format!(
96                            "Track '{}' not found on album '{}' by '{}'{} in recent scrobbles",
97                            self.track_name,
98                            self.album_name,
99                            self.edit.artist_name_original,
100                            album_artist_filter
101                        )));
102                    }
103                }
104                Err(e) => {
105                    self.completed = true;
106                    return Err(e);
107                }
108            }
109        }
110
111        self.completed = true;
112        Ok(self.result.take())
113    }
114}