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, MatcherResult}, 21 }; 22 use std::fmt::Debug; 23 24 /// Matcher created by [`Matcher::or`]. 25 /// 26 /// **For internal use only. API stablility is not guaranteed!** 27 #[doc(hidden)] 28 pub struct DisjunctionMatcher<M1, M2> { 29 m1: M1, 30 m2: M2, 31 } 32 33 impl<M1, M2> DisjunctionMatcher<M1, M2> { new(m1: M1, m2: M2) -> Self34 pub(crate) fn new(m1: M1, m2: M2) -> Self { 35 Self { m1, m2 } 36 } 37 } 38 39 impl<M1: Matcher, M2: Matcher<ActualT = M1::ActualT>> Matcher for DisjunctionMatcher<M1, M2> 40 where 41 M1::ActualT: Debug, 42 { 43 type ActualT = M1::ActualT; 44 matches(&self, actual: &M1::ActualT) -> MatcherResult45 fn matches(&self, actual: &M1::ActualT) -> MatcherResult { 46 match (self.m1.matches(actual), self.m2.matches(actual)) { 47 (MatcherResult::NoMatch, MatcherResult::NoMatch) => MatcherResult::NoMatch, 48 _ => MatcherResult::Match, 49 } 50 } 51 explain_match(&self, actual: &M1::ActualT) -> Description52 fn explain_match(&self, actual: &M1::ActualT) -> Description { 53 Description::new() 54 .nested(self.m1.explain_match(actual)) 55 .text("and") 56 .nested(self.m2.explain_match(actual)) 57 } 58 describe(&self, matcher_result: MatcherResult) -> Description59 fn describe(&self, matcher_result: MatcherResult) -> Description { 60 format!("{}, or {}", self.m1.describe(matcher_result), self.m2.describe(matcher_result)) 61 .into() 62 } 63 } 64 65 #[cfg(test)] 66 mod tests { 67 use crate::prelude::*; 68 use indoc::indoc; 69 70 #[test] or_true_true_matches() -> Result<()>71 fn or_true_true_matches() -> Result<()> { 72 verify_that!(1, anything().or(anything())) 73 } 74 75 #[test] or_true_false_matches() -> Result<()>76 fn or_true_false_matches() -> Result<()> { 77 verify_that!(1, anything().or(not(anything()))) 78 } 79 80 #[test] or_false_true_matches() -> Result<()>81 fn or_false_true_matches() -> Result<()> { 82 verify_that!(1, not(anything()).or(anything())) 83 } 84 85 #[test] or_false_false_does_not_match() -> Result<()>86 fn or_false_false_does_not_match() -> Result<()> { 87 let result = verify_that!(1, not(anything()).or(not(anything()))); 88 verify_that!( 89 result, 90 err(displays_as(contains_substring(indoc!( 91 " 92 Value of: 1 93 Expected: never matches, or never matches 94 Actual: 1, 95 which is anything 96 and 97 which is anything 98 " 99 )))) 100 ) 101 } 102 103 #[test] chained_or_matches() -> Result<()>104 fn chained_or_matches() -> Result<()> { 105 verify_that!(10, eq(1).or(eq(5)).or(ge(9))) 106 } 107 108 #[test] works_with_str_slices() -> Result<()>109 fn works_with_str_slices() -> Result<()> { 110 verify_that!("A string", ends_with("A").or(ends_with("string"))) 111 } 112 113 #[test] works_with_owned_strings() -> Result<()>114 fn works_with_owned_strings() -> Result<()> { 115 verify_that!("A string".to_string(), ends_with("A").or(ends_with("string"))) 116 } 117 } 118