git_same/config/
workspace.rs1use super::{ConfigCloneOptions, FilterOptions, SyncMode};
9use crate::types::ProviderKind;
10use serde::{Deserialize, Serialize};
11use std::path::{Path, PathBuf};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct WorkspaceProvider {
16 #[serde(default)]
18 pub kind: ProviderKind,
19
20 #[serde(default, skip_serializing_if = "Option::is_none")]
22 pub api_url: Option<String>,
23
24 #[serde(default = "default_true")]
26 pub prefer_ssh: bool,
27}
28
29fn default_true() -> bool {
30 true
31}
32
33impl Default for WorkspaceProvider {
34 fn default() -> Self {
35 Self {
36 kind: ProviderKind::GitHub,
37 api_url: None,
38 prefer_ssh: true,
39 }
40 }
41}
42
43impl WorkspaceProvider {
44 pub fn effective_api_url(&self) -> String {
46 self.api_url
47 .clone()
48 .unwrap_or_else(|| self.kind.default_api_url().to_string())
49 }
50
51 pub fn display_name(&self) -> &str {
53 self.kind.display_name()
54 }
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct WorkspaceConfig {
63 #[serde(skip)]
67 pub root_path: PathBuf,
68
69 pub provider: WorkspaceProvider,
71
72 #[serde(default)]
74 pub username: String,
75
76 #[serde(default)]
78 pub orgs: Vec<String>,
79
80 #[serde(default)]
82 pub include_repos: Vec<String>,
83
84 #[serde(default)]
86 pub exclude_repos: Vec<String>,
87
88 #[serde(default, skip_serializing_if = "Option::is_none")]
90 pub structure: Option<String>,
91
92 #[serde(default, skip_serializing_if = "Option::is_none")]
94 pub sync_mode: Option<SyncMode>,
95
96 #[serde(default, skip_serializing_if = "Option::is_none")]
98 #[serde(rename = "clone")]
99 pub clone_options: Option<ConfigCloneOptions>,
100
101 #[serde(default)]
103 pub filters: FilterOptions,
104
105 #[serde(default, skip_serializing_if = "Option::is_none")]
107 pub concurrency: Option<usize>,
108
109 #[serde(default, skip_serializing_if = "Option::is_none")]
111 pub refresh_interval: Option<u64>,
112
113 #[serde(default, skip_serializing_if = "Option::is_none")]
115 pub last_synced: Option<String>,
116}
117
118impl WorkspaceConfig {
119 pub fn new_from_root(root: &Path) -> Self {
121 Self {
122 root_path: root.to_path_buf(),
123 provider: WorkspaceProvider::default(),
124 username: String::new(),
125 orgs: Vec::new(),
126 include_repos: Vec::new(),
127 exclude_repos: Vec::new(),
128 structure: None,
129 sync_mode: None,
130 clone_options: None,
131 filters: FilterOptions::default(),
132 concurrency: None,
133 refresh_interval: None,
134 last_synced: None,
135 }
136 }
137
138 pub fn expanded_base_path(&self) -> PathBuf {
140 self.root_path.clone()
141 }
142
143 pub fn display_label(&self) -> String {
145 let path_str = tilde_collapse_path(&self.root_path);
146 format!("{} ({})", path_str, self.provider.kind.display_name())
147 }
148
149 pub fn summary(&self) -> String {
151 let orgs = if self.orgs.is_empty() {
152 "all orgs".to_string()
153 } else {
154 format!("{} org(s)", self.orgs.len())
155 };
156 let synced = self.last_synced.as_deref().unwrap_or("never synced");
157 format!("{} ({}, {})", self.display_label(), orgs, synced)
158 }
159
160 pub fn to_toml(&self) -> Result<String, crate::errors::AppError> {
162 toml::to_string_pretty(self).map_err(|e| {
163 crate::errors::AppError::config(format!("Failed to serialize workspace config: {}", e))
164 })
165 }
166
167 pub fn from_toml(content: &str) -> Result<Self, crate::errors::AppError> {
169 toml::from_str(content).map_err(|e| {
170 crate::errors::AppError::config(format!("Failed to parse workspace config: {}", e))
171 })
172 }
173}
174
175pub fn tilde_collapse_path(path: &Path) -> String {
177 let home = std::env::var("HOME")
178 .or_else(|_| std::env::var("USERPROFILE"))
179 .ok();
180
181 if let Some(home) = home {
182 let home_path = Path::new(&home);
183 if let Ok(suffix) = path.strip_prefix(home_path) {
184 if suffix.as_os_str().is_empty() {
185 return "~".to_string();
186 }
187 return format!("~{}{}", std::path::MAIN_SEPARATOR, suffix.to_string_lossy());
188 }
189 }
190
191 path.to_string_lossy().to_string()
192}
193
194#[cfg(test)]
195#[path = "workspace_tests.rs"]
196mod tests;