Skip to main content

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 let Some(album_artist_name_original) =
86                            self.edit.album_artist_name_original.as_ref()
87                        {
88                            format!(" with album artist '{album_artist_name_original}'")
89                        } else {
90                            String::new()
91                        };
92                        self.completed = true;
93                        return Err(LastFmError::Parse(format!(
94                            "Track '{}' not found on album '{}' by '{}'{} in recent scrobbles",
95                            self.track_name,
96                            self.album_name,
97                            self.edit.artist_name_original,
98                            album_artist_filter
99                        )));
100                    }
101                }
102                Err(e) => {
103                    self.completed = true;
104                    return Err(e);
105                }
106            }
107        }
108
109        self.completed = true;
110        Ok(self.result.take())
111    }
112}