using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Scraping.Selectors; namespace Scraping.Rules { /// not the same as LocatedRuleSet. IRuleClass only acts upon 1 product item at a time. RuleFamily returns many product items internal class RuleFamily { public By RowsLocator; public IRuleClass Rules; public IEnumerable GetRows(WebElement rootWebElement) => rootWebElement.FindElements(RowsLocator).ToList(); } internal interface IRuleClass { void Run(WebElement element, T productItem); } internal class BasicRule : IRuleClass { public Action Action; public BasicRule() { } public BasicRule(Action action) => Action = action; // this is only place that rules actions are actually run // error handling, logging, et al. belong here public void Run(WebElement element, T productItem) => Action(element, productItem); } internal class RuleSet : IRuleClass, IEnumerable> { private List> rules { get; } = new List>(); public void Add(IRuleClass ruleClass) => rules.Add(ruleClass); public void Add(Action action) => rules.Add(new BasicRule(action)); public void AddRange(IEnumerable> rules) => this.rules.AddRange(rules); public IEnumerator> GetEnumerator() => rules.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => rules.GetEnumerator(); public virtual void Run(WebElement element, T productItem) { foreach (var rule in rules) rule.Run(element, productItem); } } /// LocatedRuleSet loops through found items. When it's 0 or 1, LocatedRuleSet is an easy way to parse only if exists internal class LocatedRuleSet : RuleSet { public By ElementsLocator; public LocatedRuleSet(By elementsLocator) => ElementsLocator = elementsLocator; public override void Run(WebElement parentElement, T productItem) { foreach (var childElements in parentElement.FindElements(ElementsLocator)) base.Run(childElements, productItem); } } }