@@ -56,6 +56,7 @@ use schemars::JsonSchema;
5656use serde:: Deserialize ;
5757use serde:: Deserializer ;
5858use serde:: Serialize ;
59+ use serde:: de:: Error as SerdeError ;
5960
6061const RESERVED_MODEL_PROVIDER_IDS : [ & str ; 4 ] = [
6162 AMAZON_BEDROCK_PROVIDER_ID ,
@@ -86,8 +87,8 @@ const fn default_hide_agent_reasoning() -> Option<bool> {
8687 Some ( false )
8788}
8889
89- /// Backward-compatible shape for workspace restrictions in config.toml.
90- #[ derive( Serialize , Deserialize , Debug , Clone , PartialEq , JsonSchema ) ]
90+ /// Backward-compatible shape for ChatGPT workspace login restrictions in config.toml.
91+ #[ derive( Serialize , Debug , Clone , PartialEq , JsonSchema ) ]
9192#[ serde( untagged) ]
9293pub enum ForcedChatgptWorkspaceIds {
9394 Single ( String ) ,
@@ -103,6 +104,30 @@ impl ForcedChatgptWorkspaceIds {
103104 }
104105}
105106
107+ impl < ' de > Deserialize < ' de > for ForcedChatgptWorkspaceIds {
108+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
109+ where
110+ D : Deserializer < ' de > ,
111+ {
112+ #[ derive( Deserialize ) ]
113+ #[ serde( untagged) ]
114+ enum Repr {
115+ Single ( String ) ,
116+ Multiple ( Vec < String > ) ,
117+ }
118+
119+ match Repr :: deserialize ( deserializer) ? {
120+ Repr :: Single ( value) if value. contains ( ',' ) => Err ( D :: Error :: custom (
121+ "forced_chatgpt_workspace_id must be a single workspace ID string or a TOML list \
122+ of strings; comma-separated strings are not supported. Use \
123+ `forced_chatgpt_workspace_id = [\" workspace-a\" , \" workspace-b\" ]` instead.",
124+ ) ) ,
125+ Repr :: Single ( value) => Ok ( Self :: Single ( value) ) ,
126+ Repr :: Multiple ( values) => Ok ( Self :: Multiple ( values) ) ,
127+ }
128+ }
129+ }
130+
106131/// Base config deserialized from ~/.codex/config.toml.
107132#[ derive( Serialize , Deserialize , Debug , Clone , Default , PartialEq , JsonSchema ) ]
108133#[ schemars( deny_unknown_fields) ]
@@ -980,3 +1005,50 @@ pub fn validate_oss_provider(provider: &str) -> std::io::Result<()> {
9801005 ) ) ,
9811006 }
9821007}
1008+
1009+ #[ cfg( test) ]
1010+ mod tests {
1011+ use super :: * ;
1012+ use pretty_assertions:: assert_eq;
1013+
1014+ #[ test]
1015+ fn forced_chatgpt_workspace_id_accepts_single_string ( ) {
1016+ let config: ConfigToml = toml:: from_str ( r#"forced_chatgpt_workspace_id = "workspace-a""# )
1017+ . expect ( "single workspace id should deserialize" ) ;
1018+
1019+ assert_eq ! (
1020+ config
1021+ . forced_chatgpt_workspace_id
1022+ . expect( "workspace id should be set" )
1023+ . into_vec( ) ,
1024+ vec![ "workspace-a" . to_string( ) ]
1025+ ) ;
1026+ }
1027+
1028+ #[ test]
1029+ fn forced_chatgpt_workspace_id_accepts_string_list ( ) {
1030+ let config: ConfigToml =
1031+ toml:: from_str ( r#"forced_chatgpt_workspace_id = ["workspace-a", "workspace-b"]"# )
1032+ . expect ( "workspace id list should deserialize" ) ;
1033+
1034+ assert_eq ! (
1035+ config
1036+ . forced_chatgpt_workspace_id
1037+ . expect( "workspace ids should be set" )
1038+ . into_vec( ) ,
1039+ vec![ "workspace-a" . to_string( ) , "workspace-b" . to_string( ) ]
1040+ ) ;
1041+ }
1042+
1043+ #[ test]
1044+ fn forced_chatgpt_workspace_id_rejects_comma_separated_string ( ) {
1045+ let err = toml:: from_str :: < ConfigToml > (
1046+ r#"forced_chatgpt_workspace_id = "workspace-a,workspace-b""# ,
1047+ )
1048+ . expect_err ( "comma-separated string should be rejected" ) ;
1049+
1050+ let message = err. to_string ( ) ;
1051+ assert ! ( message. contains( "TOML list of strings" ) ) ;
1052+ assert ! ( message. contains( "comma-separated strings are not supported" ) ) ;
1053+ }
1054+ }
0 commit comments