Skip to content

Commit

Permalink
prototyping the common types approach
Browse files Browse the repository at this point in the history
-   this isn't as graceful as I'd hoped; the codegen approach employed relies upon processing the actual data, rather than the raw schema.
-   re: #4
  • Loading branch information
SignpostMarv committed Oct 7, 2024
1 parent ff8c5b7 commit 4704461
Show file tree
Hide file tree
Showing 31 changed files with 763 additions and 191 deletions.
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ clean:

generate: lint generate--skip-checks generate--post-build

generate--skip-checks: build generate--skip-checks--update8 generate--skip-checks--version-1
generate--skip-checks: build generate--skip-checks--common generate--skip-checks--update8 generate--skip-checks--version-1

generate--skip-checks--common:
@echo 'running ./generate-common-types.ts'
@./node_modules/.bin/ts-node ./generate-common-types.ts

generate--skip-checks--update8:
@echo 'running ./discover-types.ts'
Expand Down
7 changes: 7 additions & 0 deletions data-progress--common.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Data Progress

0.00% Complete (0 of 2)

## /Script/CoreUObject.Class'/Script/FactoryGame.FGFauxEntry'

- [ ] faux
1 change: 1 addition & 0 deletions data/common/faux.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
70 changes: 70 additions & 0 deletions generate-common-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import {
TypeDefinitionWriter,
} from './lib/TypeDefinitionWriter';
import {
NoMatchError,
} from './lib/Exceptions';
import {
writeFile,
} from 'node:fs/promises';
import {
docs,
} from './lib/helpers';

try {
performance.mark('start');
const bar = new TypeDefinitionWriter(
docs,
'common',
);
performance.measure('bootstrap', 'start');
performance.mark('bootstrap done');
await bar.write(`${import.meta.dirname}/generated-types/common/`);
performance.measure('types generated', 'bootstrap done');
const discovery = await bar.discovery;
const result = await discovery.discover_type_$defs();

process.stdout.write(
`${
JSON.stringify(result.missing_classes, null, '\t')
}\n`,
);
console.table({
'Found Types': Object.keys(result.found_types).length,
'Missing Types': result.missing_types.length,
'Found Classes': result.found_classes.length,
'Missing Classes': result.missing_classes.length,
});
/*
await writeFile(
`${import.meta.dirname}/discover-types.common.perf.json`,
`${JSON.stringify(perf(), null, '\t')}`,
);
*/
} catch (err) {
/*
await writeFile(
`${import.meta.dirname}/discover-types.common.perf.json`,
`${JSON.stringify(perf(), null, '\t')}`,
);
*/
if (err instanceof NoMatchError) {
console.error('ran into an issue');
await writeFile(
`./discovery-types.common.failure.json`,
JSON.stringify(
{
property: err.property as unknown,
message: err.message,
stack: err.stack?.split('\n'),
},
null,
'\t',
),
);

console.error(err.message, err.stack);
} else {
throw err;
}
}
5 changes: 5 additions & 0 deletions generated-types/common/classes/CoreUObject/FGFauxEntry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {faux_1__type, NativeClass__type} from '../../common/unassigned';

export type FGFauxEntry__NativeClass = NativeClass__type & {
Classes: [faux_1__type, ...faux_1__type[]];
};
30 changes: 30 additions & 0 deletions generated-types/common/common/unassigned.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {UnrealEngineString, StringStartsWith} from '../utils/validators';

export type class__type = class__no_description__type & {
mDescription: string;
};

export type class__no_description__type =
class__no_description_or_display_name__type & {
mDisplayName: string;
};

export type class__no_description_or_display_name__type = {
ClassName: Exclude<string, ''>;
};

export type faux_1__type = class__type & {
faux: UnrealEngineString__array__type;
};

export type NativeClass__type = {
NativeClass: UnrealEngineString<
'/Script/CoreUObject.Class',
StringStartsWith<'/Script/FactoryGame.FG'>
>;
};

