@@ -9,6 +9,7 @@ const AI_BLOCK_SOURCE: &str = include_str!("ai/blocklist/block.rs");
99const AI_BLOCK_STATUS_BAR_SOURCE : & str = include_str ! ( "ai/blocklist/block/status_bar.rs" ) ;
1010const AI_FACT_RULE_SOURCE : & str = include_str ! ( "ai/facts/view/rule.rs" ) ;
1111const AI_TELEMETRY_BANNER_SOURCE : & str = include_str ! ( "ai/blocklist/telemetry_banner.rs" ) ;
12+ const APP_MENUS_SOURCE : & str = include_str ! ( "app_menus.rs" ) ;
1213const AGENT_VIEW_ZERO_STATE_BLOCK_SOURCE : & str =
1314 include_str ! ( "ai/blocklist/agent_view/zero_state_block.rs" ) ;
1415const AGENT_PANEL_MOD_SOURCE : & str = include_str ! ( "agent_panel/mod.rs" ) ;
@@ -54,6 +55,117 @@ const WORKFLOW_VIEW_SOURCE: &str = include_str!("workflows/workflow_view.rs");
5455const WORKSPACE_MOD_SOURCE : & str = include_str ! ( "workspace/mod.rs" ) ;
5556const WORKSPACE_VIEW_SOURCE : & str = include_str ! ( "workspace/view.rs" ) ;
5657
58+ fn function_source < ' a > ( source : & ' a str , function : & str , next_function : & str ) -> & ' a str {
59+ source
60+ . split_once ( function)
61+ . unwrap_or_else ( || panic ! ( "{function} should exist" ) )
62+ . 1
63+ . split_once ( next_function)
64+ . unwrap_or_else ( || panic ! ( "{next_function} should follow {function}" ) )
65+ . 0
66+ }
67+
68+ fn matching_closing_brace ( source : & str , opening_brace_offset : usize ) -> Option < usize > {
69+ if source. as_bytes ( ) . get ( opening_brace_offset) != Some ( & b'{' ) {
70+ return None ;
71+ }
72+
73+ let mut depth = 0 ;
74+ for ( relative_offset, character) in source[ opening_brace_offset..] . char_indices ( ) {
75+ match character {
76+ '{' => depth += 1 ,
77+ '}' => {
78+ depth -= 1 ;
79+ if depth == 0 {
80+ return Some ( opening_brace_offset + relative_offset) ;
81+ }
82+ }
83+ _ => { }
84+ }
85+ }
86+ None
87+ }
88+
89+ fn assert_item_is_cloud_service_only ( function : & str , item : & str , description : & str ) {
90+ let item_offset = function
91+ . find ( item)
92+ . unwrap_or_else ( || panic ! ( "{description} should exist" ) ) ;
93+ let cloud_gate = "if ChannelState::cloud_services_available()" ;
94+ let gate_offset = function[ ..item_offset]
95+ . rfind ( cloud_gate)
96+ . unwrap_or_else ( || panic ! ( "{description} should have a cloud-service gate" ) ) ;
97+ let gate_open_offset = function[ gate_offset..item_offset]
98+ . find ( '{' )
99+ . map ( |offset| gate_offset + offset)
100+ . unwrap_or_else ( || panic ! ( "{description} cloud-service gate should open" ) ) ;
101+ let gate_end_offset = matching_closing_brace ( function, gate_open_offset)
102+ . unwrap_or_else ( || panic ! ( "{description} cloud-service gate should close" ) ) ;
103+
104+ assert ! (
105+ item_offset < gate_end_offset,
106+ "{description} should be hidden when hosted cloud services are unavailable"
107+ ) ;
108+ }
109+
110+ #[ test]
111+ fn cloud_service_gate_matching_supports_nested_braces ( ) {
112+ let function = r#"
113+ if ChannelState::cloud_services_available() {
114+ if nested_condition {
115+ nested_action();
116+ }
117+ CustomAction::ReferAFriend;
118+ }
119+ "# ;
120+ assert_item_is_cloud_service_only (
121+ function,
122+ "CustomAction::ReferAFriend" ,
123+ "nested referral action" ,
124+ ) ;
125+ }
126+
127+ #[ test]
128+ fn referral_app_menu_item_is_cloud_service_only ( ) {
129+ let app_menu = function_source (
130+ APP_MENUS_SOURCE ,
131+ "fn make_new_app_menu" ,
132+ "fn make_new_file_menu" ,
133+ ) ;
134+ assert_item_is_cloud_service_only (
135+ app_menu,
136+ "CustomAction::ReferAFriend" ,
137+ "referral app menu action" ,
138+ ) ;
139+ }
140+
141+ #[ test]
142+ fn oss_app_menus_do_not_build_cloud_only_items ( ) {
143+ let menu_bar = function_source ( APP_MENUS_SOURCE , "pub fn menu_bar" , "pub fn dock_menu" ) ;
144+ assert_item_is_cloud_service_only ( menu_bar, "make_new_drive_menu(ctx)" , "Drive app menu" ) ;
145+
146+ let view_menu = function_source (
147+ APP_MENUS_SOURCE ,
148+ "fn make_new_view_menu" ,
149+ "fn make_new_tab_menu" ,
150+ ) ;
151+ assert_item_is_cloud_service_only (
152+ view_menu,
153+ "CustomAction::ToggleWarpDrive" ,
154+ "Cast Drive view menu action" ,
155+ ) ;
156+
157+ let blocks_menu = function_source (
158+ APP_MENUS_SOURCE ,
159+ "fn make_new_blocks_menu" ,
160+ "fn make_new_drive_menu" ,
161+ ) ;
162+ assert_item_is_cloud_service_only (
163+ blocks_menu,
164+ "CustomAction::ViewSharedBlocks" ,
165+ "shared blocks menu action" ,
166+ ) ;
167+ }
168+
57169#[ test]
58170fn public_app_surfaces_use_castcodes_links_and_labels ( ) {
59171 assert ! ( LOGIN_SLIDE_SOURCE . contains( "PRIVACY_POLICY_URL" ) ) ;
0 commit comments