Skip to content

Commit

Permalink
refactor(experimental): rename mapCodec to transformCodec (#2434)
Browse files Browse the repository at this point in the history
Currently we have the following codec functions:

```
// Transforms Codec<T> to Codec<U> by providing mapping functions between T and U.
mapCodec(codec, U => T, T => U);

// Creates a Codec<Map<K, V>> by providing a Codec<K> and a Codec<V>.
getMapCodec(keyCodec, valueCodec);
```

Whilst they do completely different things, their names are too close to each other.

This PR fixes this by renaming `mapCodec` to `transformCodec` — i.e. `/map(Encoder|Decoder|Codec)/transform$1/g`.
  • Loading branch information
lorisleiva committed Apr 4, 2024
1 parent 2d48c09 commit 31916ae
Show file tree
Hide file tree
Showing 29 changed files with 194 additions and 167 deletions.
11 changes: 11 additions & 0 deletions .changeset/calm-crabs-tease.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@solana/codecs-data-structures': patch
'@solana/codecs-strings': patch
'@solana/transactions': patch
'@solana/codecs-core': patch
'@solana/addresses': patch
'@solana/rpc-types': patch
'@solana/options': patch
---

Renamed `mapCodec` to `transformCodec`
6 changes: 4 additions & 2 deletions packages/addresses/src/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
FixedSizeDecoder,
FixedSizeEncoder,
fixEncoderSize,
mapEncoder,
transformEncoder,
} from '@solana/codecs-core';
import { getBase58Decoder, getBase58Encoder } from '@solana/codecs-strings';
import {
Expand Down Expand Up @@ -82,7 +82,9 @@ export function address<TAddress extends string = string>(putativeAddress: TAddr
}

export function getAddressEncoder(): FixedSizeEncoder<Address, 32> {
return mapEncoder(fixEncoderSize(getMemoizedBase58Encoder(), 32), putativeAddress => address(putativeAddress));
return transformEncoder(fixEncoderSize(getMemoizedBase58Encoder(), 32), putativeAddress =>
address(putativeAddress),
);
}

export function getAddressDecoder(): FixedSizeDecoder<Address, 32> {
Expand Down
18 changes: 9 additions & 9 deletions packages/codecs-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ There is a significant library of composable codecs at your disposal, enabling y
- [`@solana/codecs-data-structures`](https://github.com/solana-labs/solana-web3.js/tree/master/packages/codecs-data-structures) for many data structure codecs such as objects, arrays, tuples, sets, maps, enums, discriminated unions, booleans, etc.
- [`@solana/options`](https://github.com/solana-labs/solana-web3.js/tree/master/packages/options) for a Rust-like `Option` type and associated codec.

You may also be interested in some of the helpers of this `@solana/codecs-core` library such as `mapCodec`, `fixCodecSize` or `reverseCodec` that create new codecs from existing ones.
You may also be interested in some of the helpers of this `@solana/codecs-core` library such as `transformCodec`, `fixCodecSize` or `reverseCodec` that create new codecs from existing ones.

Note that all of these libraries are included in the [`@solana/codecs` package](https://github.com/solana-labs/solana-web3.js/tree/master/packages/codecs) as well as the main `@solana/web3.js` package for your convenience.

Expand Down Expand Up @@ -113,14 +113,14 @@ const value = u64Codec.decode(bytes); // BigInt(42)

This relationship between the type we encode “From” and decode “To” can be generalized in TypeScript as `To extends From`.

Here’s another example using an object with default values. You can read more about the `mapEncoder` helper below.
Here’s another example using an object with default values. You can read more about the `transformEncoder` helper below.

```ts
type Person = { name: string, age: number };
type PersonInput = { name: string, age?: number };

const getPersonEncoder = (): Encoder<PersonInput> =>
mapEncoder(
transformEncoder(
getStructEncoder([
['name', addEncoderSizePrefix(getUtf8Encoder(), getU32Encoder())],
['age', getU32Encoder()],
Expand Down Expand Up @@ -285,15 +285,15 @@ const getCipherDecoder = () =>
const getCipherCodec = () => combineCodec(getCipherEncoder(), getCipherDecoder());
```

## Mapping codecs
## Transforming codecs

It is possible to transform a `Codec<T>` to a `Codec<U>` by providing two mapping functions: one that goes from `T` to `U` and one that does the opposite.

For instance, here’s how you would map a `u32` integer into a `string` representation of that number.

```ts
const getStringU32Codec = () =>
mapCodec(
transformCodec(
getU32Codec(),
(integerAsString: string): number => parseInt(integerAsString),
(integer: number): string => integer.toString(),
Expand All @@ -309,7 +309,7 @@ To illustrate that, let’s take our previous `getStringU32Codec` example but ma

```ts
const getStringU64Codec = () =>
mapCodec(
transformCodec(
getU64Codec(),
(integerInput: number | string): number | bigint =>
typeof integerInput === 'string' ? BigInt(integerAsString) : integerInput,
Expand All @@ -327,7 +327,7 @@ const getPersonCodec = (): Codec<Person> => { /*...*/ }

type PersonInput = { name: string; age?: number; }
const getPersonWithDefaultValueCodec = (): Codec<PersonInput, Person> =>
mapCodec(
transformCodec(
getPersonCodec(),
(person: PersonInput): Person => { ...person, age: person.age ?? 42 }
)
Expand All @@ -337,8 +337,8 @@ Similar helpers exist to map `Encoder` and `Decoder` instances allowing you to s

```ts
const getStringU32Encoder = () =>
mapEncoder(getU32Encoder(), (integerAsString: string): number => parseInt(integerAsString));
const getStringU32Decoder = () => mapDecoder(getU32Decoder(), (integer: number): string => integer.toString());
transformEncoder(getU32Encoder(), (integerAsString: string): number => parseInt(integerAsString));
const getStringU32Decoder = () => transformDecoder(getU32Decoder(), (integer: number): string => integer.toString());
const getStringU32Codec = () => combineCodec(getStringU32Encoder(), getStringU32Decoder());
```

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Codec, createCodec, createDecoder, createEncoder } from '../codec';
import { mapCodec, mapDecoder, mapEncoder } from '../map-codec';
import { ReadonlyUint8Array } from '../readonly-uint8array';
import { transformCodec, transformDecoder, transformEncoder } from '../transform-codec';

const numberCodec: Codec<number> = createCodec({
fixedSize: 1,
Expand All @@ -11,10 +11,10 @@ const numberCodec: Codec<number> = createCodec({
},
});

describe('mapCodec', () => {
describe('transformCodec', () => {
it('can loosen the codec input with a map', () => {
// From <number> to <number | string, number>.
const mappedCodec: Codec<number | string, number> = mapCodec(numberCodec, (value: number | string) =>
const mappedCodec: Codec<number | string, number> = transformCodec(numberCodec, (value: number | string) =>
// eslint-disable-next-line jest/no-conditional-in-test
typeof value === 'number' ? value : value.length,
);
Expand All @@ -28,7 +28,7 @@ describe('mapCodec', () => {

it('can map both the input and output of a codec', () => {
// From <number> to <number | string, string>.
const mappedCodec: Codec<number | string, string> = mapCodec(
const mappedCodec: Codec<number | string, string> = transformCodec(
numberCodec,
// eslint-disable-next-line jest/no-conditional-in-test
(value: number | string) => (typeof value === 'number' ? value : value.length),
Expand All @@ -44,7 +44,7 @@ describe('mapCodec', () => {

it('can map the input and output of a codec to the same type', () => {
// From <number> to <string>.
const mappedCodec: Codec<string> = mapCodec(
const mappedCodec: Codec<string> = transformCodec(
numberCodec,
(value: string) => value.length,
(value: number) => 'x'.repeat(value),
Expand All @@ -60,7 +60,7 @@ describe('mapCodec', () => {
it('can wrap a codec type in an object using a map', () => {
// From <number> to <{ value: number }>.
type Wrap<T> = { value: T };
const mappedCodec: Codec<Wrap<number>> = mapCodec(
const mappedCodec: Codec<Wrap<number>> = transformCodec(
numberCodec,
(value: Wrap<number>) => value.value,
(value: number): Wrap<number> => ({ value }),
Expand Down Expand Up @@ -93,7 +93,7 @@ describe('mapCodec', () => {

// From <Strict> to <Loose, Strict>.
type Loose = { discriminator?: number; label: string };
const looseCodec: Codec<Loose, Strict> = mapCodec(
const looseCodec: Codec<Loose, Strict> = transformCodec(
strictCodec,
(value: Loose): Strict => ({
discriminator: 42, // <- Default value.
Expand Down Expand Up @@ -132,7 +132,7 @@ describe('mapCodec', () => {
const bytesA = codec.encode([42, 'Hello world']);
expect(codec.decode(bytesA)).toStrictEqual([42, 'xxxxxxxxxxx']);

const mappedCodec = mapCodec(codec, (value: [number | null, string]): [number, string] => [
const mappedCodec = transformCodec(codec, (value: [number | null, string]): [number, string] => [
// eslint-disable-next-line jest/no-conditional-in-test
value[0] ?? value[1].length,
value[1],
Expand All @@ -146,7 +146,7 @@ describe('mapCodec', () => {
});
});

describe('mapEncoder', () => {
describe('transformEncoder', () => {
it('can map an encoder to another encoder', () => {
const encoderA = createEncoder({
fixedSize: 1,
Expand All @@ -156,21 +156,21 @@ describe('mapEncoder', () => {
},
});

const encoderB = mapEncoder(encoderA, (value: string): number => value.length);
const encoderB = transformEncoder(encoderA, (value: string): number => value.length);

expect(encoderB.fixedSize).toBe(1);
expect(encoderB.encode('helloworld')).toStrictEqual(new Uint8Array([10]));
});
});

describe('mapDecoder', () => {
describe('transformDecoder', () => {
it('can map an encoder to another encoder', () => {
const decoder = createDecoder({
fixedSize: 1,
read: (bytes: ReadonlyUint8Array | Uint8Array, offset = 0) => [bytes[offset], offset + 1],
});

const decoderB = mapDecoder(decoder, (value: number): string => 'x'.repeat(value));
const decoderB = transformDecoder(decoder, (value: number): string => 'x'.repeat(value));

expect(decoderB.fixedSize).toBe(1);
expect(decoderB.decode(new Uint8Array([10]))).toBe('xxxxxxxxxx');
Expand Down
45 changes: 0 additions & 45 deletions packages/codecs-core/src/__typetests__/map-codec-typetest.ts

This file was deleted.

45 changes: 45 additions & 0 deletions packages/codecs-core/src/__typetests__/transform-codec-typetest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import {
Codec,
Decoder,
Encoder,
FixedSizeCodec,
FixedSizeDecoder,
FixedSizeEncoder,
VariableSizeCodec,
VariableSizeDecoder,
VariableSizeEncoder,
} from '../codec';
import { transformCodec, transformDecoder, transformEncoder } from '../transform-codec';

{
// [transformEncoder]: It keeps track of the nested encoder's size.
transformEncoder({} as FixedSizeEncoder<string, 42>, (_: number) => '42') satisfies FixedSizeEncoder<number, 42>;
transformEncoder({} as VariableSizeEncoder<string>, (_: number) => '42') satisfies VariableSizeEncoder<number>;
transformEncoder({} as Encoder<string>, (_: number) => '42') satisfies Encoder<number>;
}

{
// [transformDecoder]: It keeps track of the nested decoder's size.
transformDecoder({} as FixedSizeDecoder<string, 42>, (_: string) => 42) satisfies FixedSizeDecoder<number, 42>;
transformDecoder({} as VariableSizeDecoder<string>, (_: string) => 42) satisfies VariableSizeDecoder<number>;
transformDecoder({} as Decoder<string>, (_: string) => 42) satisfies Decoder<number>;
}

{
// [transformCodec]: It keeps track of the nested codec's size.
transformCodec(
{} as FixedSizeCodec<string, string, 42>,
(_: number) => '42',
(_: string) => 42,
) satisfies FixedSizeCodec<number, number, 42>;
transformCodec(
{} as VariableSizeCodec<string>,
(_: number) => '42',
(_: string) => 42,
) satisfies VariableSizeCodec<number>;
transformCodec(
{} as Codec<string>,
(_: number) => '42',
(_: string) => 42,
) satisfies Codec<number>;
}
4 changes: 2 additions & 2 deletions packages/codecs-core/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
export * from './add-codec-size-prefix';
export * from './add-codec-sentinel';
export * from './add-codec-size-prefix';
export * from './assertions';
export * from './bytes';
export * from './codec';
export * from './combine-codec';
export * from './fix-codec-size';
export * from './map-codec';
export * from './offset-codec';
export * from './pad-codec';
export * from './readonly-uint8array';
export * from './resize-codec';
export * from './reverse-codec';
export * from './transform-codec';

0 comments on commit 31916ae

Please sign in to comment.