Skip to content

Commit

Permalink
fixup! fix: more generic BlockCodec, remove encoder & decoder props
Browse files Browse the repository at this point in the history
  • Loading branch information
rvagg committed Apr 21, 2021
1 parent 2ea0393 commit 0e9c146
Show file tree
Hide file tree
Showing 8 changed files with 46 additions and 75 deletions.
47 changes: 25 additions & 22 deletions src/codecs/codec.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
// @ts-check

/**
* @template {number} Code
* @template T
* @typedef {import('./interface').CodecFeature<Code, T>} CodecFeature
*/

/**
* @template {number} Code
* @template T
Expand All @@ -19,33 +13,20 @@
*/

/**
* @template {string} Name
* @template {number} Code
* @template T
* @param {Object} options
* @param {Name} options.name
* @param {Code} options.code
* @param {(data:T) => Uint8Array} options.encode
* @param {(bytes:Uint8Array) => T} options.decode
* @returns {CodecFeature<Code, T>}
* @typedef {import('./interface').BlockCodec<Code, T>} BlockCodec
*/
export const codec = ({ name, code, decode, encode }) => {
const decoder = new Decoder(name, code, decode)
const encoder = new Encoder(name, code, encode)

return { name, code, decode, encode, decoder, encoder }
}

/**
* @class
* @template T
* @template {string} Name
* @template {number} Code
* @template T
* @implements {BlockEncoder<Code, T>}
*/
export class Encoder {
/**
* @param {Name} name
* @param {string} name

This comment has been minimized.

Copy link
@Gozala

Gozala Apr 22, 2021

Contributor

The reason name used generic type as opposed to string is because that leads TS to do a precise inference

image

As per image above when generic is used TS retains actual literal in the type and subsequently generates better docs, does more useful tooltips in vscode etc..

Link to the live version of that screenshot

This comment has been minimized.

Copy link
@rvagg

rvagg Apr 22, 2021

Author Member

hm, I didn't intend to change this, I think I was at the wac-a-mole just-make-it-compile phase here, will try and squash

* @param {Code} code
* @param {(data:T) => Uint8Array} encode
*/
Expand All @@ -54,6 +35,17 @@ export class Encoder {
this.code = code
this.encode = encode
}

/**
* @template {number} Code
* @template T
* @param {BlockCodec<Code, T>} codec
* @returns {BlockEncoder<Code, T>}
*/
static fromCodec (codec) {
const { name, code, encode } = codec
return new Encoder(name, code, encode)
}
}

