mirror of
https://github.com/status-im/sourcecred.git
synced 2025-02-24 10:18:11 +00:00
Add Address.fromRaw (#1871)
This adds a fromRaw method to the Address module so that we may properly parse serialized addresses. It has a type signature of `string => Address`, and throws an error if the string is not a valid address. I basically copied the implementation out of the `assertValid` method. I considered deduplicating them (i.e. having `assertValid` simply call `fromRaw`), but `assertValid` had some extra logic around accepting a prefix for the error message, and it felt simpler to simply copy+paste rather than trying to wrap it. Test plan: I added unit tests; `yarn test` passes.
This commit is contained in:
parent
8ea16de6f9
commit
ea175a390a
@ -5,7 +5,7 @@ import deepFreeze from "deep-freeze";
|
||||
|
||||
import * as MapUtil from "../util/map";
|
||||
|
||||
export interface AddressModule<Address> {
|
||||
export interface AddressModule<Address: string> {
|
||||
/**
|
||||
* Assert at runtime that the provided address is actually a valid
|
||||
* address of this kind, throwing an error if it is not. If `what` is
|
||||
@ -73,6 +73,19 @@ export interface AddressModule<Address> {
|
||||
* e.g., `toParts(["ban"])` is not a prefix of `toParts(["banana"])`.
|
||||
*/
|
||||
hasPrefix(address: Address, prefix: Address): boolean;
|
||||
|
||||
/**
|
||||
* Interpret the provided string as an Address.
|
||||
*
|
||||
* Addresses are natively stored as strings. This method verifies
|
||||
* that the provided "raw" address is actually an Address, so that
|
||||
* you can have a type-level assurance that a string is an Address.
|
||||
*
|
||||
* This is useful if e.g. you are loading serialized Addresses.
|
||||
*
|
||||
* Throws an error if the string is not a valid Address.
|
||||
*/
|
||||
fromRaw(raw: string): Address;
|
||||
}
|
||||
|
||||
export type Options = {|
|
||||
@ -224,6 +237,28 @@ export function makeAddressModule(options: Options): AddressModule<string> {
|
||||
return address.startsWith(prefix);
|
||||
}
|
||||
|
||||
function fromRaw(address: string): Address {
|
||||
if (!address.endsWith(separator)) {
|
||||
throw new Error(
|
||||
`address does not end with separator: ${stringify(address)}`
|
||||
);
|
||||
}
|
||||
if (!address.startsWith(nonceWithSeparator)) {
|
||||
for (const [
|
||||
otherNonceWithSeparator,
|
||||
otherName,
|
||||
] of otherNoncesWithSeparators) {
|
||||
if (address.startsWith(otherNonceWithSeparator)) {
|
||||
throw new Error(
|
||||
`expected ${name}, got ${otherName}: ${stringify(address)}`
|
||||
);
|
||||
}
|
||||
}
|
||||
throw new Error(`expected ${name}, got: ${stringify(address)}`);
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
const result = {
|
||||
assertValid,
|
||||
assertValidParts,
|
||||
@ -233,6 +268,7 @@ export function makeAddressModule(options: Options): AddressModule<string> {
|
||||
toString,
|
||||
append,
|
||||
hasPrefix,
|
||||
fromRaw,
|
||||
};
|
||||
return deepFreeze(result);
|
||||
}
|
||||
|
@ -164,6 +164,33 @@ describe("core/address", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("fromRaw", () => {
|
||||
const {FooAddress, BarAddress} = makeModules();
|
||||
function thunk(x: string) {
|
||||
return () => FooAddress.fromRaw(x);
|
||||
}
|
||||
it("throws on an empty string", () => {
|
||||
expect(thunk("")).toThrow("address does not end with separator");
|
||||
});
|
||||
it("throws on a string that doesn't start with the right separator", () => {
|
||||
expect(thunk("\0")).toThrow("expected FooAddress, got");
|
||||
});
|
||||
it("throws on a string from a different address module", () => {
|
||||
expect(thunk(BarAddress.empty)).toThrow(
|
||||
"expected FooAddress, got BarAddress"
|
||||
);
|
||||
});
|
||||
function roundTrip(x) {
|
||||
expect(FooAddress.fromRaw(x)).toEqual(x);
|
||||
}
|
||||
it("works on an empty address", () => {
|
||||
roundTrip(FooAddress.empty);
|
||||
});
|
||||
it("works on a non-empty address", () => {
|
||||
roundTrip(FooAddress.fromParts(["foo", "bar"]));
|
||||
});
|
||||
});
|
||||
|
||||
describe("fromParts", () => {
|
||||
const {FooAddress, BarAddress} = makeModules();
|
||||
|
||||
|
@ -13,7 +13,7 @@ const EMPTY_ENTRY_SYMBOL = Symbol("EMPTY");
|
||||
|
||||
type Entry<V> = {|+map: RecursiveMap<V>, value: V | typeof EMPTY_ENTRY_SYMBOL|};
|
||||
type RecursiveMap<V> = Map<string, Entry<V>>;
|
||||
class BaseTrie<K, V> {
|
||||
class BaseTrie<K: string, V> {
|
||||
addressModule: AddressModule<K>;
|
||||
entry: Entry<V>;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user