Skip to content

Commit

Permalink
Merge pull request #489 from stepchowfun/permissive-signatures
Browse files Browse the repository at this point in the history
Make some type signatures more permissive
  • Loading branch information
stepchowfun authored Jun 20, 2024
2 parents e98fe44 + 333d4fc commit 4b6280b
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 105 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.12.1] - 2024-06-19

### Changed
- The TypeScript deserialization functions now have a more permissive type signature.

## [0.12.0] - 2024-06-19

### Changed
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "typical"
version = "0.12.0"
version = "0.12.1"
authors = ["Stephan Boyer <stephan@stephanboyer.com>"]
edition = "2021"
description = "Data interchange with algebraic data types."
Expand Down
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,9 @@ let message = SendEmailRequestOut {
body: "It makes serialization easy and safe.".to_owned(),
};

let file = BufWriter::new(File::create(FILE_PATH)?);
message.serialize(file)?;
let mut file = BufWriter::new(File::create(REQUEST_FILE_PATH)?);
message.serialize(&mut file)?;
file.flush()?;
```

Another program could read the file and deserialize the message as follows:
Expand Down
20 changes: 11 additions & 9 deletions examples/rust/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod types;
use {
std::{
fs::{remove_file, File},
io::{self, BufReader, BufWriter},
io::{self, BufReader, BufWriter, Write},
},
types::{
types::{
Expand All @@ -16,7 +16,7 @@ use {
const REQUEST_FILE_PATH: &str = "/tmp/request";
const RESPONSE_FILE_PATH: &str = "/tmp/response";

fn write_to_file() -> io::Result<()> {
fn write_to_files() -> io::Result<()> {
let request_message = SendEmailRequestOut {
to: "typical@example.com".to_owned(),
subject: "I love Typical!".to_owned(),
Expand All @@ -25,14 +25,16 @@ fn write_to_file() -> io::Result<()> {

let response_message = SendEmailResponseOut::Error("Example error".to_string());

let request_file = BufWriter::new(File::create(REQUEST_FILE_PATH)?);
request_message.serialize(request_file)?;
let mut request_file = BufWriter::new(File::create(REQUEST_FILE_PATH)?);
request_message.serialize(&mut request_file)?;
request_file.flush()?;

let response_file = BufWriter::new(File::create(RESPONSE_FILE_PATH)?);
response_message.serialize(response_file)
let mut response_file = BufWriter::new(File::create(RESPONSE_FILE_PATH)?);
response_message.serialize(&mut response_file)?;
response_file.flush()
}

fn read_from_file() -> io::Result<()> {
fn read_from_files() -> io::Result<()> {
let request_file = BufReader::new(File::open(REQUEST_FILE_PATH)?);
let request_message = SendEmailRequestIn::deserialize(request_file)?;

Expand All @@ -52,8 +54,8 @@ fn read_from_file() -> io::Result<()> {
}

fn main() -> io::Result<()> {
write_to_file()?;
read_from_file()?;
write_to_files()?;
read_from_files()?;
remove_file(REQUEST_FILE_PATH)?;
remove_file(RESPONSE_FILE_PATH)
}
26 changes: 6 additions & 20 deletions examples/typescript/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const requestFilePath = '/tmp/request';
const responseFilePath = '/tmp/response';
const { SendEmailRequest, SendEmailResponse } = Types;

function writeToFile(): void {
function writeToFiles(): void {
const requestMessage = {
to: 'typical@example.com',
subject: 'I love Typical!',
Expand All @@ -23,29 +23,15 @@ function writeToFile(): void {
writeFileSync(responseFilePath, Buffer.from(responseArrayBuffer));
}

function readFromFile(): void {
function readFromFiles(): void {
const requestFileContents = readFileSync(requestFilePath);
const requestMessage = SendEmailRequest.deserialize(
new DataView(
requestFileContents.buffer,
requestFileContents.byteOffset,
requestFileContents.byteLength,
),
);

const requestMessage = SendEmailRequest.deserialize(requestFileContents);
if (requestMessage instanceof Error) {
throw requestMessage;
}

const responseFileContents = readFileSync(responseFilePath);
const responseMessage = SendEmailResponse.deserialize(
new DataView(
responseFileContents.buffer,
responseFileContents.byteOffset,
responseFileContents.byteLength,
),
);

const responseMessage = SendEmailResponse.deserialize(responseFileContents);
if (responseMessage instanceof Error) {
throw responseMessage;
}
Expand All @@ -72,7 +58,7 @@ function readFromFile(): void {
return undefined; // To satisfy ESLint's `consistent-return` rule
}

writeToFile();
readFromFile();
writeToFiles();
readFromFiles();
unlinkSync(requestFilePath);
unlinkSync(responseFilePath);
7 changes: 3 additions & 4 deletions integration_tests/typescript_node/src/assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ try {
export function assertMatch<O, I>(
size: (message: O) => number,
serialize: (message: O) => ArrayBuffer,
deserialize: (dataView: DataView) => I,
deserialize: (bytes: ArrayBuffer) => I,
actual: O,
expected: unknown,
): void {
Expand All @@ -27,14 +27,13 @@ export function assertMatch<O, I>(
console.log('Expected size of the serialized message:', actualSize);

const arrayBuffer = serialize(actual);
const dataView = new DataView(arrayBuffer);
deepStrictEqual(arrayBuffer.byteLength, actualSize);
console.log('Bytes from serialization:', arrayBuffer);
console.log('Size of the serialized message:', arrayBuffer.byteLength);

writeFileSync(omnifilePath, Buffer.from(arrayBuffer), { flag: 'as' });

const replica = deserialize(dataView);
const replica = deserialize(arrayBuffer);
deepStrictEqual(replica, expected);
console.log('Message deserialized from those bytes:', replica);

Expand All @@ -44,7 +43,7 @@ export function assertMatch<O, I>(
export function assertRoundTrip<O, I, V extends O>(
size: (message: O) => number,
serialize: (message: O) => ArrayBuffer,
deserialize: (dataView: DataView) => I,
deserialize: (bytes: ArrayBuffer) => I,
message: V,
): void {
assertMatch(size, serialize, deserialize, message, message);
Expand Down
7 changes: 3 additions & 4 deletions integration_tests/typescript_web/src/assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function deepStrictEqual<T, U>(x: T, y: U): void {
export function assertMatch<O, I>(
size: (message: O) => number,
serialize: (message: O) => ArrayBuffer,
deserialize: (dataView: DataView) => I,
deserialize: (bytes: ArrayBuffer) => I,
actual: O,
expected: unknown,
): void {
Expand All @@ -32,7 +32,6 @@ export function assertMatch<O, I>(
console.log('Expected size of the serialized message:', actualSize);

const arrayBuffer = serialize(actual);
const dataView = new DataView(arrayBuffer);
deepStrictEqual(arrayBuffer.byteLength, actualSize);
console.log('Bytes from serialization:', arrayBuffer);
console.log('Size of the serialized message:', arrayBuffer.byteLength);
Expand All @@ -41,7 +40,7 @@ export function assertMatch<O, I>(
omnifileArray.set(typedArray, omnifileOffset);
omnifileOffset += arrayBuffer.byteLength;

const replica = deserialize(dataView);
const replica = deserialize(arrayBuffer);
deepStrictEqual(replica, expected);
console.log('Message deserialized from those bytes:', replica);

Expand All @@ -51,7 +50,7 @@ export function assertMatch<O, I>(
export function assertRoundTrip<O, I, V extends O>(
size: (message: O) => number,
serialize: (message: O) => ArrayBuffer,
deserialize: (dataView: DataView) => I,
deserialize: (bytes: ArrayBuffer) => I,
message: V,
): void {
assertMatch(size, serialize, deserialize, message, message);
Expand Down
93 changes: 72 additions & 21 deletions src/generate_typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,21 @@ pub fn generate(
/* eslint-disable */
export type Deserializable =
| ArrayBuffer
| DataView
| Int8Array
| Uint8Array
| Uint8ClampedArray
| Int16Array
| Uint16Array
| Int32Array
| Uint32Array
| Float32Array
| Float64Array
| BigInt64Array
| BigUint64Array;
export function unreachable(x: never): never {{
return x;
}}
Expand Down Expand Up @@ -614,7 +629,13 @@ fn write_schema<T: Write>(

writeln!(buffer)?;

write_serialize_function(buffer, indentation + 1, &declaration.name)?;
write_serialize_function(
buffer,
indentation + 1,
&declaration.variant,
&declaration.name,
&declaration.fields,
)?;

writeln!(buffer)?;

Expand Down Expand Up @@ -1001,7 +1022,13 @@ fn write_schema<T: Write>(

writeln!(buffer)?;

write_serialize_function(buffer, indentation + 1, &declaration.name)?;
write_serialize_function(
buffer,
indentation + 1,
&declaration.variant,
&declaration.name,
&declaration.fields,
)?;

writeln!(buffer)?;

Expand Down Expand Up @@ -1452,29 +1479,35 @@ fn write_size_function<T: Write>(
fn write_serialize_function<T: Write>(
buffer: &mut T,
indentation: usize,
declaration_variant: &schema::DeclarationVariant,
name: &Identifier,
fields: &[schema::Field],
) -> Result<(), fmt::Error> {
write_indentation(buffer, indentation)?;
write!(buffer, "export function serialize(message: ")?;
write_identifier(buffer, name, Pascal, Some(Out))?;
writeln!(buffer, "): ArrayBuffer {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "const messageAtlas = atlas(message);")?;
write_indentation(buffer, indentation + 1)?;
writeln!(
buffer,
"const arrayBuffer = \
new ArrayBuffer((messageAtlas as {{ $size: number }}).$size);",
)?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "const dataView = new DataView(arrayBuffer);")?;
write_indentation(buffer, indentation + 1)?;
writeln!(
buffer,
"serializeWithAtlasUnsafe(dataView, 0, message, messageAtlas);",
)?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "return arrayBuffer;")?;
if let (schema::DeclarationVariant::Choice, true) = (declaration_variant, fields.is_empty()) {
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "return unreachable(message);")?;
} else {
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "const messageAtlas = atlas(message);")?;
write_indentation(buffer, indentation + 1)?;
writeln!(
buffer,
"const arrayBuffer = new ArrayBuffer(messageAtlas.$size);",
)?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "const dataView = new DataView(arrayBuffer);")?;
write_indentation(buffer, indentation + 1)?;
writeln!(
buffer,
"serializeWithAtlasUnsafe(dataView, 0, message, messageAtlas);",
)?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "return arrayBuffer;")?;
}
write_indentation(buffer, indentation)?;
writeln!(buffer, "}}")
}
Expand All @@ -1486,13 +1519,31 @@ fn write_deserialize_function<T: Write>(
name: &Identifier,
) -> Result<(), fmt::Error> {
write_indentation(buffer, indentation)?;
write!(buffer, "export function deserialize(dataView: DataView): ")?;
write!(
buffer,
"export function deserialize(bytes: Deserializable): ",
)?;
write_identifier(buffer, name, Pascal, Some(In))?;
writeln!(buffer, " | Error {{")?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "try {{")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "return deserializeUnsafe(dataView);")?;
writeln!(buffer, "if (bytes instanceof ArrayBuffer) {{")?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "return deserializeUnsafe(new DataView(bytes));")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "if (bytes instanceof DataView) {{")?;
write_indentation(buffer, indentation + 3)?;
writeln!(buffer, "return deserializeUnsafe(bytes);")?;
write_indentation(buffer, indentation + 2)?;
writeln!(buffer, "}}")?;
write_indentation(buffer, indentation + 2)?;
writeln!(
buffer,
"return deserializeUnsafe(new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength));",
)?;
write_indentation(buffer, indentation + 1)?;
writeln!(buffer, "}} catch (e) {{")?;
write_indentation(buffer, indentation + 2)?;
Expand Down
Loading

0 comments on commit 4b6280b

Please sign in to comment.