1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // There are no visible documentation elements in this module. 16 #![doc(hidden)] 17 18 use crate::{ 19 description::Description, 20 matcher::{Matcher, MatcherBase, MatcherResult}, 21 }; 22 use std::fmt::Debug; 23 24 /// Matcher created by [`Matcher::or`] and [`any!`]. 25 /// 26 /// Both [`Matcher::or`] and [`any!`] nest on m1. In other words, 27 /// both `x.or(y).or(z)` and `any![x, y, z]` produce: 28 /// ```ignore 29 /// DisjunctionMatcher { 30 /// m1: DisjunctionMatcher { 31 /// m1: x, m2: y 32 /// }, 33 /// m2: z 34 /// } 35 /// ``` 36 /// **For internal use only. API stablility is not guaranteed!** 37 #[doc(hidden)] 38 #[derive(MatcherBase)] 39 pub struct DisjunctionMatcher<M1, M2> { 40 m1: M1, 41 m2: M2, 42 } 43 44 impl<M1, M2> DisjunctionMatcher<M1, M2> { new(m1: M1, m2: M2) -> Self45 pub fn new(m1: M1, m2: M2) -> Self { 46 Self { m1, m2 } 47 } 48 } 49 50 impl<T: Debug + Copy, M1: Matcher<T>, M2: Matcher<T>> Matcher<T> for DisjunctionMatcher<M1, M2> { matches(&self, actual: T) -> MatcherResult51 fn matches(&self, actual: T) -> MatcherResult { 52 match (self.m1.matches(actual), self.m2.matches(actual)) { 53 (MatcherResult::NoMatch, MatcherResult::NoMatch) => MatcherResult::NoMatch, 54 _ => MatcherResult::Match, 55 } 56 } 57 explain_match(&self, actual: T) -> Description58 fn explain_match(&self, actual: T) -> Description { 59 match (self.m1.matches(actual), self.m2.matches(actual)) { 60 (MatcherResult::NoMatch, MatcherResult::Match) => self.m1.explain_match(actual), 61 (MatcherResult::Match, MatcherResult::NoMatch) => self.m2.explain_match(actual), 62 (_, _) => { 63 let m1_description = self.m1.explain_match(actual); 64 if m1_description.is_disjunction_description() { 65 m1_description.nested(self.m2.explain_match(actual)) 66 } else { 67 Description::new() 68 .bullet_list() 69 .collect([m1_description, self.m2.explain_match(actual)]) 70 .disjunction_description() 71 } 72 } 73 } 74 } 75 describe(&self, matcher_result: MatcherResult) -> Description76 fn describe(&self, matcher_result: MatcherResult) -> Description { 77 let m1_description = self.m1.describe(matcher_result); 78 if m1_description.is_disjunction_description() { 79 m1_description.push_in_last_nested(self.m2.describe(matcher_result)) 80 } else { 81 let header = if matcher_result.into() { 82 "has at least one of the following properties:" 83 } else { 84 "has all of the following properties:" 85 }; 86 Description::new() 87 .text(header) 88 .nested( 89 Description::new() 90 .bullet_list() 91 .collect([m1_description, self.m2.describe(matcher_result)]), 92 ) 93 .disjunction_description() 94 } 95 } 96 } 97 98 #[cfg(test)] 99 mod tests { 100 use crate::prelude::*; 101 use indoc::indoc; 102 103 #[test] or_true_true_matches() -> Result<()>104 fn or_true_true_matches() -> Result<()> { 105 verify_that!(1, anything().or(anything())) 106 } 107 108 #[test] or_true_false_matches() -> Result<()>109 fn or_true_false_matches() -> Result<()> { 110 verify_that!(1, anything().or(not(anything()))) 111 } 112 113 #[test] or_false_true_matches() -> Result<()>114 fn or_false_true_matches() -> Result<()> { 115 verify_that!(1, not(anything()).or(anything())) 116 } 117 118 #[test] or_false_false_does_not_match() -> Result<()>119 fn or_false_false_does_not_match() -> Result<()> { 120 let result = verify_that!(1, not(anything()).or(not(anything()))); 121 verify_that!( 122 result, 123 err(displays_as(contains_substring(indoc!( 124 " 125 Value of: 1 126 Expected: has at least one of the following properties: 127 * never matches 128 * never matches 129 Actual: 1, 130 * which is anything 131 * which is anything 132 " 133 )))) 134 ) 135 } 136 137 #[test] chained_or_matches() -> Result<()>138 fn chained_or_matches() -> Result<()> { 139 verify_that!(10, eq(1).or(eq(5)).or(ge(9))) 140 } 141 142 #[test] works_with_str_slices() -> Result<()>143 fn works_with_str_slices() -> Result<()> { 144 verify_that!("A string", ends_with("A").or(ends_with("string"))) 145 } 146 147 #[test] works_with_owned_strings() -> Result<()>148 fn works_with_owned_strings() -> Result<()> { 149 verify_that!("A string".to_string(), ends_with("A").or(ends_with("string"))) 150 } 151 } 152