git_same/domain/
repo_path_template.rs1use crate::types::OwnedRepo;
4use std::path::{Component, Path, PathBuf};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct RepoPathTemplate {
9 template: String,
10}
11
12impl RepoPathTemplate {
13 pub fn new(template: impl Into<String>) -> Self {
15 Self {
16 template: template.into(),
17 }
18 }
19
20 pub fn as_str(&self) -> &str {
22 &self.template
23 }
24
25 pub fn render(&self, base_path: &Path, provider: &str, owner: &str, repo: &str) -> PathBuf {
27 let provider = sanitize_component(provider);
28 let owner = sanitize_component(owner);
29 let repo = sanitize_component(repo);
30 let rendered = self
31 .template
32 .replace("{provider}", &provider)
33 .replace("{org}", &owner)
34 .replace("{repo}", &repo);
35
36 base_path.join(rendered)
37 }
38
39 pub fn render_owned_repo(&self, base_path: &Path, repo: &OwnedRepo, provider: &str) -> PathBuf {
41 self.render(base_path, provider, &repo.owner, &repo.repo.name)
42 }
43
44 pub fn render_full_name(
46 &self,
47 base_path: &Path,
48 provider: &str,
49 full_name: &str,
50 ) -> Option<PathBuf> {
51 let (owner, repo) = full_name.split_once('/')?;
52 Some(self.render(base_path, provider, owner, repo))
53 }
54
55 pub fn scan_depth(&self) -> usize {
57 let sample = self
58 .template
59 .replace("{provider}", "provider")
60 .replace("{org}", "org")
61 .replace("{repo}", "repo");
62
63 let depth = Path::new(&sample)
64 .components()
65 .filter(|c| matches!(c, Component::Normal(_)))
66 .count();
67
68 depth.max(1)
69 }
70}
71
72fn sanitize_component(value: &str) -> String {
73 let trimmed = value.trim();
74 if trimmed.is_empty() {
75 return "_".to_string();
76 }
77
78 let mut sanitized = trimmed
79 .replace(['/', '\\'], "_")
80 .replace("..", "__")
81 .trim()
82 .to_string();
83
84 if sanitized.is_empty() {
85 sanitized = "_".to_string();
86 }
87
88 sanitized
89}
90
91impl Default for RepoPathTemplate {
92 fn default() -> Self {
93 Self::new("{org}/{repo}")
94 }
95}
96
97#[cfg(test)]
98#[path = "repo_path_template_tests.rs"]
99mod tests;