lib/
entrypoint.rs

1use crate::cli::{Cli, Command};
2use crate::config;
3use crate::destinations::Destinations;
4use crate::errors::MusketError;
5use crate::shooters::{bluesky_shooter, linkedin_shooter, mastodon_shooter, turso_shooter};
6use crate::sources::{instapaper, Bookmark};
7use clap::Parser;
8use tracing::{debug, level_filters::LevelFilter};
9use tracing_subscriber::EnvFilter;
10
11/// Runs the main logic of the application.
12///
13/// # Errors
14///
15/// This function will return an error if any of the commands fail.
16#[allow(clippy::too_many_lines)]
17pub async fn run() -> Result<Vec<String>, MusketError> {
18    let mut success_messages: Vec<String> = vec![];
19    let cli = Cli::parse();
20
21    tracing_subscriber::fmt()
22        .with_env_filter(
23            EnvFilter::builder()
24                .with_default_directive(LevelFilter::INFO.into())
25                .from_env_lossy(),
26        )
27        .without_time()
28        .init();
29
30    match cli.cmd {
31        Command::Init { force } => {
32            debug!("Inside run function. Command::Init");
33
34            let overwrite = force.unwrap_or(false);
35
36            if config::configuration_exists()? && !overwrite {
37                return Err(MusketError::Cli {
38                    message: "The configuration file already exists. If you want to overwrite it, please run the musket init command with the -f, --force option.".to_string(),
39                });
40            }
41
42            match config::configure() {
43                Ok(_) => {
44                    success_messages.push(format!("The configuration file has been created here: \"{}\". \nTo start using Musket, please complete the configuration file with your data.",
45                    config::get_configuration_path()
46                        .unwrap_or_default()
47                        .to_string_lossy()));
48                }
49                Err(e) => return Err(e.into()),
50            }
51        }
52        Command::Fire {
53            url,
54            from,
55            destination,
56            tags,
57            commentary,
58            language,
59        } => {
60            debug!("Inside run function. Command::Fire");
61
62            if !config::configuration_exists()? {
63                return Err(MusketError::Cli {
64                    message: "The configuration file does not exist. To send any URL to any destination, please first run the musket init command and next fill the configuration file.".to_string(),
65                });
66            }
67
68            if url.is_none() && from.is_none() {
69                return Err(MusketError::Cli {
70                    message: "Neither the url nor the from flags are present. Set, at least, one of them.".to_string(),
71                });
72            }
73
74            if destination.is_none() {
75                return Err(MusketError::Cli {
76                    message: "The url cannot be sent to a non-existing destination. Set, at least, one valid destination.".to_string(),
77                });
78            }
79
80            let cfg = config::configure()?;
81            let mut tags = tags.unwrap_or_default();
82            let destinations = destination.unwrap_or_default();
83            let mut url = url.unwrap_or_default();
84            let mut bookmark: Bookmark = Bookmark {
85                id: 0,
86                url: String::new(),
87                tags: vec![],
88            };
89
90            if from.is_some() {
91                let instapaper = instapaper::Instapaper::new(
92                    &cfg.instapaper.username,
93                    &cfg.instapaper.password,
94                    &cfg.instapaper.consumer_key,
95                    &cfg.instapaper.consumer_secret,
96                );
97                bookmark = instapaper.get_bookmark().await?;
98                url = bookmark.url;
99                tags = bookmark.tags;
100                success_messages.push(format!(
101                    "The bookmark \"{0}\" with this url \"{1}\" has been obtained from Instapaper.",
102                    bookmark.id, url
103                ));
104            }
105
106            for target in destinations {
107                match target {
108                    Destinations::All => {
109                        success_messages.push(
110                            bluesky_shooter(
111                                &cfg,
112                                &url,
113                                tags.clone(),
114                                commentary.as_ref(),
115                                language.as_ref(),
116                            )
117                            .await?,
118                        );
119                        success_messages.push(
120                            linkedin_shooter(
121                                &cfg,
122                                &url,
123                                tags.clone(),
124                                commentary.as_ref(),
125                                language.as_ref(),
126                            )
127                            .await?,
128                        );
129                        success_messages.push(
130                            mastodon_shooter(
131                                &cfg,
132                                &url,
133                                tags.clone(),
134                                commentary.as_ref(),
135                                language.as_ref(),
136                            )
137                            .await?,
138                        );
139                        success_messages.push(turso_shooter(&cfg, &url, tags.clone(), None).await?);
140                    }
141                    Destinations::Bluesky => {
142                        success_messages.push(
143                            bluesky_shooter(
144                                &cfg,
145                                &url,
146                                tags.clone(),
147                                commentary.as_ref(),
148                                language.as_ref(),
149                            )
150                            .await?,
151                        );
152                    }
153                    Destinations::LinkedIn => {
154                        success_messages.push(
155                            linkedin_shooter(
156                                &cfg,
157                                &url,
158                                tags.clone(),
159                                commentary.as_ref(),
160                                language.as_ref(),
161                            )
162                            .await?,
163                        );
164                    }
165                    Destinations::Mastodon => {
166                        success_messages.push(
167                            mastodon_shooter(
168                                &cfg,
169                                &url,
170                                tags.clone(),
171                                commentary.as_ref(),
172                                language.as_ref(),
173                            )
174                            .await?,
175                        );
176                    }
177                    Destinations::Turso => {
178                        success_messages.push(turso_shooter(&cfg, &url, tags.clone(), None).await?);
179                    }
180                }
181            }
182
183            if from.is_some() {
184                let instapaper = instapaper::Instapaper::new(
185                    &cfg.instapaper.username,
186                    &cfg.instapaper.password,
187                    &cfg.instapaper.consumer_key,
188                    &cfg.instapaper.consumer_secret,
189                );
190                instapaper.delete_bookmark(bookmark.id).await?;
191                success_messages.push(format!(
192                    "The bookmark \"{0}\" with this url \"{1}\" has been deleted of Instapaper.",
193                    bookmark.id, url
194                ));
195            }
196        }
197    }
198    Ok(success_messages)
199}