Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ in `tools/codegen/`, or a spec, run `make codegen`, and commit the result.
`make lint` fails on stale output. [docs/research.md](docs/research.md)
explains how to refresh the specs.

The script stops with an error on spec features it can't map yet, such as
`oneOf`, PATCH, header parameters, and inline response schemas, rather than
dropping them. `tools/codegen/test_codegen.py` covers each of these checks, and
`make lint` runs it.

## Code style

- Public functions return `kalshi::Result<T>` (`std::expected<T, Error>`) and
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ lint:
$(CPP_SOURCES) | xargs -0 $(CLANG_FORMAT) --dry-run --Werror
$(PYTHON) tools/cpp_auto_audit.py
CLANG_FORMAT="$(CLANG_FORMAT)" $(PYTHON) tools/codegen/generate.py --check
$(PYTHON) -m unittest discover -q -s tools/codegen

lint-docs:
markdownlint-cli2
Expand Down
6 changes: 4 additions & 2 deletions docs/research.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ shasum -a 256 spec/*.yaml
make codegen test
```

Review the diff of the generated files, update the table above, and note
user-visible changes in `CHANGELOG.md`. Kalshi's changelog at
If `make codegen` stops on a spec feature it can't map, extend
`tools/codegen/` to support it before committing the new spec. Review the diff
of the generated files, update the table above, and note user-visible changes
in `CHANGELOG.md`. Kalshi's changelog at
<https://docs.kalshi.com/changelog> explains most changes.
41 changes: 34 additions & 7 deletions tools/codegen/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,23 @@
ACRONYMS = {"RFQs": "Rfqs", "RFQ": "Rfq", "FCM": "Fcm", "ID": "Id", "API": "Api", "MVE": "Mve"}

CPP_RESERVED = {
"and", "auto", "bool", "break", "case", "catch", "char", "class", "const", "continue",
"default", "delete", "do", "double", "else", "enum", "explicit", "export", "extern",
"false", "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable",
"namespace", "new", "not", "operator", "or", "private", "protected", "public", "return",
"short", "signed", "sizeof", "static", "struct", "switch", "template", "this", "throw",
"true", "try", "typedef", "typename", "union", "unsigned", "using", "virtual", "void",
"volatile", "while", "xor",
"alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break",
"case", "catch", "char", "char8_t", "char16_t", "char32_t", "class", "compl", "concept",
"const", "const_cast", "consteval", "constexpr", "constinit", "continue", "co_await",
"co_return", "co_yield", "decltype", "default", "delete", "do", "double", "dynamic_cast",
"else", "enum", "explicit", "export", "extern", "false", "float", "for", "friend", "goto",
"if", "inline", "int", "long", "mutable", "namespace", "new", "noexcept", "not", "not_eq",
"nullptr", "operator", "or", "or_eq", "private", "protected", "public", "register",
"reinterpret_cast", "requires", "return", "short", "signed", "sizeof", "static",
"static_assert", "static_cast", "struct", "switch", "template", "this", "thread_local",
"throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using",
"virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq",
}

# Schema keywords with no C++ mapping yet. Without this check they would
# become empty structs or RawJson, and no test would notice.
UNSUPPORTED_KEYWORDS = ("oneOf", "anyOf", "not", "discriminator")

# A server value spelled "unknown" shares the Unknown enumerator every enum has.
UNKNOWN_VALUE = "unknown"

Expand Down Expand Up @@ -99,6 +107,7 @@ class Member:
required: bool = False
base_kind: str = "" # "string", "enum", "struct", "vector", ...
element: str = "" # struct or enum name for validation
read_only: bool = False


@dataclass
Expand All @@ -108,6 +117,24 @@ class StructType:
doc: str = ""


def check_schema(schema: dict, where: str, max_all_of: int) -> None:
"""Stops on schema features that would otherwise map to a wrong or empty type."""
for keyword in UNSUPPORTED_KEYWORDS:
if keyword in schema:
raise SystemExit(f"{where}: `{keyword}` is not supported; extend tools/codegen first")
if len(schema.get("allOf", [])) > max_all_of:
raise SystemExit(f"{where}: `allOf` with {len(schema['allOf'])} members is not supported")
ref = schema.get("$ref")
if ref is not None and not re.fullmatch(r"#/components/schemas/[^/]+", ref):
raise SystemExit(f"{where}: {ref} is not a component schema")


def check_identifier(owner: str, json_name: str) -> None:
"""Members keep their JSON names so Glaze can reflect them; each must be a C++ name."""
if not re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", json_name) or json_name in CPP_RESERVED:
raise SystemExit(f"{owner}.{json_name} needs a rename; add glz::meta support first")


def check_enums(enums: dict[str, EnumType]) -> None:
for enum in enums.values():
names = [enumerator(v) for v in enum.known]
Expand Down
Loading
Loading