From d9aa4005ca841de61c41596fed0447f61b802911 Mon Sep 17 00:00:00 2001 From: Joshua Smithrud <54606601+Josmithr@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:55:22 -0700 Subject: [PATCH] refactor(tree): Extract leaf schemas into their own module (#20289) Extracted from #20271 --- .../tree/src/simple-tree/leafNodeSchema.ts | 70 +++++++++++++++++++ .../dds/tree/src/simple-tree/schemaFactory.ts | 67 +++--------------- 2 files changed, 78 insertions(+), 59 deletions(-) create mode 100644 packages/dds/tree/src/simple-tree/leafNodeSchema.ts diff --git a/packages/dds/tree/src/simple-tree/leafNodeSchema.ts b/packages/dds/tree/src/simple-tree/leafNodeSchema.ts new file mode 100644 index 000000000000..262efe831151 --- /dev/null +++ b/packages/dds/tree/src/simple-tree/leafNodeSchema.ts @@ -0,0 +1,70 @@ +/*! + * Copyright (c) Microsoft Corporation and contributors. All rights reserved. + * Licensed under the MIT License. + */ + +import { assert } from "@fluidframework/core-utils"; +import { TreeNodeSchemaIdentifier, TreeValue } from "../core/index.js"; +import { leaf } from "../domains/index.js"; +import { + LeafNodeSchema as FlexLeafNodeSchema, + FlexTreeNode, + isFlexTreeNode, + valueSchemaAllows, +} from "../feature-libraries/index.js"; +import { NodeKind, TreeNodeSchema, TreeNodeSchemaNonClass } from "./schemaTypes.js"; +import { setFlexSchemaFromClassSchema } from "./toFlexSchema.js"; + +type UnbrandedName = T["name"] extends TreeNodeSchemaIdentifier< + infer Name extends string +> + ? Name + : T["name"]; + +/** + * Instances of this class are schema for leaf nodes. + * @remarks + * Unlike other schema, leaf schema are class instances instead of classes themselves. + * This is because the instance type (the tree node type) for leaves are not objects, + * so those instances can't be instances of a schema based class. + * @privateRemarks + * This class refers to the underlying flex tree schema in its constructor, so this class can't be included in the package API. + */ +class LeafNodeSchema + implements TreeNodeSchemaNonClass, NodeKind.Leaf, TreeValue> +{ + public readonly identifier: UnbrandedName; + public readonly kind = NodeKind.Leaf; + public readonly info: T["info"]; + public readonly implicitlyConstructable = true as const; + public create(data: TreeValue | FlexTreeNode): TreeValue { + if (isFlexTreeNode(data)) { + const value = data.value; + assert(valueSchemaAllows(this.info, value), "invalid value"); + return value; + } + return data; + } + + public constructor(schema: T) { + setFlexSchemaFromClassSchema(this, schema); + this.identifier = schema.name as UnbrandedName; + this.info = schema.info; + } +} + +/** + * Wrapper around LeafNodeSchema's constructor that provides the return type that is desired in the package public API. + */ +function makeLeaf( + schema: T, +): TreeNodeSchema, NodeKind.Leaf, TreeValue, TreeValue> { + return new LeafNodeSchema(schema); +} + +// Leaf schema shared between all SchemaFactory instances. +export const stringSchema = makeLeaf(leaf.string); +export const numberSchema = makeLeaf(leaf.number); +export const booleanSchema = makeLeaf(leaf.boolean); +export const nullSchema = makeLeaf(leaf.null); +export const handleSchema = makeLeaf(leaf.handle); diff --git a/packages/dds/tree/src/simple-tree/schemaFactory.ts b/packages/dds/tree/src/simple-tree/schemaFactory.ts index 1f1964b37b1c..ae6fafb9606a 100644 --- a/packages/dds/tree/src/simple-tree/schemaFactory.ts +++ b/packages/dds/tree/src/simple-tree/schemaFactory.ts @@ -6,10 +6,8 @@ import { assert, unreachableCase } from "@fluidframework/core-utils"; import { UsageError } from "@fluidframework/telemetry-utils"; import { TreeNodeSchemaIdentifier, TreeValue } from "../core/index.js"; -import { leaf } from "../domains/index.js"; import { FlexFieldNodeSchema, - LeafNodeSchema as FlexLeafNodeSchema, FlexMapNodeSchema, FlexObjectNodeSchema, FlexTreeNode, @@ -18,9 +16,15 @@ import { isLazy, markEager, typeNameSymbol, - valueSchemaAllows, } from "../feature-libraries/index.js"; import { RestrictiveReadonlyRecord, getOrCreate, isReadonlyArray } from "../util/index.js"; +import { + booleanSchema, + handleSchema, + nullSchema, + numberSchema, + stringSchema, +} from "./leafNodeSchema.js"; import { arrayNodePrototypeProperties, createArrayNodeProxy, @@ -45,62 +49,13 @@ import { TreeMapNode, TreeNodeSchema, TreeNodeSchemaClass, - TreeNodeSchemaNonClass, WithType, type, } from "./schemaTypes.js"; -import { getFlexSchema, setFlexSchemaFromClassSchema } from "./toFlexSchema.js"; +import { getFlexSchema } from "./toFlexSchema.js"; import { TreeArrayNode } from "./treeArrayNode.js"; import { TreeNode } from "./types.js"; -/** - * Instances of this class are schema for leaf nodes. - * @remarks - * Unlike other schema, leaf schema are class instances instead of classes themselves. - * This is because the instance type (the tree node type) for leaves are not objects, - * so those instances can't be instances of a schema based class. - * @privateRemarks - * This class refers to the underlying flex tree schema in its constructor, so this class can't be included in the package API. - */ -class LeafNodeSchema - implements TreeNodeSchemaNonClass, NodeKind.Leaf, TreeValue> -{ - public readonly identifier: UnbrandedName; - public readonly kind = NodeKind.Leaf; - public readonly info: T["info"]; - public readonly implicitlyConstructable = true as const; - public create(data: TreeValue | FlexTreeNode): TreeValue { - if (isFlexTreeNode(data)) { - const value = data.value; - assert(valueSchemaAllows(this.info, value), "invalid value"); - return value; - } - return data; - } - - public constructor(schema: T) { - setFlexSchemaFromClassSchema(this, schema); - this.identifier = schema.name as UnbrandedName; - this.info = schema.info; - } -} - -/** - * Wrapper around LeafNodeSchema's constructor that provides the return type that is desired in the package public API. - */ -function makeLeaf( - schema: T, -): TreeNodeSchema, NodeKind.Leaf, TreeValue, TreeValue> { - return new LeafNodeSchema(schema); -} - -// Leaf schema shared between all SchemaFactory instances. -const stringSchema = makeLeaf(leaf.string); -const numberSchema = makeLeaf(leaf.number); -const booleanSchema = makeLeaf(leaf.boolean); -const nullSchema = makeLeaf(leaf.null); -const handleSchema = makeLeaf(leaf.handle); - /** * Gets the leaf domain schema compatible with a given {@link TreeValue}. */ @@ -124,12 +79,6 @@ export function schemaFromValue(value: TreeValue): TreeNodeSchema { } } -type UnbrandedName = T["name"] extends TreeNodeSchemaIdentifier< - infer Name extends string -> - ? Name - : T["name"]; - /** * The name of a schema produced by {@link SchemaFactory}, including its optional scope prefix. *