mvs-data.ts 4.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. /**
  2. * Copyright (c) 2023 mol* contributors, licensed under MIT, See LICENSE file for more info.
  3. *
  4. * @author Adam Midlik <midlik@gmail.com>
  5. */
  6. import { treeValidationIssues } from './tree/generic/tree-schema';
  7. import { treeToString } from './tree/generic/tree-utils';
  8. import { Root, createMVSBuilder } from './tree/mvs/mvs-builder';
  9. import { MVSTree, MVSTreeSchema } from './tree/mvs/mvs-tree';
  10. /** Top level of the MolViewSpec (MVS) data format. */
  11. export interface MVSData {
  12. /** MolViewSpec tree */
  13. root: MVSTree,
  14. /** Associated metadata */
  15. metadata: MVSMetadata,
  16. }
  17. interface MVSMetadata {
  18. /** Version of the spec used to write this tree */
  19. version: string,
  20. /** Name of this view */
  21. title?: string,
  22. /** Detailed description of this view */
  23. description?: string,
  24. /** Format of the description */
  25. description_format?: 'markdown' | 'plaintext',
  26. /** Timestamp when this view was exported */
  27. timestamp: string,
  28. }
  29. export const MVSData = {
  30. /** Currently supported major version of MolViewSpec format (e.g. 1 for version '1.0.8') */
  31. SupportedVersion: 1,
  32. /** Parse MVSJ (MolViewSpec-JSON) format to `MVSData`. Does not include any validation. */
  33. fromMVSJ(mvsjString: string): MVSData {
  34. const result: MVSData = JSON.parse(mvsjString);
  35. const major = majorVersion(result?.metadata?.version);
  36. if (major === undefined) {
  37. console.error('Loaded MVS does not contain valid version info.');
  38. } else if (major > (majorVersion(MVSData.SupportedVersion) ?? 0)) {
  39. console.warn(`Loaded MVS is of higher version (${result.metadata.version}) than currently supported version (${MVSData.SupportedVersion}). Some features may not work as expected.`);
  40. }
  41. return result;
  42. },
  43. /** Encode `MVSData` to MVSJ (MolViewSpec-JSON) string. Use `space` parameter to control formatting (as with `JSON.stringify`). */
  44. toMVSJ(mvsData: MVSData, space?: string | number): string {
  45. return JSON.stringify(mvsData, undefined, space);
  46. },
  47. /** Validate `MVSData`. Return `true` if OK; `false` if not OK.
  48. * If `options.noExtra` is true, presence of any extra node parameters is treated as an issue. */
  49. isValid(mvsData: MVSData, options: { noExtra?: boolean } = {}): boolean {
  50. return MVSData.validationIssues(mvsData, options) === undefined;
  51. },
  52. /** Validate `MVSData`. Return `undefined` if OK; list of issues if not OK.
  53. * If `options.noExtra` is true, presence of any extra node parameters is treated as an issue. */
  54. validationIssues(mvsData: MVSData, options: { noExtra?: boolean } = {}): string[] | undefined {
  55. const version = mvsData?.metadata?.version;
  56. if (typeof version !== 'string') return [`"version" in MVS must be a string, not ${typeof version}: ${version}`];
  57. if (mvsData.root === undefined) return [`"root" missing in MVS`];
  58. return treeValidationIssues(MVSTreeSchema, mvsData.root, options);
  59. },
  60. /** Return a human-friendly textual representation of `mvsData`. */
  61. toPrettyString(mvsData: MVSData): string {
  62. const title = mvsData.metadata.title !== undefined ? ` "${mvsData.metadata.title}"` : '';
  63. return `MolViewSpec tree${title} (version ${mvsData.metadata.version}, created ${mvsData.metadata.timestamp}):\n${treeToString(mvsData.root)}`;
  64. },
  65. /** Create a new MolViewSpec builder containing only a root node. Example of MVS builder usage:
  66. *
  67. * ```
  68. * const builder = MVSData.createBuilder();
  69. * builder.canvas({ background_color: 'white' });
  70. * const struct = builder.download({ url: 'https://www.ebi.ac.uk/pdbe/entry-files/download/1og2_updated.cif' }).parse({ format: 'mmcif' }).modelStructure();
  71. * struct.component().representation().color({ color: HexColor('#3050F8') });
  72. * console.log(MVSData.toPrettyString(builder.getState()));
  73. * ```
  74. */
  75. createBuilder(): Root {
  76. return createMVSBuilder();
  77. },
  78. };
  79. /** Get the major version from a semantic version string, e.g. '1.0.8' -> 1 */
  80. function majorVersion(semanticVersion: string | number): number | undefined {
  81. if (typeof semanticVersion === 'string') return parseInt(semanticVersion.split('.')[0]);
  82. if (typeof semanticVersion === 'number') return Math.floor(semanticVersion);
  83. console.error(`Version should be a string, not ${typeof semanticVersion}: ${semanticVersion}`);
  84. return undefined;
  85. }