Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b6b5986

Browse files
committedMar 6, 2025··
added composite validation rules and listeners for added/removed validation rules (these features are not yet used)
1 parent 2eec57f commit b6b5986

File tree

1 file changed

+90
-2
lines changed

1 file changed

+90
-2
lines changed
 

‎packages/typir/src/services/validation.ts

+90-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66

77
import { Type, isType } from '../graph/type-node.js';
88
import { TypirServices } from '../typir.js';
9-
import { RuleOptions, RuleRegistry } from '../utils/rule-registration.js';
9+
import { RuleCollectorListener, RuleOptions, RuleRegistry } from '../utils/rule-registration.js';
1010
import { TypirProblem, isSpecificTypirProblem } from '../utils/utils-definitions.js';
1111
import { TypeCheckStrategy, createTypeCheckStrategy } from '../utils/utils-type-comparison.js';
12+
import { removeFromArray, toArray } from '../utils/utils.js';
1213
import { TypeInferenceCollector } from './inference.js';
1314
import { ProblemPrinter } from './printing.js';
1415

@@ -180,6 +181,12 @@ export class DefaultValidationConstraints<LanguageType = unknown> implements Val
180181
}
181182
}
182183

184+
185+
export interface ValidationCollectorListener<LanguageType = unknown> {
186+
onAddedValidationRule(rule: ValidationRule<LanguageType>, options: ValidationRuleOptions): void;
187+
onRemovedValidationRule(rule: ValidationRule<LanguageType>, options: ValidationRuleOptions): void;
188+
}
189+
183190
export interface ValidationRuleOptions extends RuleOptions {
184191
// no additional properties so far
185192
}
@@ -201,18 +208,26 @@ export interface ValidationCollector<LanguageType = unknown> {
201208
* @param options the same options as given for the registration of the validation rule must be given for the removal!
202209
*/
203210
removeValidationRule(rule: ValidationRule<LanguageType>, options?: Partial<ValidationRuleOptions>): void;
211+
212+
addListener(listener: ValidationCollectorListener<LanguageType>): void;
213+
removeListener(listener: ValidationCollectorListener<LanguageType>): void;
204214
}
205215

