1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 use anyhow::Result;
18 use serde::Serialize;
19 use tinytemplate::TinyTemplate;
20 
21 use aconfig_protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
22 
23 use std::collections::HashMap;
24 
25 use crate::codegen;
26 use crate::codegen::CodegenMode;
27 use crate::commands::OutputFile;
28 
generate_rust_code<I>( package: &str, flag_ids: HashMap<String, u16>, parsed_flags_iter: I, codegen_mode: CodegenMode, allow_instrumentation: bool, ) -> Result<OutputFile> where I: Iterator<Item = ProtoParsedFlag>,29 pub fn generate_rust_code<I>(
30     package: &str,
31     flag_ids: HashMap<String, u16>,
32     parsed_flags_iter: I,
33     codegen_mode: CodegenMode,
34     allow_instrumentation: bool,
35 ) -> Result<OutputFile>
36 where
37     I: Iterator<Item = ProtoParsedFlag>,
38 {
39     let template_flags: Vec<TemplateParsedFlag> = parsed_flags_iter
40         .map(|pf| TemplateParsedFlag::new(package, flag_ids.clone(), &pf))
41         .collect();
42     let has_readwrite = template_flags.iter().any(|item| item.readwrite);
43     let container = (template_flags.first().expect("zero template flags").container).to_string();
44     let context = TemplateContext {
45         package: package.to_string(),
46         template_flags,
47         modules: package.split('.').map(|s| s.to_string()).collect::<Vec<_>>(),
48         has_readwrite,
49         allow_instrumentation,
50         container,
51     };
52     let mut template = TinyTemplate::new();
53     template.add_template(
54         "rust_code_gen",
55         match codegen_mode {
56             CodegenMode::Test => include_str!("../../templates/rust_test.template"),
57             CodegenMode::Exported | CodegenMode::ForceReadOnly | CodegenMode::Production => {
58                 include_str!("../../templates/rust.template")
59             }
60         },
61     )?;
62     let contents = template.render("rust_code_gen", &context)?;
63     let path = ["src", "lib.rs"].iter().collect();
64     Ok(OutputFile { contents: contents.into(), path })
65 }
66 
67 #[derive(Serialize)]
68 struct TemplateContext {
69     pub package: String,
70     pub template_flags: Vec<TemplateParsedFlag>,
71     pub modules: Vec<String>,
72     pub has_readwrite: bool,
73     pub allow_instrumentation: bool,
74     pub container: String,
75 }
76 
77 #[derive(Serialize)]
78 struct TemplateParsedFlag {
79     pub readwrite: bool,
80     pub default_value: String,
81     pub name: String,
82     pub container: String,
83     pub flag_offset: u16,
84     pub device_config_namespace: String,
85     pub device_config_flag: String,
86 }
87 
88 impl TemplateParsedFlag {
89     #[allow(clippy::nonminimal_bool)]
new(package: &str, flag_offsets: HashMap<String, u16>, pf: &ProtoParsedFlag) -> Self90     fn new(package: &str, flag_offsets: HashMap<String, u16>, pf: &ProtoParsedFlag) -> Self {
91         Self {
92             readwrite: pf.permission() == ProtoFlagPermission::READ_WRITE,
93             default_value: match pf.state() {
94                 ProtoFlagState::ENABLED => "true".to_string(),
95                 ProtoFlagState::DISABLED => "false".to_string(),
96             },
97             name: pf.name().to_string(),
98             container: pf.container().to_string(),
99             flag_offset: *flag_offsets.get(pf.name()).expect("didnt find package offset :("),
100             device_config_namespace: pf.namespace().to_string(),
101             device_config_flag: codegen::create_device_config_ident(package, pf.name())
102                 .expect("values checked at flag parse time"),
103         }
104     }
105 }
106 
107 #[cfg(test)]
108 mod tests {
109     use super::*;
110 
111     const PROD_EXPECTED: &str = r#"
112 //! codegenerated rust flag lib
113 use aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};
114 use std::path::Path;
115 use std::io::Write;
116 use log::{log, LevelFilter, Level};
117 
118 static STORAGE_MIGRATION_MARKER_FILE: &str =
119     "/metadata/aconfig_test_missions/mission_1";
120 static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
121 
122 /// flag provider
123 pub struct FlagProvider;
124 
125 lazy_static::lazy_static! {
126     /// flag value cache for disabled_rw
127     static ref CACHED_disabled_rw: bool = flags_rust::GetServerConfigurableFlag(
128         "aconfig_flags.aconfig_test",
129         "com.android.aconfig.test.disabled_rw",
130         "false") == "true";
131 
132     /// flag value cache for disabled_rw_exported
133     static ref CACHED_disabled_rw_exported: bool = flags_rust::GetServerConfigurableFlag(
134         "aconfig_flags.aconfig_test",
135         "com.android.aconfig.test.disabled_rw_exported",
136         "false") == "true";
137 
138     /// flag value cache for disabled_rw_in_other_namespace
139     static ref CACHED_disabled_rw_in_other_namespace: bool = flags_rust::GetServerConfigurableFlag(
140         "aconfig_flags.other_namespace",
141         "com.android.aconfig.test.disabled_rw_in_other_namespace",
142         "false") == "true";
143 
144     /// flag value cache for enabled_rw
145     static ref CACHED_enabled_rw: bool = flags_rust::GetServerConfigurableFlag(
146         "aconfig_flags.aconfig_test",
147         "com.android.aconfig.test.enabled_rw",
148         "true") == "true";
149 
150 }
151 
152 impl FlagProvider {
153     /// query flag disabled_ro
154     pub fn disabled_ro(&self) -> bool {
155         false
156     }
157 
158     /// query flag disabled_rw
159     pub fn disabled_rw(&self) -> bool {
160         *CACHED_disabled_rw
161     }
162 
163     /// query flag disabled_rw_exported
164     pub fn disabled_rw_exported(&self) -> bool {
165         *CACHED_disabled_rw_exported
166     }
167 
168     /// query flag disabled_rw_in_other_namespace
169     pub fn disabled_rw_in_other_namespace(&self) -> bool {
170         *CACHED_disabled_rw_in_other_namespace
171     }
172 
173     /// query flag enabled_fixed_ro
174     pub fn enabled_fixed_ro(&self) -> bool {
175         true
176     }
177 
178     /// query flag enabled_fixed_ro_exported
179     pub fn enabled_fixed_ro_exported(&self) -> bool {
180         true
181     }
182 
183     /// query flag enabled_ro
184     pub fn enabled_ro(&self) -> bool {
185         true
186     }
187 
188     /// query flag enabled_ro_exported
189     pub fn enabled_ro_exported(&self) -> bool {
190         true
191     }
192 
193     /// query flag enabled_rw
194     pub fn enabled_rw(&self) -> bool {
195         *CACHED_enabled_rw
196     }
197 }
198 
199 /// flag provider
200 pub static PROVIDER: FlagProvider = FlagProvider;
201 
202 /// query flag disabled_ro
203 #[inline(always)]
204 pub fn disabled_ro() -> bool {
205     false
206 }
207 
208 /// query flag disabled_rw
209 #[inline(always)]
210 pub fn disabled_rw() -> bool {
211     PROVIDER.disabled_rw()
212 }
213 
214 /// query flag disabled_rw_exported
215 #[inline(always)]
216 pub fn disabled_rw_exported() -> bool {
217     PROVIDER.disabled_rw_exported()
218 }
219 
220 /// query flag disabled_rw_in_other_namespace
221 #[inline(always)]
222 pub fn disabled_rw_in_other_namespace() -> bool {
223     PROVIDER.disabled_rw_in_other_namespace()
224 }
225 
226 /// query flag enabled_fixed_ro
227 #[inline(always)]
228 pub fn enabled_fixed_ro() -> bool {
229     true
230 }
231 
232 /// query flag enabled_fixed_ro_exported
233 #[inline(always)]
234 pub fn enabled_fixed_ro_exported() -> bool {
235     true
236 }
237 
238 /// query flag enabled_ro
239 #[inline(always)]
240 pub fn enabled_ro() -> bool {
241     true
242 }
243 
244 /// query flag enabled_ro_exported
245 #[inline(always)]
246 pub fn enabled_ro_exported() -> bool {
247     true
248 }
249 
250 /// query flag enabled_rw
251 #[inline(always)]
252 pub fn enabled_rw() -> bool {
253     PROVIDER.enabled_rw()
254 }
255 "#;
256 
257     const PROD_INSTRUMENTED_EXPECTED: &str = r#"
258 //! codegenerated rust flag lib
259 use aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};
260 use std::path::Path;
261 use std::io::Write;
262 use log::{log, LevelFilter, Level};
263 
264 static STORAGE_MIGRATION_MARKER_FILE: &str =
265     "/metadata/aconfig_test_missions/mission_1";
266 static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
267 
268 /// flag provider
269 pub struct FlagProvider;
270 
271 lazy_static::lazy_static! {
272 
273     static ref PACKAGE_OFFSET: Result<Option<u32>, AconfigStorageError> = unsafe {
274         get_mapped_storage_file("system", StorageFileType::PackageMap)
275         .and_then(|package_map| get_package_read_context(&package_map, "com.android.aconfig.test"))
276         .map(|context| context.map(|c| c.boolean_start_index))
277     };
278 
279     static ref FLAG_VAL_MAP: Result<Mmap, AconfigStorageError> = unsafe {
280         get_mapped_storage_file("system", StorageFileType::FlagVal)
281     };
282     /// flag value cache for disabled_rw
283 
284     static ref CACHED_disabled_rw: bool = {
285         let result = flags_rust::GetServerConfigurableFlag(
286             "aconfig_flags.aconfig_test",
287             "com.android.aconfig.test.disabled_rw",
288             "false") == "true";
289 
290         if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
291             // This will be called multiple times. Subsequent calls after the first are noops.
292             logger::init(
293                 logger::Config::default()
294                     .with_tag_on_device(MIGRATION_LOG_TAG)
295                     .with_max_level(LevelFilter::Info));
296 
297             let aconfig_storage_result = FLAG_VAL_MAP
298                 .as_ref()
299                 .map_err(|err| format!("failed to get flag val map: {err}"))
300                 .and_then(|flag_val_map| {
301                     PACKAGE_OFFSET
302                         .as_ref()
303                         .map_err(|err| format!("failed to get package read offset: {err}"))
304                         .and_then(|package_offset| {
305                             match package_offset {
306                                 Some(offset) => {
307                                     get_boolean_flag_value(&flag_val_map, offset + 1)
308                                         .map_err(|err| format!("failed to get flag: {err}"))
309                                 },
310                                 None => Err("no context found for package 'com.android.aconfig.test'".to_string())
311                             }
312                         })
313                     });
314 
315             match aconfig_storage_result {
316                 Ok(storage_result) if storage_result == result => {
317                     log!(Level::Info, "AconfigTestMission1: success! flag 'disabled_rw' contained correct value. Legacy storage was {result}, new storage was {storage_result}");
318                 },
319                 Ok(storage_result) => {
320                     log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'disabled_rw'. Legacy storage was {result}, new storage was {storage_result}");
321                 },
322                 Err(err) => {
323                     log!(Level::Error, "AconfigTestMission1: error: {err}")
324                 }
325             }
326         }
327 
328         result
329         };
330 
331     /// flag value cache for disabled_rw_exported
332 
333     static ref CACHED_disabled_rw_exported: bool = {
334         let result = flags_rust::GetServerConfigurableFlag(
335             "aconfig_flags.aconfig_test",
336             "com.android.aconfig.test.disabled_rw_exported",
337             "false") == "true";
338 
339         if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
340             // This will be called multiple times. Subsequent calls after the first are noops.
341             logger::init(
342                 logger::Config::default()
343                     .with_tag_on_device(MIGRATION_LOG_TAG)
344                     .with_max_level(LevelFilter::Info));
345 
346             let aconfig_storage_result = FLAG_VAL_MAP
347                 .as_ref()
348                 .map_err(|err| format!("failed to get flag val map: {err}"))
349                 .and_then(|flag_val_map| {
350                     PACKAGE_OFFSET
351                         .as_ref()
352                         .map_err(|err| format!("failed to get package read offset: {err}"))
353                         .and_then(|package_offset| {
354                             match package_offset {
355                                 Some(offset) => {
356                                     get_boolean_flag_value(&flag_val_map, offset + 2)
357                                         .map_err(|err| format!("failed to get flag: {err}"))
358                                 },
359                                 None => Err("no context found for package 'com.android.aconfig.test'".to_string())
360                             }
361                         })
362                     });
363 
364             match aconfig_storage_result {
365                 Ok(storage_result) if storage_result == result => {
366                     log!(Level::Info, "AconfigTestMission1: success! flag 'disabled_rw_exported' contained correct value. Legacy storage was {result}, new storage was {storage_result}");
367                 },
368                 Ok(storage_result) => {
369                     log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'disabled_rw_exported'. Legacy storage was {result}, new storage was {storage_result}");
370                 },
371                 Err(err) => {
372                     log!(Level::Error, "AconfigTestMission1: error: {err}")
373                 }
374             }
375         }
376 
377         result
378         };
379 
380     /// flag value cache for disabled_rw_in_other_namespace
381 
382     static ref CACHED_disabled_rw_in_other_namespace: bool = {
383         let result = flags_rust::GetServerConfigurableFlag(
384             "aconfig_flags.other_namespace",
385             "com.android.aconfig.test.disabled_rw_in_other_namespace",
386             "false") == "true";
387 
388         if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
389             // This will be called multiple times. Subsequent calls after the first are noops.
390             logger::init(
391                 logger::Config::default()
392                     .with_tag_on_device(MIGRATION_LOG_TAG)
393                     .with_max_level(LevelFilter::Info));
394 
395             let aconfig_storage_result = FLAG_VAL_MAP
396                 .as_ref()
397                 .map_err(|err| format!("failed to get flag val map: {err}"))
398                 .and_then(|flag_val_map| {
399                     PACKAGE_OFFSET
400                         .as_ref()
401                         .map_err(|err| format!("failed to get package read offset: {err}"))
402                         .and_then(|package_offset| {
403                             match package_offset {
404                                 Some(offset) => {
405                                     get_boolean_flag_value(&flag_val_map, offset + 3)
406                                         .map_err(|err| format!("failed to get flag: {err}"))
407                                 },
408                                 None => Err("no context found for package 'com.android.aconfig.test'".to_string())
409                             }
410                         })
411                     });
412 
413             match aconfig_storage_result {
414                 Ok(storage_result) if storage_result == result => {
415                     log!(Level::Info, "AconfigTestMission1: success! flag 'disabled_rw_in_other_namespace' contained correct value. Legacy storage was {result}, new storage was {storage_result}");
416                 },
417                 Ok(storage_result) => {
418                     log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'disabled_rw_in_other_namespace'. Legacy storage was {result}, new storage was {storage_result}");
419                 },
420                 Err(err) => {
421                     log!(Level::Error, "AconfigTestMission1: error: {err}")
422                 }
423             }
424         }
425 
426         result
427         };
428 
429     /// flag value cache for enabled_rw
430 
431     static ref CACHED_enabled_rw: bool = {
432         let result = flags_rust::GetServerConfigurableFlag(
433             "aconfig_flags.aconfig_test",
434             "com.android.aconfig.test.enabled_rw",
435             "true") == "true";
436 
437         if Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
438             // This will be called multiple times. Subsequent calls after the first are noops.
439             logger::init(
440                 logger::Config::default()
441                     .with_tag_on_device(MIGRATION_LOG_TAG)
442                     .with_max_level(LevelFilter::Info));
443 
444             let aconfig_storage_result = FLAG_VAL_MAP
445                 .as_ref()
446                 .map_err(|err| format!("failed to get flag val map: {err}"))
447                 .and_then(|flag_val_map| {
448                     PACKAGE_OFFSET
449                         .as_ref()
450                         .map_err(|err| format!("failed to get package read offset: {err}"))
451                         .and_then(|package_offset| {
452                             match package_offset {
453                                 Some(offset) => {
454                                     get_boolean_flag_value(&flag_val_map, offset + 8)
455                                         .map_err(|err| format!("failed to get flag: {err}"))
456                                 },
457                                 None => Err("no context found for package 'com.android.aconfig.test'".to_string())
458                             }
459                         })
460                     });
461 
462             match aconfig_storage_result {
463                 Ok(storage_result) if storage_result == result => {
464                     log!(Level::Info, "AconfigTestMission1: success! flag 'enabled_rw' contained correct value. Legacy storage was {result}, new storage was {storage_result}");
465                 },
466                 Ok(storage_result) => {
467                     log!(Level::Error, "AconfigTestMission1: error: mismatch for flag 'enabled_rw'. Legacy storage was {result}, new storage was {storage_result}");
468                 },
469                 Err(err) => {
470                     log!(Level::Error, "AconfigTestMission1: error: {err}")
471                 }
472             }
473         }
474 
475         result
476         };
477 
478 }
479 
480 impl FlagProvider {
481 
482 
483     /// query flag disabled_ro
484     pub fn disabled_ro(&self) -> bool {
485         false
486     }
487 
488     /// query flag disabled_rw
489     pub fn disabled_rw(&self) -> bool {
490         *CACHED_disabled_rw
491     }
492 
493     /// query flag disabled_rw_exported
494     pub fn disabled_rw_exported(&self) -> bool {
495         *CACHED_disabled_rw_exported
496     }
497 
498     /// query flag disabled_rw_in_other_namespace
499     pub fn disabled_rw_in_other_namespace(&self) -> bool {
500         *CACHED_disabled_rw_in_other_namespace
501     }
502 
503     /// query flag enabled_fixed_ro
504     pub fn enabled_fixed_ro(&self) -> bool {
505         true
506     }
507 
508     /// query flag enabled_fixed_ro_exported
509     pub fn enabled_fixed_ro_exported(&self) -> bool {
510         true
511     }
512 
513     /// query flag enabled_ro
514     pub fn enabled_ro(&self) -> bool {
515         true
516     }
517 
518     /// query flag enabled_ro_exported
519     pub fn enabled_ro_exported(&self) -> bool {
520         true
521     }
522 
523     /// query flag enabled_rw
524     pub fn enabled_rw(&self) -> bool {
525         *CACHED_enabled_rw
526     }
527 
528 
529 }
530 
531 /// flag provider
532 pub static PROVIDER: FlagProvider = FlagProvider;
533 
534 
535 /// query flag disabled_ro
536 #[inline(always)]
537 pub fn disabled_ro() -> bool {
538 
539 
540     let result = false;
541     if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
542         return result;
543     }
544 
545     // This will be called multiple times. Subsequent calls after the first
546     // are noops.
547     logger::init(
548         logger::Config::default()
549             .with_tag_on_device(MIGRATION_LOG_TAG)
550             .with_max_level(LevelFilter::Info),
551     );
552 
553     unsafe {
554         let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
555             Ok(file) => file,
556             Err(err) => {
557                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}");
558                 return result;
559             }
560         };
561 
562         let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
563             Ok(Some(context)) => context,
564             Ok(None) => {
565                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': did not get context");
566                 return result;
567             },
568             Err(err) => {
569                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}");
570                 return result;
571             }
572         };
573         let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
574             Ok(val_map) => val_map,
575             Err(err) => {
576                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}");
577                 return result;
578             }
579         };
580         let value = match get_boolean_flag_value(&flag_val_map, 0 + package_read_context.boolean_start_index) {
581             Ok(val) => val,
582             Err(err) => {
583                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'disabled_ro': {err}");
584                 return result;
585             }
586         };
587 
588         if result != value {
589             log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'disabled_ro'. Legacy storage was {result}, new storage was {value}");
590         } else {
591             let default_value = false;
592             log!(Level::Info, "AconfigTestMission1: success! flag 'disabled_ro' contained correct value. Legacy storage was {default_value}, new storage was {value}");
593         }
594     }
595 
596     result
597 
598 }
599 
600 /// query flag disabled_rw
601 #[inline(always)]
602 pub fn disabled_rw() -> bool {
603     PROVIDER.disabled_rw()
604 }
605 
606 /// query flag disabled_rw_exported
607 #[inline(always)]
608 pub fn disabled_rw_exported() -> bool {
609     PROVIDER.disabled_rw_exported()
610 }
611 
612 /// query flag disabled_rw_in_other_namespace
613 #[inline(always)]
614 pub fn disabled_rw_in_other_namespace() -> bool {
615     PROVIDER.disabled_rw_in_other_namespace()
616 }
617 
618 /// query flag enabled_fixed_ro
619 #[inline(always)]
620 pub fn enabled_fixed_ro() -> bool {
621 
622 
623     let result = true;
624     if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
625         return result;
626     }
627 
628     // This will be called multiple times. Subsequent calls after the first
629     // are noops.
630     logger::init(
631         logger::Config::default()
632             .with_tag_on_device(MIGRATION_LOG_TAG)
633             .with_max_level(LevelFilter::Info),
634     );
635 
636     unsafe {
637         let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
638             Ok(file) => file,
639             Err(err) => {
640                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}");
641                 return result;
642             }
643         };
644 
645         let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
646             Ok(Some(context)) => context,
647             Ok(None) => {
648                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': did not get context");
649                 return result;
650             },
651             Err(err) => {
652                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}");
653                 return result;
654             }
655         };
656         let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
657             Ok(val_map) => val_map,
658             Err(err) => {
659                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}");
660                 return result;
661             }
662         };
663         let value = match get_boolean_flag_value(&flag_val_map, 4 + package_read_context.boolean_start_index) {
664             Ok(val) => val,
665             Err(err) => {
666                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro': {err}");
667                 return result;
668             }
669         };
670 
671         if result != value {
672             log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_fixed_ro'. Legacy storage was {result}, new storage was {value}");
673         } else {
674             let default_value = true;
675             log!(Level::Info, "AconfigTestMission1: success! flag 'enabled_fixed_ro' contained correct value. Legacy storage was {default_value}, new storage was {value}");
676         }
677     }
678 
679     result
680 
681 }
682 
683 /// query flag enabled_fixed_ro_exported
684 #[inline(always)]
685 pub fn enabled_fixed_ro_exported() -> bool {
686 
687 
688     let result = true;
689     if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
690         return result;
691     }
692 
693     // This will be called multiple times. Subsequent calls after the first
694     // are noops.
695     logger::init(
696         logger::Config::default()
697             .with_tag_on_device(MIGRATION_LOG_TAG)
698             .with_max_level(LevelFilter::Info),
699     );
700 
701     unsafe {
702         let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
703             Ok(file) => file,
704             Err(err) => {
705                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}");
706                 return result;
707             }
708         };
709 
710         let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
711             Ok(Some(context)) => context,
712             Ok(None) => {
713                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': did not get context");
714                 return result;
715             },
716             Err(err) => {
717                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}");
718                 return result;
719             }
720         };
721         let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
722             Ok(val_map) => val_map,
723             Err(err) => {
724                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}");
725                 return result;
726             }
727         };
728         let value = match get_boolean_flag_value(&flag_val_map, 5 + package_read_context.boolean_start_index) {
729             Ok(val) => val,
730             Err(err) => {
731                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_fixed_ro_exported': {err}");
732                 return result;
733             }
734         };
735 
736         if result != value {
737             log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_fixed_ro_exported'. Legacy storage was {result}, new storage was {value}");
738         } else {
739             let default_value = true;
740             log!(Level::Info, "AconfigTestMission1: success! flag 'enabled_fixed_ro_exported' contained correct value. Legacy storage was {default_value}, new storage was {value}");
741         }
742     }
743 
744     result
745 
746 }
747 
748 /// query flag enabled_ro
749 #[inline(always)]
750 pub fn enabled_ro() -> bool {
751 
752 
753     let result = true;
754     if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
755         return result;
756     }
757 
758     // This will be called multiple times. Subsequent calls after the first
759     // are noops.
760     logger::init(
761         logger::Config::default()
762             .with_tag_on_device(MIGRATION_LOG_TAG)
763             .with_max_level(LevelFilter::Info),
764     );
765 
766     unsafe {
767         let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
768             Ok(file) => file,
769             Err(err) => {
770                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}");
771                 return result;
772             }
773         };
774 
775         let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
776             Ok(Some(context)) => context,
777             Ok(None) => {
778                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': did not get context");
779                 return result;
780             },
781             Err(err) => {
782                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}");
783                 return result;
784             }
785         };
786         let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
787             Ok(val_map) => val_map,
788             Err(err) => {
789                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}");
790                 return result;
791             }
792         };
793         let value = match get_boolean_flag_value(&flag_val_map, 6 + package_read_context.boolean_start_index) {
794             Ok(val) => val,
795             Err(err) => {
796                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro': {err}");
797                 return result;
798             }
799         };
800 
801         if result != value {
802             log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_ro'. Legacy storage was {result}, new storage was {value}");
803         } else {
804             let default_value = true;
805             log!(Level::Info, "AconfigTestMission1: success! flag 'enabled_ro' contained correct value. Legacy storage was {default_value}, new storage was {value}");
806         }
807     }
808 
809     result
810 
811 }
812 
813 /// query flag enabled_ro_exported
814 #[inline(always)]
815 pub fn enabled_ro_exported() -> bool {
816 
817 
818     let result = true;
819     if !Path::new(STORAGE_MIGRATION_MARKER_FILE).exists() {
820         return result;
821     }
822 
823     // This will be called multiple times. Subsequent calls after the first
824     // are noops.
825     logger::init(
826         logger::Config::default()
827             .with_tag_on_device(MIGRATION_LOG_TAG)
828             .with_max_level(LevelFilter::Info),
829     );
830 
831     unsafe {
832         let package_map = match get_mapped_storage_file("system", StorageFileType::PackageMap) {
833             Ok(file) => file,
834             Err(err) => {
835                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}");
836                 return result;
837             }
838         };
839 
840         let package_read_context = match get_package_read_context(&package_map, "com.android.aconfig.test") {
841             Ok(Some(context)) => context,
842             Ok(None) => {
843                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': did not get context");
844                 return result;
845             },
846             Err(err) => {
847                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}");
848                 return result;
849             }
850         };
851         let flag_val_map = match get_mapped_storage_file("system", StorageFileType::FlagVal) {
852             Ok(val_map) => val_map,
853             Err(err) => {
854                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}");
855                 return result;
856             }
857         };
858         let value = match get_boolean_flag_value(&flag_val_map, 7 + package_read_context.boolean_start_index) {
859             Ok(val) => val,
860             Err(err) => {
861                 log!(Level::Error, "AconfigTestMission1: error: failed to read flag 'enabled_ro_exported': {err}");
862                 return result;
863             }
864         };
865 
866         if result != value {
867             log!(Level::Error, "AconfigTestMission1: error: flag mismatch for 'enabled_ro_exported'. Legacy storage was {result}, new storage was {value}");
868         } else {
869             let default_value = true;
870             log!(Level::Info, "AconfigTestMission1: success! flag 'enabled_ro_exported' contained correct value. Legacy storage was {default_value}, new storage was {value}");
871         }
872     }
873 
874     result
875 
876 }
877 
878 /// query flag enabled_rw
879 #[inline(always)]
880 pub fn enabled_rw() -> bool {
881     PROVIDER.enabled_rw()
882 }
883 "#;
884 
885     const TEST_EXPECTED: &str = r#"
886 //! codegenerated rust flag lib
887 
888 use std::collections::BTreeMap;
889 use std::sync::Mutex;
890 
891 /// flag provider
892 pub struct FlagProvider {
893     overrides: BTreeMap<&'static str, bool>,
894 }
895 
896 impl FlagProvider {
897     /// query flag disabled_ro
898     pub fn disabled_ro(&self) -> bool {
899         self.overrides.get("disabled_ro").copied().unwrap_or(
900             false
901         )
902     }
903 
904     /// set flag disabled_ro
905     pub fn set_disabled_ro(&mut self, val: bool) {
906         self.overrides.insert("disabled_ro", val);
907     }
908 
909     /// query flag disabled_rw
910     pub fn disabled_rw(&self) -> bool {
911         self.overrides.get("disabled_rw").copied().unwrap_or(
912             flags_rust::GetServerConfigurableFlag(
913                 "aconfig_flags.aconfig_test",
914                 "com.android.aconfig.test.disabled_rw",
915                 "false") == "true"
916         )
917     }
918 
919     /// set flag disabled_rw
920     pub fn set_disabled_rw(&mut self, val: bool) {
921         self.overrides.insert("disabled_rw", val);
922     }
923 
924     /// query flag disabled_rw_exported
925     pub fn disabled_rw_exported(&self) -> bool {
926         self.overrides.get("disabled_rw_exported").copied().unwrap_or(
927             flags_rust::GetServerConfigurableFlag(
928                 "aconfig_flags.aconfig_test",
929                 "com.android.aconfig.test.disabled_rw_exported",
930                 "false") == "true"
931         )
932     }
933 
934     /// set flag disabled_rw_exported
935     pub fn set_disabled_rw_exported(&mut self, val: bool) {
936         self.overrides.insert("disabled_rw_exported", val);
937     }
938 
939     /// query flag disabled_rw_in_other_namespace
940     pub fn disabled_rw_in_other_namespace(&self) -> bool {
941         self.overrides.get("disabled_rw_in_other_namespace").copied().unwrap_or(
942             flags_rust::GetServerConfigurableFlag(
943                 "aconfig_flags.other_namespace",
944                 "com.android.aconfig.test.disabled_rw_in_other_namespace",
945                 "false") == "true"
946         )
947     }
948 
949     /// set flag disabled_rw_in_other_namespace
950     pub fn set_disabled_rw_in_other_namespace(&mut self, val: bool) {
951         self.overrides.insert("disabled_rw_in_other_namespace", val);
952     }
953 
954     /// query flag enabled_fixed_ro
955     pub fn enabled_fixed_ro(&self) -> bool {
956         self.overrides.get("enabled_fixed_ro").copied().unwrap_or(
957             true
958         )
959     }
960 
961     /// set flag enabled_fixed_ro
962     pub fn set_enabled_fixed_ro(&mut self, val: bool) {
963         self.overrides.insert("enabled_fixed_ro", val);
964     }
965 
966     /// query flag enabled_fixed_ro_exported
967     pub fn enabled_fixed_ro_exported(&self) -> bool {
968         self.overrides.get("enabled_fixed_ro_exported").copied().unwrap_or(
969             true
970         )
971     }
972 
973     /// set flag enabled_fixed_ro_exported
974     pub fn set_enabled_fixed_ro_exported(&mut self, val: bool) {
975         self.overrides.insert("enabled_fixed_ro_exported", val);
976     }
977 
978     /// query flag enabled_ro
979     pub fn enabled_ro(&self) -> bool {
980         self.overrides.get("enabled_ro").copied().unwrap_or(
981             true
982         )
983     }
984 
985     /// set flag enabled_ro
986     pub fn set_enabled_ro(&mut self, val: bool) {
987         self.overrides.insert("enabled_ro", val);
988     }
989 
990     /// query flag enabled_ro_exported
991     pub fn enabled_ro_exported(&self) -> bool {
992         self.overrides.get("enabled_ro_exported").copied().unwrap_or(
993             true
994         )
995     }
996 
997     /// set flag enabled_ro_exported
998     pub fn set_enabled_ro_exported(&mut self, val: bool) {
999         self.overrides.insert("enabled_ro_exported", val);
1000     }
1001 
1002     /// query flag enabled_rw
1003     pub fn enabled_rw(&self) -> bool {
1004         self.overrides.get("enabled_rw").copied().unwrap_or(
1005             flags_rust::GetServerConfigurableFlag(
1006                 "aconfig_flags.aconfig_test",
1007                 "com.android.aconfig.test.enabled_rw",
1008                 "true") == "true"
1009         )
1010     }
1011 
1012     /// set flag enabled_rw
1013     pub fn set_enabled_rw(&mut self, val: bool) {
1014         self.overrides.insert("enabled_rw", val);
1015     }
1016 
1017     /// clear all flag overrides
1018     pub fn reset_flags(&mut self) {
1019         self.overrides.clear();
1020     }
1021 }
1022 
1023 /// flag provider
1024 pub static PROVIDER: Mutex<FlagProvider> = Mutex::new(
1025     FlagProvider {overrides: BTreeMap::new()}
1026 );
1027 
1028 /// query flag disabled_ro
1029 #[inline(always)]
1030 pub fn disabled_ro() -> bool {
1031     PROVIDER.lock().unwrap().disabled_ro()
1032 }
1033 
1034 /// set flag disabled_ro
1035 #[inline(always)]
1036 pub fn set_disabled_ro(val: bool) {
1037     PROVIDER.lock().unwrap().set_disabled_ro(val);
1038 }
1039 
1040 /// query flag disabled_rw
1041 #[inline(always)]
1042 pub fn disabled_rw() -> bool {
1043     PROVIDER.lock().unwrap().disabled_rw()
1044 }
1045 
1046 /// set flag disabled_rw
1047 #[inline(always)]
1048 pub fn set_disabled_rw(val: bool) {
1049     PROVIDER.lock().unwrap().set_disabled_rw(val);
1050 }
1051 
1052 /// query flag disabled_rw_exported
1053 #[inline(always)]
1054 pub fn disabled_rw_exported() -> bool {
1055     PROVIDER.lock().unwrap().disabled_rw_exported()
1056 }
1057 
1058 /// set flag disabled_rw_exported
1059 #[inline(always)]
1060 pub fn set_disabled_rw_exported(val: bool) {
1061     PROVIDER.lock().unwrap().set_disabled_rw_exported(val);
1062 }
1063 
1064 /// query flag disabled_rw_in_other_namespace
1065 #[inline(always)]
1066 pub fn disabled_rw_in_other_namespace() -> bool {
1067     PROVIDER.lock().unwrap().disabled_rw_in_other_namespace()
1068 }
1069 
1070 /// set flag disabled_rw_in_other_namespace
1071 #[inline(always)]
1072 pub fn set_disabled_rw_in_other_namespace(val: bool) {
1073     PROVIDER.lock().unwrap().set_disabled_rw_in_other_namespace(val);
1074 }
1075 
1076 /// query flag enabled_fixed_ro
1077 #[inline(always)]
1078 pub fn enabled_fixed_ro() -> bool {
1079     PROVIDER.lock().unwrap().enabled_fixed_ro()
1080 }
1081 
1082 /// set flag enabled_fixed_ro
1083 #[inline(always)]
1084 pub fn set_enabled_fixed_ro(val: bool) {
1085     PROVIDER.lock().unwrap().set_enabled_fixed_ro(val);
1086 }
1087 
1088 /// query flag enabled_fixed_ro_exported
1089 #[inline(always)]
1090 pub fn enabled_fixed_ro_exported() -> bool {
1091     PROVIDER.lock().unwrap().enabled_fixed_ro_exported()
1092 }
1093 
1094 /// set flag enabled_fixed_ro_exported
1095 #[inline(always)]
1096 pub fn set_enabled_fixed_ro_exported(val: bool) {
1097     PROVIDER.lock().unwrap().set_enabled_fixed_ro_exported(val);
1098 }
1099 
1100 /// query flag enabled_ro
1101 #[inline(always)]
1102 pub fn enabled_ro() -> bool {
1103     PROVIDER.lock().unwrap().enabled_ro()
1104 }
1105 
1106 /// set flag enabled_ro
1107 #[inline(always)]
1108 pub fn set_enabled_ro(val: bool) {
1109     PROVIDER.lock().unwrap().set_enabled_ro(val);
1110 }
1111 
1112 /// query flag enabled_ro_exported
1113 #[inline(always)]
1114 pub fn enabled_ro_exported() -> bool {
1115     PROVIDER.lock().unwrap().enabled_ro_exported()
1116 }
1117 
1118 /// set flag enabled_ro_exported
1119 #[inline(always)]
1120 pub fn set_enabled_ro_exported(val: bool) {
1121     PROVIDER.lock().unwrap().set_enabled_ro_exported(val);
1122 }
1123 
1124 /// query flag enabled_rw
1125 #[inline(always)]
1126 pub fn enabled_rw() -> bool {
1127     PROVIDER.lock().unwrap().enabled_rw()
1128 }
1129 
1130 /// set flag enabled_rw
1131 #[inline(always)]
1132 pub fn set_enabled_rw(val: bool) {
1133     PROVIDER.lock().unwrap().set_enabled_rw(val);
1134 }
1135 
1136 /// clear all flag override
1137 pub fn reset_flags() {
1138     PROVIDER.lock().unwrap().reset_flags()
1139 }
1140 "#;
1141 
1142     const EXPORTED_EXPECTED: &str = r#"
1143 //! codegenerated rust flag lib
1144 use aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};
1145 use std::path::Path;
1146 use std::io::Write;
1147 use log::{log, LevelFilter, Level};
1148 
1149 static STORAGE_MIGRATION_MARKER_FILE: &str =
1150     "/metadata/aconfig_test_missions/mission_1";
1151 static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
1152 
1153 /// flag provider
1154 pub struct FlagProvider;
1155 
1156 lazy_static::lazy_static! {
1157     /// flag value cache for disabled_rw_exported
1158     static ref CACHED_disabled_rw_exported: bool = flags_rust::GetServerConfigurableFlag(
1159         "aconfig_flags.aconfig_test",
1160         "com.android.aconfig.test.disabled_rw_exported",
1161         "false") == "true";
1162 
1163     /// flag value cache for enabled_fixed_ro_exported
1164     static ref CACHED_enabled_fixed_ro_exported: bool = flags_rust::GetServerConfigurableFlag(
1165         "aconfig_flags.aconfig_test",
1166         "com.android.aconfig.test.enabled_fixed_ro_exported",
1167         "false") == "true";
1168 
1169     /// flag value cache for enabled_ro_exported
1170     static ref CACHED_enabled_ro_exported: bool = flags_rust::GetServerConfigurableFlag(
1171         "aconfig_flags.aconfig_test",
1172         "com.android.aconfig.test.enabled_ro_exported",
1173         "false") == "true";
1174 
1175 }
1176 
1177 impl FlagProvider {
1178     /// query flag disabled_rw_exported
1179     pub fn disabled_rw_exported(&self) -> bool {
1180         *CACHED_disabled_rw_exported
1181     }
1182 
1183     /// query flag enabled_fixed_ro_exported
1184     pub fn enabled_fixed_ro_exported(&self) -> bool {
1185         *CACHED_enabled_fixed_ro_exported
1186     }
1187 
1188     /// query flag enabled_ro_exported
1189     pub fn enabled_ro_exported(&self) -> bool {
1190         *CACHED_enabled_ro_exported
1191     }
1192 }
1193 
1194 /// flag provider
1195 pub static PROVIDER: FlagProvider = FlagProvider;
1196 
1197 /// query flag disabled_rw_exported
1198 #[inline(always)]
1199 pub fn disabled_rw_exported() -> bool {
1200     PROVIDER.disabled_rw_exported()
1201 }
1202 
1203 /// query flag enabled_fixed_ro_exported
1204 #[inline(always)]
1205 pub fn enabled_fixed_ro_exported() -> bool {
1206     PROVIDER.enabled_fixed_ro_exported()
1207 }
1208 
1209 /// query flag enabled_ro_exported
1210 #[inline(always)]
1211 pub fn enabled_ro_exported() -> bool {
1212     PROVIDER.enabled_ro_exported()
1213 }
1214 "#;
1215 
1216     const FORCE_READ_ONLY_EXPECTED: &str = r#"
1217 //! codegenerated rust flag lib
1218 use aconfig_storage_read_api::{Mmap, AconfigStorageError, StorageFileType, PackageReadContext, get_mapped_storage_file, get_boolean_flag_value, get_package_read_context};
1219 use std::path::Path;
1220 use std::io::Write;
1221 use log::{log, LevelFilter, Level};
1222 
1223 static STORAGE_MIGRATION_MARKER_FILE: &str =
1224     "/metadata/aconfig_test_missions/mission_1";
1225 static MIGRATION_LOG_TAG: &str = "AconfigTestMission1";
1226 
1227 /// flag provider
1228 pub struct FlagProvider;
1229 
1230 impl FlagProvider {
1231     /// query flag disabled_ro
1232     pub fn disabled_ro(&self) -> bool {
1233         false
1234     }
1235 
1236     /// query flag disabled_rw
1237     pub fn disabled_rw(&self) -> bool {
1238         false
1239     }
1240 
1241     /// query flag disabled_rw_in_other_namespace
1242     pub fn disabled_rw_in_other_namespace(&self) -> bool {
1243         false
1244     }
1245 
1246     /// query flag enabled_fixed_ro
1247     pub fn enabled_fixed_ro(&self) -> bool {
1248         true
1249     }
1250 
1251     /// query flag enabled_ro
1252     pub fn enabled_ro(&self) -> bool {
1253         true
1254     }
1255 
1256     /// query flag enabled_rw
1257     pub fn enabled_rw(&self) -> bool {
1258         true
1259     }
1260 }
1261 
1262 /// flag provider
1263 pub static PROVIDER: FlagProvider = FlagProvider;
1264 
1265 /// query flag disabled_ro
1266 #[inline(always)]
1267 pub fn disabled_ro() -> bool {
1268     false
1269 }
1270 
1271 /// query flag disabled_rw
1272 #[inline(always)]
1273 pub fn disabled_rw() -> bool {
1274     false
1275 }
1276 
1277 /// query flag disabled_rw_in_other_namespace
1278 #[inline(always)]
1279 pub fn disabled_rw_in_other_namespace() -> bool {
1280     false
1281 }
1282 
1283 /// query flag enabled_fixed_ro
1284 #[inline(always)]
1285 pub fn enabled_fixed_ro() -> bool {
1286     true
1287 }
1288 
1289 /// query flag enabled_ro
1290 #[inline(always)]
1291 pub fn enabled_ro() -> bool {
1292     true
1293 }
1294 
1295 /// query flag enabled_rw
1296 #[inline(always)]
1297 pub fn enabled_rw() -> bool {
1298     true
1299 }
1300 "#;
1301     use crate::commands::assign_flag_ids;
1302 
test_generate_rust_code(mode: CodegenMode, allow_instrumentation: bool, expected: &str)1303     fn test_generate_rust_code(mode: CodegenMode, allow_instrumentation: bool, expected: &str) {
1304         let parsed_flags = crate::test::parse_test_flags();
1305         let modified_parsed_flags =
1306             crate::commands::modify_parsed_flags_based_on_mode(parsed_flags, mode).unwrap();
1307         let flag_ids =
1308             assign_flag_ids(crate::test::TEST_PACKAGE, modified_parsed_flags.iter()).unwrap();
1309         let generated = generate_rust_code(
1310             crate::test::TEST_PACKAGE,
1311             flag_ids,
1312             modified_parsed_flags.into_iter(),
1313             mode,
1314             allow_instrumentation,
1315         )
1316         .unwrap();
1317         assert_eq!("src/lib.rs", format!("{}", generated.path.display()));
1318         assert_eq!(
1319             None,
1320             crate::test::first_significant_code_diff(
1321                 expected,
1322                 &String::from_utf8(generated.contents).unwrap()
1323             )
1324         );
1325     }
1326 
1327     #[test]
test_generate_rust_code_for_prod()1328     fn test_generate_rust_code_for_prod() {
1329         test_generate_rust_code(CodegenMode::Production, false, PROD_EXPECTED);
1330     }
1331 
1332     #[test]
test_generate_rust_code_for_prod_instrumented()1333     fn test_generate_rust_code_for_prod_instrumented() {
1334         test_generate_rust_code(CodegenMode::Production, true, PROD_INSTRUMENTED_EXPECTED);
1335     }
1336 
1337     #[test]
test_generate_rust_code_for_test()1338     fn test_generate_rust_code_for_test() {
1339         test_generate_rust_code(CodegenMode::Test, false, TEST_EXPECTED);
1340     }
1341 
1342     #[test]
test_generate_rust_code_for_exported()1343     fn test_generate_rust_code_for_exported() {
1344         test_generate_rust_code(CodegenMode::Exported, false, EXPORTED_EXPECTED);
1345     }
1346 
1347     #[test]
test_generate_rust_code_for_force_read_only()1348     fn test_generate_rust_code_for_force_read_only() {
1349         test_generate_rust_code(CodegenMode::ForceReadOnly, false, FORCE_READ_ONLY_EXPECTED);
1350     }
1351 }
1352