1use std::collections::{BTreeMap, HashMap};
6
7use serde_json::{json, Value};
8
9use crate::{
10 error::{FMLError, Result},
11 frontend::DefaultBlock,
12 intermediate_representation::{FeatureDef, ObjectDef, PropDef, TypeRef},
13};
14
15pub struct DefaultsMerger<'object> {
16 objects: &'object BTreeMap<String, ObjectDef>,
17
18 supported_channels: Vec<String>,
19 channel: Option<String>,
20}
21
22impl<'object> DefaultsMerger<'object> {
23 pub fn new(
24 objects: &'object BTreeMap<String, ObjectDef>,
25 supported_channels: Vec<String>,
26 channel: Option<String>,
27 ) -> Self {
28 Self {
29 objects,
30 supported_channels,
31 channel,
32 }
33 }
34
35 #[cfg(test)]
36 pub fn new_with_channel(
37 objects: &'object BTreeMap<String, ObjectDef>,
38 supported_channels: Vec<String>,
39 channel: String,
40 ) -> Self {
41 Self::new(objects, supported_channels, Some(channel.to_string()))
42 }
43
44 fn collect_feature_defaults(&self, feature: &FeatureDef) -> serde_json::Value {
45 self.collect_props_defaults(&feature.props)
46 }
47
48 fn collect_object_defaults(&self, name: &str) -> serde_json::Value {
49 let obj = self
50 .objects
51 .get(name)
52 .unwrap_or_else(|| panic!("Object named {} is not defined", name));
53
54 self.collect_props_defaults(&obj.props)
55 }
56
57 fn collect_props_defaults(&self, props: &Vec<PropDef>) -> Value {
58 let mut res = serde_json::value::Map::new();
59 for p in props {
60 res.insert(p.name(), self.collect_prop_defaults(&p.typ, &p.default));
61 }
62 serde_json::Value::Object(res)
63 }
64
65 fn collect_prop_defaults(&self, typ: &TypeRef, v: &serde_json::Value) -> serde_json::Value {
66 match typ {
67 TypeRef::Object(name) => merge_two_defaults(&self.collect_object_defaults(name), v),
68 TypeRef::EnumMap(_, v_type) => self.collect_map_defaults(v_type, v),
69 TypeRef::StringMap(v_type) => self.collect_map_defaults(v_type, v),
70 _ => v.clone(),
71 }
72 }
73
74 fn collect_map_defaults(&self, v_type: &TypeRef, obj: &serde_json::Value) -> serde_json::Value {
75 let map = obj
76 .as_object()
77 .unwrap_or_else(|| panic!("Expected a JSON object as a default"));
78 let mut res = serde_json::value::Map::new();
79 for (k, v) in map {
80 let collected = self.collect_prop_defaults(v_type, v);
81 res.insert(k.clone(), collected);
82 }
83 serde_json::Value::Object(res)
84 }
85
86 pub fn merge_feature_defaults(
151 &self,
152 feature_def: &mut FeatureDef,
153 defaults: &Option<Vec<DefaultBlock>>,
154 ) -> Result<(), FMLError> {
155 let variable_defaults = self.collect_feature_defaults(feature_def);
156 let defaults_to_merge = self.channel_specific_defaults(defaults)?;
157 let merged = merge_two_defaults(&variable_defaults, &defaults_to_merge);
158
159 self.overwrite_defaults(feature_def, &merged);
160 Ok(())
161 }
162
163 pub(crate) fn overwrite_defaults(&self, feature_def: &mut FeatureDef, merged: &Value) {
174 let map = merged.as_object().expect("`merged` value not a map");
175
176 for p in &mut feature_def.props {
177 if let Some(v) = map.get(&p.name) {
178 p.default = v.clone();
179 }
180 }
181 }
182
183 fn channel_specific_defaults(&self, defaults: &Option<Vec<DefaultBlock>>) -> Result<Value> {
184 let supported_channels = self.supported_channels.as_slice();
185 let channel = &self.channel;
186 if let Some(channel) = channel {
187 if !supported_channels.iter().any(|c| c == channel) {
188 return Err(FMLError::InvalidChannelError(
189 channel.into(),
190 supported_channels.into(),
191 ));
192 }
193 }
194 let empty_object = json!({});
195 if let Some(defaults) = defaults {
196 let no_channel = "NO CHANNEL SPECIFIED".to_string();
198 let merged_defaults =
199 collect_channel_defaults(defaults, supported_channels, &no_channel)?;
200 let channel = self.channel.as_ref().unwrap_or(&no_channel);
201 let merged = merged_defaults[channel].clone();
202 Ok(merged)
203 } else {
204 Ok(empty_object)
205 }
206 }
207
208 pub(crate) fn merge_feature_config(&self, feature_def: &FeatureDef, value: &Value) -> Value {
211 let defaults = self.collect_feature_defaults(feature_def);
212 merge_two_defaults(&defaults, value)
213 }
214}
215
216fn merge_two_defaults(
228 old_default: &serde_json::Value,
229 new_default: &serde_json::Value,
230) -> serde_json::Value {
231 use serde_json::Value::Object;
232 match (old_default.clone(), new_default.clone()) {
233 (Object(old), Object(new)) => {
234 let mut merged = serde_json::Map::new();
235 for (key, val) in old {
236 merged.insert(key, val);
237 }
238 for (key, val) in new {
239 if let Some(old_val) = merged.get(&key).cloned() {
240 merged.insert(key, merge_two_defaults(&old_val, &val));
241 } else {
242 merged.insert(key, val);
243 }
244 }
245 Object(merged)
246 }
247 (_, new) => new,
248 }
249}
250
251fn collect_channel_defaults(
270 defaults: &[DefaultBlock],
271 channels: &[String],
272 no_channel: &str,
273) -> Result<HashMap<String, serde_json::Value>> {
274 let mut channel_map = channels
276 .iter()
277 .map(|channel_name| (channel_name.clone(), json!({})))
278 .collect::<HashMap<_, _>>();
279 channel_map.insert(no_channel.to_string(), json!({}));
280 for default in defaults {
281 if let Some(channels_for_default) = &default.merge_channels() {
282 for channel in channels_for_default {
283 if let Some(old_default) = channel_map.get(channel).cloned() {
284 if default.targeting.is_none() {
285 let merged = merge_two_defaults(&old_default, &default.value);
287 channel_map.insert(channel.clone(), merged);
288 }
289 } else {
290 return Err(FMLError::InvalidChannelError(
291 channel.into(),
292 channels.into(),
293 ));
294 }
295 }
296 } else {
298 channel_map = channel_map
299 .into_iter()
300 .map(|(channel, old_default)| {
301 (channel, merge_two_defaults(&old_default, &default.value))
302 })
303 .collect();
304 }
305 }
306 Ok(channel_map)
307}
308
309#[cfg(test)]
310mod unit_tests {
311 use crate::intermediate_representation::PropDef;
312
313 use super::*;
314 use serde_json::json;
315
316 #[test]
317 fn test_merge_two_defaults_both_objects_no_intersection() -> Result<()> {
318 let old_default = json!({
319 "button-color": "blue",
320 "dialog_option": "greetings",
321 "is_enabled": false,
322 "num_items": 5
323 });
324 let new_default = json!({
325 "new_homepage": true,
326 "item_order": ["first", "second", "third"],
327 });
328 let merged = merge_two_defaults(&old_default, &new_default);
329 assert_eq!(
330 json!({
331 "button-color": "blue",
332 "dialog_option": "greetings",
333 "is_enabled": false,
334 "num_items": 5,
335 "new_homepage": true,
336 "item_order": ["first", "second", "third"],
337 }),
338 merged
339 );
340 Ok(())
341 }
342
343 #[test]
344 fn test_merge_two_defaults_intersecting_different_types() -> Result<()> {
345 let old_default = json!({
347 "button-color": "blue",
348 "dialog_option": "greetings",
349 "is_enabled": {
350 "value": false
351 },
352 "num_items": 5
353 });
354 let new_default = json!({
355 "new_homepage": true,
356 "is_enabled": true,
357 "item_order": ["first", "second", "third"],
358 });
359 let merged = merge_two_defaults(&old_default, &new_default);
360 assert_eq!(
361 json!({
362 "button-color": "blue",
363 "dialog_option": "greetings",
364 "is_enabled": true,
365 "num_items": 5,
366 "new_homepage": true,
367 "item_order": ["first", "second", "third"],
368 }),
369 merged
370 );
371 Ok(())
372 }
373
374 #[test]
375 fn test_merge_two_defaults_non_map_intersection() -> Result<()> {
376 let old_default = json!({
378 "button-color": "blue",
379 "dialog_option": "greetings",
380 "is_enabled": false,
381 "num_items": 5
382 });
383 let new_default = json!({
384 "button-color": "green",
385 "new_homepage": true,
386 "is_enabled": true,
387 "num_items": 10,
388 "item_order": ["first", "second", "third"],
389 });
390 let merged = merge_two_defaults(&old_default, &new_default);
391 assert_eq!(
392 json!({
393 "button-color": "green",
394 "dialog_option": "greetings",
395 "is_enabled": true,
396 "num_items": 10,
397 "new_homepage": true,
398 "item_order": ["first", "second", "third"],
399 }),
400 merged
401 );
402 Ok(())
403 }
404
405 #[test]
406 fn test_merge_two_defaults_map_intersection_recursive_merge() -> Result<()> {
407 let old_default = json!({
409 "button-color": "blue",
410 "dialog_item": {
411 "title": "hello",
412 "message": "bobo",
413 "priority": 10,
414 },
415 "is_enabled": false,
416 "num_items": 5
417 });
418 let new_default = json!({
419 "button-color": "green",
420 "new_homepage": true,
421 "is_enabled": true,
422 "dialog_item": {
423 "message": "fofo",
424 "priority": 11,
425 "subtitle": "hey there"
426 },
427 "num_items": 10,
428 "item_order": ["first", "second", "third"],
429 });
430 let merged = merge_two_defaults(&old_default, &new_default);
431 assert_eq!(
432 json!({
433 "button-color": "green",
434 "dialog_item": {
435 "title": "hello",
436 "message": "fofo",
437 "priority": 11,
438 "subtitle": "hey there"
439 },
440 "is_enabled": true,
441 "num_items": 10,
442 "new_homepage": true,
443 "item_order": ["first", "second", "third"],
444 }),
445 merged
446 );
447 Ok(())
448 }
449
450 #[test]
451 fn test_merge_two_defaults_highlevel_non_maps() -> Result<()> {
452 let old_default = json!(["array", "json"]);
453 let new_default = json!(["another", "array"]);
454 let merged = merge_two_defaults(&old_default, &new_default);
455 assert_eq!(json!(["another", "array"]), merged);
456 Ok(())
457 }
458
459 #[test]
460 fn test_channel_defaults_channels_no_merging() -> Result<()> {
461 let input: Vec<DefaultBlock> = serde_json::from_value(json!([
462 {
463 "channel": "release",
464 "value": {
465 "button-color": "green"
466 }
467 },
468 {
469 "channel": "nightly",
470 "value": {
471 "button-color": "dark-green"
472 }
473 },
474 {
475 "channel": "beta",
476 "value": {
477 "button-color": "light-green"
478 }
479 }
480 ]))?;
481 let res = collect_channel_defaults(
482 &input,
483 &[
484 "release".to_string(),
485 "nightly".to_string(),
486 "beta".to_string(),
487 ],
488 "",
489 )?;
490 assert_eq!(
491 vec![
492 (
493 "release".to_string(),
494 json!({
495 "button-color": "green"
496 })
497 ),
498 (
499 "nightly".to_string(),
500 json!({
501 "button-color": "dark-green"
502 })
503 ),
504 (
505 "beta".to_string(),
506 json!({
507 "button-color": "light-green"
508 })
509 ),
510 ("".to_string(), json!({}),),
511 ]
512 .into_iter()
513 .collect::<HashMap<_, _>>(),
514 res
515 );
516 Ok(())
517 }
518
519 #[test]
520 fn test_channel_defaults_channels_merging_same_channel() -> Result<()> {
521 let input: Vec<DefaultBlock> = serde_json::from_value(json!([
522 {
523 "channel": "release",
524 "value": {
525 "button-color": "green"
526 }
527 },
528 {
529 "channel": "nightly",
530 "value": {
531 "button-color": "dark-green",
532 "title": "heya"
533 }
534 },
535 {
536 "channel": "beta",
537 "value": {
538 "button-color": "light-green"
539 }
540 },
541 {
542 "channel": "nightly",
543 "value": {
544 "button-color": "dark-red",
545 "subtitle": "hello",
546 }
547 },
548 {
549 "channel": "beta",
550 "value": {
551 "title": "hello there"
552 }
553 }
554 ]))?;
555 let res = collect_channel_defaults(
556 &input,
557 &[
558 "release".to_string(),
559 "nightly".to_string(),
560 "beta".to_string(),
561 ],
562 "",
563 )?;
564 assert_eq!(
565 vec![
566 (
567 "release".to_string(),
568 json!({
569 "button-color": "green"
570 })
571 ),
572 (
573 "nightly".to_string(),
574 json!({
575 "button-color": "dark-red",
576 "title": "heya",
577 "subtitle": "hello"
578 })
579 ),
580 (
581 "beta".to_string(),
582 json!({
583 "button-color": "light-green",
584 "title": "hello there"
585 })
586 ),
587 ("".to_string(), json!({}),),
588 ]
589 .into_iter()
590 .collect::<HashMap<_, _>>(),
591 res
592 );
593 Ok(())
594 }
595
596 #[test]
597 fn test_channel_defaults_no_channel_applies_to_all() -> Result<()> {
598 let input: Vec<DefaultBlock> = serde_json::from_value(json!([
599 {
600 "channel": "release",
601 "value": {
602 "button-color": "green"
603 }
604 },
605 {
606 "channel": "nightly",
607 "value": {
608 "button-color": "dark-green"
609 }
610 },
611 {
612 "channel": "beta",
613 "value": {
614 "button-color": "light-green"
615 }
616 },
617 {
618 "value": {
619 "title": "heya"
620 }
621 }
622 ]))?;
623 let res = collect_channel_defaults(
624 &input,
625 &[
626 "release".to_string(),
627 "nightly".to_string(),
628 "beta".to_string(),
629 ],
630 "",
631 )?;
632 assert_eq!(
633 vec![
634 (
635 "release".to_string(),
636 json!({
637 "button-color": "green",
638 "title": "heya"
639 })
640 ),
641 (
642 "nightly".to_string(),
643 json!({
644 "button-color": "dark-green",
645 "title": "heya"
646 })
647 ),
648 (
649 "beta".to_string(),
650 json!({
651 "button-color": "light-green",
652 "title": "heya"
653 })
654 ),
655 (
656 "".to_string(),
657 json!({
658 "title": "heya",
659 }),
660 )
661 ]
662 .into_iter()
663 .collect::<HashMap<_, _>>(),
664 res
665 );
666 Ok(())
667 }
668
669 #[test]
670 fn test_channel_defaults_no_channel_overwrites_all() -> Result<()> {
671 let input: Vec<DefaultBlock> = serde_json::from_value(json!([
672 {
673 "channel": "release",
674 "value": {
675 "button-color": "green"
676 }
677 },
678 {
679 "channel": "nightly",
680 "value": {
681 "button-color": "dark-green"
682 }
683 },
684 {
685 "channel": "beta",
686 "value": {
687 "button-color": "light-green"
688 }
689 },
690 {
691 "value": {
692 "button-color": "red"
693 }
694 }
695 ]))?;
696 let res = collect_channel_defaults(
697 &input,
698 &[
699 "release".to_string(),
700 "nightly".to_string(),
701 "beta".to_string(),
702 ],
703 "",
704 )?;
705 assert_eq!(
706 vec![
707 (
708 "release".to_string(),
709 json!({
710 "button-color": "red"
711 })
712 ),
713 (
714 "nightly".to_string(),
715 json!({
716 "button-color": "red"
717 })
718 ),
719 (
720 "beta".to_string(),
721 json!({
722 "button-color": "red"
723 })
724 ),
725 (
726 "".to_string(),
727 json!({
728 "button-color": "red",
729 }),
730 )
731 ]
732 .into_iter()
733 .collect::<HashMap<_, _>>(),
734 res
735 );
736 Ok(())
737 }
738
739 #[test]
740 fn test_channel_defaults_no_channel_gets_overwritten_if_followed_by_channel() -> Result<()> {
741 let input: Vec<DefaultBlock> = serde_json::from_value(json!([
742 {
743 "channel": "release",
744 "value": {
745 "button-color": "green"
746 }
747 },
748 {
749 "channel": "nightly",
750 "value": {
751 "button-color": "dark-green"
752 }
753 },
754 {
755 "channel": "beta",
756 "value": {
757 "button-color": "light-green"
758 }
759 },
760 {
761 "value": {
762 "button-color": "red"
763 }
764 },
765 {
766 "channel": "nightly",
767 "value": {
768 "button-color": "dark-red"
769 }
770 }
771 ]))?;
772 let res = collect_channel_defaults(
773 &input,
774 &[
775 "release".to_string(),
776 "nightly".to_string(),
777 "beta".to_string(),
778 ],
779 "",
780 )?;
781 assert_eq!(
782 vec![
783 (
784 "release".to_string(),
785 json!({
786 "button-color": "red"
787 })
788 ),
789 (
790 "nightly".to_string(),
791 json!({
792 "button-color": "dark-red"
793 })
794 ),
795 (
796 "beta".to_string(),
797 json!({
798 "button-color": "red"
799 })
800 ),
801 (
802 "".to_string(),
803 json!({
804 "button-color": "red",
805 }),
806 )
807 ]
808 .into_iter()
809 .collect::<HashMap<_, _>>(),
810 res
811 );
812 Ok(())
813 }
814
815 #[test]
816 fn test_channel_defaults_channels_multiple() -> Result<()> {
817 let input: Vec<DefaultBlock> = serde_json::from_value(json!([
818 {
819 "channels": ["release", "beta"],
820 "value": {
821 "button-color": "green"
822 }
823 },
824 ]))?;
825 let res =
826 collect_channel_defaults(&input, &["release".to_string(), "beta".to_string()], "")?;
827 assert_eq!(
828 vec![
829 (
830 "release".to_string(),
831 json!({
832 "button-color": "green"
833 })
834 ),
835 (
836 "beta".to_string(),
837 json!({
838 "button-color": "green"
839 })
840 ),
841 ("".to_string(), json!({}),)
842 ]
843 .into_iter()
844 .collect::<HashMap<_, _>>(),
845 res
846 );
847 Ok(())
848 }
849
850 #[test]
851 fn test_channel_defaults_channel_multiple_merge_channels_multiple() -> Result<()> {
852 let input: Vec<DefaultBlock> = serde_json::from_value(json!([
853 {
854 "channel": "nightly, debug",
855 "channels": ["release", "beta"],
856 "value": {
857 "button-color": "green"
858 }
859 },
860 ]))?;
861 let res = collect_channel_defaults(
862 &input,
863 &[
864 "release".to_string(),
865 "beta".to_string(),
866 "nightly".to_string(),
867 "debug".to_string(),
868 ],
869 "",
870 )?;
871 assert_eq!(
872 vec![
873 (
874 "release".to_string(),
875 json!({
876 "button-color": "green"
877 })
878 ),
879 (
880 "beta".to_string(),
881 json!({
882 "button-color": "green"
883 })
884 ),
885 (
886 "nightly".to_string(),
887 json!({
888 "button-color": "green"
889 })
890 ),
891 (
892 "debug".to_string(),
893 json!({
894 "button-color": "green"
895 })
896 ),
897 ("".to_string(), json!({}),)
898 ]
899 .into_iter()
900 .collect::<HashMap<_, _>>(),
901 res
902 );
903 Ok(())
904 }
905
906 #[test]
907 fn test_channel_defaults_fail_if_invalid_channel_supplied() -> Result<()> {
908 let input: Vec<DefaultBlock> = serde_json::from_value(json!([
909 {
910 "channel": "release",
911 "value": {
912 "button-color": "green"
913 }
914 },
915 {
916 "channel": "nightly",
917 "value": {
918 "button-color": "dark-green"
919 }
920 },
921 {
922 "channel": "beta",
923 "value": {
924 "button-color": "light-green"
925 }
926 },
927 {
928 "channel": "bobo",
929 "value": {
930 "button-color": "no color"
931 }
932 }
933 ]))?;
934 let res = collect_channel_defaults(
935 &input,
936 &[
937 "release".to_string(),
938 "nightly".to_string(),
939 "beta".to_string(),
940 ],
941 "",
942 )
943 .expect_err("Should return error");
944 if let FMLError::InvalidChannelError(channel, _supported) = res {
945 assert!(channel.contains("bobo"));
946 } else {
947 panic!(
948 "Should have returned a InvalidChannelError, returned {:?}",
949 res
950 )
951 }
952 Ok(())
953 }
954
955 #[test]
956 fn test_channel_defaults_empty_default_created_if_none_supplied_in_feature() -> Result<()> {
957 let input: Vec<DefaultBlock> = serde_json::from_value(json!([
958 {
959 "channel": "release",
960 "value": {
961 "button-color": "green"
962 }
963 },
964 {
965 "channel": "nightly",
966 "value": {
967 "button-color": "dark-green"
968 }
969 },
970 ]))?;
973 let res = collect_channel_defaults(
974 &input,
975 &[
976 "release".to_string(),
977 "nightly".to_string(),
978 "beta".to_string(),
979 ],
980 "",
981 )?;
982 assert_eq!(
983 vec![
984 (
985 "release".to_string(),
986 json!({
987 "button-color": "green"
988 })
989 ),
990 (
991 "nightly".to_string(),
992 json!({
993 "button-color": "dark-green"
994 })
995 ),
996 ("beta".to_string(), json!({})),
997 ("".to_string(), json!({}),)
998 ]
999 .into_iter()
1000 .collect::<HashMap<_, _>>(),
1001 res
1002 );
1003 Ok(())
1004 }
1005
1006 #[test]
1007 fn test_merge_feature_default_unsupported_channel() -> Result<()> {
1008 let mut feature_def: FeatureDef = Default::default();
1009 let objects = Default::default();
1010 let merger = DefaultsMerger::new_with_channel(
1011 &objects,
1012 vec!["release".into(), "beta".into()],
1013 "nightly".into(),
1014 );
1015 let err = merger
1016 .merge_feature_defaults(&mut feature_def, &None)
1017 .expect_err("Should return an error");
1018 if let FMLError::InvalidChannelError(channel, _supported) = err {
1019 assert!(channel.contains("nightly"));
1020 } else {
1021 panic!(
1022 "Should have returned an InvalidChannelError, returned: {:?}",
1023 err
1024 );
1025 }
1026 Ok(())
1027 }
1028
1029 #[test]
1030 fn test_merge_feature_default_overwrite_field_default_based_on_channel() -> Result<()> {
1031 let mut feature_def = FeatureDef {
1032 props: vec![PropDef::new(
1033 "button-color",
1034 &TypeRef::String,
1035 &json!("blue"),
1036 )],
1037 ..Default::default()
1038 };
1039 let default_blocks = serde_json::from_value(json!([
1040 {
1041 "channel": "nightly",
1042 "value": {
1043 "button-color": "dark-green"
1044 }
1045 },
1046 {
1047 "channel": "release",
1048 "value": {
1049 "button-color": "green"
1050 }
1051 },
1052 {
1053 "channel": "beta",
1054 "value": {
1055 "button-color": "light-green"
1056 }
1057 },
1058 ]))?;
1059 let objects = Default::default();
1060 let merger = DefaultsMerger::new_with_channel(
1061 &objects,
1062 vec!["release".into(), "beta".into(), "nightly".into()],
1063 "nightly".into(),
1064 );
1065 merger.merge_feature_defaults(&mut feature_def, &default_blocks)?;
1066 assert_eq!(
1067 feature_def.props,
1068 vec![PropDef::new(
1069 "button-color",
1070 &TypeRef::String,
1071 &json!("dark-green"),
1072 )]
1073 );
1074 Ok(())
1075 }
1076
1077 #[test]
1078 fn test_merge_feature_default_field_default_not_overwritten_if_no_feature_default_for_channel(
1079 ) -> Result<()> {
1080 let mut feature_def = FeatureDef {
1081 props: vec![PropDef::new(
1082 "button-color",
1083 &TypeRef::String,
1084 &json!("blue"),
1085 )],
1086 ..Default::default()
1087 };
1088 let default_blocks = serde_json::from_value(json!([{
1089 "channel": "release",
1090 "value": {
1091 "button-color": "green"
1092 }
1093 },
1094 {
1095 "channel": "beta",
1096 "value": {
1097 "button-color": "light-green"
1098 }
1099 }]))?;
1100 let objects = Default::default();
1101 let merger = DefaultsMerger::new_with_channel(
1102 &objects,
1103 vec!["release".into(), "beta".into(), "nightly".into()],
1104 "nightly".into(),
1105 );
1106 merger.merge_feature_defaults(&mut feature_def, &default_blocks)?;
1107 assert_eq!(
1108 feature_def.props,
1109 vec![PropDef::new(
1110 "button-color",
1111 &TypeRef::String,
1112 &json!("blue"),
1113 )]
1114 );
1115 Ok(())
1116 }
1117
1118 #[test]
1119 fn test_merge_feature_default_overwrite_nested_field_default() -> Result<()> {
1120 let mut feature_def = FeatureDef {
1121 props: vec![PropDef::new(
1122 "Dialog",
1123 &TypeRef::String,
1124 &json!({
1125 "button-color": "blue",
1126 "title": "hello",
1127 "inner": {
1128 "bobo": "fofo",
1129 "other-field": "other-value"
1130 }
1131 }),
1132 )],
1133
1134 ..Default::default()
1135 };
1136 let default_blocks = serde_json::from_value(json!([
1137 {
1138 "channel": "nightly",
1139 "value": {
1140 "Dialog": {
1141 "button-color": "dark-green",
1142 "inner": {
1143 "bobo": "nightly"
1144 }
1145 }
1146 }
1147 },
1148 {
1149 "channel": "release",
1150 "value": {
1151 "Dialog": {
1152 "button-color": "green",
1153 "inner": {
1154 "bobo": "release",
1155 "new-field": "new-value"
1156 }
1157 }
1158 }
1159 },
1160 {
1161 "channel": "beta",
1162 "value": {
1163 "Dialog": {
1164 "button-color": "light-green",
1165 "inner": {
1166 "bobo": "beta"
1167 }
1168 }
1169 }
1170 },
1171 ]))?;
1172 let objects = Default::default();
1173 let merger = DefaultsMerger::new_with_channel(
1174 &objects,
1175 vec!["release".into(), "beta".into(), "nightly".into()],
1176 "release".into(),
1177 );
1178 merger.merge_feature_defaults(&mut feature_def, &default_blocks)?;
1179 assert_eq!(
1180 feature_def.props,
1181 vec![PropDef::new(
1182 "Dialog",
1183 &TypeRef::String,
1184 &json!({
1185 "button-color": "green",
1186 "title": "hello",
1187 "inner": {
1188 "bobo": "release",
1189 "other-field": "other-value",
1190 "new-field": "new-value"
1191 }
1192 })
1193 )]
1194 );
1195 Ok(())
1196 }
1197
1198 #[test]
1199 fn test_merge_feature_default_overwrite_field_default_based_on_channel_using_only_no_channel_default(
1200 ) -> Result<()> {
1201 let mut feature_def = FeatureDef {
1202 props: vec![PropDef::new(
1203 "button-color",
1204 &TypeRef::String,
1205 &json!("blue"),
1206 )],
1207 ..Default::default()
1208 };
1209 let default_blocks = serde_json::from_value(json!([
1210 {
1213 "value": {
1214 "button-color": "dark-green"
1215 }
1216 },
1217 {
1218 "channel": "release",
1219 "value": {
1220 "button-color": "green"
1221 }
1222 },
1223 {
1224 "channel": "beta",
1225 "value": {
1226 "button-color": "light-green"
1227 }
1228 },
1229 ]))?;
1230 let objects = Default::default();
1231 let merger = DefaultsMerger::new_with_channel(
1232 &objects,
1233 vec!["release".into(), "beta".into(), "nightly".into()],
1234 "nightly".into(),
1235 );
1236 merger.merge_feature_defaults(&mut feature_def, &default_blocks)?;
1237 assert_eq!(
1238 feature_def.props,
1239 vec![PropDef::new(
1240 "button-color",
1241 &TypeRef::String,
1242 &json!("dark-green"),
1243 )]
1244 );
1245 Ok(())
1246 }
1247}