206-
export class DefaultValidationCollector<LanguageType = unknown> implements ValidationCollector<LanguageType> {
216+
export class DefaultValidationCollector<LanguageType = unknown> implements ValidationCollector<LanguageType>, RuleCollectorListener<ValidationRule<LanguageType>> {
207217
protected readonly services: TypirServices<LanguageType>;
218+
protected readonly listeners: Array<ValidationCollectorListener<LanguageType>> = [];
208219

209220
protected readonly ruleRegistryStateLess: RuleRegistry<ValidationRuleStateless<LanguageType>>;
210221
protected readonly ruleRegistryBeforeAfter: RuleRegistry<ValidationRuleWithBeforeAfter<LanguageType>>;
211222

212223
constructor(services: TypirServices<LanguageType>) {
213224
this.services = services;
225+
214226
this.ruleRegistryStateLess = new RuleRegistry(services as TypirServices);
227+
this.ruleRegistryStateLess.addListener(this);
228+
215229
this.ruleRegistryBeforeAfter = new RuleRegistry(services as TypirServices);
230+
this.ruleRegistryBeforeAfter.addListener(this);
216231
}
217232

218233
protected createAcceptor(problems: Array<ValidationProblem<LanguageType>>): ValidationProblemAcceptor<LanguageType> {
@@ -298,4 +313,77 @@ export class DefaultValidationCollector<LanguageType = unknown> implements Valid
298313
}
299314
}
300315

316+
addListener(listener: ValidationCollectorListener<LanguageType>): void {
317+
this.listeners.push(listener);
318+
}
319+
removeListener(listener: ValidationCollectorListener<LanguageType>): void {
320+
removeFromArray(listener, this.listeners);
321+
}
322+
323+
onAddedRule(rule: ValidationRule<LanguageType, LanguageType>, diffOptions: RuleOptions): void {
324+
// listeners of the composite will be notified about all added inner rules
325+
this.listeners.forEach(listener => listener.onAddedValidationRule(rule, diffOptions));
326+
}
327+
onRemovedRule(rule: ValidationRule<LanguageType, LanguageType>, diffOptions: RuleOptions): void {
328+
// listeners of the composite will be notified about all removed inner rules
329+
this.listeners.forEach(listener => listener.onRemovedValidationRule(rule, diffOptions));
330+
}
331+
}
332+
333+
334+
export class CompositeValidationRule<LanguageType = unknown> extends DefaultValidationCollector<LanguageType> implements ValidationRuleWithBeforeAfter<LanguageType> {
335+
/** The collector for inference rules, at which this composite rule should be registered. */
336+
protected readonly collectorToRegisterThisRule: ValidationCollector<LanguageType>;
337+
338+
constructor(services: TypirServices<LanguageType>, collectorToRegisterThisRule: ValidationCollector<LanguageType>) {
339+
super(services);
340+
this.collectorToRegisterThisRule = collectorToRegisterThisRule;
341+
}
342+
343+
beforeValidation(languageRoot: LanguageType, accept: ValidationProblemAcceptor<LanguageType>, _typir: TypirServices<LanguageType>): void {
344+
this.validateBefore(languageRoot).forEach(v => accept(v));
345+
}
346+
347+
validation(languageNode: LanguageType, accept: ValidationProblemAcceptor<LanguageType>, _typir: TypirServices<LanguageType>): void {
348+
this.validate(languageNode).forEach(v => accept(v));
349+
}
350+
351+
afterValidation(languageRoot: LanguageType, accept: ValidationProblemAcceptor<LanguageType>, _typir: TypirServices<LanguageType>): void {
352+
this.validateAfter(languageRoot).forEach(v => accept(v));
353+
}
354+
355+
override onAddedRule(rule: ValidationRule<LanguageType, LanguageType>, diffOptions: RuleOptions): void {
356+
// an inner rule was added
357+
super.onAddedRule(rule, diffOptions);
358+
359+
// this composite rule needs to be registered also for all the language keys of the new inner rule
360+
this.collectorToRegisterThisRule.addValidationRule(this, {
361+
...diffOptions,
362+
boundToType: undefined,
363+
});
364+
}
365+
366+
override onRemovedRule(rule: ValidationRule<LanguageType>, diffOptions: RuleOptions): void {
367+
// an inner rule was removed
368+
super.onRemovedRule(rule, diffOptions);
369+
370+
// remove this composite rule for all language keys for which no inner rules are registered anymore
371+
if (diffOptions.languageKey === undefined) {
372+
if (this.ruleRegistryStateLess.getRulesByLanguageKey(undefined).length <= 0 && this.ruleRegistryBeforeAfter.getRulesByLanguageKey(undefined).length <= 0) {
373+
this.collectorToRegisterThisRule.removeValidationRule(this, {
374+
...diffOptions,
375+
languageKey: undefined,
376+
boundToType: undefined, // a composite rule is never bound to a type, since it manages this feature itself
377+
});
378+
}
379+
} else {
380+
const languageKeysToUnregister = toArray(diffOptions.languageKey)
381+
.filter(key => this.ruleRegistryStateLess.getRulesByLanguageKey(key).length <= 0 && this.ruleRegistryBeforeAfter.getRulesByLanguageKey(key).length <= 0);
382+
this.collectorToRegisterThisRule.removeValidationRule(this, {
383+
...diffOptions,
384+
languageKey: languageKeysToUnregister,
385+
boundToType: undefined, // a composite rule is never bound to a type, since it manages this feature itself
386+
});
387+
}
388+
}
301389
}

0 commit comments

Comments
 (0)
Please sign in to comment.