@typespec/compiler
Version:
TypeSpec Compiler Preview
174 lines • 6.21 kB
JavaScript
import { Temporal } from "temporal-polyfill";
import { ignoreDiagnostics } from "../core/diagnostics.js";
import { getProperty } from "../core/semantic-walker.js";
import { isArrayModelType, isUnknownType } from "../core/type-utils.js";
import { getEncode, resolveEncodedName } from "./decorators.js";
/**
* Serialize the given TypeSpec value as a JSON object using the given type and its encoding annotations.
* The Value MUST be assignable to the given type.
*/
export function serializeValueAsJson(program, value, type, encodeAs) {
if (type.kind === "ModelProperty") {
return serializeValueAsJson(program, value, type.type, encodeAs ?? getEncode(program, type));
}
switch (value.valueKind) {
case "NullValue":
return null;
case "BooleanValue":
case "StringValue":
return value.value;
case "NumericValue":
return value.value.asNumber();
case "EnumValue":
return value.value.value ?? value.value.name;
case "ArrayValue":
return value.values.map((v) => serializeValueAsJson(program, v, type.kind === "Model" && isArrayModelType(program, type)
? type.indexer.value
: program.checker.anyType));
case "ObjectValue":
return serializeObjectValueAsJson(program, value, type);
case "ScalarValue":
return serializeScalarValueAsJson(program, value, type, encodeAs);
}
}
/** Try to get the property of the type */
function getPropertyOfType(type, name) {
switch (type.kind) {
case "Model":
return getProperty(type, name) ?? type.indexer?.value;
case "Intrinsic":
if (isUnknownType(type)) {
return type;
}
else {
return;
}
default:
return undefined;
}
}
function resolveUnions(program, value, type) {
if (type.kind !== "Union") {
return type;
}
const exactValueType = program.checker.getValueExactType(value);
for (const variant of type.variants.values()) {
if (ignoreDiagnostics(program.checker.isTypeAssignableTo(exactValueType ?? value.type, variant.type, value))) {
return variant.type;
}
}
return type;
}
function serializeObjectValueAsJson(program, value, type) {
type = resolveUnions(program, value, type) ?? type;
const obj = {};
for (const propValue of value.properties.values()) {
const definition = getPropertyOfType(type, propValue.name);
if (definition) {
const name = definition.kind === "ModelProperty"
? resolveEncodedName(program, definition, "application/json")
: propValue.name;
obj[name] = serializeValueAsJson(program, propValue.value, definition);
}
}
return obj;
}
function resolveKnownScalar(program, scalar) {
const encode = getEncode(program, scalar);
if (program.checker.isStdType(scalar)) {
switch (scalar.name) {
case "utcDateTime":
case "offsetDateTime":
case "plainDate":
case "plainTime":
case "duration":
return { scalar: scalar, encodeAs: encode };
case "unixTimestamp32":
break;
default:
return undefined;
}
}
if (scalar.baseScalar) {
const result = resolveKnownScalar(program, scalar.baseScalar);
return result && { scalar: result.scalar, encodeAs: encode };
}
return undefined;
}
function serializeScalarValueAsJson(program, value, type, encodeAs) {
const result = resolveKnownScalar(program, value.scalar);
if (result === undefined) {
return serializeValueAsJson(program, value.value.args[0], value.value.args[0].type);
}
encodeAs = encodeAs ?? result.encodeAs;
switch (result.scalar.name) {
case "utcDateTime":
return ScalarSerializers.utcDateTime(value.value.args[0].value, encodeAs);
case "offsetDateTime":
return ScalarSerializers.offsetDateTime(value.value.args[0].value, encodeAs);
case "plainDate":
return ScalarSerializers.plainDate(value.value.args[0].value);
case "plainTime":
return ScalarSerializers.plainTime(value.value.args[0].value);
case "duration":
return ScalarSerializers.duration(value.value.args[0].value, encodeAs);
}
}
const ScalarSerializers = {
utcDateTime: (value, encodeAs) => {
if (encodeAs === undefined || encodeAs.encoding === "rfc3339") {
return value;
}
const date = new Date(value);
switch (encodeAs.encoding) {
case "unixTimestamp":
return Math.floor(date.getTime() / 1000);
case "rfc7231":
return date.toUTCString();
default:
return date.toISOString();
}
},
offsetDateTime: (value, encodeAs) => {
if (encodeAs === undefined || encodeAs.encoding === "rfc3339") {
return value;
}
const date = new Date(value);
switch (encodeAs.encoding) {
case "rfc7231":
return date.toUTCString();
default:
return date.toISOString();
}
},
plainDate: (value) => {
return value;
},
plainTime: (value) => {
return value;
},
duration: (value, encodeAs) => {
const duration = Temporal.Duration.from(value);
switch (encodeAs?.encoding) {
case "seconds":
if (isInteger(encodeAs.type)) {
return Math.floor(duration.total({ unit: "seconds" }));
}
else {
return duration.total({ unit: "seconds" });
}
default:
return duration.toString();
}
},
};
function isInteger(scalar) {
while (scalar.baseScalar) {
scalar = scalar.baseScalar;
if (scalar.name === "integer") {
return true;
}
}
return false;
}
//# sourceMappingURL=examples.js.map