export type UnrealEngineString__array__type = [
UnrealEngineString,
...UnrealEngineString[],
];
35 changes: 35 additions & 0 deletions generated-types/common/utils/validators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
export type StringStartsWith<prefix extends string> = keyof {
[pseudo_key in keyof {
[key: string]: unknown;
} as pseudo_key extends string ? `${prefix}${pseudo_key}` : never]: string;
};

export type StringPassedRegExp<
pattern extends string,
T extends string = string,
> = T & {
[pseudo_key in pattern]: never;
};

export class UnrealEngineString<
left extends string = string,
right extends string = string,
> {
readonly left: left;
readonly right: right;
protected constructor(left: left, right: right) {
this.left = left;
this.right = right;
}
static fromString<
left extends string = string,
right extends string = string,
>(value: string): UnrealEngineString<left, right> {
const result = /^([^']+)'(?:"([^"]+)"|([^"]+))'$/.exec(value);
if (!result) throw new Error(`Not an UnrealEngineString (${value})`);
return new UnrealEngineString<left, right>(
result[1] as left,
(result[2] || result[3]) as right
);
}
}
15 changes: 13 additions & 2 deletions lib/CustomParsingTypes/TypedString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Ajv, {
SchemaObject,
} from 'ajv/dist/2020';
import {
common_ref,
local_ref,
} from '../StringStartsWith';
import {
Expand Down Expand Up @@ -131,6 +132,7 @@ export function generate_object_parent_schema() {

export function generate_typed_string_$defs(
$defs:local_ref<string>[],
common_$defs: common_ref<string>[],
) {
return {
...UnrealEngineString_schema_definitions,
Expand All @@ -143,7 +145,10 @@ export function generate_typed_string_$defs(
properties: {
$ref: {
type: 'string',
enum: $defs,
enum: [
...$defs,
...common_$defs,
],
},
},
},
Expand Down Expand Up @@ -275,18 +280,23 @@ export class TypedString

configure_ajv(
$defs:{[key: string]: SchemaObject},
common_$defs: {[key: string]: SchemaObject},
ajv:Ajv,
) {
if (this.already_configured.has(ajv)) {
return;
}

const local_refs = Object.keys($defs).map(local_ref);
const common_refs = Object.keys(common_$defs).map(common_ref);

this.already_configured.add(ajv);

const meta_schema = {
$defs: generate_typed_string_$defs(local_refs),
$defs: generate_typed_string_$defs(
local_refs,
common_refs,
),
oneOf: [
{$ref: '#/$defs/typed_string_object_type'},
{$ref: '#/$defs/typed_string_object_pattern_type'},
Expand All @@ -297,6 +307,7 @@ export class TypedString

const formatter = new ValueToRegexFormatter(
$defs,
common_$defs,
);

ajv.addKeyword({
Expand Down
74 changes: 73 additions & 1 deletion lib/CustomParsingTypes/ValueToRegexFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
annoyingly_have_to_escape_property,
} from './CustomPairingTypes';
import {
common_ref,
local_ref,
} from '../StringStartsWith';
import {
Expand Down Expand Up @@ -75,17 +76,26 @@ export class ValueToRegexFormatter
private typed_string_pattern_general;
private UnrealEngineString;
private readonly $defs:{[key: string]: SchemaObject};
private readonly common_$defs: {[key: string]: SchemaObject};
private readonly supported_$ref_object:{[key: local_ref<string>]: string};
// eslint-disable-next-line max-len
private readonly supported_common_$ref_object: {[key: common_ref<string>]: string};

constructor($defs:{[key: string]: SchemaObject}) {
constructor(
$defs:{[key: string]: SchemaObject},
common_$defs: {[key: string]: SchemaObject},
) {
this.$defs = $defs;
this.common_$defs = common_$defs;
this.UnrealEngineString = UnrealEngineString.ajv_macro_generator(
true,
);
this.typed_string_const = typed_string_const;
this.typed_string_enum = typed_string_enum;
this.typed_string_pattern_general = typed_string_pattern_general;
this.supported_$ref_object = this.prepare_$ref_regex($defs);
// eslint-disable-next-line max-len
this.supported_common_$ref_object = this.prepare_common_$ref_regex(common_$defs);
}

pattern_from_value(value:unknown): {pattern: string} {
Expand All @@ -106,6 +116,18 @@ export class ValueToRegexFormatter
);
}

private is_supported_common_$ref_object(
maybe:unknown,
): maybe is {$ref: common_ref<string>} {
return (
object_only_has_that_property(maybe, '$ref', is_string)
&& object_has_property(
this.supported_common_$ref_object,
maybe.$ref,
)
);
}

private is_supported_definition(maybe:SchemaObject): boolean
{
return (
Expand Down Expand Up @@ -161,6 +183,20 @@ export class ValueToRegexFormatter
}))
}

private prepare_common_$ref_regex(
$defs:{[key: string]: SchemaObject},
): {[key: common_ref<string>]: string} {
return Object.fromEntries(Object.entries($defs)
.filter(maybe => {
return this.is_supported_definition(maybe[1])
}).map(entry => {
return [
common_ref(entry[0]),
this.value_to_regex(entry[1]),
];
}))
}

private Texture2D_basic_regex(): string
{
return `(?:Texture2D \\/Game\\/FactoryGame\\/(?:[A-Z][A-Za-z0-9_.]+/)*[A-Z][A-Za-z_.0-9-]+(?::[A-Z][A-Za-z0-9]+)?)`;
Expand Down Expand Up @@ -247,6 +283,8 @@ export class ValueToRegexFormatter
return this.typed_string_array_inner_to_regex(value);
} else if (this.is_supported_$ref_object(value)) {
return this.supported_$ref_object[value.$ref];
} else if (this.is_supported_common_$ref_object(value)) {
return this.supported_common_$ref_object[value.$ref];
} else if(this.typed_string_const.is_supported_schema(value)) {
return this.typed_string_const.value_regex(value);
} else if(this.typed_string_enum.is_supported_schema(value)) {
Expand Down Expand Up @@ -278,6 +316,28 @@ export class ValueToRegexFormatter
value.$ref.substring(8)
].oneOf as {[key: string]: unknown}[];

return `(?:${
oneOf
.map((e:SchemaObject) => this.value_to_regex(e))
.join('|')
})`;
} else if (
object_only_has_that_property(value, '$ref', is_string)
&& value.$ref.startsWith('common.schema.json#/$defs/')
&& value.$ref.substring(26) in this.common_$defs
&& object_only_has_that_property(
this.common_$defs[value.$ref.substring(26)],
'oneOf',
)
&& is_non_empty_array(
this.common_$defs[value.$ref.substring(26)].oneOf,
value_is_non_array_object,
)
) {
const oneOf = this.common_$defs[
value.$ref.substring(15)
].oneOf as {[key: string]: unknown}[];

return `(?:${
oneOf
.map((e:SchemaObject) => this.value_to_regex(e))
Expand All @@ -295,6 +355,18 @@ export class ValueToRegexFormatter
return this.value_to_regex(
this.$defs[value.$ref.substring(8)],
);
} else if (
undefined === this.supported_common_$ref_object
&& object_only_has_that_property(value, '$ref', is_string)
&& value.$ref.startsWith('common.schema.json#/$defs/')
&& value.$ref.substring(26) in this.common_$defs
&& this.is_supported_definition(
this.common_$defs[value.$ref.substring(26)],
)
) {
return this.value_to_regex(
this.common_$defs[value.$ref.substring(26)],
);
} else if (
this.is_Texture2D_basic(value)
) {
Expand Down
Loading

0 comments on commit 4704461

Please sign in to comment.