@radixdlt/atom
Version:
Container for CRUD instructions known as 'Particles' that are sent to the Radix decentralized ledger
263 lines (235 loc) • 7.67 kB
text/typescript
import {
AnyDownParticle,
AnySpunParticle,
AnyUpParticle,
DownParticle,
ParticleBase,
Spin,
SpunParticleT,
SpunParticleBase,
UpParticle,
} from './_types'
import { isSpin } from './meta/spin'
import { err, ok, Result } from 'neverthrow'
import {
DSONEncoding,
DSONPrimitive,
JSONEncoding,
taggedObjectDecoder,
} from '@radixdlt/data-formats'
import { SERIALIZER_KEY } from '../_types'
import { JSONDecoding } from '../utils'
const SERIALIZER = 'radix.spun_particle'
const JSONDecoder = taggedObjectDecoder(
SERIALIZER,
SERIALIZER_KEY,
)((input: SpunParticleBase) => ok(anySpunParticle(input)))
const jsonDecoding = JSONDecoding.withDecoders(JSONDecoder).create<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
SpunParticleT<any>
>()
/* eslint-disable max-params */
const anySpunParticlesEquals = (
lhs: SpunParticleBase,
rhs: SpunParticleBase,
): boolean => {
return (
lhs.spin === rhs.spin &&
lhs.particle.equals(rhs.particle) &&
rhs.particle.equals(lhs.particle)
)
}
/* eslint-enable max-params */
/**
* Creates an AnySpunParticle (type-erased SpunParticle) with a specified spin.
*
* @param spunParticleBase {SpunParticleBase} A particle of any type of to give a spin.
* @returns {AnySpunParticle} an AnySpunParticle (type-erased SpunParticle) with a specified spin.
*/
export const anySpunParticle = (
spunParticleBase: SpunParticleBase,
): AnySpunParticle => ({
...spunParticleBase,
...JSONEncoding(SERIALIZER)({
particle: spunParticleBase.particle,
spin: spunParticleBase.spin,
}),
...DSONEncoding(SERIALIZER)({
particle: spunParticleBase.particle,
spin: DSONPrimitive(spunParticleBase.spin),
}),
equals: (other: SpunParticleBase): boolean =>
anySpunParticlesEquals(spunParticleBase, other),
downedAsAny: (): Result<AnyDownParticle, Error> =>
spunParticleBase.spin === Spin.UP
? ok(anyDownParticle(spunParticleBase.particle))
: err(new Error('Cannot down a particle with spin Down')),
})
/**
* Creates a typed SpunParticle with a specified spin.
*
* @param particle {P} A particle of typed type `P` to give a spin.
* @param spin {Spin} The spun of the particle
* @returns {SpunParticleT<P>} a typed SpunParticle with a specified spin.
* @template P a specific type of particle, that **is a** `ParticleBase`
*/
export const spunParticle = <P extends ParticleBase>(
input: Readonly<{
spin: Spin
particle: P
}>,
): SpunParticleT<P> => {
const anySpun = anySpunParticle(input)
return {
...anySpun,
particle: input.particle,
eraseToAny: () => anySpun,
downed: (): Result<DownParticle<P>, Error> =>
input.spin === Spin.UP
? ok(downParticle(input.particle))
: err(new Error('Cannot down a particle with spin Down')),
}
}
/**
* Creates a typed UpParticle, a container for typed particle with the at compile time known Spin.UP.
*
* @param particle {P} A particle of typed type `P` to give the spin UP.
* @returns {UpParticle<P>} a typed UpParticle, a container for typed particle with the at compile time known Spin.UP.
* @template P a specific type of particle, that **is a** `ParticleBase`
*/
export const upParticle = <P extends ParticleBase>(
particle: P,
): UpParticle<P> => {
const spin = Spin.UP
const spun = spunParticle({ particle, spin })
return {
...spun,
spin,
toSpunParticle: () => spun,
eraseToAnyUp: () => anyUpParticle(particle),
}
}
/**
* Creates a typed DownParticle, a container for typed particle with the at compile time known Spin.Down.
*
* @param particle {P} A particle of typed type `P` to give the spin DOWN.
* @returns {DownParticle<P>} a typed DownParticle, a container for typed particle with the at compile time known Spin.Down.
* @template P a specific type of particle, that **is a** `ParticleBase`
*/
export const downParticle = <P extends ParticleBase>(
particle: P,
): DownParticle<P> => {
const spin = Spin.DOWN
const spun = spunParticle({ particle, spin })
return {
...spun,
spin,
toSpunParticle: () => spun,
eraseToAnyDown: () => anyDownParticle(particle),
}
}
/**
* Creates a typed SpunParticle with Spin.UP.
*
* @param particle {P} A particle of typed type `P` to give the spin UP.
* @returns {SpunParticleT<P>} a typed SpunParticle with a spin UP.
* @template P a specific type of particle, that **is a** `ParticleBase`
*/
export const spunUpParticle = <P extends ParticleBase>(
particle: P,
): SpunParticleT<P> => upParticle(particle).toSpunParticle()
/**
* Creates a typed SpunParticle with Spin.DOWN.
*
* @param particle {P} A particle of typed type `P` to give the spin DOWN.
* @returns {SpunParticleT<P>} a typed SpunParticle with a spin DOWN.
* @template P a specific type of particle, that **is a** `ParticleBase`
*/
export const spunDownParticle = <P extends ParticleBase>(
particle: P,
): SpunParticleT<P> => downParticle(particle).toSpunParticle()
/**
* Creates an AnyUpParticle (type-erased UpParticle) with the at compile time known spin UP.
*
* @param particle {ParticleBase} A particle of any type of to give the spin UP.
* @returns {AnyUpParticle} an AnyUpParticle (type-erased UpParticle) with the at compile time known spin UP.
*/
export const anyUpParticle = (particle: ParticleBase): AnyUpParticle => {
const spin = Spin.UP
const anySpun = anySpunParticle({ particle, spin })
return {
...anySpun,
spin,
toAnySpunParticle: () => anySpun,
}
}
/**
* Creates an AnyDownParticle (type-erased DownParticle) with the at compile time known spin DOWN.
*
* @param particle {ParticleBase} A particle of any type of to give the spin DOWN.
* @returns {AnyDownParticle} an AnyDownParticle (type-erased DownParticle) with the at compile time known spin DOWN.
*/
export const anyDownParticle = (particle: ParticleBase): AnyDownParticle => {
const spin = Spin.DOWN
const anySpun = anySpunParticle({ particle, spin })
return {
...anySpun,
spin,
toAnySpunParticle: () => anySpun,
}
}
export const asAnyUpParticle = (
spunParticle: SpunParticleBase,
): Result<AnyUpParticle, Error> => {
if (spunParticle.spin !== Spin.UP) {
return err(new Error('Particle does not have spin UP.'))
}
return ok(anyUpParticle(spunParticle.particle))
}
export const asAnyDownParticle = (
spunParticle: SpunParticleBase,
): Result<AnyDownParticle, Error> => {
if (spunParticle.spin !== Spin.DOWN) {
return err(new Error('Particle does not have spin DOWN.'))
}
return ok(anyDownParticle(spunParticle.particle))
}
export const asUpParticle = <P extends ParticleBase>(
spunParticle: SpunParticleT<P>,
): Result<UpParticle<P>, Error> => {
if (spunParticle.spin !== Spin.UP) {
return err(new Error('Particle does not have spin UP.'))
}
return ok(upParticle(spunParticle.particle))
}
export const asDownParticle = <P extends ParticleBase>(
spunParticle: SpunParticleT<P>,
): Result<DownParticle<P>, Error> => {
if (spunParticle.spin !== Spin.DOWN) {
return err(new Error('Particle does not have spin DOWN.'))
}
return ok(downParticle(spunParticle.particle))
}
const isParticleBase = (something: unknown): something is ParticleBase => {
const inspection = something as ParticleBase
return inspection.equals !== undefined
}
// eslint-disable-next-line complexity
export const isAnySpunParticle = (
something: unknown,
): something is AnySpunParticle => {
const inspection = something as AnySpunParticle
return (
inspection.spin !== undefined &&
isSpin(inspection.spin) &&
inspection.particle !== undefined &&
isParticleBase(inspection.particle) &&
inspection.equals !== undefined &&
inspection.downedAsAny() !== undefined
)
}
export const SpunParticle = {
...jsonDecoding,
JSONDecoder,
SERIALIZER,
}