mirror of
https://github.com/littensy/charm.git
synced 2026-10-20 17:51:37 +00:00
feat(sync): add flatten() function (#43)
This commit is contained in:
parent
63388761f8
commit
8063ea3142
4 changed files with 89 additions and 0 deletions
28
packages/charm-sync/src/flatten.luau
Normal file
28
packages/charm-sync/src/flatten.luau
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
local types = require(script.Parent.types)
|
||||||
|
type AtomMap = types.AtomMap
|
||||||
|
|
||||||
|
type NestedAtomMap = {
|
||||||
|
[string]: NestedAtomMap | () -> (),
|
||||||
|
}
|
||||||
|
|
||||||
|
local function flatten(atoms: NestedAtomMap): AtomMap
|
||||||
|
local result: AtomMap = {}
|
||||||
|
|
||||||
|
local function visit(node: NestedAtomMap, path: string)
|
||||||
|
for key, value in node do
|
||||||
|
local location = if path == "" then key else path .. "/" .. key
|
||||||
|
|
||||||
|
if type(value) == "table" then
|
||||||
|
visit(value, location)
|
||||||
|
else
|
||||||
|
result[location] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
visit(atoms, "")
|
||||||
|
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
return flatten
|
||||||
35
packages/charm-sync/src/index.d.ts
vendored
35
packages/charm-sync/src/index.d.ts
vendored
|
|
@ -5,6 +5,8 @@ export as namespace CharmSync;
|
||||||
|
|
||||||
type Cleanup = () => void;
|
type Cleanup = () => void;
|
||||||
|
|
||||||
|
type Key = string | number | symbol;
|
||||||
|
|
||||||
declare namespace CharmSync {
|
declare namespace CharmSync {
|
||||||
type AtomMap = Record<string, Atom<any>>;
|
type AtomMap = Record<string, Atom<any>>;
|
||||||
|
|
||||||
|
|
@ -60,6 +62,39 @@ declare namespace CharmSync {
|
||||||
*/
|
*/
|
||||||
function isNone(value: unknown): value is None;
|
function isNone(value: unknown): value is None;
|
||||||
|
|
||||||
|
interface NestedAtomMap {
|
||||||
|
readonly [key: string]: NestedAtomMap | Atom<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppendPath<Path extends Key | undefined, Name extends Key> = undefined extends Path
|
||||||
|
? Name
|
||||||
|
: Name extends string
|
||||||
|
? Path extends string
|
||||||
|
? `${Path}/${Name}`
|
||||||
|
: never
|
||||||
|
: never;
|
||||||
|
|
||||||
|
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
||||||
|
|
||||||
|
type IntersectValues<T> = UnionToIntersection<{ readonly [K in keyof T]: T[K] }[keyof T]>;
|
||||||
|
|
||||||
|
type FlattenNestedAtoms<Atoms extends NestedAtomMap, Path extends Key | undefined = undefined> = IntersectValues<{
|
||||||
|
readonly [Name in keyof Atoms as AppendPath<Path, Name>]: Atoms[Name] extends Atom<any>
|
||||||
|
? { readonly [K in AppendPath<Path, Name>]: Atoms[Name] }
|
||||||
|
: Atoms[Name] extends NestedAtomMap
|
||||||
|
? FlattenNestedAtoms<Atoms[Name], AppendPath<Path, Name>>
|
||||||
|
: never;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flattens a nested atom map into a single object with slash-separated
|
||||||
|
* keys. Useful for recursively collecting atoms returned by modules.
|
||||||
|
*
|
||||||
|
* @param atoms The nested atom map to flatten.
|
||||||
|
* @return A flattened atom map.
|
||||||
|
*/
|
||||||
|
function flatten<Atoms extends NestedAtomMap>(atoms: Atoms): FlattenNestedAtoms<Atoms>;
|
||||||
|
|
||||||
type DataTypes = {
|
type DataTypes = {
|
||||||
[P in keyof CheckableTypes as P extends keyof CheckablePrimitives ? never : P]: CheckableTypes[P];
|
[P in keyof CheckableTypes as P extends keyof CheckablePrimitives ? never : P]: CheckableTypes[P];
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
local client = require(script.client)
|
local client = require(script.client)
|
||||||
|
local flatten = require(script.flatten)
|
||||||
local patch = require(script.patch)
|
local patch = require(script.patch)
|
||||||
local server = require(script.server)
|
local server = require(script.server)
|
||||||
local types = require(script.types)
|
local types = require(script.types)
|
||||||
|
|
@ -12,5 +13,6 @@ export type SyncPayload = types.SyncPayload
|
||||||
return {
|
return {
|
||||||
client = client,
|
client = client,
|
||||||
server = server,
|
server = server,
|
||||||
|
flatten = flatten,
|
||||||
isNone = patch.isNone,
|
isNone = patch.isNone,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
tests/charm-sync/flatten.spec.luau
Normal file
24
tests/charm-sync/flatten.spec.luau
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||||
|
|
||||||
|
local Charm = require(ReplicatedStorage.DevPackages.Charm)
|
||||||
|
local atom = Charm.atom
|
||||||
|
local CharmSync = require(ReplicatedStorage.DevPackages.CharmSync)
|
||||||
|
local flatten = CharmSync.flatten
|
||||||
|
|
||||||
|
return function()
|
||||||
|
it("flattens atoms", function()
|
||||||
|
local input = {
|
||||||
|
a = atom(0),
|
||||||
|
b = { c = atom(0) },
|
||||||
|
d = { e = { f = atom(0) } },
|
||||||
|
}
|
||||||
|
local result = flatten(input)
|
||||||
|
|
||||||
|
expect(result.a).to.equal(input.a)
|
||||||
|
expect(result["b/c"]).to.equal(input.b.c)
|
||||||
|
expect(result["d/e/f"]).to.equal(input.d.e.f)
|
||||||
|
|
||||||
|
expect(result.b).to.never.be.ok()
|
||||||
|
expect(result.d).to.never.be.ok()
|
||||||
|
end)
|
||||||
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue