selene-core 0.4.2

selene-core is the backend for Selene, a local-first music player
Documentation
use std::str::FromStr;

use serde::{Deserialize, Serialize};

use crate::{REQWEST_CLIENT, library::track::lyric_data::PlainLyrics, synced_lyrics::SyncedLyrics};

const LRCLIB_SEARCH_ORIGIN: &str = "https://lrclib.net/api/search";

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SearchRecord {
    pub id: u64,
    pub name: String,
    pub track_name: String,
    pub artist_name: String,
    pub album_name: String,
    pub duration: Option<f64>,
    pub instrumental: bool,
    pub plain_lyrics: Option<String>,
    pub synced_lyrics: Option<String>,
}

#[derive(Serialize)]
pub struct SearchQuery<'a> {
    #[serde(rename = "track_name")]
    track_title: &'a str,

    #[serde(rename = "artist_name", skip_serializing_if = "Option::is_none")]
    main_artist: Option<&'a str>,

    #[serde(rename = "album_name", skip_serializing_if = "Option::is_none")]
    album_title: Option<&'a str>,
}

impl<'a> SearchQuery<'a> {
    pub fn new(track_title: &'a str) -> Self {
        Self {
            track_title,
            main_artist: None,
            album_title: None,
        }
    }

    pub fn main_artist(mut self, artist: Option<&'a str>) -> Self {
        self.main_artist = artist;
        self
    }

    pub fn album_title(mut self, title: Option<&'a str>) -> Self {
        self.album_title = title;
        self
    }

    pub fn run(self) -> Result<Vec<SearchRecord>, reqwest::Error> {
        let response = REQWEST_CLIENT
            .get(LRCLIB_SEARCH_ORIGIN)
            .query(&self)
            .send()?;

        response.json()
    }
}

impl SearchRecord {
    /// Returns [`true`] if the record is instrumental
    pub fn is_instrumental(&self) -> bool {
        self.instrumental
    }

    /// Returns the [`PlainLyrics`] of a song, if any
    pub fn plain_lyrics(&self) -> Option<PlainLyrics> {
        self.plain_lyrics
            .as_deref()
            .map(|lyrics| PlainLyrics::from_str(lyrics).unwrap())
    }

    /// Returns the [`SyncedLyrics`] of a song, if any
    pub fn synced_lyrics(&self) -> Option<SyncedLyrics> {
        self.synced_lyrics
            .as_deref()
            .and_then(|lyrics| SyncedLyrics::from_synced_lyrics(lyrics).ok())
    }
}