refactorings in parsing
This commit is contained in:
@@ -1,120 +0,0 @@
|
|||||||
import { Category } from '../domain/Category';
|
|
||||||
import { Application } from '../domain/Application';
|
|
||||||
import { Script } from '@/domain/Script';
|
|
||||||
// import applicationFile from 'js-yaml-loader!@/application/application.yaml';
|
|
||||||
// import applicationFile from 'json-loader!yaml-loader!@/application/application.yaml';
|
|
||||||
import applicationFile, { YamlCategory, YamlScript, YamlDocumentable } from 'js-yaml-loader!./application.yaml';
|
|
||||||
// import test from './test-loader!./test.txt';
|
|
||||||
|
|
||||||
interface ApplicationResult {
|
|
||||||
readonly application: Application;
|
|
||||||
readonly selectedScripts: Script[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ApplicationParser {
|
|
||||||
|
|
||||||
public static buildApplication(): ApplicationResult {
|
|
||||||
const name = applicationFile.name as string;
|
|
||||||
const version = applicationFile.version as number;
|
|
||||||
const categories = new Array<Category>();
|
|
||||||
const selectedScripts = new Array<Script>();
|
|
||||||
if (!applicationFile.actions || applicationFile.actions.length <= 0) {
|
|
||||||
throw new Error('Application does not define any action');
|
|
||||||
}
|
|
||||||
for (const action of applicationFile.actions) {
|
|
||||||
const category = ApplicationParser.parseCategory(action, selectedScripts);
|
|
||||||
categories.push(category);
|
|
||||||
}
|
|
||||||
const app = new Application(name, version, categories);
|
|
||||||
return { application: app, selectedScripts };
|
|
||||||
}
|
|
||||||
|
|
||||||
private static categoryIdCounter = 0;
|
|
||||||
|
|
||||||
private static parseCategory(category: YamlCategory, selectedScripts: Script[]): Category {
|
|
||||||
if (!category.children || category.children.length <= 0) {
|
|
||||||
throw Error('Category has no children');
|
|
||||||
}
|
|
||||||
const subCategories = new Array<Category>();
|
|
||||||
const subScripts = new Array<Script>();
|
|
||||||
for (const categoryOrScript of category.children) {
|
|
||||||
if (ApplicationParser.isCategory(categoryOrScript)) {
|
|
||||||
const subCategory = ApplicationParser.parseCategory(categoryOrScript as YamlCategory, selectedScripts);
|
|
||||||
subCategories.push(subCategory);
|
|
||||||
} else if (ApplicationParser.isScript(categoryOrScript)) {
|
|
||||||
const yamlScript = categoryOrScript as YamlScript;
|
|
||||||
const script = new Script(
|
|
||||||
/* name */
|
|
||||||
yamlScript.name,
|
|
||||||
/* code */
|
|
||||||
yamlScript.code,
|
|
||||||
/* docs */
|
|
||||||
this.parseDocUrls(yamlScript));
|
|
||||||
subScripts.push(script);
|
|
||||||
if (yamlScript.default === true) {
|
|
||||||
selectedScripts.push(script);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw new Error(`Child element is neither a category or a script.
|
|
||||||
Parent: ${category.category}, element: ${categoryOrScript}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new Category(
|
|
||||||
/*id*/
|
|
||||||
ApplicationParser.categoryIdCounter++,
|
|
||||||
/*name*/
|
|
||||||
category.category,
|
|
||||||
/*docs*/
|
|
||||||
this.parseDocUrls(category),
|
|
||||||
/*categories*/
|
|
||||||
subCategories,
|
|
||||||
/*scripts*/
|
|
||||||
subScripts,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static parseDocUrls(documentable: YamlDocumentable): ReadonlyArray<string> {
|
|
||||||
const docs = documentable.docs;
|
|
||||||
if (!docs) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const result = new Array<string>();
|
|
||||||
if (docs instanceof Array) {
|
|
||||||
for (const doc of docs) {
|
|
||||||
if (typeof doc !== 'string') {
|
|
||||||
throw new Error('Docs field (documentation url) must be an array of strings');
|
|
||||||
}
|
|
||||||
this.validateUrl(doc);
|
|
||||||
result.push(doc);
|
|
||||||
}
|
|
||||||
} else if (typeof docs === 'string') {
|
|
||||||
this.validateUrl(docs);
|
|
||||||
result.push(docs);
|
|
||||||
} else {
|
|
||||||
throw new Error('Docs field (documentation url) must a string or array of strings');
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static isScript(categoryOrScript: any): boolean {
|
|
||||||
return categoryOrScript.code && categoryOrScript.code.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static isCategory(categoryOrScript: any): boolean {
|
|
||||||
return categoryOrScript.category && categoryOrScript.category.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static validateUrl(docUrl: string): void {
|
|
||||||
if (!docUrl) {
|
|
||||||
throw new Error('Documentation url is null or empty');
|
|
||||||
}
|
|
||||||
if (docUrl.includes('\n')) {
|
|
||||||
throw new Error('Documentation url cannot be multi-lined.');
|
|
||||||
}
|
|
||||||
const res = docUrl.match(
|
|
||||||
/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g);
|
|
||||||
if (res == null) {
|
|
||||||
throw new Error(`Invalid documentation url: ${docUrl}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
29
src/application/Parser/ApplicationParser.ts
Normal file
29
src/application/Parser/ApplicationParser.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { Category } from '../../domain/Category';
|
||||||
|
import { Application } from '../../domain/Application';
|
||||||
|
import { Script } from '@/domain/Script';
|
||||||
|
import applicationFile from 'js-yaml-loader!./../application.yaml';
|
||||||
|
import { parseCategory } from './CategoryParser';
|
||||||
|
|
||||||
|
interface ApplicationResult {
|
||||||
|
readonly application: Application;
|
||||||
|
readonly selectedScripts: Script[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildApplication(): ApplicationResult {
|
||||||
|
const name = applicationFile.name as string;
|
||||||
|
const version = applicationFile.version as number;
|
||||||
|
const categories = new Array<Category>();
|
||||||
|
const selectedScripts = new Array<Script>();
|
||||||
|
if (!applicationFile.actions || applicationFile.actions.length <= 0) {
|
||||||
|
throw new Error('Application does not define any action');
|
||||||
|
}
|
||||||
|
for (const action of applicationFile.actions) {
|
||||||
|
const category = parseCategory({
|
||||||
|
category: action,
|
||||||
|
selectedScripts,
|
||||||
|
});
|
||||||
|
categories.push(category);
|
||||||
|
}
|
||||||
|
const app = new Application(name, version, categories);
|
||||||
|
return { application: app, selectedScripts };
|
||||||
|
}
|
||||||
70
src/application/Parser/CategoryParser.ts
Normal file
70
src/application/Parser/CategoryParser.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { YamlCategory, YamlScript } from 'js-yaml-loader!./application.yaml';
|
||||||
|
import { Script } from '@/domain/Script';
|
||||||
|
import { Category } from '../../domain/Category';
|
||||||
|
import { parseDocUrls } from './DocumentationParser';
|
||||||
|
|
||||||
|
let categoryIdCounter: number = 0;
|
||||||
|
|
||||||
|
interface IParsingContext {
|
||||||
|
category: YamlCategory;
|
||||||
|
selectedScripts: Script[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ICategoryChildren {
|
||||||
|
subCategories: Category[];
|
||||||
|
subScripts: Script[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseCategory(context: IParsingContext): Category {
|
||||||
|
if (!context.category.children || context.category.children.length <= 0) {
|
||||||
|
throw Error('Category has no children');
|
||||||
|
}
|
||||||
|
const children: ICategoryChildren = {
|
||||||
|
subCategories: new Array<Category>(),
|
||||||
|
subScripts: new Array<Script>(),
|
||||||
|
};
|
||||||
|
for (const categoryOrScript of context.category.children) {
|
||||||
|
parseCategoryChild(categoryOrScript, children, context);
|
||||||
|
}
|
||||||
|
return new Category(
|
||||||
|
/*id*/ categoryIdCounter++,
|
||||||
|
/*name*/ context.category.category,
|
||||||
|
/*docs*/ parseDocUrls(context.category),
|
||||||
|
/*categories*/ children.subCategories,
|
||||||
|
/*scripts*/ children.subScripts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseCategoryChild(
|
||||||
|
categoryOrScript: any, children: ICategoryChildren, parent: IParsingContext) {
|
||||||
|
if (isCategory(categoryOrScript)) {
|
||||||
|
const subCategory = parseCategory(
|
||||||
|
{
|
||||||
|
category: categoryOrScript as YamlCategory,
|
||||||
|
selectedScripts: parent.selectedScripts,
|
||||||
|
});
|
||||||
|
children.subCategories.push(subCategory);
|
||||||
|
} else if (isScript(categoryOrScript)) {
|
||||||
|
const yamlScript = categoryOrScript as YamlScript;
|
||||||
|
const script = new Script(
|
||||||
|
/* name */ yamlScript.name,
|
||||||
|
/* code */ yamlScript.code,
|
||||||
|
/* docs */ parseDocUrls(yamlScript));
|
||||||
|
children.subScripts.push(script);
|
||||||
|
if (yamlScript.default === true) {
|
||||||
|
parent.selectedScripts.push(script);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new Error(`Child element is neither a category or a script.
|
||||||
|
Parent: ${parent.category.category}, element: ${categoryOrScript}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function isScript(categoryOrScript: any): boolean {
|
||||||
|
return categoryOrScript.code && categoryOrScript.code.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isCategory(categoryOrScript: any): boolean {
|
||||||
|
return categoryOrScript.category && categoryOrScript.category.length > 0;
|
||||||
|
}
|
||||||
49
src/application/Parser/DocumentationParser.ts
Normal file
49
src/application/Parser/DocumentationParser.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { YamlDocumentable } from 'js-yaml-loader!./application.yaml';
|
||||||
|
|
||||||
|
export function parseDocUrls(documentable: YamlDocumentable): ReadonlyArray<string> {
|
||||||
|
const docs = documentable.docs;
|
||||||
|
if (!docs) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const result = new DocumentationUrls();
|
||||||
|
if (docs instanceof Array) {
|
||||||
|
for (const doc of docs) {
|
||||||
|
if (typeof doc !== 'string') {
|
||||||
|
throw new Error('Docs field (documentation url) must be an array of strings');
|
||||||
|
}
|
||||||
|
result.add(doc);
|
||||||
|
}
|
||||||
|
} else if (typeof docs === 'string') {
|
||||||
|
result.add(docs);
|
||||||
|
} else {
|
||||||
|
throw new Error('Docs field (documentation url) must a string or array of strings');
|
||||||
|
}
|
||||||
|
return result.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
class DocumentationUrls {
|
||||||
|
private readonly urls = new Array<string>();
|
||||||
|
|
||||||
|
public add(url: string) {
|
||||||
|
validateUrl(url);
|
||||||
|
this.urls.push(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getAll(): ReadonlyArray<string> {
|
||||||
|
return this.urls;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateUrl(docUrl: string): void {
|
||||||
|
if (!docUrl) {
|
||||||
|
throw new Error('Documentation url is null or empty');
|
||||||
|
}
|
||||||
|
if (docUrl.includes('\n')) {
|
||||||
|
throw new Error('Documentation url cannot be multi-lined.');
|
||||||
|
}
|
||||||
|
const res = docUrl.match(
|
||||||
|
/(http(s)?:\/\/.)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)/g);
|
||||||
|
if (res == null) {
|
||||||
|
throw new Error(`Invalid documentation url: ${docUrl}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ import { IUserSelection } from './Selection/IUserSelection';
|
|||||||
import { AsyncLazy } from '../../infrastructure/Threading/AsyncLazy';
|
import { AsyncLazy } from '../../infrastructure/Threading/AsyncLazy';
|
||||||
import { Signal } from '../../infrastructure/Events/Signal';
|
import { Signal } from '../../infrastructure/Events/Signal';
|
||||||
import { ICategory } from '../../domain/ICategory';
|
import { ICategory } from '../../domain/ICategory';
|
||||||
import { ApplicationParser } from '../ApplicationParser';
|
import { buildApplication } from '../Parser/ApplicationParser';
|
||||||
import { IApplicationState } from './IApplicationState';
|
import { IApplicationState } from './IApplicationState';
|
||||||
import { Script } from '../../domain/Script';
|
import { Script } from '../../domain/Script';
|
||||||
import { Application } from '../../domain/Application';
|
import { Application } from '../../domain/Application';
|
||||||
@@ -21,7 +21,7 @@ export class ApplicationState implements IApplicationState {
|
|||||||
|
|
||||||
/** Application instance with all scripts. */
|
/** Application instance with all scripts. */
|
||||||
private static instance = new AsyncLazy<IApplicationState>(() => {
|
private static instance = new AsyncLazy<IApplicationState>(() => {
|
||||||
const app = ApplicationParser.buildApplication();
|
const app = buildApplication();
|
||||||
const state = new ApplicationState(app.application, app.selectedScripts);
|
const state = new ApplicationState(app.application, app.selectedScripts);
|
||||||
return Promise.resolve(state);
|
return Promise.resolve(state);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user