diff --git a/src/parser/ValidationError.ts b/src/parser/ValidationError.ts new file mode 100644 index 0000000..c56501d --- /dev/null +++ b/src/parser/ValidationError.ts @@ -0,0 +1,5 @@ +export class ValidationError extends Error { + constructor(msg: string) { + super(msg); + } +} diff --git a/src/parser/index.ts b/src/parser/index.ts index 71f2bae..8ce650d 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -1,5 +1,6 @@ import { Workout } from "../ast"; import { parseTokens } from "./parser"; import { tokenize } from "./tokenizer"; +import { validate } from "./validate"; -export const parse = (source: string): Workout => parseTokens(tokenize(source)); +export const parse = (source: string): Workout => validate(parseTokens(tokenize(source))); diff --git a/src/parser/parser.test.ts b/src/parser/parser.test.ts index 5a71658..28a437e 100644 --- a/src/parser/parser.test.ts +++ b/src/parser/parser.test.ts @@ -526,4 +526,17 @@ Rest: 5:00 50% } `); }); + + it("throws error when comment offset is outside of interval length", () => { + expect(() => + parse(` +Name: My Workout +Interval: 2:00 90% + @ 0:00 Find your rythm. + @ 3:10 Try to settle in for the effort +`), + ).toThrowErrorMatchingInlineSnapshot( + `"Comment \\"@ 190 Try to settle in for the effort\\" has offset outside of interval"`, + ); + }); }); diff --git a/src/parser/validate.ts b/src/parser/validate.ts new file mode 100644 index 0000000..c5ce2d8 --- /dev/null +++ b/src/parser/validate.ts @@ -0,0 +1,19 @@ +import { Workout, Interval, Comment } from "../ast"; +import { ValidationError } from "./ValidationError"; + +const isCommentWithinInterval = (comment: Comment, interval: Interval): boolean => { + return comment.offset.seconds < interval.duration.seconds; +}; + +const validateCommentOffsets = (interval: Interval) => { + for (const comment of interval.comments) { + if (!isCommentWithinInterval(comment, interval)) { + throw new ValidationError(`Comment "@ ${comment.offset.seconds} ${comment.text}" has offset outside of interval`); + } + } +}; + +export const validate = (workout: Workout): Workout => { + workout.intervals.forEach(validateCommentOffsets); + return workout; +};