blob: 4ddada79941589e39fed917e6264d19f015698b7 [file] [log] [blame]
Mårten Kongstadfe753f52023-04-26 09:13:03 +02001/*
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// When building with the Android tool-chain
18//
19// - an external crate `aconfig_protos` will be generated
20// - the feature "cargo" will be disabled
21//
22// When building with cargo
23//
24// - a local sub-module will be generated in OUT_DIR and included in this file
25// - the feature "cargo" will be enabled
26//
27// This module hides these differences from the rest of aconfig.
28
Mårten Kongstadbb520722023-04-26 13:16:41 +020029// ---- When building with the Android tool-chain ----
Mårten Kongstadfe753f52023-04-26 09:13:03 +020030#[cfg(not(feature = "cargo"))]
Mårten Kongstadf9422522023-06-14 08:38:46 +020031mod auto_generated {
32 pub use aconfig_protos::aconfig::Flag_declaration as ProtoFlagDeclaration;
33 pub use aconfig_protos::aconfig::Flag_declarations as ProtoFlagDeclarations;
34 pub use aconfig_protos::aconfig::Flag_permission as ProtoFlagPermission;
35 pub use aconfig_protos::aconfig::Flag_state as ProtoFlagState;
36 pub use aconfig_protos::aconfig::Flag_value as ProtoFlagValue;
37 pub use aconfig_protos::aconfig::Flag_values as ProtoFlagValues;
38 pub use aconfig_protos::aconfig::Parsed_flag as ProtoParsedFlag;
39 pub use aconfig_protos::aconfig::Parsed_flags as ProtoParsedFlags;
40 pub use aconfig_protos::aconfig::Tracepoint as ProtoTracepoint;
41}
Mårten Kongstada1029092023-05-08 11:51:59 +020042
Mårten Kongstadbb520722023-04-26 13:16:41 +020043// ---- When building with cargo ----
Mårten Kongstadfe753f52023-04-26 09:13:03 +020044#[cfg(feature = "cargo")]
Mårten Kongstadf9422522023-06-14 08:38:46 +020045mod auto_generated {
46 // include! statements should be avoided (because they import file contents verbatim), but
47 // because this is only used during local development, and only if using cargo instead of the
48 // Android tool-chain, we allow it
49 include!(concat!(env!("OUT_DIR"), "/aconfig_proto/mod.rs"));
50 pub use aconfig::Flag_declaration as ProtoFlagDeclaration;
51 pub use aconfig::Flag_declarations as ProtoFlagDeclarations;
52 pub use aconfig::Flag_permission as ProtoFlagPermission;
53 pub use aconfig::Flag_state as ProtoFlagState;
54 pub use aconfig::Flag_value as ProtoFlagValue;
55 pub use aconfig::Flag_values as ProtoFlagValues;
56 pub use aconfig::Parsed_flag as ProtoParsedFlag;
57 pub use aconfig::Parsed_flags as ProtoParsedFlags;
58 pub use aconfig::Tracepoint as ProtoTracepoint;
59}
Mårten Kongstada1029092023-05-08 11:51:59 +020060
Mårten Kongstadbb520722023-04-26 13:16:41 +020061// ---- Common for both the Android tool-chain and cargo ----
Mårten Kongstadf9422522023-06-14 08:38:46 +020062pub use auto_generated::*;
63
Mårten Kongstadbb520722023-04-26 13:16:41 +020064use anyhow::Result;
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020065use paste::paste;
Mårten Kongstadbb520722023-04-26 13:16:41 +020066
Mårten Kongstad403658f2023-06-14 09:51:56 +020067fn try_from_text_proto<T>(s: &str) -> Result<T>
Mårten Kongstadbb520722023-04-26 13:16:41 +020068where
69 T: protobuf::MessageFull,
70{
Mårten Kongstadbb520722023-04-26 13:16:41 +020071 protobuf::text_format::parse_from_str(s).map_err(|e| e.into())
72}
Mårten Kongstad403658f2023-06-14 09:51:56 +020073
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020074macro_rules! ensure_required_fields {
75 ($type:expr, $struct:expr, $($field:expr),+) => {
76 $(
77 paste! {
78 ensure!($struct.[<has_ $field>](), "bad {}: missing {}", $type, $field);
79 }
80 )+
81 };
82}
83
Mårten Kongstad403658f2023-06-14 09:51:56 +020084pub mod flag_declaration {
85 use super::*;
86 use crate::codegen;
87 use anyhow::ensure;
88
89 pub fn verify_fields(pdf: &ProtoFlagDeclaration) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020090 ensure_required_fields!("flag declaration", pdf, "name", "namespace", "description");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +020091
Mårten Kongstad403658f2023-06-14 09:51:56 +020092 ensure!(codegen::is_valid_name_ident(pdf.name()), "bad flag declaration: bad name");
93 ensure!(codegen::is_valid_name_ident(pdf.namespace()), "bad flag declaration: bad name");
94 ensure!(!pdf.description().is_empty(), "bad flag declaration: empty description");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +020095
Mårten Kongstad1b8636b2023-06-22 10:12:24 +020096 // ProtoFlagDeclaration.bug: Vec<String>: may be empty, no checks needed
97
Mårten Kongstad403658f2023-06-14 09:51:56 +020098 Ok(())
99 }
100}
101
102pub mod flag_declarations {
103 use super::*;
104 use crate::codegen;
105 use anyhow::ensure;
106
107 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagDeclarations> {
108 let pdf: ProtoFlagDeclarations = super::try_from_text_proto(s)?;
109 verify_fields(&pdf)?;
110 Ok(pdf)
111 }
112
113 pub fn verify_fields(pdf: &ProtoFlagDeclarations) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200114 ensure_required_fields!("flag declarations", pdf, "package");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200115
Mårten Kongstad403658f2023-06-14 09:51:56 +0200116 ensure!(
117 codegen::is_valid_package_ident(pdf.package()),
118 "bad flag declarations: bad package"
119 );
120 for flag_declaration in pdf.flag.iter() {
121 super::flag_declaration::verify_fields(flag_declaration)?;
122 }
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200123
Mårten Kongstad403658f2023-06-14 09:51:56 +0200124 Ok(())
125 }
126}
127
128pub mod flag_value {
129 use super::*;
130 use crate::codegen;
131 use anyhow::ensure;
132
133 pub fn verify_fields(fv: &ProtoFlagValue) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200134 ensure_required_fields!("flag value", fv, "package", "name", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200135
Mårten Kongstad403658f2023-06-14 09:51:56 +0200136 ensure!(codegen::is_valid_package_ident(fv.package()), "bad flag value: bad package");
137 ensure!(codegen::is_valid_name_ident(fv.name()), "bad flag value: bad name");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200138
Mårten Kongstad403658f2023-06-14 09:51:56 +0200139 Ok(())
140 }
141}
142
143pub mod flag_values {
144 use super::*;
145
146 pub fn try_from_text_proto(s: &str) -> Result<ProtoFlagValues> {
147 let pfv: ProtoFlagValues = super::try_from_text_proto(s)?;
148 verify_fields(&pfv)?;
149 Ok(pfv)
150 }
151
152 pub fn verify_fields(pfv: &ProtoFlagValues) -> Result<()> {
153 for flag_value in pfv.flag_value.iter() {
154 super::flag_value::verify_fields(flag_value)?;
155 }
156 Ok(())
157 }
158}
159
160pub mod tracepoint {
161 use super::*;
162 use anyhow::ensure;
163
164 pub fn verify_fields(tp: &ProtoTracepoint) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200165 ensure_required_fields!("tracepoint", tp, "source", "state", "permission");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200166
Mårten Kongstad403658f2023-06-14 09:51:56 +0200167 ensure!(!tp.source().is_empty(), "bad tracepoint: empty source");
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200168
Mårten Kongstad403658f2023-06-14 09:51:56 +0200169 Ok(())
170 }
171}
172
173pub mod parsed_flag {
174 use super::*;
175 use crate::codegen;
176 use anyhow::ensure;
177
178 pub fn verify_fields(pf: &ProtoParsedFlag) -> Result<()> {
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200179 ensure_required_fields!(
180 "parsed flag",
181 pf,
182 "package",
183 "name",
184 "namespace",
185 "description",
186 "state",
187 "permission"
188 );
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200189
Mårten Kongstad403658f2023-06-14 09:51:56 +0200190 ensure!(codegen::is_valid_package_ident(pf.package()), "bad parsed flag: bad package");
191 ensure!(codegen::is_valid_name_ident(pf.name()), "bad parsed flag: bad name");
192 ensure!(codegen::is_valid_name_ident(pf.namespace()), "bad parsed flag: bad namespace");
193 ensure!(!pf.description().is_empty(), "bad parsed flag: empty description");
194 ensure!(!pf.trace.is_empty(), "bad parsed flag: empty trace");
195 for tp in pf.trace.iter() {
196 super::tracepoint::verify_fields(tp)?;
197 }
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200198
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200199 // ProtoParsedFlag.bug: Vec<String>: may be empty, no checks needed
200
Mårten Kongstad403658f2023-06-14 09:51:56 +0200201 Ok(())
202 }
Mårten Kongstad206a3822023-07-07 08:52:52 +0200203
204 pub fn path_to_declaration(pf: &ProtoParsedFlag) -> &str {
205 debug_assert!(!pf.trace.is_empty());
206 pf.trace[0].source()
207 }
Mårten Kongstad403658f2023-06-14 09:51:56 +0200208}
209
210pub mod parsed_flags {
211 use super::*;
212 use anyhow::bail;
213 use std::cmp::Ordering;
214
215 pub fn try_from_binary_proto(bytes: &[u8]) -> Result<ProtoParsedFlags> {
216 let message: ProtoParsedFlags = protobuf::Message::parse_from_bytes(bytes)?;
217 verify_fields(&message)?;
218 Ok(message)
219 }
220
221 pub fn verify_fields(pf: &ProtoParsedFlags) -> Result<()> {
Mårten Kongstad206a3822023-07-07 08:52:52 +0200222 use crate::protos::parsed_flag::path_to_declaration;
223
Mårten Kongstad403658f2023-06-14 09:51:56 +0200224 let mut previous: Option<&ProtoParsedFlag> = None;
225 for parsed_flag in pf.parsed_flag.iter() {
226 if let Some(prev) = previous {
227 let a = create_sorting_key(prev);
228 let b = create_sorting_key(parsed_flag);
229 match a.cmp(&b) {
230 Ordering::Less => {}
Mårten Kongstad206a3822023-07-07 08:52:52 +0200231 Ordering::Equal => bail!(
232 "bad parsed flags: duplicate flag {} (defined in {} and {})",
233 a,
234 path_to_declaration(prev),
235 path_to_declaration(parsed_flag)
236 ),
Mårten Kongstad403658f2023-06-14 09:51:56 +0200237 Ordering::Greater => {
238 bail!("bad parsed flags: not sorted: {} comes before {}", a, b)
239 }
240 }
241 }
242 super::parsed_flag::verify_fields(parsed_flag)?;
243 previous = Some(parsed_flag);
244 }
245 Ok(())
246 }
247
248 pub fn merge(parsed_flags: Vec<ProtoParsedFlags>) -> Result<ProtoParsedFlags> {
249 let mut merged = ProtoParsedFlags::new();
250 for mut pfs in parsed_flags.into_iter() {
251 merged.parsed_flag.append(&mut pfs.parsed_flag);
252 }
253 merged.parsed_flag.sort_by_cached_key(create_sorting_key);
254 verify_fields(&merged)?;
255 Ok(merged)
256 }
257
Zhi Dou92cf0ec2023-07-19 19:29:22 +0000258 pub fn sort_parsed_flags(pf: &mut ProtoParsedFlags) {
259 pf.parsed_flag.sort_by_key(create_sorting_key);
260 }
261
Mårten Kongstad403658f2023-06-14 09:51:56 +0200262 fn create_sorting_key(pf: &ProtoParsedFlag) -> String {
263 format!("{}.{}", pf.package(), pf.name())
264 }
265}
266
267#[cfg(test)]
268mod tests {
269 use super::*;
270
271 #[test]
272 fn test_flag_declarations_try_from_text_proto() {
273 // valid input
274 let flag_declarations = flag_declarations::try_from_text_proto(
275 r#"
276package: "com.foo.bar"
277flag {
278 name: "first"
279 namespace: "first_ns"
280 description: "This is the description of the first flag."
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200281 bug: "123"
282 bug: "abc"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200283}
284flag {
285 name: "second"
286 namespace: "second_ns"
287 description: "This is the description of the second flag."
288}
289"#,
290 )
291 .unwrap();
292 assert_eq!(flag_declarations.package(), "com.foo.bar");
293 let first = flag_declarations.flag.iter().find(|pf| pf.name() == "first").unwrap();
294 assert_eq!(first.name(), "first");
295 assert_eq!(first.namespace(), "first_ns");
296 assert_eq!(first.description(), "This is the description of the first flag.");
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200297 assert_eq!(first.bug.len(), 2);
298 assert_eq!(first.bug[0], "123");
299 assert_eq!(first.bug[1], "abc");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200300 let second = flag_declarations.flag.iter().find(|pf| pf.name() == "second").unwrap();
301 assert_eq!(second.name(), "second");
302 assert_eq!(second.namespace(), "second_ns");
303 assert_eq!(second.description(), "This is the description of the second flag.");
Mårten Kongstad1b8636b2023-06-22 10:12:24 +0200304 assert_eq!(second.bug.len(), 0);
Mårten Kongstad403658f2023-06-14 09:51:56 +0200305
306 // bad input: missing package in flag declarations
307 let error = flag_declarations::try_from_text_proto(
308 r#"
309flag {
310 name: "first"
311 namespace: "first_ns"
312 description: "This is the description of the first flag."
313}
314flag {
315 name: "second"
316 namespace: "second_ns"
317 description: "This is the description of the second flag."
318}
319"#,
320 )
321 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200322 assert_eq!(format!("{:?}", error), "bad flag declarations: missing package");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200323
324 // bad input: missing namespace in flag declaration
325 let error = flag_declarations::try_from_text_proto(
326 r#"
327package: "com.foo.bar"
328flag {
329 name: "first"
330 description: "This is the description of the first flag."
331}
332flag {
333 name: "second"
334 namespace: "second_ns"
335 description: "This is the description of the second flag."
336}
337"#,
338 )
339 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200340 assert_eq!(format!("{:?}", error), "bad flag declaration: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200341
342 // bad input: bad package name in flag declarations
343 let error = flag_declarations::try_from_text_proto(
344 r#"
345package: "_com.FOO__BAR"
346flag {
347 name: "first"
348 namespace: "first_ns"
349 description: "This is the description of the first flag."
350}
351flag {
352 name: "second"
353 namespace: "second_ns"
354 description: "This is the description of the second flag."
355}
356"#,
357 )
358 .unwrap_err();
359 assert!(format!("{:?}", error).contains("bad flag declarations: bad package"));
360
361 // bad input: bad name in flag declaration
362 let error = flag_declarations::try_from_text_proto(
363 r#"
364package: "com.foo.bar"
365flag {
366 name: "FIRST"
367 namespace: "first_ns"
368 description: "This is the description of the first flag."
369}
370flag {
371 name: "second"
372 namespace: "second_ns"
373 description: "This is the description of the second flag."
374}
375"#,
376 )
377 .unwrap_err();
378 assert!(format!("{:?}", error).contains("bad flag declaration: bad name"));
379 }
380
381 #[test]
382 fn test_flag_values_try_from_text_proto() {
383 // valid input
384 let flag_values = flag_values::try_from_text_proto(
385 r#"
386flag_value {
387 package: "com.first"
388 name: "first"
389 state: DISABLED
390 permission: READ_ONLY
391}
392flag_value {
393 package: "com.second"
394 name: "second"
395 state: ENABLED
396 permission: READ_WRITE
397}
398"#,
399 )
400 .unwrap();
401 let first = flag_values.flag_value.iter().find(|fv| fv.name() == "first").unwrap();
402 assert_eq!(first.package(), "com.first");
403 assert_eq!(first.name(), "first");
404 assert_eq!(first.state(), ProtoFlagState::DISABLED);
405 assert_eq!(first.permission(), ProtoFlagPermission::READ_ONLY);
406 let second = flag_values.flag_value.iter().find(|fv| fv.name() == "second").unwrap();
407 assert_eq!(second.package(), "com.second");
408 assert_eq!(second.name(), "second");
409 assert_eq!(second.state(), ProtoFlagState::ENABLED);
410 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
411
412 // bad input: bad package in flag value
413 let error = flag_values::try_from_text_proto(
414 r#"
415flag_value {
416 package: "COM.FIRST"
417 name: "first"
418 state: DISABLED
419 permission: READ_ONLY
420}
421"#,
422 )
423 .unwrap_err();
424 assert!(format!("{:?}", error).contains("bad flag value: bad package"));
425
426 // bad input: bad name in flag value
427 let error = flag_values::try_from_text_proto(
428 r#"
429flag_value {
430 package: "com.first"
431 name: "FIRST"
432 state: DISABLED
433 permission: READ_ONLY
434}
435"#,
436 )
437 .unwrap_err();
438 assert!(format!("{:?}", error).contains("bad flag value: bad name"));
439
440 // bad input: missing state in flag value
441 let error = flag_values::try_from_text_proto(
442 r#"
443flag_value {
444 package: "com.first"
445 name: "first"
446 permission: READ_ONLY
447}
448"#,
449 )
450 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200451 assert_eq!(format!("{:?}", error), "bad flag value: missing state");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200452
453 // bad input: missing permission in flag value
454 let error = flag_values::try_from_text_proto(
455 r#"
456flag_value {
457 package: "com.first"
458 name: "first"
459 state: DISABLED
460}
461"#,
462 )
463 .unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200464 assert_eq!(format!("{:?}", error), "bad flag value: missing permission");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200465 }
466
467 fn try_from_binary_proto_from_text_proto(text_proto: &str) -> Result<ProtoParsedFlags> {
468 use protobuf::Message;
469
470 let parsed_flags: ProtoParsedFlags = try_from_text_proto(text_proto)?;
471 let mut binary_proto = Vec::new();
472 parsed_flags.write_to_vec(&mut binary_proto)?;
473 parsed_flags::try_from_binary_proto(&binary_proto)
474 }
475
476 #[test]
477 fn test_parsed_flags_try_from_text_proto() {
478 // valid input
479 let text_proto = r#"
480parsed_flag {
481 package: "com.first"
482 name: "first"
483 namespace: "first_ns"
484 description: "This is the description of the first flag."
485 state: DISABLED
486 permission: READ_ONLY
487 trace {
488 source: "flags.declarations"
489 state: DISABLED
490 permission: READ_ONLY
491 }
492}
493parsed_flag {
494 package: "com.second"
495 name: "second"
496 namespace: "second_ns"
497 description: "This is the description of the second flag."
498 state: ENABLED
499 permission: READ_WRITE
500 trace {
501 source: "flags.declarations"
502 state: DISABLED
503 permission: READ_ONLY
504 }
505 trace {
506 source: "flags.values"
507 state: ENABLED
508 permission: READ_WRITE
509 }
510}
511"#;
512 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
513 assert_eq!(parsed_flags.parsed_flag.len(), 2);
514 let second = parsed_flags.parsed_flag.iter().find(|fv| fv.name() == "second").unwrap();
515 assert_eq!(second.package(), "com.second");
516 assert_eq!(second.name(), "second");
517 assert_eq!(second.namespace(), "second_ns");
518 assert_eq!(second.description(), "This is the description of the second flag.");
519 assert_eq!(second.state(), ProtoFlagState::ENABLED);
520 assert_eq!(second.permission(), ProtoFlagPermission::READ_WRITE);
521 assert_eq!(2, second.trace.len());
522 assert_eq!(second.trace[0].source(), "flags.declarations");
523 assert_eq!(second.trace[0].state(), ProtoFlagState::DISABLED);
524 assert_eq!(second.trace[0].permission(), ProtoFlagPermission::READ_ONLY);
525 assert_eq!(second.trace[1].source(), "flags.values");
526 assert_eq!(second.trace[1].state(), ProtoFlagState::ENABLED);
527 assert_eq!(second.trace[1].permission(), ProtoFlagPermission::READ_WRITE);
528
529 // valid input: empty
530 let parsed_flags = try_from_binary_proto_from_text_proto("").unwrap();
531 assert!(parsed_flags.parsed_flag.is_empty());
532
533 // bad input: empty trace
534 let text_proto = r#"
535parsed_flag {
536 package: "com.first"
537 name: "first"
538 namespace: "first_ns"
539 description: "This is the description of the first flag."
540 state: DISABLED
541 permission: READ_ONLY
542}
543"#;
544 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
545 assert_eq!(format!("{:?}", error), "bad parsed flag: empty trace");
546
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200547 // bad input: missing namespace in parsed_flag
Mårten Kongstad403658f2023-06-14 09:51:56 +0200548 let text_proto = r#"
549parsed_flag {
550 package: "com.first"
551 name: "first"
552 description: "This is the description of the first flag."
553 state: DISABLED
554 permission: READ_ONLY
555 trace {
556 source: "flags.declarations"
557 state: DISABLED
558 permission: READ_ONLY
559 }
560}
561"#;
562 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstada2e5ab82023-06-19 16:28:54 +0200563 assert_eq!(format!("{:?}", error), "bad parsed flag: missing namespace");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200564
565 // bad input: parsed_flag not sorted by package
566 let text_proto = r#"
567parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200568 package: "bbb.bbb"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200569 name: "first"
570 namespace: "first_ns"
571 description: "This is the description of the first flag."
572 state: DISABLED
573 permission: READ_ONLY
574 trace {
575 source: "flags.declarations"
576 state: DISABLED
577 permission: READ_ONLY
578 }
579}
580parsed_flag {
Mårten Kongstad19776d12023-06-29 10:38:02 +0200581 package: "aaa.aaa"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200582 name: "second"
583 namespace: "second_ns"
584 description: "This is the description of the second flag."
585 state: ENABLED
586 permission: READ_WRITE
587 trace {
588 source: "flags.declarations"
589 state: DISABLED
590 permission: READ_ONLY
591 }
592}
593"#;
594 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
595 assert_eq!(
596 format!("{:?}", error),
Mårten Kongstad19776d12023-06-29 10:38:02 +0200597 "bad parsed flags: not sorted: bbb.bbb.first comes before aaa.aaa.second"
Mårten Kongstad403658f2023-06-14 09:51:56 +0200598 );
599
600 // bad input: parsed_flag not sorted by name
601 let text_proto = r#"
602parsed_flag {
603 package: "com.foo"
604 name: "bbb"
605 namespace: "first_ns"
606 description: "This is the description of the first flag."
607 state: DISABLED
608 permission: READ_ONLY
609 trace {
610 source: "flags.declarations"
611 state: DISABLED
612 permission: READ_ONLY
613 }
614}
615parsed_flag {
616 package: "com.foo"
617 name: "aaa"
618 namespace: "second_ns"
619 description: "This is the description of the second flag."
620 state: ENABLED
621 permission: READ_WRITE
622 trace {
623 source: "flags.declarations"
624 state: DISABLED
625 permission: READ_ONLY
626 }
627}
628"#;
629 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
630 assert_eq!(
631 format!("{:?}", error),
632 "bad parsed flags: not sorted: com.foo.bbb comes before com.foo.aaa"
633 );
634
635 // bad input: duplicate flags
636 let text_proto = r#"
637parsed_flag {
638 package: "com.foo"
639 name: "bar"
640 namespace: "first_ns"
641 description: "This is the description of the first flag."
642 state: DISABLED
643 permission: READ_ONLY
644 trace {
645 source: "flags.declarations"
646 state: DISABLED
647 permission: READ_ONLY
648 }
649}
650parsed_flag {
651 package: "com.foo"
652 name: "bar"
653 namespace: "second_ns"
654 description: "This is the description of the second flag."
655 state: ENABLED
656 permission: READ_WRITE
657 trace {
658 source: "flags.declarations"
659 state: DISABLED
660 permission: READ_ONLY
661 }
662}
663"#;
664 let error = try_from_binary_proto_from_text_proto(text_proto).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200665 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.foo.bar (defined in flags.declarations and flags.declarations)");
666 }
667
668 #[test]
669 fn test_parsed_flag_path_to_declaration() {
670 let text_proto = r#"
671parsed_flag {
672 package: "com.foo"
673 name: "bar"
674 namespace: "first_ns"
675 description: "This is the description of the first flag."
676 state: DISABLED
677 permission: READ_ONLY
678 trace {
679 source: "flags.declarations"
680 state: DISABLED
681 permission: READ_ONLY
682 }
683 trace {
684 source: "flags.values"
685 state: ENABLED
686 permission: READ_ONLY
687 }
688}
689"#;
690 let parsed_flags = try_from_binary_proto_from_text_proto(text_proto).unwrap();
691 let parsed_flag = &parsed_flags.parsed_flag[0];
692 assert_eq!(
693 crate::protos::parsed_flag::path_to_declaration(parsed_flag),
694 "flags.declarations"
695 );
Mårten Kongstad403658f2023-06-14 09:51:56 +0200696 }
697
698 #[test]
699 fn test_parsed_flags_merge() {
700 let text_proto = r#"
701parsed_flag {
702 package: "com.first"
703 name: "first"
704 namespace: "first_ns"
705 description: "This is the description of the first flag."
706 state: DISABLED
707 permission: READ_ONLY
708 trace {
709 source: "flags.declarations"
710 state: DISABLED
711 permission: READ_ONLY
712 }
713}
714parsed_flag {
715 package: "com.second"
716 name: "second"
717 namespace: "second_ns"
718 description: "This is the description of the second flag."
719 state: ENABLED
720 permission: READ_WRITE
721 trace {
722 source: "flags.declarations"
723 state: DISABLED
724 permission: READ_ONLY
725 }
726}
727"#;
728 let expected = try_from_binary_proto_from_text_proto(text_proto).unwrap();
729
730 let text_proto = r#"
731parsed_flag {
732 package: "com.first"
733 name: "first"
734 namespace: "first_ns"
735 description: "This is the description of the first flag."
736 state: DISABLED
737 permission: READ_ONLY
738 trace {
739 source: "flags.declarations"
740 state: DISABLED
741 permission: READ_ONLY
742 }
743}
744"#;
745 let first = try_from_binary_proto_from_text_proto(text_proto).unwrap();
746
747 let text_proto = r#"
748parsed_flag {
749 package: "com.second"
750 name: "second"
751 namespace: "second_ns"
752 description: "This is the description of the second flag."
753 state: ENABLED
754 permission: READ_WRITE
755 trace {
756 source: "flags.declarations"
757 state: DISABLED
758 permission: READ_ONLY
759 }
760}
761"#;
762 let second = try_from_binary_proto_from_text_proto(text_proto).unwrap();
763
764 // bad cases
765 let error = parsed_flags::merge(vec![first.clone(), first.clone()]).unwrap_err();
Mårten Kongstad206a3822023-07-07 08:52:52 +0200766 assert_eq!(format!("{:?}", error), "bad parsed flags: duplicate flag com.first.first (defined in flags.declarations and flags.declarations)");
Mårten Kongstad403658f2023-06-14 09:51:56 +0200767
768 // valid cases
769 assert!(parsed_flags::merge(vec![]).unwrap().parsed_flag.is_empty());
770 assert_eq!(first, parsed_flags::merge(vec![first.clone()]).unwrap());
771 assert_eq!(expected, parsed_flags::merge(vec![first.clone(), second.clone()]).unwrap());
772 assert_eq!(expected, parsed_flags::merge(vec![second, first]).unwrap());
773 }
774}