Filter.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. //
  2. // Filter.swift
  3. // SwiftyBeaver
  4. //
  5. // Created by Jeff Roberts on 5/31/16.
  6. // Copyright © 2015 Sebastian Kreutzberger
  7. // Some rights reserved: http://opensource.org/licenses/MIT
  8. //
  9. import Foundation
  10. /// FilterType is a protocol that describes something that determines
  11. /// whether or not a message gets logged. A filter answers a Bool when it
  12. /// is applied to a value. If the filter passes, it shall return true,
  13. /// false otherwise.
  14. ///
  15. /// A filter must contain a target, which identifies what it filters against
  16. /// A filter can be required meaning that all required filters against a specific
  17. /// target must pass in order for the message to be logged. At least one non-required
  18. /// filter must pass in order for the message to be logged
  19. public protocol FilterType : class {
  20. func apply(_ value: Any) -> Bool
  21. func getTarget() -> Filter.TargetType
  22. func isRequired() -> Bool
  23. func isExcluded() -> Bool
  24. func reachedMinLevel(_ level: SwiftyBeaver.Level) -> Bool
  25. }
  26. /// Filters is syntactic sugar used to easily construct filters
  27. public class Filters {
  28. public static let Path = PathFilterFactory.self
  29. public static let Function = FunctionFilterFactory.self
  30. public static let Message = MessageFilterFactory.self
  31. }
  32. /// Filter is an abstract base class for other filters
  33. public class Filter {
  34. public enum TargetType {
  35. case Path(Filter.ComparisonType)
  36. case Function(Filter.ComparisonType)
  37. case Message(Filter.ComparisonType)
  38. }
  39. public enum ComparisonType {
  40. case StartsWith([String], Bool)
  41. case Contains([String], Bool)
  42. case Excludes([String], Bool)
  43. case EndsWith([String], Bool)
  44. case Equals([String], Bool)
  45. case Custom((String) -> Bool)
  46. }
  47. let targetType: Filter.TargetType
  48. let required: Bool
  49. let minLevel: SwiftyBeaver.Level
  50. public init(_ target: Filter.TargetType, required: Bool, minLevel: SwiftyBeaver.Level) {
  51. self.targetType = target
  52. self.required = required
  53. self.minLevel = minLevel
  54. }
  55. public func getTarget() -> Filter.TargetType {
  56. return self.targetType
  57. }
  58. public func isRequired() -> Bool {
  59. return self.required
  60. }
  61. public func isExcluded() -> Bool {
  62. return false
  63. }
  64. /// returns true of set minLevel is >= as given level
  65. public func reachedMinLevel(_ level: SwiftyBeaver.Level) -> Bool {
  66. //print("checking if given level \(level) >= \(minLevel)")
  67. return level.rawValue >= minLevel.rawValue
  68. }
  69. }
  70. /// CompareFilter is a FilterType that can filter based upon whether a target
  71. /// starts with, contains or ends with a specific string. CompareFilters can be
  72. /// case sensitive.
  73. public class CompareFilter: Filter, FilterType {
  74. private var filterComparisonType: Filter.ComparisonType?
  75. override public init(_ target: Filter.TargetType, required: Bool, minLevel: SwiftyBeaver.Level) {
  76. super.init(target, required: required, minLevel: minLevel)
  77. let comparisonType: Filter.ComparisonType?
  78. switch self.getTarget() {
  79. case let .Function(comparison):
  80. comparisonType = comparison
  81. case let .Path(comparison):
  82. comparisonType = comparison
  83. case let .Message(comparison):
  84. comparisonType = comparison
  85. /*default:
  86. comparisonType = nil*/
  87. }
  88. self.filterComparisonType = comparisonType
  89. }
  90. public func apply(_ value: Any) -> Bool {
  91. guard let value = value as? String else {
  92. return false
  93. }
  94. guard let filterComparisonType = self.filterComparisonType else {
  95. return false
  96. }
  97. let matches: Bool
  98. switch filterComparisonType {
  99. case let .Contains(strings, caseSensitive):
  100. matches = !strings.filter { string in
  101. return caseSensitive ? value.contains(string) :
  102. value.lowercased().contains(string.lowercased())
  103. }.isEmpty
  104. case let .Excludes(strings, caseSensitive):
  105. matches = !strings.filter { string in
  106. return caseSensitive ? !value.contains(string) :
  107. !value.lowercased().contains(string.lowercased())
  108. }.isEmpty
  109. case let .StartsWith(strings, caseSensitive):
  110. matches = !strings.filter { string in
  111. return caseSensitive ? value.hasPrefix(string) :
  112. value.lowercased().hasPrefix(string.lowercased())
  113. }.isEmpty
  114. case let .EndsWith(strings, caseSensitive):
  115. matches = !strings.filter { string in
  116. return caseSensitive ? value.hasSuffix(string) :
  117. value.lowercased().hasSuffix(string.lowercased())
  118. }.isEmpty
  119. case let .Equals(strings, caseSensitive):
  120. matches = !strings.filter { string in
  121. return caseSensitive ? value == string :
  122. value.lowercased() == string.lowercased()
  123. }.isEmpty
  124. case let .Custom(predicate):
  125. matches = predicate(value)
  126. }
  127. return matches
  128. }
  129. override public func isExcluded() -> Bool {
  130. guard let filterComparisonType = self.filterComparisonType else { return false }
  131. switch filterComparisonType {
  132. case .Excludes(_, _):
  133. return true
  134. default:
  135. return false
  136. }
  137. }
  138. }
  139. // Syntactic sugar for creating a function comparison filter
  140. public class FunctionFilterFactory {
  141. public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
  142. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  143. return CompareFilter(.Function(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
  144. }
  145. public static func contains(_ strings: String..., caseSensitive: Bool = false,
  146. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  147. return CompareFilter(.Function(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
  148. }
  149. public static func excludes(_ strings: String..., caseSensitive: Bool = false,
  150. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  151. return CompareFilter(.Function(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
  152. }
  153. public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
  154. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  155. return CompareFilter(.Function(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
  156. }
  157. public static func equals(_ strings: String..., caseSensitive: Bool = false,
  158. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  159. return CompareFilter(.Function(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
  160. }
  161. public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
  162. return CompareFilter(.Function(.Custom(filterPredicate)), required: required, minLevel: minLevel)
  163. }
  164. }
  165. // Syntactic sugar for creating a message comparison filter
  166. public class MessageFilterFactory {
  167. public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
  168. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  169. return CompareFilter(.Message(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
  170. }
  171. public static func contains(_ strings: String..., caseSensitive: Bool = false,
  172. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  173. return CompareFilter(.Message(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
  174. }
  175. public static func excludes(_ strings: String..., caseSensitive: Bool = false,
  176. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  177. return CompareFilter(.Message(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
  178. }
  179. public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
  180. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  181. return CompareFilter(.Message(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
  182. }
  183. public static func equals(_ strings: String..., caseSensitive: Bool = false,
  184. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  185. return CompareFilter(.Message(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
  186. }
  187. public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
  188. return CompareFilter(.Message(.Custom(filterPredicate)), required: required, minLevel: minLevel)
  189. }
  190. }
  191. // Syntactic sugar for creating a path comparison filter
  192. public class PathFilterFactory {
  193. public static func startsWith(_ prefixes: String..., caseSensitive: Bool = false,
  194. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  195. return CompareFilter(.Path(.StartsWith(prefixes, caseSensitive)), required: required, minLevel: minLevel)
  196. }
  197. public static func contains(_ strings: String..., caseSensitive: Bool = false,
  198. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  199. return CompareFilter(.Path(.Contains(strings, caseSensitive)), required: required, minLevel: minLevel)
  200. }
  201. public static func excludes(_ strings: String..., caseSensitive: Bool = false,
  202. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  203. return CompareFilter(.Path(.Excludes(strings, caseSensitive)), required: required, minLevel: minLevel)
  204. }
  205. public static func endsWith(_ suffixes: String..., caseSensitive: Bool = false,
  206. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  207. return CompareFilter(.Path(.EndsWith(suffixes, caseSensitive)), required: required, minLevel: minLevel)
  208. }
  209. public static func equals(_ strings: String..., caseSensitive: Bool = false,
  210. required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose) -> FilterType {
  211. return CompareFilter(.Path(.Equals(strings, caseSensitive)), required: required, minLevel: minLevel)
  212. }
  213. public static func custom(required: Bool = false, minLevel: SwiftyBeaver.Level = .verbose, filterPredicate: @escaping (String) -> Bool) -> FilterType {
  214. return CompareFilter(.Path(.Custom(filterPredicate)), required: required, minLevel: minLevel)
  215. }
  216. }
  217. extension Filter.TargetType : Equatable {
  218. }
  219. // The == does not compare associated values for each enum. Instead == evaluates to true
  220. // if both enums are the same "types", ignoring the associated values of each enum
  221. public func == (lhs: Filter.TargetType, rhs: Filter.TargetType) -> Bool {
  222. switch (lhs, rhs) {
  223. case (.Path(_), .Path(_)):
  224. return true
  225. case (.Function(_), .Function(_)):
  226. return true
  227. case (.Message(_), .Message(_)):
  228. return true
  229. default:
  230. return false
  231. }
  232. }