@@ -7,11 +7,45 @@ pub struct Config {
77 pub profile : Vec < Profile > ,
88}
99
10+ #[ derive( Debug , Clone , Deserialize ) ]
11+ pub struct AiConfig {
12+ /// Provider: "openai" | "anthropic" | "ollama"
13+ pub provider : String ,
14+ /// API key (no aplica a ollama local)
15+ #[ serde( default ) ]
16+ pub api_key : String ,
17+ /// Modelo a usar, ej: "gpt-4o", "claude-3-5-sonnet-20241022", "llama3"
18+ pub model : String ,
19+ /// URL base opcional. Por defecto usa el endpoint público del provider.
20+ /// Para ollama: "http://localhost:11434"
21+ pub base_url : Option < String > ,
22+ }
23+
24+ impl AiConfig {
25+ pub fn endpoint ( & self ) -> String {
26+ if let Some ( base) = & self . base_url {
27+ match self . provider . as_str ( ) {
28+ "ollama" => format ! ( "{}/api/chat" , base. trim_end_matches( '/' ) ) ,
29+ _ => base. clone ( ) ,
30+ }
31+ } else {
32+ match self . provider . as_str ( ) {
33+ "openai" => "https://api.openai.com/v1/chat/completions" . to_string ( ) ,
34+ "anthropic" => "https://api.anthropic.com/v1/messages" . to_string ( ) ,
35+ "ollama" => "http://localhost:11434/api/chat" . to_string ( ) ,
36+ other => panic ! ( "Provider desconocido: {}" , other) ,
37+ }
38+ }
39+ }
40+ }
41+
1042#[ derive( Debug , Clone , Deserialize ) ]
1143pub struct Profile {
1244 pub name : String ,
1345 pub email : String ,
1446 pub token : String ,
47+ /// Configuración de IA para este perfil (opcional)
48+ pub ai : Option < AiConfig > ,
1549 #[ serde( default ) ]
1650 pub repo : Vec < RepoEntry > ,
1751}
@@ -67,3 +101,55 @@ pub fn load_config(path: &str) -> Result<Config, String> {
67101 toml:: from_str :: < Config > ( & content)
68102 . map_err ( |e| format ! ( "Error al parsear '{}': {}" , path, e) )
69103}
104+
105+ #[ derive( Debug , Clone , Deserialize ) ]
106+ pub struct RepoEntry {
107+ pub provider : String , // github | gitlab | bitbucket
108+ pub owner : String ,
109+ pub name : String ,
110+ }
111+
112+ impl RepoEntry {
113+ /// Construye la URL de clone con el token embebido según el provider.
114+ pub fn clone_url ( & self , token : & str ) -> String {
115+ match self . provider . as_str ( ) {
116+ "github" => format ! (
117+ "https://{}@github.com/{}/{}.git" ,
118+ token, self . owner, self . name
119+ ) ,
120+ "gitlab" => format ! (
121+ "https://oauth2:{}@gitlab.com/{}/{}.git" ,
122+ token, self . owner, self . name
123+ ) ,
124+ "bitbucket" => format ! (
125+ "https://x-token-auth:{}@bitbucket.org/{}/{}.git" ,
126+ token, self . owner, self . name
127+ ) ,
128+ other => format ! (
129+ "https://{}@{}/{}/{}.git" ,
130+ token, other, self . owner, self . name
131+ ) ,
132+ }
133+ }
134+
135+ /// Devuelve la ruta de caché local para este repo.
136+ /// ~/.cache/git-reports/{profile_name}/{provider}/{owner}/{name}
137+ pub fn cache_path ( & self , profile_name : & str ) -> std:: path:: PathBuf {
138+ let base = dirs_next:: cache_dir ( )
139+ . unwrap_or_else ( || std:: path:: PathBuf :: from ( "/tmp" ) )
140+ . join ( "git-reports" )
141+ . join ( profile_name)
142+ . join ( & self . provider )
143+ . join ( & self . owner )
144+ . join ( & self . name ) ;
145+ base
146+ }
147+ }
148+
149+ /// Carga y deserializa el archivo config.toml desde la ruta indicada.
150+ pub fn load_config ( path : & str ) -> Result < Config , String > {
151+ let content = fs:: read_to_string ( Path :: new ( path) )
152+ . map_err ( |e| format ! ( "No se pudo leer '{}': {}" , path, e) ) ?;
153+ toml:: from_str :: < Config > ( & content)
154+ . map_err ( |e| format ! ( "Error al parsear '{}': {}" , path, e) )
155+ }
0 commit comments