github.com/go-email-validator/go-email-validator@v0.0.0-20230409163946-b8b9e6a0552e/pkg/ev/validator_dep.go (about) 1 package ev 2 3 import ( 4 "sync" 5 ) 6 7 // DepValidatorName is name of validator with dependencies 8 const DepValidatorName ValidatorName = "depValidator" 9 10 // ValidatorMap alias for map[ValidatorName]Validator 11 type ValidatorMap map[ValidatorName]Validator 12 13 // NewDepsError creates DepsError 14 func NewDepsError() error { 15 return &DepsError{} 16 } 17 18 // DepsError is DepValidatorName error 19 type DepsError struct{} 20 21 func (*DepsError) Error() string { 22 return "DepsError" 23 } 24 25 // NewDepValidator instantiates DepValidatorName validator 26 func NewDepValidator(deps ValidatorMap) Validator { 27 return depValidator{deps: deps} 28 } 29 30 type depValidator struct { 31 AValidatorWithoutDeps 32 deps ValidatorMap 33 } 34 35 func (d depValidator) Validate(input Input, _ ...ValidationResult) ValidationResult { 36 var waiters, waitersMutex = make(map[ValidatorName][]*sync.WaitGroup), sync.RWMutex{} 37 var validationResultsByName, validationResultsMutex = make(map[ValidatorName]ValidationResult), sync.RWMutex{} 38 var isValid = true 39 var starter, finisher = sync.WaitGroup{}, sync.WaitGroup{} 40 starter.Add(1) 41 finisher.Add(len(d.deps)) 42 43 for key, validator := range d.deps { 44 var depWaiter *sync.WaitGroup 45 var depWaiters []*sync.WaitGroup 46 var deps []ValidatorName 47 var ok bool 48 49 deps = validator.GetDeps() 50 if len(deps) > 0 { 51 depWaiter = &sync.WaitGroup{} 52 depWaiter.Add(len(deps)) 53 54 for _, dep := range deps { 55 if depWaiters, ok = waiters[dep]; !ok { 56 depWaiters = make([]*sync.WaitGroup, 0) 57 } 58 waiters[dep] = append(depWaiters, depWaiter) 59 } 60 } 61 62 go func(key ValidatorName, validator Validator, depWaiter *sync.WaitGroup) { 63 var results []ValidationResult 64 65 // TODO add recover 66 starter.Wait() 67 if depWaiter != nil { 68 depWaiter.Wait() 69 70 results = make([]ValidationResult, len(deps)) 71 validationResultsMutex.RLock() 72 for i, dep := range deps { 73 results[i] = validationResultsByName[dep] 74 } 75 validationResultsMutex.RUnlock() 76 } 77 78 var result = validator.Validate(input, results...) 79 validationResultsMutex.Lock() 80 validationResultsByName[key] = result 81 isValid = isValid && result.IsValid() 82 validationResultsMutex.Unlock() 83 84 waitersMutex.RLock() 85 if depWaiters, ok = waiters[key]; ok { 86 for _, depWaiter := range depWaiters { 87 depWaiter.Done() 88 } 89 } 90 waitersMutex.RUnlock() 91 finisher.Done() 92 }(key, validator, depWaiter) 93 } 94 starter.Done() 95 finisher.Wait() 96 97 return NewDepValidatorResult(isValid, validationResultsByName) 98 } 99 100 // DepResult is alias for results of nested validators 101 type DepResult map[ValidatorName]ValidationResult 102 103 // DepValidationResult is representation of DepValidatorName result 104 type DepValidationResult interface { 105 ValidationResult 106 GetResults() DepResult 107 } 108 109 // NewDepValidatorResult returns DepValidatorName result 110 func NewDepValidatorResult(isValid bool, results DepResult) ValidationResult { 111 return depValidationResult{ 112 isValid: isValid, 113 results: results, 114 } 115 } 116 117 type depValidationResult struct { 118 isValid bool 119 results DepResult 120 } 121 122 func (d depValidationResult) GetResults() DepResult { 123 return d.results 124 } 125 126 func (d depValidationResult) IsValid() bool { 127 return d.isValid 128 } 129 130 func (d depValidationResult) Errors() (errors []error) { 131 for _, result := range d.GetResults() { 132 errors = append(errors, result.Errors()...) 133 } 134 135 return errors 136 } 137 138 func (d depValidationResult) HasErrors() bool { 139 for _, result := range d.GetResults() { 140 if result.HasErrors() { 141 return true 142 } 143 } 144 145 return false 146 } 147 148 func (d depValidationResult) Warnings() (warnings []error) { 149 for _, result := range d.GetResults() { 150 warnings = append(warnings, result.Warnings()...) 151 } 152 153 return warnings 154 } 155 156 func (d depValidationResult) HasWarnings() bool { 157 for _, result := range d.GetResults() { 158 if result.HasWarnings() { 159 return true 160 } 161 } 162 163 return false 164 } 165 166 func (d depValidationResult) ValidatorName() ValidatorName { 167 return DepValidatorName 168 }