generate.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /**
  2. * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Alexander Rose <alexander.rose@weirdbyte.de>
  5. */
  6. import { Database, Filter, Column } from './schema';
  7. import { indentString } from '../../../mol-util/string';
  8. import { FieldPath } from '../../../mol-io/reader/cif/schema';
  9. function header(name: string, info: string, moldataImportPath: string) {
  10. return `/**
  11. * Copyright (c) 2017-2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
  12. *
  13. * Code-generated '${name}' schema file. ${info}
  14. *
  15. * @author molstar/ciftools package
  16. */
  17. import { Database, Column } from '${moldataImportPath}/db';
  18. import Schema = Column.Schema;`;
  19. }
  20. function footer(name: string) {
  21. return `
  22. export type ${name}_Schema = typeof ${name}_Schema;
  23. export interface ${name}_Database extends Database<${name}_Schema> {};`;
  24. }
  25. function getTypeShorthands(schema: Database, fields?: Filter) {
  26. const types = new Set<string>();
  27. Object.keys(schema.tables).forEach(table => {
  28. if (fields && !fields[table]) return;
  29. const { columns } = schema.tables[table];
  30. Object.keys(columns).forEach(columnName => {
  31. if (fields && !fields[table][columnName]) return;
  32. const col = schema.tables[table].columns[columnName];
  33. if (col.type === 'enum') types.add(col.subType);
  34. types.add(col.type);
  35. });
  36. });
  37. const shorthands: string[] = [];
  38. types.forEach(type => {
  39. switch (type) {
  40. case 'str': shorthands.push('const str = Schema.str;'); break;
  41. case 'ustr': shorthands.push('const ustr = Schema.ustr;'); break;
  42. case 'lstr': shorthands.push('const lstr = Schema.lstr;'); break;
  43. case 'int': shorthands.push('const int = Schema.int;'); break;
  44. case 'float': shorthands.push('const float = Schema.float;'); break;
  45. case 'coord': shorthands.push('const coord = Schema.coord;'); break;
  46. case 'enum': shorthands.push('const Aliased = Schema.Aliased;'); break;
  47. case 'matrix': shorthands.push('const Matrix = Schema.Matrix;'); break;
  48. case 'vector': shorthands.push('const Vector = Schema.Vector;'); break;
  49. case 'list': shorthands.push('const List = Schema.List;'); break;
  50. }
  51. });
  52. return shorthands.join('\n');
  53. }
  54. function getTypeDef(c: Column): string {
  55. switch (c.type) {
  56. case 'str': return 'str';
  57. case 'int': return 'int';
  58. case 'float': return 'float';
  59. case 'coord': return 'coord';
  60. case 'enum':
  61. return `Aliased<'${c.values.map(v => v.replace(/'/g, '\\\'')).join(`' | '`)}'>(${c.subType})`;
  62. case 'matrix':
  63. return `Matrix(${c.rows}, ${c.columns})`;
  64. case 'vector':
  65. return `Vector(${c.length})`;
  66. case 'list':
  67. if (c.subType === 'int') {
  68. return `List('${c.separator}', x => parseInt(x, 10))`;
  69. } else if (c.subType === 'float' || c.subType === 'coord') {
  70. return `List('${c.separator}', x => parseFloat(x))`;
  71. } else {
  72. return `List('${c.separator}', x => x)`;
  73. }
  74. }
  75. }
  76. const reSafePropertyName = /^[a-zA-Z_$][0-9a-zA-Z_$]*$/;
  77. function safePropertyString(name: string) { return name.match(reSafePropertyName) ? name : `'${name}'`; }
  78. function doc(description: string, spacesCount: number) {
  79. const spaces = ' '.repeat(spacesCount);
  80. return [
  81. `${spaces}/**`,
  82. `${indentString(description, 1, `${spaces} * `)}`.replace(/ +\n/g, '\n'),
  83. `${spaces} */`
  84. ].join('\n');
  85. }
  86. export function generate(name: string, info: string, schema: Database, fields: Filter | undefined, moldataImportPath: string, addAliases: boolean) {
  87. const codeLines: string[] = [];
  88. if (fields) {
  89. Object.keys(fields).forEach(table => {
  90. if (table in schema.tables) {
  91. const schemaTable = schema.tables[table];
  92. Object.keys(fields[table]).forEach(column => {
  93. if (!(column in schemaTable.columns)) {
  94. console.log(`filter field '${table}.${column}' not found in schema`);
  95. }
  96. });
  97. } else {
  98. console.log(`filter category '${table}' not found in schema`);
  99. }
  100. });
  101. }
  102. codeLines.push(`export const ${name}_Schema = {`);
  103. Object.keys(schema.tables).forEach(table => {
  104. if (fields && !fields[table]) return;
  105. const { description, columns } = schema.tables[table];
  106. if (description) codeLines.push(doc(description, 4));
  107. codeLines.push(` ${safePropertyString(table)}: {`);
  108. Object.keys(columns).forEach(columnName => {
  109. if (fields && !fields[table][columnName]) return;
  110. const c = columns[columnName];
  111. const typeDef = getTypeDef(c);
  112. if (c.description) codeLines.push(doc(c.description, 8));
  113. codeLines.push(` ${safePropertyString(columnName)}: ${typeDef},`);
  114. });
  115. codeLines.push(' },');
  116. });
  117. codeLines.push('};');
  118. if (addAliases) {
  119. codeLines.push('');
  120. codeLines.push(`export const ${name}_Aliases = {`);
  121. Object.keys(schema.aliases).forEach(path => {
  122. const [table, columnName] = path.split('.');
  123. if (fields && !fields[table]) return;
  124. if (fields && !fields[table][columnName]) return;
  125. const filteredAliases = new Set<string>();
  126. schema.aliases[path].forEach(p => {
  127. if (!FieldPath.equal(p, path)) filteredAliases.add(FieldPath.canonical(p));
  128. });
  129. if (filteredAliases.size === 0) return;
  130. codeLines.push(` ${safePropertyString(path)}: [`);
  131. filteredAliases.forEach(alias => {
  132. codeLines.push(` '${alias}',`);
  133. });
  134. codeLines.push(' ],');
  135. });
  136. codeLines.push('};');
  137. }
  138. return `${header(name, info, moldataImportPath)}\n\n${getTypeShorthands(schema, fields)}\n\n${codeLines.join('\n')}\n${footer(name)}`;
  139. }