SignalWire

 view release on metacpan or  search on metacpan

porting-sdk/scripts/extract_relay_schemas.py  view on Meta::CPAN

        inner = m_list.group(1).strip()
        return {
            "type": "array",
            "items": _csharp_type_to_schema(inner, classes, seen),
        }
    # Container: Dictionary<K, V>
    m_dict = re.match(r"^Dictionary<\s*\w+\s*,\s*(.+)>$", t)
    if m_dict:
        return {
            "type": "object",
            "additionalProperties": _csharp_type_to_schema(m_dict.group(1).strip(), classes, seen),
        }
    # Array T[]
    if t.endswith("[]"):
        inner = t[:-2].strip()
        return {
            "type": "array",
            "items": _csharp_type_to_schema(inner, classes, seen),
        }

    # Primitive
    if t in _PRIMITIVE_TYPE_TO_SCHEMA:
        return dict(_PRIMITIVE_TYPE_TO_SCHEMA[t])

    # Newtonsoft enum-as-string converters: any enum type becomes string.
    if t.endswith("DeviceType") or t.endswith("PlayType") or "DetectType" in t or "AudioDirection" in t or "RecordType" in t:
        return {"type": "string"}

    # Local class lookup (custom DTO, possibly nested)
    if t in classes:
        if t in seen:
            return {"type": "object"}  # break cycle
        return _class_to_schema(classes[t], classes, seen | {t})

    # Unknown — permissive
    return {}


def _class_to_schema(
    cls: CSharpClass,
    classes: dict[str, CSharpClass],
    seen: set[str] | None = None,
) -> dict[str, Any]:
    """Build a JSON-Schema object schema from a parsed C# class."""
    seen = seen or set()
    properties: dict[str, Any] = {}
    required: list[str] = []
    for field in cls.fields:
        sub = _csharp_type_to_schema(field.csharp_type, classes, seen)
        # Mark nullable types explicitly.
        if field.nullable:
            # JSON-Schema 2020-12: type can be array including "null"
            if isinstance(sub.get("type"), str):
                sub = dict(sub)
                sub["type"] = [sub["type"], "null"]
        properties[field.json_name] = sub
        if field.required_always:
            required.append(field.json_name)
    schema: dict[str, Any] = {"type": "object", "properties": properties}
    if required:
        schema["required"] = required
    # We allow additional fields — many params are forward-compat.
    schema["additionalProperties"] = True
    return schema


def _root_schema(
    title: str,
    description: str,
    payload_schema: dict[str, Any],
    extra_meta: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Wrap an inner payload schema in a top-level JSON-Schema document."""
    out: dict[str, Any] = {
        "$schema": JSON_SCHEMA_VERSION,
        "title": title,
        "description": description,
    }
    if extra_meta:
        out.update(extra_meta)
    # Fold the payload into the root schema.
    for k, v in payload_schema.items():
        out[k] = v
    return out


# ---------------------------------------------------------------------------
# Method-name mapping (CSharp class basename → RELAY method)
# ---------------------------------------------------------------------------


# Sub-command suffixes that produce dotted method names (e.g.
# ``PublicCallPlayPause`` → ``calling.play.pause``). Listed longest-first so
# ``StartInputTimers`` beats ``Stop``.
_SUBCOMMAND_SUFFIXES: tuple[str, ...] = (
    "StartInputTimers",
    "Resume",
    "Pause",
    "Stop",
    "Volume",
)

# Hard-coded base names for dotted methods where the base is itself a
# multi-word camel chunk (e.g. ``PublicCallPlayAndCollect`` → ``play_and_collect``,
# whose stop becomes ``play_and_collect.stop``).
_KNOWN_BASE_METHODS: tuple[str, ...] = (
    "PlayAndCollect",
    "Play",
    "Record",
    "Detect",
    "Collect",
    "Pay",
    "SendFax",
    "ReceiveFax",
    "Tap",
    "Stream",
    "Transcribe",
    "Ai",
    "Denoise",
    "Queue",
)



( run in 0.796 second using v1.01-cache-2.11-cpan-71847e10f99 )