Skip to main content

purple_ssh/handler/
sync.rs

1use std::sync::Arc;
2use std::sync::atomic::AtomicBool;
3use std::sync::mpsc;
4
5use log::{error, info, warn};
6
7use crate::event::AppEvent;
8
9pub fn spawn_provider_sync(
10    section: &crate::providers::config::ProviderSection,
11    tx: mpsc::Sender<AppEvent>,
12    cancel: Arc<AtomicBool>,
13) {
14    // Use full ProviderConfigId (provider[:label]) as the event/sync key so
15    // two labeled configs of the same provider don't collide on the syncing
16    // HashMap or in event payloads.
17    let name = section.id.to_string();
18    let token = section.token.clone();
19    let section_clone = section.clone();
20    let tx_fallback = tx.clone();
21    let name_fallback = name.clone();
22    log::debug!("Spawning provider sync thread: {name}");
23    if let Err(e) = std::thread::Builder::new()
24        .name(format!("sync-{}", name))
25        .spawn(move || {
26            let provider = match crate::providers::get_provider_with_config(&section_clone) {
27                Some(p) => p,
28                None => {
29                    warn!("[config] Unknown provider requested for sync: {name}");
30                    let _ = tx.send(AppEvent::SyncError {
31                        provider: name,
32                        message: crate::messages::SYNC_UNKNOWN_PROVIDER.to_string(),
33                    });
34                    return;
35                }
36            };
37            info!("Provider sync started: {name}");
38            let progress_tx = tx.clone();
39            let progress_name = name.clone();
40            let progress = move |msg: &str| {
41                let _ = progress_tx.send(AppEvent::SyncProgress {
42                    provider: progress_name.clone(),
43                    message: msg.to_string(),
44                });
45            };
46            match provider.fetch_hosts_with_progress(&token, &cancel, &progress) {
47                Ok(hosts) => {
48                    if hosts.is_empty() {
49                        warn!("[config] Provider sync returned 0 hosts: {name} (check API token permissions)");
50                    } else {
51                        info!("Provider sync completed: {name}, {} hosts found", hosts.len());
52                    }
53                    let _ = tx.send(AppEvent::SyncComplete {
54                        provider: name,
55                        hosts,
56                    });
57                }
58                Err(crate::providers::ProviderError::PartialResult {
59                    hosts,
60                    failures,
61                    total,
62                }) => {
63                    warn!("[external] Provider sync partial: {name}, {} hosts, {} failures", hosts.len(), failures);
64                    let _ = tx.send(AppEvent::SyncPartial {
65                        provider: name,
66                        hosts,
67                        failures,
68                        total,
69                    });
70                }
71                Err(e) => {
72                    error!("[external] Provider sync failed: {name}: {e}");
73                    let _ = tx.send(AppEvent::SyncError {
74                        provider: name,
75                        message: e.to_string(),
76                    });
77                }
78            }
79        })
80    {
81        error!(
82            "[purple] Failed to spawn sync thread for {}: {}",
83            name_fallback, e
84        );
85        let _ = tx_fallback.send(AppEvent::SyncError {
86            provider: name_fallback,
87            message: crate::messages::SYNC_THREAD_SPAWN_FAILED.to_string(),
88        });
89    }
90}