def spec_to_dict(spec: GDSSpec) -> dict[str, Any]:
"""Serialize a GDSSpec to a plain dict (JSON-compatible)."""
return {
"name": spec.name,
"description": spec.description,
"types": {
name: {
"name": t.name,
"python_type": t.python_type.__name__,
"description": t.description,
"units": t.units,
}
for name, t in spec.types.items()
},
"spaces": {
name: {
"name": s.name,
"schema": {fname: tdef.name for fname, tdef in s.fields.items()},
"description": s.description,
}
for name, s in spec.spaces.items()
},
"entities": {
name: {
"name": e.name,
"description": e.description,
"variables": {
vname: {
"name": v.name,
"type": v.typedef.name,
"description": v.description,
"symbol": v.symbol,
}
for vname, v in e.variables.items()
},
}
for name, e in spec.entities.items()
},
"blocks": {name: _block_to_dict(b) for name, b in spec.blocks.items()},
"wirings": {
name: {
"name": w.name,
"description": w.description,
"blocks": list(w.block_names),
"wires": [
{
"source": wire.source,
"target": wire.target,
"space": wire.space,
"optional": wire.optional,
}
for wire in w.wires
],
}
for name, w in spec.wirings.items()
},
"parameters": {
name: {
"name": p.name,
"typedef": p.typedef.name,
"python_type": p.typedef.python_type.__name__,
"description": p.description,
"bounds": list(p.bounds) if p.bounds is not None else None,
}
for name, p in spec.parameter_schema.parameters.items()
},
}