Skip to content

Commit

Permalink
fix: Merge pull request #6 from tscircuit/insert-toarray
Browse files Browse the repository at this point in the history
support for insert method and toArray method
  • Loading branch information
seveibar authored Aug 17, 2024
2 parents 506eaf7 + f798fc2 commit ccc360d
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 18 deletions.
1 change: 1 addition & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
},
"style": {
"noNonNullAssertion": "off",
"noUselessElse": "off",
"useFilenamingConvention": {
"level": "error",
"options": {
Expand Down
78 changes: 60 additions & 18 deletions lib/su.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,83 +7,125 @@ import type {

type SoupOps<
K extends AnySoupElement["type"],
T extends AnySoupElement | AnySoupElementInput
T extends AnySoupElement | AnySoupElementInput,
> = {
get: (id: string) => Extract<T, { type: K }> | null
select: (selector: string) => Extract<T, { type: K }> | null
getWhere: (where: any) => Extract<T, { type: K }> | null
getUsing: (using: {
[key: `${string}_id`]: string
}) => Extract<T, { type: K }> | null
insert: (
elm: Omit<Extract<T, { type: K }>, "type" | `${K}_id`>,
) => Extract<T, { type: K }>
list: (where?: any) => Extract<T, { type: K }>[]
}

export type SoupUtilObject = {
export type SoupUtilObjects = {
[K in AnySoupElement["type"]]: SoupOps<K, AnySoupElement>
} & {
toArray: () => AnySoupElement[]
}
export type SoupInputUtilObject = {
export type SoupInputUtilObjects = {
[K in AnySoupElementInput["type"]]: SoupOps<K, AnySoupElementInput>
}

export type GetSoupUtilObject = ((soup: AnySoupElement[]) => SoupUtilObject) & {
unparsed: (soup: AnySoupElementInput[]) => SoupInputUtilObject
export type GetSoupUtilFn = ((soup: AnySoupElement[]) => SoupUtilObjects) & {
unparsed: (soup: AnySoupElementInput[]) => SoupInputUtilObjects
}

export const su: GetSoupUtilObject = ((soup: AnySoupElement[]) => {
interface InternalStore {
counts: Record<string, number>
}

export const su: GetSoupUtilFn = ((soup: AnySoupElement[]) => {
let internalStore: InternalStore = (soup as any)._internal_store
if (!internalStore) {
internalStore = {
counts: {},
} as InternalStore
;(soup as any)._internal_store = internalStore

// Initialize counts
for (const elm of soup) {
const type = elm.type
internalStore.counts[type] ??= 1
const idNum = Number.parseInt((elm as any)[`${type}_id`].split("_").pop())
if (!Number.isNaN(idNum)) {
internalStore.counts[type] = Math.max(internalStore.counts[type], idNum)

Check failure on line 55 in lib/su.ts

View workflow job for this annotation

GitHub Actions / test

Argument of type 'number | undefined' is not assignable to parameter of type 'number'.

Check failure on line 55 in lib/su.ts

View workflow job for this annotation

GitHub Actions / publish

Argument of type 'number | undefined' is not assignable to parameter of type 'number'.
}
}
}
const su = new Proxy(
{},
{
get: (proxy_target: any, component_type: string) => {
if (component_type === "toArray") {
return () => soup
}

return {
get: (id: string) =>
soup.find(
(e: any) =>
e.type === component_type && e[`${component_type}_id`] === id
e.type === component_type && e[`${component_type}_id`] === id,
),
getUsing: (using: any) => {
const keys = Object.keys(using)
if (keys.length !== 1) {
throw new Error(
"getUsing requires exactly one key, e.g. { pcb_component_id }"
"getUsing requires exactly one key, e.g. { pcb_component_id }",
)
}
const join_key = keys[0] as string
const join_type = join_key.replace("_id", "")
const joiner: any = soup.find(
(e: any) =>
e.type === join_type && e[join_key] === using[join_key]
e.type === join_type && e[join_key] === using[join_key],
)
if (!joiner) return null
return soup.find(
(e: any) =>
e.type === component_type &&
e[`${component_type}_id`] === joiner[`${component_type}_id`]
e[`${component_type}_id`] === joiner[`${component_type}_id`],
)
},
getWhere: (where: any) => {
const keys = Object.keys(where)
return soup.find(
(e: any) =>
e.type === component_type &&
keys.every((key) => e[key] === where[key])
keys.every((key) => e[key] === where[key]),
)
},
list: (where?: any) => {
const keys = !where ? [] : Object.keys(where)
return soup.filter(
(e: any) =>
e.type === component_type &&
keys.every((key) => e[key] === where[key])
keys.every((key) => e[key] === where[key]),
)
},
insert: (elm: any) => {
internalStore.counts[component_type] ??= -1
internalStore.counts[component_type]++
const index = internalStore.counts[component_type]
const newElm = {
type: component_type,
[`${component_type}_id`]: `${component_type}_${index}`,
...elm,
}
soup.push(newElm)
return newElm
},
select: (selector: string) => {
// TODO when applySelector is isolated we can use it, until then we
// do a poor man's selector implementation for two common cases
if (component_type === "source_component") {
return soup.find(
(e) =>
e.type === "source_component" &&
e.name === selector.replace(/\./g, "")
e.name === selector.replace(/\./g, ""),
)
} else if (
component_type === "pcb_port" ||
Expand All @@ -95,7 +137,7 @@ export const su: GetSoupUtilObject = ((soup: AnySoupElement[]) => {
.split(/[\s\>]+/)
const source_component = soup.find(
(e) =>
e.type === "source_component" && e.name === component_name
e.type === "source_component" && e.name === component_name,
) as SourceComponentBase
if (!source_component) return null
const source_port = soup.find(
Expand All @@ -104,7 +146,7 @@ export const su: GetSoupUtilObject = ((soup: AnySoupElement[]) => {
e.source_component_id ===
source_component.source_component_id &&
(e.name === port_selector ||
(e.port_hints ?? []).includes(port_selector!))
(e.port_hints ?? []).includes(port_selector!)),
) as SourcePort
if (!source_port) return null
if (component_type === "source_port") return source_port
Expand All @@ -113,20 +155,20 @@ export const su: GetSoupUtilObject = ((soup: AnySoupElement[]) => {
return soup.find(
(e) =>
e.type === "pcb_port" &&
e.source_port_id === source_port.source_port_id
e.source_port_id === source_port.source_port_id,
)
} else if (component_type === "schematic_port") {
return soup.find(
(e) =>
e.type === "schematic_port" &&
e.source_port_id === source_port.source_port_id
e.source_port_id === source_port.source_port_id,
)
}
}
},
}
},
}
},
)

return su
Expand Down
38 changes: 38 additions & 0 deletions tests/insert.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { AnySoupElement } from "@tscircuit/soup"
import { su } from "../index"
import test from "ava"

test("insert", (t) => {
const soup: AnySoupElement[] = [
{
type: "source_component",
source_component_id: "simple_resistor_0",
name: "R1",
supplier_part_numbers: {},
ftype: "simple_resistor",
resistance: 10_000,
},
{
type: "source_port",
name: "left",
source_port_id: "source_port_0",
source_component_id: "simple_resistor_0",
},
]

const pp = su(soup).pcb_port.insert({
layers: ["top"],
pcb_component_id: "",
source_port_id: "source_port_0",
x: 0,
y: 0,
})

t.is(pp?.pcb_port_id, "pcb_port_0")

const pcb_port = su(soup)
.toArray()
.find((elm) => elm.type === "pcb_port")!

t.truthy(pcb_port)
})

0 comments on commit ccc360d

Please sign in to comment.