sync_rs/
config.rs

1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3use std::io::{self, Write};
4
5#[derive(Debug, Serialize, Deserialize, Clone)]
6pub struct RemoteEntry {
7    pub name: String,
8    pub remote_host: String,
9    pub remote_dir: String,
10    #[serde(default)]
11    pub override_paths: Vec<String>,
12    #[serde(default)]
13    pub post_sync_command: Option<String>,
14    #[serde(default)]
15    pub preferred: bool,
16}
17
18pub fn prompt_remote_info() -> Result<(String, String)> {
19    let mut remote_host = String::new();
20    let mut remote_dir = String::new();
21
22    print!("Enter remote host (e.g., user@host): ");
23    io::stdout().flush()?;
24    io::stdin().read_line(&mut remote_host)?;
25
26    print!("Enter remote directory (relative to remote home): ");
27    io::stdout().flush()?;
28    io::stdin().read_line(&mut remote_dir)?;
29
30    Ok((
31        remote_host.trim().to_string(),
32        remote_dir.trim().to_string(),
33    ))
34}
35
36pub fn select_remote(entries: &[RemoteEntry]) -> Result<String> {
37    println!("Multiple remote configurations found. Please select one:");
38
39    for (i, entry) in entries.iter().enumerate() {
40        println!(
41            "{}: {} ({}:{})",
42            i + 1,
43            entry.name,
44            entry.remote_host,
45            entry.remote_dir
46        );
47    }
48
49    let mut selection = String::new();
50    print!("Enter selection (1-{}): ", entries.len());
51    io::stdout().flush()?;
52    io::stdin().read_line(&mut selection)?;
53
54    let index = selection
55        .trim()
56        .parse::<usize>()
57        .context("Invalid selection")?
58        - 1;
59
60    if index >= entries.len() {
61        anyhow::bail!("Selection out of range");
62    }
63
64    Ok(entries[index].name.clone())
65}
66
67pub fn list_remotes(cache: &crate::cache::RemoteMap, current_dir: &str) -> Result<()> {
68    let empty_vec: Vec<RemoteEntry> = Vec::new();
69    let entries = cache.get(current_dir).unwrap_or(&empty_vec);
70
71    if entries.is_empty() {
72        println!("No remote configurations found for this directory.");
73        return Ok(());
74    }
75
76    println!("Remote configurations for this directory:");
77    for (i, entry) in entries.iter().enumerate() {
78        let preferred = if entry.preferred { " (preferred)" } else { "" };
79        println!(
80            "{}: {}{} ({}:{})",
81            i + 1,
82            entry.name,
83            preferred,
84            entry.remote_host,
85            entry.remote_dir
86        );
87    }
88
89    Ok(())
90}
91
92pub fn remove_remote(
93    cache: &mut crate::cache::RemoteMap,
94    current_dir: &str,
95    name: &str,
96) -> Result<()> {
97    let entries = cache
98        .get_mut(current_dir)
99        .context("No remotes found for this directory")?;
100
101    let initial_len = entries.len();
102    entries.retain(|e| e.name != name);
103
104    if entries.len() == initial_len {
105        anyhow::bail!("Remote with name '{}' not found", name);
106    }
107
108    println!("Removed remote configuration '{}'", name);
109    Ok(())
110}
111
112// Generate a unique name based on the host name
113pub fn generate_unique_name(
114    host: &str,
115    cache: &crate::cache::RemoteMap,
116    current_dir: &str,
117) -> String {
118    // Extract username@hostname or just hostname
119    let base_name = host.split(':').next().unwrap_or(host);
120
121    // If there are no entries for this directory or no entries with this base name, use it as is
122    if !cache.contains_key(current_dir) || !cache[current_dir].iter().any(|e| e.name == base_name) {
123        return base_name.to_string();
124    }
125
126    // Find the highest index used for this base name
127    let mut highest_index = 0;
128    for entry in &cache[current_dir] {
129        if entry.name == base_name {
130            highest_index = 1;
131        } else if entry.name.starts_with(&format!("{}_", base_name)) {
132            if let Ok(index) = entry.name[base_name.len() + 1..].parse::<usize>() {
133                highest_index = highest_index.max(index + 1);
134            }
135        }
136    }
137
138    // Return the base name with the next available index
139    format!("{}_{}", base_name, highest_index)
140}