11// Copyright (c) Microsoft Corporation.
22// Licensed under the MIT License.
33
4+ use lazy_static:: lazy_static;
45use log:: trace;
56use pet_core:: manager:: { EnvManager , EnvManagerType } ;
7+ use pet_fs:: path:: resolve_any_symlink;
8+ use regex:: Regex ;
69use std:: { env, path:: PathBuf } ;
710
811use crate :: env_variables:: EnvVariables ;
912
13+ lazy_static ! {
14+ /// Matches Homebrew Cellar path for poetry: /Cellar/poetry/X.Y.Z or /Cellar/poetry/X.Y.Z_N
15+ static ref HOMEBREW_POETRY_VERSION : Regex =
16+ Regex :: new( r"/Cellar/poetry/(\d+\.\d+\.\d+)" ) . expect( "error parsing Homebrew poetry version regex" ) ;
17+ }
18+
1019#[ derive( Clone , PartialEq , Eq , Debug ) ]
1120pub struct PoetryManager {
1221 pub executable : PathBuf ,
22+ pub version : Option < String > ,
1323}
1424
1525impl PoetryManager {
1626 pub fn find ( executable : Option < PathBuf > , env_variables : & EnvVariables ) -> Option < Self > {
1727 if let Some ( executable) = executable {
1828 if executable. is_file ( ) {
19- return Some ( PoetryManager { executable } ) ;
29+ let version = Self :: extract_version_from_path ( & executable) ;
30+ return Some ( PoetryManager { executable, version } ) ;
2031 }
2132 }
2233
@@ -107,7 +118,8 @@ impl PoetryManager {
107118 }
108119 for executable in search_paths {
109120 if executable. is_file ( ) {
110- return Some ( PoetryManager { executable } ) ;
121+ let version = Self :: extract_version_from_path ( & executable) ;
122+ return Some ( PoetryManager { executable, version } ) ;
111123 }
112124 }
113125
@@ -116,12 +128,14 @@ impl PoetryManager {
116128 for each in env:: split_paths ( env_path) {
117129 let executable = each. join ( "poetry" ) ;
118130 if executable. is_file ( ) {
119- return Some ( PoetryManager { executable } ) ;
131+ let version = Self :: extract_version_from_path ( & executable) ;
132+ return Some ( PoetryManager { executable, version } ) ;
120133 }
121134 if std:: env:: consts:: OS == "windows" {
122135 let executable = each. join ( "poetry.exe" ) ;
123136 if executable. is_file ( ) {
124- return Some ( PoetryManager { executable } ) ;
137+ let version = Self :: extract_version_from_path ( & executable) ;
138+ return Some ( PoetryManager { executable, version } ) ;
125139 }
126140 }
127141 }
@@ -130,10 +144,35 @@ impl PoetryManager {
130144 trace ! ( "Poetry exe not found" ) ;
131145 None
132146 }
147+
148+ /// Extracts poetry version from Homebrew Cellar path.
149+ ///
150+ /// Homebrew installs poetry to paths like:
151+ /// - macOS ARM: /opt/homebrew/Cellar/poetry/1.8.3_2/bin/poetry
152+ /// - macOS Intel: /usr/local/Cellar/poetry/1.8.3/bin/poetry
153+ /// - Linux: /home/linuxbrew/.linuxbrew/Cellar/poetry/1.8.3/bin/poetry
154+ ///
155+ /// The symlink at /opt/homebrew/bin/poetry points to the Cellar path.
156+ fn extract_version_from_path ( executable : & PathBuf ) -> Option < String > {
157+ // First try to resolve the symlink to get the actual Cellar path
158+ let resolved = resolve_any_symlink ( executable) . unwrap_or_else ( || executable. clone ( ) ) ;
159+ let path_str = resolved. to_string_lossy ( ) ;
160+
161+ // Check if this is a Homebrew Cellar path and extract version
162+ if let Some ( captures) = HOMEBREW_POETRY_VERSION . captures ( & path_str) {
163+ if let Some ( version_match) = captures. get ( 1 ) {
164+ let version = version_match. as_str ( ) . to_string ( ) ;
165+ trace ! ( "Extracted Poetry version {} from Homebrew path: {:?}" , version, resolved) ;
166+ return Some ( version) ;
167+ }
168+ }
169+ None
170+ }
171+
133172 pub fn to_manager ( & self ) -> EnvManager {
134173 EnvManager {
135174 executable : self . executable . clone ( ) ,
136- version : None ,
175+ version : self . version . clone ( ) ,
137176 tool : EnvManagerType :: Poetry ,
138177 }
139178 }
0 commit comments