@@ -9,10 +9,10 @@ use std::fs;
99use std:: io;
1010use std:: path:: PathBuf ;
1111
12- /// Platform session state - tracks selected project and organization
12+ /// Platform session state - tracks selected project, organization, and environment
1313///
1414/// This is a separate system from conversation persistence - it tracks
15- /// which platform project/org the user has selected for platform operations.
15+ /// which platform project/org/environment the user has selected for platform operations.
1616#[ derive( Debug , Clone , Serialize , Deserialize , Default ) ]
1717pub struct PlatformSession {
1818 /// Selected platform project UUID
@@ -23,6 +23,10 @@ pub struct PlatformSession {
2323 pub org_id : Option < String > ,
2424 /// Organization name
2525 pub org_name : Option < String > ,
26+ /// Selected environment UUID
27+ pub environment_id : Option < String > ,
28+ /// Human-readable environment name
29+ pub environment_name : Option < String > ,
2630 /// When the session was last updated
2731 pub last_updated : Option < DateTime < Utc > > ,
2832}
@@ -45,16 +49,47 @@ impl PlatformSession {
4549 project_name : Some ( project_name) ,
4650 org_id : Some ( org_id) ,
4751 org_name : Some ( org_name) ,
52+ environment_id : None ,
53+ environment_name : None ,
4854 last_updated : Some ( Utc :: now ( ) ) ,
4955 }
5056 }
5157
52- /// Clears the selected project
58+ /// Creates a platform session with a selected project and environment
59+ pub fn with_environment (
60+ project_id : String ,
61+ project_name : String ,
62+ org_id : String ,
63+ org_name : String ,
64+ environment_id : String ,
65+ environment_name : String ,
66+ ) -> Self {
67+ Self {
68+ project_id : Some ( project_id) ,
69+ project_name : Some ( project_name) ,
70+ org_id : Some ( org_id) ,
71+ org_name : Some ( org_name) ,
72+ environment_id : Some ( environment_id) ,
73+ environment_name : Some ( environment_name) ,
74+ last_updated : Some ( Utc :: now ( ) ) ,
75+ }
76+ }
77+
78+ /// Clears the selected project and environment
5379 pub fn clear ( & mut self ) {
5480 self . project_id = None ;
5581 self . project_name = None ;
5682 self . org_id = None ;
5783 self . org_name = None ;
84+ self . environment_id = None ;
85+ self . environment_name = None ;
86+ self . last_updated = Some ( Utc :: now ( ) ) ;
87+ }
88+
89+ /// Clears only the selected environment (keeps project)
90+ pub fn clear_environment ( & mut self ) {
91+ self . environment_id = None ;
92+ self . environment_name = None ;
5893 self . last_updated = Some ( Utc :: now ( ) ) ;
5994 }
6095
@@ -63,6 +98,11 @@ impl PlatformSession {
6398 self . project_id . is_some ( )
6499 }
65100
101+ /// Returns true if an environment is currently selected
102+ pub fn is_environment_selected ( & self ) -> bool {
103+ self . environment_id . is_some ( )
104+ }
105+
66106 /// Returns the path to the platform session file
67107 ///
68108 /// Location: `~/.syncable/platform-session.json`
@@ -105,11 +145,13 @@ impl PlatformSession {
105145
106146 /// Returns a display string for the current context
107147 ///
108- /// Format: "[org/project]" or "[no project selected]"
148+ /// Format: "[org/project/env]", "[org/project]", or "[no project selected]"
109149 pub fn display_context ( & self ) -> String {
110- match ( & self . org_name , & self . project_name ) {
111- ( Some ( org) , Some ( project) ) => format ! ( "[{}/{}]" , org, project) ,
112- ( None , Some ( project) ) => format ! ( "[{}]" , project) ,
150+ match ( & self . org_name , & self . project_name , & self . environment_name ) {
151+ ( Some ( org) , Some ( project) , Some ( env) ) => format ! ( "[{}/{}/{}]" , org, project, env) ,
152+ ( Some ( org) , Some ( project) , None ) => format ! ( "[{}/{}]" , org, project) ,
153+ ( None , Some ( project) , Some ( env) ) => format ! ( "[{}/{}]" , project, env) ,
154+ ( None , Some ( project) , None ) => format ! ( "[{}]" , project) ,
113155 _ => "[no project selected]" . to_string ( ) ,
114156 }
115157 }
@@ -157,7 +199,18 @@ mod tests {
157199
158200 #[ test]
159201 fn test_display_context ( ) {
160- // Full context
202+ // Full context with environment
203+ let session = PlatformSession :: with_environment (
204+ "id" . to_string ( ) ,
205+ "project" . to_string ( ) ,
206+ "oid" . to_string ( ) ,
207+ "org" . to_string ( ) ,
208+ "env-id" . to_string ( ) ,
209+ "prod" . to_string ( ) ,
210+ ) ;
211+ assert_eq ! ( session. display_context( ) , "[org/project/prod]" ) ;
212+
213+ // Project only (no env)
161214 let session = PlatformSession :: with_project (
162215 "id" . to_string ( ) ,
163216 "project" . to_string ( ) ,
@@ -172,6 +225,8 @@ mod tests {
172225 project_name : Some ( "project" . to_string ( ) ) ,
173226 org_id : None ,
174227 org_name : None ,
228+ environment_id : None ,
229+ environment_name : None ,
175230 last_updated : None ,
176231 } ;
177232 assert_eq ! ( session. display_context( ) , "[project]" ) ;
@@ -181,6 +236,69 @@ mod tests {
181236 assert_eq ! ( session. display_context( ) , "[no project selected]" ) ;
182237 }
183238
239+ #[ test]
240+ fn test_with_environment ( ) {
241+ let session = PlatformSession :: with_environment (
242+ "proj-123" . to_string ( ) ,
243+ "my-project" . to_string ( ) ,
244+ "org-456" . to_string ( ) ,
245+ "my-org" . to_string ( ) ,
246+ "env-789" . to_string ( ) ,
247+ "production" . to_string ( ) ,
248+ ) ;
249+
250+ assert ! ( session. is_project_selected( ) ) ;
251+ assert ! ( session. is_environment_selected( ) ) ;
252+ assert_eq ! ( session. project_id, Some ( "proj-123" . to_string( ) ) ) ;
253+ assert_eq ! ( session. environment_id, Some ( "env-789" . to_string( ) ) ) ;
254+ assert_eq ! ( session. environment_name, Some ( "production" . to_string( ) ) ) ;
255+ assert_eq ! ( session. display_context( ) , "[my-org/my-project/production]" ) ;
256+ }
257+
258+ #[ test]
259+ fn test_clear_environment ( ) {
260+ let mut session = PlatformSession :: with_environment (
261+ "proj-123" . to_string ( ) ,
262+ "my-project" . to_string ( ) ,
263+ "org-456" . to_string ( ) ,
264+ "my-org" . to_string ( ) ,
265+ "env-789" . to_string ( ) ,
266+ "production" . to_string ( ) ,
267+ ) ;
268+
269+ assert ! ( session. is_environment_selected( ) ) ;
270+
271+ session. clear_environment ( ) ;
272+
273+ assert ! ( session. is_project_selected( ) ) ; // Project still selected
274+ assert ! ( !session. is_environment_selected( ) ) ; // Environment cleared
275+ assert_eq ! ( session. display_context( ) , "[my-org/my-project]" ) ;
276+ }
277+
278+ #[ test]
279+ fn test_is_environment_selected ( ) {
280+ let session = PlatformSession :: new ( ) ;
281+ assert ! ( !session. is_environment_selected( ) ) ;
282+
283+ let session = PlatformSession :: with_project (
284+ "proj-123" . to_string ( ) ,
285+ "my-project" . to_string ( ) ,
286+ "org-456" . to_string ( ) ,
287+ "my-org" . to_string ( ) ,
288+ ) ;
289+ assert ! ( !session. is_environment_selected( ) ) ;
290+
291+ let session = PlatformSession :: with_environment (
292+ "proj-123" . to_string ( ) ,
293+ "my-project" . to_string ( ) ,
294+ "org-456" . to_string ( ) ,
295+ "my-org" . to_string ( ) ,
296+ "env-789" . to_string ( ) ,
297+ "staging" . to_string ( ) ,
298+ ) ;
299+ assert ! ( session. is_environment_selected( ) ) ;
300+ }
301+
184302 #[ test]
185303 fn test_save_and_load ( ) {
186304 // Use a temp directory for testing
0 commit comments