/**
Expand All @@ -73,4 +65,15 @@ export class Decoder {
this.code = code
this.decode = decode
}

/**
* @template {number} Code
* @template T
* @param {BlockCodec<Code, T>} codec
* @returns {BlockDecoder<Code, T>}
*/
static fromCodec (codec) {
const { name, code, decode } = codec
return new Decoder(name, code, decode)
}
}
15 changes: 2 additions & 13 deletions src/codecs/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,16 @@ export interface BlockEncoder<Code extends number, T> {
* IPLD decoder part of the codec.
*/
export interface BlockDecoder<Code extends number, T> {
name: string

This comment has been minimized.

Copy link
@Gozala

Gozala Apr 22, 2021

Contributor

I had some reason to leave out name property, sadly I can no longer recall what was it. I would leave it out unless there is a good reason to have it.

This comment has been minimized.

Copy link
@rvagg

rvagg Apr 22, 2021

Author Member

yeah, I was trying to think of what that might be but it's in the Decoder class so it looked like an oversight here

will remove it from here

code: Code
decode(bytes: ByteView<T>): T
}

/**
* IPLD codec that is just Encoder + Decoder however it is
* separate those capabilties as sender requires encoder and receiver
* requires decoder.
* An IPLD codec is a combination of both encoder and decoder.
*/
export interface BlockCodec<Code extends number, T> extends BlockEncoder<Code, T>, BlockDecoder<Code, T> {}

/**
* A roll-up type from which you can derive any of `BlockCodec`,
* `BlockEncoder` or `BlockDecoder`. Intended as the interface exported
* by codec implementations.
*/
export interface CodecFeature<Code extends number, T> extends BlockCodec<Code, T> {
encoder: BlockEncoder<Code, T>,
decoder: BlockDecoder<Code, T>
}

// This just a hack to retain type information about the data that
// is encoded `T` Because it's a union `data` field is never going
// to be usable anyway.
Expand Down
10 changes: 4 additions & 6 deletions src/codecs/json.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
// @ts-check

import { codec } from './codec.js'

/**
* @template {number} Code
* @template T
* @typedef {import('./interface').CodecFeature<Code, T>} CodecFeature
* @typedef {import('./interface').BlockCodec<Code, T>} BlockCodec
*/

/**
* @template T
* @type {CodecFeature<0x0200, T>}
* @type {BlockCodec<0x0200, T>}
*/
export const { name, code, decode, encode, decoder, encoder } = codec({
export const { name, code, encode, decode } = {
name: 'json',
code: 0x0200,
encode: json => new TextEncoder().encode(JSON.stringify(json)),
decode: bytes => JSON.parse(new TextDecoder().decode(bytes))
})
}
9 changes: 4 additions & 5 deletions src/codecs/raw.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
// @ts-check

import { coerce } from '../bytes.js'
import { codec } from './codec.js'

/**
* @template {number} Code
* @template T
* @typedef {import('./interface').CodecFeature<Code, T>} CodecFeature
* @typedef {import('./interface').BlockCodec<Code, T>} BlockCodec
*/

/**
Expand All @@ -17,11 +16,11 @@ const raw = (bytes) => coerce(bytes)

/**
* @template T
* @type {CodecFeature<0x55, Uint8Array>}
* @type {BlockCodec<0x55, Uint8Array>}
*/
export const { name, code, decode, encode, decoder, encoder } = codec({
export const { name, code, encode, decode } = {
name: 'raw',
code: 0x55,
decode: raw,
encode: raw
})
}
5 changes: 2 additions & 3 deletions test/test-legacy.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { legacy } from 'multiformats/legacy'
import * as rawCodec from 'multiformats/codecs/raw'
import * as jsonCodec from 'multiformats/codecs/json'
import { sha256, sha512 } from 'multiformats/hashes/sha2'
import { codec } from 'multiformats/codecs/codec'
import { CID } from 'multiformats/cid'

const same = assert.deepStrictEqual
Expand Down Expand Up @@ -36,7 +35,7 @@ describe('multicodec', () => {
raw = legacy(rawCodec, { hashes })
json = legacy(jsonCodec, { hashes })
link = await raw.util.cid(Buffer.from('test'))
custom = legacy(codec({
custom = legacy({
name: 'custom',
code: 6787678,
encode: o => {
Expand All @@ -52,7 +51,7 @@ describe('multicodec', () => {
if (obj.o.link) obj.link = CID.asCID(link)
return obj
}
}), { hashes })
}, { hashes })
})
test('encode/decode raw', () => {
const buff = raw.util.serialize(Buffer.from('test'))
Expand Down
29 changes: 6 additions & 23 deletions test/test-multicodec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import * as bytes from '../src/bytes.js'
import assert from 'assert'
import * as raw from 'multiformats/codecs/raw'
import * as json from 'multiformats/codecs/json'
import { codec } from 'multiformats/codecs/codec'
import { Encoder, Decoder } from 'multiformats/codecs/codec'

const same = assert.deepStrictEqual
const test = it

Expand Down Expand Up @@ -31,37 +32,19 @@ describe('multicodec', () => {
same(json.decode(buff), { hello: 'world' })
})

test('json.encoder', () => {
const { encoder } = json
same(encoder === json.encoder, true, 'getter cached decoder')

test('json Encoder', () => {
const encoder = Encoder.fromCodec(json)
const buff = encoder.encode({ hello: 'world' })
same(buff, bytes.fromString(JSON.stringify({ hello: 'world' })))
})

test('json.decoder', () => {
const { decoder } = json
same(decoder === json.decoder, true, 'getter cached encoder')

test('json Decoder', () => {
const decoder = Decoder.fromCodec(json)
const buff = json.encode({ hello: 'world' })
same(decoder.decode(buff), { hello: 'world' })
})

test('raw cannot encode string', async () => {
await testThrow(() => raw.encode('asdf'), 'Unknown type, must be binary type')
})

test('add with function', () => {
const blip = codec({
code: 200,
name: 'blip',
encode: (a) => a[1],
decode: (a) => a
})

const two = bytes.fromString('two')
const three = bytes.fromString('three')
same(blip.encode(['one', two, three]), two)
same(blip.decode(three, 200), three)
})
})
2 changes: 1 addition & 1 deletion test/ts-use/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
"multiformats": "file:../../dist/"
},
"scripts": {
"test": "npm install && npx -p typescript tsc --noEmit"
"test": "npm install && npm_config_yes=true npx -p typescript tsc --noEmit"
}
}
4 changes: 2 additions & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@
"skipLibCheck": true,
"stripInternal": true,
"resolveJsonModule": true,
"baseUrl": ".",
"paths": {
"multiformats": [
"src"
]
},
"baseUrl": "."
}
},
"include": [
"src"
Expand Down

0 comments on commit 0e9c146

Please sign in to comment.