namespace AIStudio.Models.Matching; /// /// How much a rule claims to know, computed from the rule itself. /// /// /// This is the heart of the whole rebuild. In the old rules, which branch won was decided by where /// it stood in the file, so a block for one family could swallow another one -- the Llama block ate /// the DeepSeek distills because it happened to come first -- and nothing in the language noticed. /// Here nobody writes an order. A rule saying more about a name beats a rule saying less, and /// "deepseek-r1" says more than "llama" without anyone deciding that it should. /// /// Two rules of equal specificity which can match the same name are a mistake, not a coin toss. /// The index reports them, and resolving still picks the same one every time, so a build never /// depends on which rule was registered first. /// /// What a rule wrote down by hand to override all of the below. /// How tightly the pattern is bound to the name. /// How much of the name the pattern spells out. /// How many further name parts the rule requires or forbids. /// Whether the rule is tied to a provider, a vendor, or both. public readonly record struct RuleSpecificity(int ExplicitRank, int Kind, int PatternLength, int Conditions, int Binding) : IComparable { /// /// Works out how specific a pattern is. /// /// The pattern to measure. /// Its specificity. public static RuleSpecificity Of(MatchPattern pattern) => new( ExplicitRank: pattern.ExplicitRank, Kind: WeightOf(pattern.Kind), PatternLength: pattern.Text.Length, Conditions: pattern.AlsoContains.Count + pattern.NotContains.Count, Binding: (pattern.OnlyOn is null ? 0 : 1) + (pattern.OnlyFrom is null ? 0 : 1)); /// /// Compares two specificities, most specific last. /// /// /// The criteria are weighed in the order they are written in this type, and a tuple compares /// exactly that way: the first difference decides, the rest is never looked at. The hand /// written rank comes first because an emergency exit which the length of some other pattern /// can overrule is not an exit at all. /// /// The specificity to compare against. /// A negative number when this one is less specific, zero when they are equal. public int CompareTo(RuleSpecificity other) => (this.ExplicitRank, this.Kind, this.PatternLength, this.Conditions, this.Binding) .CompareTo((other.ExplicitRank, other.Kind, other.PatternLength, other.Conditions, other.Binding)); private static int WeightOf(MatchKind kind) => kind switch { MatchKind.EXACT => 3, MatchKind.PREFIX => 2, MatchKind.SEGMENT => 1, _ => 0, }; }