1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use std::path::Path;
use async_trait::async_trait;
use color_eyre::eyre::{Result, eyre};
use sync_dis_boi::music_api::DynMusicApi;
use sync_dis_boi::spotify::SpotifyApi;
use sync_dis_boi::tidal::TidalApi;
use sync_dis_boi::yt_music::YtMusicApi;
use crate::args::{MusicPlatformDst, MusicPlatformSrc, RootArgs};
#[async_trait]
pub trait BuildApi {
async fn parse(&self, args: &RootArgs, config_dir: &Path) -> Result<DynMusicApi>;
}
#[macro_export]
macro_rules! impl_build_api {
($id:ident) => {
#[async_trait]
impl BuildApi for $id {
async fn parse(&self, args: &RootArgs, config_dir: &Path) -> Result<DynMusicApi> {
let api: DynMusicApi = match &self {
Self::YtMusic {
client_id,
client_secret,
clear_cache,
headers,
..
} => {
if let Some(headers) = headers {
Box::new(YtMusicApi::new_headers(headers, args.config.clone()).await?)
} else {
let Some(client_id) = client_id else {
return Err(eyre!("Missing Youtube Music client_id"));
};
let Some(client_secret) = client_secret else {
return Err(eyre!("Missing Youtube Music client_secret"));
};
let oauth_token_path = config_dir.join("ytmusic_oauth.json");
Box::new(
YtMusicApi::new_oauth(
client_id,
client_secret,
oauth_token_path,
*clear_cache,
args.config.clone(),
)
.await?,
)
}
}
Self::Tidal {
client_id,
client_secret,
clear_cache,
..
} => {
let oauth_token_path = config_dir.join("tidal_oauth.json");
Box::new(
TidalApi::new(
client_id,
client_secret,
oauth_token_path,
*clear_cache,
args.config.clone(),
)
.await?,
)
}
Self::Spotify {
client_id,
client_secret,
clear_cache,
..
} => {
let oauth_token_path = config_dir.join("spotify_oauth.json");
Box::new(
SpotifyApi::new(
&client_id,
&client_secret,
oauth_token_path,
*clear_cache,
args.config.clone(),
)
.await?,
)
}
#[allow(unreachable_patterns)]
_ => return Err(eyre!("Invalid API type: {:?}", self)),
};
Ok(api)
}
}
};
}
// INFO: Hack to support command chaining with clap
// related issue: https://github.com/clap-rs/clap/issues/2222
impl_build_api!(MusicPlatformSrc);
impl_build_api!(MusicPlatformDst);
impl MusicPlatformSrc {
pub fn get_dst(&self) -> &MusicPlatformDst {
match self {
Self::YtMusic { dst, .. } => dst,
Self::Spotify { dst, .. } => dst,
Self::Tidal { dst, .. } => dst,
}
}
}