1- use super :: Provider ;
1+ use super :: { Provider , ProviderUrl } ;
22use crate :: { Result , SecretSpecError } ;
33use secrecy:: { ExposeSecret , SecretString } ;
44use serde:: { Deserialize , Serialize } ;
55use std:: collections:: HashMap ;
66use std:: fs;
77use std:: path:: PathBuf ;
8- use url:: Url ;
98
109/// Configuration for the dotenv provider.
1110///
@@ -43,7 +42,7 @@ impl Default for DotEnvConfig {
4342 }
4443}
4544
46- impl TryFrom < & Url > for DotEnvConfig {
45+ impl TryFrom < & ProviderUrl > for DotEnvConfig {
4746 type Error = SecretSpecError ;
4847
4948 /// Creates a DotEnvConfig from a URL.
@@ -56,45 +55,24 @@ impl TryFrom<&Url> for DotEnvConfig {
5655 /// - `dotenv:///absolute/path` - Absolute path
5756 /// - `dotenv://.env` - Relative path (authority as filename)
5857 /// - `dotenv://` - Uses default `.env` in current directory
59- ///
60- /// # Examples
61- ///
62- /// ```ignore
63- /// use url::Url;
64- /// use secretspec::provider::dotenv::DotEnvConfig;
65- ///
66- /// let url = Url::parse("dotenv:///.env.production").unwrap();
67- /// let config: DotEnvConfig = (&url).try_into().unwrap();
68- /// assert_eq!(config.path.to_str().unwrap(), "/.env.production");
69- /// ```
70- fn try_from ( url : & Url ) -> std:: result:: Result < Self , Self :: Error > {
58+ fn try_from ( url : & ProviderUrl ) -> std:: result:: Result < Self , Self :: Error > {
7159 if url. scheme ( ) != "dotenv" {
7260 return Err ( SecretSpecError :: ProviderOperationFailed ( format ! (
7361 "Invalid scheme '{}' for dotenv provider" ,
7462 url. scheme( )
7563 ) ) ) ;
7664 }
7765
78- // For dotenv URLs:
79- // - dotenv:///absolute/path -> url.path() = "/absolute/path"
80- // - dotenv://.env -> url.host_str() = ".env", url.path() = ""
81- // - dotenv:// -> url.host_str() = None, url.path() = ""
82-
83- let path = if url. path ( ) != "" && url. path ( ) != "/" {
84- // Check if this is an absolute path (starts with /) or has a host
85- if let Some ( host) = url. host_str ( ) {
86- // Case like dotenv://config/.env.local -> host="config", path="/.env.local"
87- // We want "config/.env.local"
88- format ! ( "{}{}" , host, url. path( ) )
66+ let path_str = url. path ( ) ;
67+ let path = if path_str != "" && path_str != "/" {
68+ if let Some ( host) = url. host ( ) {
69+ format ! ( "{}{}" , host, path_str)
8970 } else {
90- // Absolute path from dotenv:///path
91- url. path ( ) . to_string ( )
71+ path_str
9272 }
93- } else if let Some ( host) = url. host_str ( ) {
94- // Relative path from dotenv://filename
95- host. to_string ( )
73+ } else if let Some ( host) = url. host ( ) {
74+ host
9675 } else {
97- // Default case dotenv://
9876 ".env" . to_string ( )
9977 } ;
10078
@@ -169,13 +147,8 @@ impl Provider for DotEnvProvider {
169147 let path_str = self . config . path . display ( ) . to_string ( ) ;
170148
171149 if path_str == ".env" {
172- // Default case - just return "dotenv"
173150 "dotenv" . to_string ( )
174- } else if path_str. starts_with ( '/' ) {
175- // Absolute path
176- format ! ( "dotenv:{}" , path_str)
177151 } else {
178- // Relative path
179152 format ! ( "dotenv:{}" , path_str)
180153 }
181154 }
@@ -309,28 +282,30 @@ mod tests {
309282
310283 #[ test]
311284 fn test_dotenv_url_parsing ( ) {
285+ use url:: Url ;
286+
312287 // Test with absolute path using three slashes - this is the main syntax we want to support
313- let url = Url :: parse ( "dotenv:///tmp/test/.env" ) . unwrap ( ) ;
288+ let url = ProviderUrl :: new ( Url :: parse ( "dotenv:///tmp/test/.env" ) . unwrap ( ) ) ;
314289 let config: DotEnvConfig = ( & url) . try_into ( ) . unwrap ( ) ;
315290 assert_eq ! ( config. path. to_str( ) . unwrap( ) , "/tmp/test/.env" ) ;
316291
317292 // Test with relative path using two slashes - authority as filename
318- let url = Url :: parse ( "dotenv://.env" ) . unwrap ( ) ;
293+ let url = ProviderUrl :: new ( Url :: parse ( "dotenv://.env" ) . unwrap ( ) ) ;
319294 let config: DotEnvConfig = ( & url) . try_into ( ) . unwrap ( ) ;
320295 assert_eq ! ( config. path. to_str( ) . unwrap( ) , ".env" ) ;
321296
322297 // Test with relative path in subdirectory
323- let url = Url :: parse ( "dotenv://config/.env.local" ) . unwrap ( ) ;
298+ let url = ProviderUrl :: new ( Url :: parse ( "dotenv://config/.env.local" ) . unwrap ( ) ) ;
324299 let config: DotEnvConfig = ( & url) . try_into ( ) . unwrap ( ) ;
325300 assert_eq ! ( config. path. to_str( ) . unwrap( ) , "config/.env.local" ) ;
326301
327302 // Test with default (empty after //)
328- let url = Url :: parse ( "dotenv://" ) . unwrap ( ) ;
303+ let url = ProviderUrl :: new ( Url :: parse ( "dotenv://" ) . unwrap ( ) ) ;
329304 let config: DotEnvConfig = ( & url) . try_into ( ) . unwrap ( ) ;
330305 assert_eq ! ( config. path. to_str( ) . unwrap( ) , ".env" ) ;
331306
332307 // Test with relative path - host part becomes first part of path
333- let url = Url :: parse ( "dotenv://foobar/custom/path/.env" ) . unwrap ( ) ;
308+ let url = ProviderUrl :: new ( Url :: parse ( "dotenv://foobar/custom/path/.env" ) . unwrap ( ) ) ;
334309 let config: DotEnvConfig = ( & url) . try_into ( ) . unwrap ( ) ;
335310 assert_eq ! ( config. path. to_str( ) . unwrap( ) , "foobar/custom/path/.env" ) ;
336311 }
0 commit comments