@@ -900,6 +900,39 @@ pub fn remove_session(site: &str, org: Option<&str>) -> Result<()> {
900900 write_sessions ( & sessions)
901901}
902902
903+ /// Look up the site for a named org session. Returns None if no session exists
904+ /// for that org, or if multiple sessions share the same org name on different
905+ /// sites (ambiguous — caller must pass DD_SITE explicitly). On the ambiguous
906+ /// path, prints a one-line warning to stderr naming the conflicting sites so
907+ /// the user knows the auto-resolution gave up.
908+ #[ cfg( not( target_arch = "wasm32" ) ) ]
909+ pub fn find_session_site ( org : & str ) -> Option < String > {
910+ let sessions = list_sessions ( ) . ok ( ) ?;
911+ let mut sites: Vec < String > = sessions
912+ . into_iter ( )
913+ . filter ( |s| s. org . as_deref ( ) == Some ( org) )
914+ . map ( |s| s. site )
915+ . collect ( ) ;
916+ sites. sort ( ) ;
917+ sites. dedup ( ) ;
918+ match sites. len ( ) {
919+ 0 => None ,
920+ 1 => sites. pop ( ) ,
921+ _ => {
922+ // The caller (Config::from_env / apply_org_override) handles the
923+ // resulting None by leaving cfg.site at whatever it was — which
924+ // may be a default, an env-set site, or a previously-resolved
925+ // org's site — so we do not promise "falling back to default" here.
926+ eprintln ! (
927+ "Warning: org '{org}' has saved sessions on multiple sites ({}); \
928+ not auto-selecting one. Set DD_SITE to disambiguate.",
929+ sites. join( ", " )
930+ ) ;
931+ None
932+ }
933+ }
934+ }
935+
903936#[ cfg( not( target_arch = "wasm32" ) ) ]
904937fn write_sessions ( sessions : & [ SessionEntry ] ) -> Result < ( ) > {
905938 let path = match sessions_path ( ) {
@@ -1280,6 +1313,63 @@ mod tests {
12801313 assert ! ( result. is_ok( ) ) ;
12811314 }
12821315
1316+ #[ test]
1317+ fn test_find_session_site_unique_match ( ) {
1318+ let _lock = crate :: test_utils:: ENV_LOCK . blocking_lock ( ) ;
1319+ let tmp = TempDir :: new ( "find_sess_unique" ) ;
1320+ std:: env:: set_var ( "PUP_CONFIG_DIR" , tmp. path ( ) ) ;
1321+
1322+ save_session ( "custom.datadoghq.com" , Some ( "prod-child" ) ) . unwrap ( ) ;
1323+ save_session ( "datadoghq.com" , None ) . unwrap ( ) ;
1324+ let result = find_session_site ( "prod-child" ) ;
1325+ std:: env:: remove_var ( "PUP_CONFIG_DIR" ) ;
1326+
1327+ assert_eq ! ( result. as_deref( ) , Some ( "custom.datadoghq.com" ) ) ;
1328+ }
1329+
1330+ #[ test]
1331+ fn test_find_session_site_no_match ( ) {
1332+ let _lock = crate :: test_utils:: ENV_LOCK . blocking_lock ( ) ;
1333+ let tmp = TempDir :: new ( "find_sess_none" ) ;
1334+ std:: env:: set_var ( "PUP_CONFIG_DIR" , tmp. path ( ) ) ;
1335+
1336+ save_session ( "datadoghq.com" , Some ( "prod-child" ) ) . unwrap ( ) ;
1337+ let result = find_session_site ( "nonexistent" ) ;
1338+ std:: env:: remove_var ( "PUP_CONFIG_DIR" ) ;
1339+
1340+ assert ! ( result. is_none( ) ) ;
1341+ }
1342+
1343+ #[ test]
1344+ fn test_find_session_site_ambiguous_returns_none ( ) {
1345+ let _lock = crate :: test_utils:: ENV_LOCK . blocking_lock ( ) ;
1346+ let tmp = TempDir :: new ( "find_sess_amb" ) ;
1347+ std:: env:: set_var ( "PUP_CONFIG_DIR" , tmp. path ( ) ) ;
1348+
1349+ // Same org name registered against two different sites → caller must
1350+ // disambiguate via DD_SITE rather than us picking one.
1351+ save_session ( "datadoghq.com" , Some ( "shared-name" ) ) . unwrap ( ) ;
1352+ save_session ( "datadoghq.eu" , Some ( "shared-name" ) ) . unwrap ( ) ;
1353+ let result = find_session_site ( "shared-name" ) ;
1354+ std:: env:: remove_var ( "PUP_CONFIG_DIR" ) ;
1355+
1356+ assert ! ( result. is_none( ) ) ;
1357+ }
1358+
1359+ #[ test]
1360+ fn test_find_session_site_skips_default_session ( ) {
1361+ let _lock = crate :: test_utils:: ENV_LOCK . blocking_lock ( ) ;
1362+ let tmp = TempDir :: new ( "find_sess_default" ) ;
1363+ std:: env:: set_var ( "PUP_CONFIG_DIR" , tmp. path ( ) ) ;
1364+
1365+ // The unnamed (org=None) session must not match any --org lookup.
1366+ save_session ( "datadoghq.eu" , None ) . unwrap ( ) ;
1367+ let result = find_session_site ( "anything" ) ;
1368+ std:: env:: remove_var ( "PUP_CONFIG_DIR" ) ;
1369+
1370+ assert ! ( result. is_none( ) ) ;
1371+ }
1372+
12831373 // --- detect_backend ---------------------------------------------------------
12841374
12851375 // Exercises the FileStorage fallback when the auto-detect keychain probe fails,
0 commit comments