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 Key = string | number | symbol;
|
||||
|
||||
declare namespace CharmSync {
|
||||
type AtomMap = Record<string, Atom<any>>;
|
||||
|
||||
|
|
@ -60,6 +62,39 @@ declare namespace CharmSync {
|
|||
*/
|
||||
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 = {
|
||||
[P in keyof CheckableTypes as P extends keyof CheckablePrimitives ? never : P]: CheckableTypes[P];
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
local client = require(script.client)
|
||||
local flatten = require(script.flatten)
|
||||
local patch = require(script.patch)
|
||||
local server = require(script.server)
|
||||
local types = require(script.types)
|
||||
|
|
@ -12,5 +13,6 @@ export type SyncPayload = types.SyncPayload
|
|||
return {
|
||||
client = client,
|
||||
server = server,
|
||||
flatten = flatten,
|
||||
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