Skip to content

Commit

Permalink
Improve child utils
Browse files Browse the repository at this point in the history
  • Loading branch information
ShaitanLyss committed Jul 31, 2024
1 parent 64df454 commit 36882b0
Show file tree
Hide file tree
Showing 3 changed files with 213 additions and 21 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@selenite/commons",
"version": "0.18.12",
"version": "0.18.13",
"repository": "github:ShaitanLyss/selenite-commons",
"license": "MIT",
"keywords": [
Expand Down
133 changes: 133 additions & 0 deletions src/lib/utils/xsd.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { describe, expect, it } from 'vitest';
import { ComplexType, ChildProps } from './xsd';


describe('XMLSchema', () => {
describe('complexType', () => {
describe('childProps', () => {
it('should have the appropriate state', () => {
const child = new ChildProps({
type: 'Solvers',
minOccurs: 1,
maxOccurs: 1
});
expect(child.unique).toBe(true);
expect(child.required).toBe(true);
});
describe('should be unique if max occurs <= 1', () => {
it('max occurs = 1', () => {
const child = new ChildProps({
type: 'Solvers',
maxOccurs: 1
});
expect(child.unique).toBe(true);
});
it('max occurs = 2', () => {
const child = new ChildProps({
type: 'Solvers',
maxOccurs: 2
});
expect(child.unique).toBe(false);
});
it('max occurs = 0', () => {
const child = new ChildProps({
type: 'Solvers',
maxOccurs: 0
});
expect(child.unique).toBe(true);
});
it('max occurs = undefined', () => {
const child = new ChildProps({
type: 'Solvers'
});
expect(child.unique).toBe(false);
});
});
describe('should be required if min occurs >= 1', () => {
it('min occurs = 1', () => {
const child = new ChildProps({
type: 'Solvers',
minOccurs: 1
});
expect(child.required).toBe(true);
});
it('min occurs = 2', () => {
const child = new ChildProps({
type: 'Solvers',
minOccurs: 2
});
expect(child.required).toBe(true);
});
it('min occurs = 0', () => {
const child = new ChildProps({
type: 'Solvers',
minOccurs: 0
});
expect(child.required).toBe(false);
});
it('min occurs = undefined', () => {
const child = new ChildProps({
type: 'Solvers'
});
expect(child.required).toBe(false);
});
});
});
describe('optionalChildren', () => {
it('should return optional children', () => {
const complex = new ComplexType({
name: 'Complex',
children: [
new ChildProps({ type: 'Child1', minOccurs: 1 }),
new ChildProps({ type: 'Child2' }), // optional
new ChildProps({ type: 'Child3', minOccurs: 0 }), // optional
new ChildProps({ type: 'Child4', minOccurs: 0, maxOccurs: 1 }), // optional
new ChildProps({ type: 'Child5', minOccurs: 10 })
]
});
expect(complex.optionalChildren).toEqual([
new ChildProps({ type: 'Child2' }),
new ChildProps({ type: 'Child3', minOccurs: 0 }),
new ChildProps({ type: 'Child4', minOccurs: 0, maxOccurs: 1 })
]);
});
});
describe('requiredChildren', () => {
it('should return required children', () => {
const complex = new ComplexType({
name: 'Complex',
children: [
new ChildProps({ type: 'Child1', minOccurs: 1 }),
new ChildProps({ type: 'Child2' }), // optional
new ChildProps({ type: 'Child3', minOccurs: 0 }), // optional
new ChildProps({ type: 'Child4', minOccurs: 0, maxOccurs: 1 }), // optional
new ChildProps({ type: 'Child5', minOccurs: 10 })
]
});
expect(complex.requiredChildren).toEqual([
new ChildProps({ type: 'Child1', minOccurs: 1 }),
new ChildProps({ type: 'Child5', minOccurs: 10 })
]);
});
});
describe('uniqueChildren', () => {
it('should return unique children', () => {
const complex = new ComplexType({
name: 'Complex',
children: [
new ChildProps({ type: 'Child1', minOccurs: 1, maxOccurs: 1 }),
new ChildProps({ type: 'Child2', maxOccurs: 1 }), // unique
new ChildProps({ type: 'Child3', minOccurs: 0, maxOccurs: 1 }), // unique
new ChildProps({ type: 'Child4', minOccurs: 0, maxOccurs: 2 }), // not unique
new ChildProps({ type: 'Child5', minOccurs: 10 })
]
});
expect(complex.uniqueChildren).toEqual([
new ChildProps({ type: 'Child1', minOccurs: 1, maxOccurs: 1 }),
new ChildProps({ type: 'Child2', maxOccurs: 1 }),
new ChildProps({ type: 'Child3', minOccurs: 0, maxOccurs: 1 })
]);
});
});
});
});
99 changes: 79 additions & 20 deletions src/lib/utils/xsd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import init, { parse_xsd } from '@selenite/commons-rs';
import { isBrowser } from './html.svelte';
import type { PartialBy } from '$lib/type';

/**
* A simple datatype in an XML schema.
Expand Down Expand Up @@ -36,26 +37,68 @@ export type Attribute = {
required: boolean;
};

export type ChildProps = {
export class ChildProps {
type: string;
minOccurs?: number
maxOccurs?: number
};
minOccurs?: number;
maxOccurs?: number;

constructor(params: { type: string; minOccurs?: number; maxOccurs?: number }) {
this.type = params.type;
this.minOccurs = params.minOccurs;
this.maxOccurs = params.maxOccurs;
}

get unique(): boolean {
return this.maxOccurs !== undefined && this.maxOccurs <= 1;
}

get required(): boolean {
return this.minOccurs !== undefined && this.minOccurs >= 1;
}
}

/** A complex type in an XML schema definition.
*/
export type ComplexType = {
export class ComplexType {
/** Name of the complex type. */
name: string;
/** Documentation of the complex type. */
doc?: string;
/** Attributes of the complex type. */
attrs: Attribute[];
get attrs(): Attribute[] {
return Object.values(this.attributes);
}
attributes: Map<string, Attribute>;
children: ChildProps[];
childTypes: string[];
};

constructor(params: {
name: string;
attributes?: Attribute[];
doc?: string;
children?: ChildProps[];
}) {
this.name = params.name;
this.doc = params.doc;
this.attributes = new Map(params.attributes?.map((a) => [a.name, a]));
this.children = params.children ?? [];
}

get childTypes(): string[] {
return this.children.map(c => c.type);
}

get optionalChildren(): ChildProps[] {
return this.children.filter(c => !c.required);
}

get requiredChildren(): ChildProps[] {
return this.children.filter(c => c.required);
}

get uniqueChildren(): ChildProps[] {
return this.children.filter(c => c.unique);
}
}

/**
* A tree view of an XML schema.
Expand Down Expand Up @@ -87,6 +130,17 @@ export class XmlSchema {
return this.complexTypes;
}

addComplexType(...complexs: ComplexType[]) {
for (const c of complexs) {
this.complexTypes.set(c.name, c);
}
}
addSimpleType(...simples: SimpleType[]) {
for (const s of simples) {
this.simpleTypes.set(s.name, s);
}
}

/** Map which associates the names of complex XML types to their parents' names. */
get parentsMap(): Map<XMLTypeName, XMLTypeName[]> {
const res = new Map<string, string[]>();
Expand Down Expand Up @@ -126,7 +180,7 @@ export class XmlSchema {
continue;
}
current[type] = {};
const children = this.typeMap.get(type)?.children.map(c => c.type);
const children = this.typeMap.get(type)?.children.map((c) => c.type);
if (!children) continue;
rec(current[type], children, [...already_visited, type]);
}
Expand Down Expand Up @@ -202,18 +256,23 @@ export async function parseXsd(xsd: string): Promise<XmlSchema | undefined> {
doc
};
});
res.complexTypes.set(name, {
res.complexTypes.set(
name,
get attrs() {
return Object.values(this.attributes) as Attribute[];
},
attributes: new Map(attrs.map((a) => [a.name, a])),
get childTypes() {
return (this.children as ChildProps[]).map(c => c.type);
},
doc,
children: children?.map(c => ({type: c.type_name, maxOccurs: c.max_occurs, minOccurs: c.min_occurs})) ?? []
});
new ComplexType({
name,
attributes: attrs,
doc,
children:
children?.map(
(c) =>
new ChildProps({
type: c.type_name,
maxOccurs: c.max_occurs,
minOccurs: c.min_occurs
})
) ?? []
})
);
}
schema.free();
return res;
Expand Down

0 comments on commit 36882b0

Please sign in to comment.