• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! `aconfig_protos` is a crate for the protos defined for aconfig
18 // When building with the Android tool-chain
19 //
20 //   - an external crate `aconfig_protos` will be generated
21 //   - the feature "cargo" will be disabled
22 //
23 // When building with cargo
24 //
25 //   - a local sub-module will be generated in OUT_DIR and included in this file
26 //   - the feature "cargo" will be enabled
27 //
28 // This module hides these differences from the rest of aconfig.
29 
30 // ---- When building with the Android tool-chain ----
31 #[cfg(not(feature = "cargo"))]
32 mod auto_generated {
33     pub use aconfig_rust_proto::aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
34     pub use aconfig_rust_proto::aconfig::Flag_declaration as ProtoFlagDeclaration;
35     pub use aconfig_rust_proto::aconfig::Flag_declarations as ProtoFlagDeclarations;
36     pub use aconfig_rust_proto::aconfig::Flag_metadata as ProtoFlagMetadata;
37     pub use aconfig_rust_proto::aconfig::Flag_permission as ProtoFlagPermission;
38     pub use aconfig_rust_proto::aconfig::Flag_state as ProtoFlagState;
39     pub use aconfig_rust_proto::aconfig::Flag_value as ProtoFlagValue;
40     pub use aconfig_rust_proto::aconfig::Flag_values as ProtoFlagValues;
41     pub use aconfig_rust_proto::aconfig::Parsed_flag as ProtoParsedFlag;
42     pub use aconfig_rust_proto::aconfig::Parsed_flags as ProtoParsedFlags;
43     pub use aconfig_rust_proto::aconfig::Tracepoint as ProtoTracepoint;
44 }
45 
46 // ---- When building with cargo ----
47 #[cfg(feature = "cargo")]
48 mod auto_generated {
49     // include! statements should be avoided (because they import file contents verbatim), but
50     // because this is only used during local development, and only if using cargo instead of the
51     // Android tool-chain, we allow it
52     include!(concat!(env!("OUT_DIR"), "/aconfig_proto/mod.rs"));
53     pub use aconfig::flag_metadata::Flag_purpose as ProtoFlagPurpose;
54     pub use aconfig::Flag_declaration as ProtoFlagDeclaration;
55     pub use aconfig::Flag_declarations as ProtoFlagDeclarations;
56     pub use aconfig::Flag_metadata as ProtoFlagMetadata;
57     pub use aconfig::Flag_permission as ProtoFlagPermission;
58     pub use aconfig::Flag_state as ProtoFlagState;
59     pub use aconfig::Flag_value as ProtoFlagValue;
60     pub use aconfig::Flag_values as ProtoFlagValues;
61     pub use aconfig::Parsed_flag as ProtoParsedFlag;
62     pub use aconfig::Parsed_flags as ProtoParsedFlags;
63     pub use aconfig::Tracepoint as ProtoTracepoint;
64 }
65 
66 // ---- Common for both the Android tool-chain and cargo ----
67 pub use auto_generated::*;
68 
69 use anyhow::Result;
70 use paste::paste;
71 
72 /// Path to proto file
73 const ACONFIG_PROTO_PATH: &str = "//build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto";
74 
75 /// Check if the name identifier is valid
is_valid_name_ident(s: &str) -> bool76 pub fn is_valid_name_ident(s: &str) -> bool {
77     // Identifiers must match [a-z][a-z0-9_]*, except consecutive underscores are not allowed
78     if s.contains("__") {
79         return false;
80     }
81     let mut chars = s.chars();
82     let Some(first) = chars.next() else {
83         return false;
84     };
85     if !first.is_ascii_lowercase() {
86         return false;
87     }
88     chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '_')
89 }
90 
91 /// Check if the package identifier is valid
is_valid_package_ident(s: &str) -> bool92 pub fn is_valid_package_ident(s: &str) -> bool {
93     if !s.contains('.') {
94         return false;
95     }
96     s.split('.').all(is_valid_name_ident)
97 }
98 
99 /// Check if the container identifier is valid
is_valid_container_ident(s: &str) -> bool100 pub fn is_valid_container_ident(s: &str) -> bool {
101     s.split('.').all(is_valid_name_ident)
102 }
103 
try_from_text_proto<T>(s: &str) -> Result<T> where T: protobuf::MessageFull,104 fn try_from_text_proto<T>(s: &str) -> Result<T>
105 where
106     T: protobuf::MessageFull,
107 {
108     protobuf::text_format::parse_from_str(s).map_err(|e| e.into())
109 }
110 
111 macro_rules! ensure_required_fields {
112     ($type:expr, $struct:expr, $($field:expr),+) => {
113         $(
114         paste! {
115             ensure!($struct.[<has_ $field>](), "bad {}: missing {}", $type, $field);
116         }
117         )+
118     };
119 }
120 
121 /// Utility module for flag_declaration proto
122 pub mod flag_declaration {
123     use super::*;
124     use anyhow::ensure;
125 
126     /// Ensure the proto instance is valid by checking its fields
verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()>127     pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> {
128         ensure_required_fields!("flag declaration", pdf, "name", "namespace", "description");
129 
130         ensure!(
131             is_valid_name_ident(pdf.name()),
132             "bad flag declaration: bad name {} expected snake_case string; \
133         see {ACONFIG_PROTO_PATH} for details",
134             pdf.name()
135         );
136         ensure!(
137             is_valid_name_ident(pdf.namespace()),
138             "bad flag declaration: bad namespace {} expected snake_case string; \
139         see {ACONFIG_PROTO_PATH} for details",
140             pdf.namespace()
141         );
142         ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description");
143         ensure!(pdf.bug.len() == 1, "bad flag declaration: exactly one bug required");
144 
145         Ok(())
146     }
147 }
148 
149 /// Utility module for flag_declarations proto
150 pub mod flag_declarations {
151     use super::*;
152     use anyhow::ensure;
153 
154     /// Construct a proto instance from a textproto string content
try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations>155     pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {
156         let pdf: ProtoFlagDeclarations = super::try_from_text_proto(s)?;
157         verify_fields(&pdf)?;
158         Ok(pdf)
159     }
160 
161     /// Ensure the proto instance is valid by checking its fields
verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()>162     pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> {
163         ensure_required_fields!("flag declarations", pdf, "package");
164         // TODO(b/312769710): Make the container field required.
165         ensure!(
166             is_valid_package_ident(pdf.package()),
167             "bad flag declarations: bad package {} expected snake_case strings delimited by dots; \
168         see {ACONFIG_PROTO_PATH} for details",
169             pdf.package()
170         );
171         ensure!(
172             !pdf.has_container() || is_valid_container_ident(pdf.container()),
173             "bad flag declarations: bad container"
174         );
175         for flag_declaration in pdf.flag.iter() {
176             super::flag_declaration::verify_fields(flag_declaration)?;
177         }
178 
179         Ok(())
180     }
181 }
182 
183 /// Utility module for flag_value proto
184 pub mod flag_value {
185     use super::*;
186     use anyhow::ensure;
187 
188     /// Ensure the proto instance is valid by checking its fields
verify_fields(fv: &ProtoFlagValue) -> Result<()>189     pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
190         ensure_required_fields!("flag value", fv, "package", "name", "state", "permission");
191 
192         ensure!(
193             is_valid_package_ident(fv.package()),
194             "bad flag value: bad package {} expected snake_case strings delimited by dots; \
195         see {ACONFIG_PROTO_PATH} for details",
196             fv.package()
197         );
198         ensure!(
199             is_valid_name_ident(fv.name()),
200             "bad flag value: bad name {} expected snake_case string; \
201         see {ACONFIG_PROTO_PATH} for details",
202             fv.name()
203         );
204 
205         Ok(())
206     }
207 }
208 
209 /// Utility module for flag_values proto
210 pub mod flag_values {
211     use super::*;
212 
213     /// Construct a proto instance from a textproto string content
try_from_text_proto(s: &str) -> Result<ProtoFlagValues>214     pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagValues> {
215         let pfv: ProtoFlagValues = super::try_from_text_proto(s)?;
216         verify_fields(&pfv)?;
217         Ok(pfv)
218     }
219 
220     /// Ensure the proto instance is valid by checking its fields
verify_fields(pfv: &ProtoFlagValues) -> Result<()>221     pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> {
222         for flag_value in pfv.flag_value.iter() {
223             super::flag_value::verify_fields(flag_value)?;
224         }
225         Ok(())
226     }
227 }
228 
229 /// Utility module for flag_permission proto enum
230 pub mod flag_permission {
231     use super::*;
232     use anyhow::bail;
233 
234     /// Construct a flag permission proto enum from string
parse_from_str(permission: &str) -> Result<ProtoFlagPermission>235     pub fn parse_from_str(permission: &str) -> Result<ProtoFlagPermission> {
236         match permission.to_ascii_lowercase().as_str() {
237             "read_write" => Ok(ProtoFlagPermission::READ_WRITE),
238             "read_only" => Ok(ProtoFlagPermission::READ_ONLY),
239             _ => bail!("Permission needs to be read_only or read_write."),
240         }
241     }
242 
243     /// Serialize flag permission proto enum to string
to_string(permission: &ProtoFlagPermission) -> &str244     pub fn to_string(permission: &ProtoFlagPermission) -> &str {
245         match permission {
246             ProtoFlagPermission::READ_WRITE => "read_write",
247             ProtoFlagPermission::READ_ONLY => "read_only",
248         }
249     }
250 }
251 
252 /// Utility module for tracepoint proto
253 pub mod tracepoint {
254     use super::*;
255     use anyhow::ensure;
256 
257     /// Ensure the proto instance is valid by checking its fields
verify_fields(tp: &ProtoTracepoint) -> Result<()>258     pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> {
259         ensure_required_fields!("tracepoint", tp, "source", "state", "permission");
260 
261         ensure!(!tp.source().is_empty(), "bad tracepoint: empty source");
262 
263         Ok(())
264     }
265 }
266 
267 /// Utility module for parsed_flag proto
268 pub mod parsed_flag {
269     use super::*;
270     use anyhow::ensure;
271 
272     /// Ensure the proto instance is valid by checking its fields
verify_fields(pf: &ProtoParsedFlag) -> Result<()>273     pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
274         ensure_required_fields!(
275             "parsed flag",
276             pf,
277             "package",
278             "name",
279             "namespace",
280             "description",
281             "state",
282             "permission"
283         );
284 
285         ensure!(
286             is_valid_package_ident(pf.package()),
287             "bad parsed flag: bad package {} expected snake_case strings delimited by dots; \
288         see {ACONFIG_PROTO_PATH} for details",
289             pf.package()
290         );
291         ensure!(
292             !pf.has_container() || is_valid_container_ident(pf.container()),
293             "bad parsed flag: bad container"
294         );
295         ensure!(
296             is_valid_name_ident(pf.name()),
297             "bad parsed flag: bad name {} expected snake_case string; \
298         see {ACONFIG_PROTO_PATH} for details",
299             pf.name()
300         );
301         ensure!(
302             is_valid_name_ident(pf.namespace()),
303             "bad parsed flag: bad namespace {} expected snake_case string; \
304         see {ACONFIG_PROTO_PATH} for details",
305             pf.namespace()
306         );
307         ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
308         ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
309         for tp in pf.trace.iter() {
310             super::tracepoint::verify_fields(tp)?;
311         }
312         ensure!(pf.bug.len() == 1, "bad flag declaration: exactly one bug required");
313         if pf.is_fixed_read_only() {
314             ensure!(
315                 pf.permission() == ProtoFlagPermission::READ_ONLY,
316                 "bad parsed flag: flag is is_fixed_read_only but permission is not READ_ONLY"
317             );
318             for tp in pf.trace.iter() {
319                 ensure!(tp.permission() == ProtoFlagPermission::READ_ONLY,
320                 "bad parsed flag: flag is is_fixed_read_only but a tracepoint's permission is not READ_ONLY"
321                 );
322             }
323         }
324 
325         Ok(())
326     }
327 
328     /// Get the file path of the corresponding flag declaration
path_to_declaration(pf: &ProtoParsedFlag) -> &str329     pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {
330         debug_assert!(!pf.trace.is_empty());
331         pf.trace[0].source()
332     }
333 }
334 
335 /// Utility module for parsed_flags proto
336 pub mod parsed_flags {
337     use super::*;
338     use anyhow::bail;
339     use std::cmp::Ordering;
340 
341     /// Construct a proto instance from a binary proto bytes
try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags>342     pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
343         let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
344         verify_fields(&message)?;
345         Ok(message)
346     }
347 
348     /// Ensure the proto instance is valid by checking its fields
verify_fields(pf: &ProtoParsedFlags) -> Result<()>349     pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
350         use crate::parsed_flag::path_to_declaration;
351 
352         let mut previous: Option<&ProtoParsedFlag> = None;
353         for parsed_flag in pf.parsed_flag.iter() {
354             if let Some(prev) = previous {
355                 let a = create_sorting_key(prev);
356                 let b = create_sorting_key(parsed_flag);
357                 match a.cmp(&b) {
358                     Ordering::Less => {}
359                     Ordering::Equal => bail!(
360                         "bad parsed flags: duplicate flag {} (defined in {} and {})",
361                         a,
362                         path_to_declaration(prev),
363                         path_to_declaration(parsed_flag)
364                     ),
365                     Ordering::Greater => {
366                         bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
367                     }
368                 }
369             }
370             super::parsed_flag::verify_fields(parsed_flag)?;
371             previous = Some(parsed_flag);
372         }
373         Ok(())
374     }
375 
376     /// Merge multipe parsed_flags proto
merge(parsed_flags: Vec<ProtoParsedFlags>, dedup: bool) -> Result<ProtoParsedFlags>377     pub fn merge(parsed_flags: Vec<ProtoParsedFlags>, dedup: bool) -> Result<ProtoParsedFlags> {
378         let mut merged = ProtoParsedFlags::new();
379         for mut pfs in parsed_flags.into_iter() {
380             merged.parsed_flag.append(&mut pfs.parsed_flag);
381         }
382         merged.parsed_flag.sort_by_cached_key(create_sorting_key);
383         if dedup {
384             // Deduplicate identical protobuf messages.  Messages with the same sorting key but
385             // different fields (including the path to the original source file) will not be
386             // deduplicated and trigger an error in verify_fields.
387             merged.parsed_flag.dedup();
388         }
389         verify_fields(&merged)?;
390         Ok(merged)
391     }
392 
393     /// Sort parsed flags
sort_parsed_flags(pf: &mut ProtoParsedFlags)394     pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
395         pf.parsed_flag.sort_by_key(create_sorting_key);
396     }
397 
create_sorting_key(pf: &ProtoParsedFlag) -> String398     fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
399         pf.fully_qualified_name()
400     }
401 }
402 
403 /// ParsedFlagExt trait
404 pub trait ParsedFlagExt {
405     /// Return the fully qualified name
fully_qualified_name(&self) -> String406     fn fully_qualified_name(&self) -> String;
407 }
408 
409 impl ParsedFlagExt for ProtoParsedFlag {
fully_qualified_name(&self) -> String410     fn fully_qualified_name(&self) -> String {
411         format!("{}.{}", self.package(), self.name())
412     }
413 }
414 
415 #[cfg(test)]
416 mod tests {
417     use super::*;
418 
419     #[test]
test_flag_declarations_try_from_text_proto()420     fn test_flag_declarations_try_from_text_proto() {
421         // valid input
422         let flag_declarations = flag_declarations::try_from_text_proto(
423             r#"
424 package: "com.foo.bar"
425 container: "system"
426 flag {
427     name: "first"
428     namespace: "first_ns"
429     description: "This is the description of the first flag."
430     bug: "123"
431     is_exported: true
432 }
433 flag {
434     name: "second"
435     namespace: "second_ns"
436     description: "This is the description of the second flag."
437     bug: "abc"
438     is_fixed_read_only: true
439 }
440 "#,
441         )
442         .unwrap();
443         assert_eq!(flag_declarations.package(), "com.foo.bar");
444         assert_eq!(flag_declarations.container(), "system");
445         let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
446         assert_eq!(first.name(), "first");
447         assert_eq!(first.namespace(), "first_ns");
448         assert_eq!(first.description(), "This is the description of the first flag.");
449         assert_eq!(first.bug, vec!["123"]);
450         assert!(!first.is_fixed_read_only());
451         assert!(first.is_exported());
452         let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
453         assert_eq!(second.name(), "second");
454         assert_eq!(second.namespace(), "second_ns");
455         assert_eq!(second.description(), "This is the description of the second flag.");
456         assert_eq!(second.bug, vec!["abc"]);
457         assert!(second.is_fixed_read_only());
458         assert!(!second.is_exported());
459 
460         // valid input: missing container in flag declarations is supported
461         let flag_declarations = flag_declarations::try_from_text_proto(
462             r#"
463 package: "com.foo.bar"
464 flag {
465     name: "first"
466     namespace: "first_ns"
467     description: "This is the description of the first flag."
468     bug: "123"
469 }
470 "#,
471         )
472         .unwrap();
473         assert_eq!(flag_declarations.container(), "");
474         assert!(!flag_declarations.has_container());
475 
476         // bad input: missing package in flag declarations
477         let error = flag_declarations::try_from_text_proto(
478             r#"
479 container: "system"
480 flag {
481     name: "first"
482     namespace: "first_ns"
483     description: "This is the description of the first flag."
484 }
485 flag {
486     name: "second"
487     namespace: "second_ns"
488     description: "This is the description of the second flag."
489 }
490 "#,
491         )
492         .unwrap_err();
493         assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
494 
495         // bad input: missing namespace in flag declaration
496         let error = flag_declarations::try_from_text_proto(
497             r#"
498 package: "com.foo.bar"
499 container: "system"
500 flag {
501     name: "first"
502     description: "This is the description of the first flag."
503 }
504 flag {
505     name: "second"
506     namespace: "second_ns"
507     description: "This is the description of the second flag."
508 }
509 "#,
510         )
511         .unwrap_err();
512         assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
513 
514         // bad input: bad package name in flag declarations
515         let error = flag_declarations::try_from_text_proto(
516             r#"
517 package: "_com.FOO__BAR"
518 container: "system"
519 flag {
520     name: "first"
521     namespace: "first_ns"
522     description: "This is the description of the first flag."
523 }
524 flag {
525     name: "second"
526     namespace: "second_ns"
527     description: "This is the description of the second flag."
528 }
529 "#,
530         )
531         .unwrap_err();
532         assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
533 
534         // bad input: bad name in flag declaration
535         let error = flag_declarations::try_from_text_proto(
536             r#"
537 package: "com.foo.bar"
538 container: "system"
539 flag {
540     name: "FIRST"
541     namespace: "first_ns"
542     description: "This is the description of the first flag."
543 }
544 flag {
545     name: "second"
546     namespace: "second_ns"
547     description: "This is the description of the second flag."
548 }
549 "#,
550         )
551         .unwrap_err();
552         assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
553 
554         // bad input: no bug entries in flag declaration
555         let error = flag_declarations::try_from_text_proto(
556             r#"
557 package: "com.foo.bar"
558 container: "system"
559 flag {
560     name: "first"
561     namespace: "first_ns"
562     description: "This is the description of the first flag."
563 }
564 "#,
565         )
566         .unwrap_err();
567         assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
568 
569         // bad input: multiple bug entries in flag declaration
570         let error = flag_declarations::try_from_text_proto(
571             r#"
572 package: "com.foo.bar"
573 container: "system"
574 flag {
575     name: "first"
576     namespace: "first_ns"
577     description: "This is the description of the first flag."
578     bug: "123"
579     bug: "abc"
580 }
581 "#,
582         )
583         .unwrap_err();
584         assert!(format!("{:?}", error).contains("bad flag declaration: exactly one bug required"));
585 
586         // bad input: invalid container name in flag declaration
587         let error = flag_declarations::try_from_text_proto(
588             r#"
589 package: "com.foo.bar"
590 container: "__bad_bad_container.com"
591 flag {
592     name: "first"
593     namespace: "first_ns"
594     description: "This is the description of the first flag."
595     bug: "123"
596     bug: "abc"
597 }
598 "#,
599         )
600         .unwrap_err();
601         assert!(format!("{:?}", error).contains("bad flag declarations: bad container"));
602 
603         // TODO(b/312769710): Verify error when container is missing.
604     }
605 
606     #[test]
test_flag_values_try_from_text_proto()607     fn test_flag_values_try_from_text_proto() {
608         // valid input
609         let flag_values = flag_values::try_from_text_proto(
610             r#"
611 flag_value {
612     package: "com.first"
613     name: "first"
614     state: DISABLED
615     permission: READ_ONLY
616 }
617 flag_value {
618     package: "com.second"
619     name: "second"
620     state: ENABLED
621     permission: READ_WRITE
622 }
623 "#,
624         )
625         .unwrap();
626         let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
627         assert_eq!(first.package(), "com.first");
628         assert_eq!(first.name(), "first");
629         assert_eq!(first.state(), ProtoFlagState::DISABLED);
630         assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
631         let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
632         assert_eq!(second.package(), "com.second");
633         assert_eq!(second.name(), "second");
634         assert_eq!(second.state(), ProtoFlagState::ENABLED);
635         assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
636 
637         // bad input: bad package in flag value
638         let error = flag_values::try_from_text_proto(
639             r#"
640 flag_value {
641     package: "COM.FIRST"
642     name: "first"
643     state: DISABLED
644     permission: READ_ONLY
645 }
646 "#,
647         )
648         .unwrap_err();
649         assert!(format!("{:?}", error).contains("bad flag value: bad package"));
650 
651         // bad input: bad name in flag value
652         let error = flag_values::try_from_text_proto(
653             r#"
654 flag_value {
655     package: "com.first"
656     name: "FIRST"
657     state: DISABLED
658     permission: READ_ONLY
659 }
660 "#,
661         )
662         .unwrap_err();
663         assert!(format!("{:?}", error).contains("bad flag value: bad name"));
664 
665         // bad input: missing state in flag value
666         let error = flag_values::try_from_text_proto(
667             r#"
668 flag_value {
669     package: "com.first"
670     name: "first"
671     permission: READ_ONLY
672 }
673 "#,
674         )
675         .unwrap_err();
676         assert_eq!(format!("{:?}", error), "bad flag value: missing state");
677 
678         // bad input: missing permission in flag value
679         let error = flag_values::try_from_text_proto(
680             r#"
681 flag_value {
682     package: "com.first"
683     name: "first"
684     state: DISABLED
685 }
686 "#,
687         )
688         .unwrap_err();
689         assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
690     }
691 
try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags>692     fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
693         use protobuf::Message;
694 
695         let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
696         let mut binary_proto = Vec::new();
697         parsed_flags.write_to_vec(&mut binary_proto)?;
698         parsed_flags::try_from_binary_proto(&binary_proto)
699     }
700 
701     #[test]
test_parsed_flags_try_from_text_proto()702     fn test_parsed_flags_try_from_text_proto() {
703         // valid input
704         let text_proto = r#"
705 parsed_flag {
706     package: "com.first"
707     name: "first"
708     namespace: "first_ns"
709     description: "This is the description of the first flag."
710     bug: "SOME_BUG"
711     state: DISABLED
712     permission: READ_ONLY
713     trace {
714         source: "flags.declarations"
715         state: DISABLED
716         permission: READ_ONLY
717     }
718     container: "system"
719 }
720 parsed_flag {
721     package: "com.second"
722     name: "second"
723     namespace: "second_ns"
724     description: "This is the description of the second flag."
725     bug: "SOME_BUG"
726     state: ENABLED
727     permission: READ_ONLY
728     trace {
729         source: "flags.declarations"
730         state: DISABLED
731         permission: READ_ONLY
732     }
733     trace {
734         source: "flags.values"
735         state: ENABLED
736         permission: READ_ONLY
737     }
738     is_fixed_read_only: true
739     container: "system"
740 }
741 "#;
742         let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
743         assert_eq!(parsed_flags.parsed_flag.len(), 2);
744         let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
745         assert_eq!(second.package(), "com.second");
746         assert_eq!(second.name(), "second");
747         assert_eq!(second.namespace(), "second_ns");
748         assert_eq!(second.description(), "This is the description of the second flag.");
749         assert_eq!(second.bug, vec!["SOME_BUG"]);
750         assert_eq!(second.state(), ProtoFlagState::ENABLED);
751         assert_eq!(second.permission(), ProtoFlagPermission::READ_ONLY);
752         assert_eq!(2, second.trace.len());
753         assert_eq!(second.trace[0].source(), "flags.declarations");
754         assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
755         assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
756         assert_eq!(second.trace[1].source(), "flags.values");
757         assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
758         assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_ONLY);
759         assert!(second.is_fixed_read_only());
760 
761         // valid input: empty
762         let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
763         assert!(parsed_flags.parsed_flag.is_empty());
764 
765         // bad input: empty trace
766         let text_proto = r#"
767 parsed_flag {
768     package: "com.first"
769     name: "first"
770     namespace: "first_ns"
771     description: "This is the description of the first flag."
772     state: DISABLED
773     permission: READ_ONLY
774     container: "system"
775 }
776 "#;
777         let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
778         assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
779 
780         // bad input: missing namespace in parsed_flag
781         let text_proto = r#"
782 parsed_flag {
783     package: "com.first"
784     name: "first"
785     description: "This is the description of the first flag."
786     state: DISABLED
787     permission: READ_ONLY
788     trace {
789         source: "flags.declarations"
790         state: DISABLED
791         permission: READ_ONLY
792     }
793     container: "system"
794 }
795 "#;
796         let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
797         assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
798 
799         // bad input: parsed_flag not sorted by package
800         let text_proto = r#"
801 parsed_flag {
802     package: "bbb.bbb"
803     name: "first"
804     namespace: "first_ns"
805     description: "This is the description of the first flag."
806     bug: ""
807     state: DISABLED
808     permission: READ_ONLY
809     trace {
810         source: "flags.declarations"
811         state: DISABLED
812         permission: READ_ONLY
813     }
814     container: "system"
815 }
816 parsed_flag {
817     package: "aaa.aaa"
818     name: "second"
819     namespace: "second_ns"
820     description: "This is the description of the second flag."
821     bug: ""
822     state: ENABLED
823     permission: READ_WRITE
824     trace {
825         source: "flags.declarations"
826         state: DISABLED
827         permission: READ_ONLY
828     }
829     container: "system"
830 }
831 "#;
832         let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
833         assert_eq!(
834             format!("{:?}", error),
835             "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
836         );
837 
838         // bad input: parsed_flag not sorted by name
839         let text_proto = r#"
840 parsed_flag {
841     package: "com.foo"
842     name: "bbb"
843     namespace: "first_ns"
844     description: "This is the description of the first flag."
845     bug: ""
846     state: DISABLED
847     permission: READ_ONLY
848     trace {
849         source: "flags.declarations"
850         state: DISABLED
851         permission: READ_ONLY
852     }
853     container: "system"
854 }
855 parsed_flag {
856     package: "com.foo"
857     name: "aaa"
858     namespace: "second_ns"
859     description: "This is the description of the second flag."
860     bug: ""
861     state: ENABLED
862     permission: READ_WRITE
863     trace {
864         source: "flags.declarations"
865         state: DISABLED
866         permission: READ_ONLY
867     }
868     container: "system"
869 }
870 "#;
871         let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
872         assert_eq!(
873             format!("{:?}", error),
874             "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
875         );
876 
877         // bad input: duplicate flags
878         let text_proto = r#"
879 parsed_flag {
880     package: "com.foo"
881     name: "bar"
882     namespace: "first_ns"
883     description: "This is the description of the first flag."
884     bug: ""
885     state: DISABLED
886     permission: READ_ONLY
887     trace {
888         source: "flags.declarations"
889         state: DISABLED
890         permission: READ_ONLY
891     }
892     container: "system"
893 }
894 parsed_flag {
895     package: "com.foo"
896     name: "bar"
897     namespace: "second_ns"
898     description: "This is the description of the second flag."
899     bug: ""
900     state: ENABLED
901     permission: READ_WRITE
902     trace {
903         source: "flags.declarations"
904         state: DISABLED
905         permission: READ_ONLY
906     }
907     container: "system"
908 }
909 "#;
910         let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
911         assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
912     }
913 
914     #[test]
test_parsed_flag_path_to_declaration()915     fn test_parsed_flag_path_to_declaration() {
916         let text_proto = r#"
917 parsed_flag {
918     package: "com.foo"
919     name: "bar"
920     namespace: "first_ns"
921     description: "This is the description of the first flag."
922     bug: "b/12345678"
923     state: DISABLED
924     permission: READ_ONLY
925     trace {
926         source: "flags.declarations"
927         state: DISABLED
928         permission: READ_ONLY
929     }
930     trace {
931         source: "flags.values"
932         state: ENABLED
933         permission: READ_ONLY
934     }
935     container: "system"
936 }
937 "#;
938         let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
939         let parsed_flag = &parsed_flags.parsed_flag[0];
940         assert_eq!(crate::parsed_flag::path_to_declaration(parsed_flag), "flags.declarations");
941     }
942 
943     #[test]
test_parsed_flags_merge()944     fn test_parsed_flags_merge() {
945         let text_proto = r#"
946 parsed_flag {
947     package: "com.first"
948     name: "first"
949     namespace: "first_ns"
950     description: "This is the description of the first flag."
951     bug: "a"
952     state: DISABLED
953     permission: READ_ONLY
954     trace {
955         source: "flags.declarations"
956         state: DISABLED
957         permission: READ_ONLY
958     }
959     container: "system"
960 }
961 parsed_flag {
962     package: "com.second"
963     name: "second"
964     namespace: "second_ns"
965     description: "This is the description of the second flag."
966     bug: "b"
967     state: ENABLED
968     permission: READ_WRITE
969     trace {
970         source: "flags.declarations"
971         state: DISABLED
972         permission: READ_ONLY
973     }
974     container: "system"
975 }
976 "#;
977         let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
978 
979         let text_proto = r#"
980 parsed_flag {
981     package: "com.first"
982     name: "first"
983     namespace: "first_ns"
984     description: "This is the description of the first flag."
985     bug: "a"
986     state: DISABLED
987     permission: READ_ONLY
988     trace {
989         source: "flags.declarations"
990         state: DISABLED
991         permission: READ_ONLY
992     }
993     container: "system"
994 }
995 "#;
996         let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
997 
998         let text_proto = r#"
999 parsed_flag {
1000     package: "com.second"
1001     name: "second"
1002     namespace: "second_ns"
1003     bug: "b"
1004     description: "This is the description of the second flag."
1005     state: ENABLED
1006     permission: READ_WRITE
1007     trace {
1008         source: "flags.declarations"
1009         state: DISABLED
1010         permission: READ_ONLY
1011     }
1012     container: "system"
1013 }
1014 "#;
1015         let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
1016 
1017         let text_proto = r#"
1018 parsed_flag {
1019     package: "com.second"
1020     name: "second"
1021     namespace: "second_ns"
1022     bug: "b"
1023     description: "This is the description of the second flag."
1024     state: ENABLED
1025     permission: READ_WRITE
1026     trace {
1027         source: "duplicate/flags.declarations"
1028         state: DISABLED
1029         permission: READ_ONLY
1030     }
1031 }
1032 "#;
1033         let second_duplicate = try_from_binary_proto_from_text_proto(text_proto).unwrap();
1034 
1035         // bad cases
1036 
1037         // two of the same flag with dedup disabled
1038         let error = parsed_flags::merge(vec![first.clone(), first.clone()], false).unwrap_err();
1039         assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.first.first (defined in flags.declarations and flags.declarations)");
1040 
1041         // two conflicting flags with dedup disabled
1042         let error =
1043             parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], false).unwrap_err();
1044         assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
1045 
1046         // two conflicting flags with dedup enabled
1047         let error =
1048             parsed_flags::merge(vec![second.clone(), second_duplicate.clone()], true).unwrap_err();
1049         assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.second.second (defined in flags.declarations and duplicate/flags.declarations)");
1050 
1051         // valid cases
1052         assert!(parsed_flags::merge(vec![], false).unwrap().parsed_flag.is_empty());
1053         assert!(parsed_flags::merge(vec![], true).unwrap().parsed_flag.is_empty());
1054         assert_eq!(first, parsed_flags::merge(vec![first.clone()], false).unwrap());
1055         assert_eq!(first, parsed_flags::merge(vec![first.clone()], true).unwrap());
1056         assert_eq!(
1057             expected,
1058             parsed_flags::merge(vec![first.clone(), second.clone()], false).unwrap()
1059         );
1060         assert_eq!(
1061             expected,
1062             parsed_flags::merge(vec![first.clone(), second.clone()], true).unwrap()
1063         );
1064         assert_eq!(
1065             expected,
1066             parsed_flags::merge(vec![second.clone(), first.clone()], false).unwrap()
1067         );
1068         assert_eq!(
1069             expected,
1070             parsed_flags::merge(vec![second.clone(), first.clone()], true).unwrap()
1071         );
1072 
1073         // two identical flags with dedup enabled
1074         assert_eq!(first, parsed_flags::merge(vec![first.clone(), first.clone()], true).unwrap());
1075     }
1076 }
1077