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";
|
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
|
* 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
|
* 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"])`.
|
* e.g., `toParts(["ban"])` is not a prefix of `toParts(["banana"])`.
|
||||||
*/
|
*/
|
||||||
hasPrefix(address: Address, prefix: Address): boolean;
|
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 = {|
|
export type Options = {|
|
||||||
@ -224,6 +237,28 @@ export function makeAddressModule(options: Options): AddressModule<string> {
|
|||||||
return address.startsWith(prefix);
|
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 = {
|
const result = {
|
||||||
assertValid,
|
assertValid,
|
||||||
assertValidParts,
|
assertValidParts,
|
||||||
@ -233,6 +268,7 @@ export function makeAddressModule(options: Options): AddressModule<string> {
|
|||||||
toString,
|
toString,
|
||||||
append,
|
append,
|
||||||
hasPrefix,
|
hasPrefix,
|
||||||
|
fromRaw,
|
||||||
};
|
};
|
||||||
return deepFreeze(result);
|
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", () => {
|
describe("fromParts", () => {
|
||||||
const {FooAddress, BarAddress} = makeModules();
|
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 Entry<V> = {|+map: RecursiveMap<V>, value: V | typeof EMPTY_ENTRY_SYMBOL|};
|
||||||
type RecursiveMap<V> = Map<string, Entry<V>>;
|
type RecursiveMap<V> = Map<string, Entry<V>>;
|
||||||
class BaseTrie<K, V> {
|
class BaseTrie<K: string, V> {
|
||||||
addressModule: AddressModule<K>;
|
addressModule: AddressModule<K>;
|
||||||
entry: Entry<V>;
|
entry: Entry<V>;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user