refactor(eventsub): use snake_case transform by default (#5916)
This commit is contained in:
@@ -1,31 +1,78 @@
|
||||
from typing import List, Tuple
|
||||
from typing import Optional
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
CommentCommands = List[Tuple[str, str]]
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def parse_comment_commands(raw_comment: str) -> CommentCommands:
|
||||
comment_commands: CommentCommands = []
|
||||
class CommentCommands:
|
||||
# Transform the key from whatever-case
|
||||
# By default, all keys are transformed into snake_case
|
||||
# e.g. `userID` transforms to `user_id`
|
||||
name_transform: str = "snake_case"
|
||||
|
||||
def clean_comment_line(line: str) -> str:
|
||||
return line.replace("/", "").replace("*", "").strip()
|
||||
# Whether the key should completely change its name
|
||||
# If set, `name_transform` will do nothing
|
||||
name_change: Optional[str] = None
|
||||
|
||||
comment_lines = [line for line in map(clean_comment_line, raw_comment.splitlines()) if line != ""]
|
||||
# Don't fail when an optional object exists and its data is bad
|
||||
dont_fail_on_deserialization: bool = False
|
||||
|
||||
for comment in comment_lines:
|
||||
parts = comment.split("=", 2)
|
||||
if len(parts) != 2:
|
||||
continue
|
||||
# Deserialization hint, current use-cases can be replaced with
|
||||
# https://github.com/Chatterino/chatterino2/issues/5912
|
||||
tag: Optional[str] = None
|
||||
|
||||
command = parts[0].strip()
|
||||
value = parts[1].strip()
|
||||
comment_commands.append((command, value))
|
||||
inner_root: str = ""
|
||||
|
||||
return comment_commands
|
||||
def __init__(self, parent: Optional["CommentCommands"] = None) -> None:
|
||||
if parent is not None:
|
||||
self.name_transform = parent.name_transform
|
||||
self.name_change = parent.name_change
|
||||
self.dont_fail_on_deserialization = parent.dont_fail_on_deserialization
|
||||
self.tag = parent.tag
|
||||
self.inner_root = parent.inner_root
|
||||
|
||||
def parse(self, raw_comment: str) -> None:
|
||||
def clean_comment_line(line: str) -> str:
|
||||
return line.replace("/", "").replace("*", "").strip()
|
||||
|
||||
comment_lines = [line for line in map(clean_comment_line, raw_comment.splitlines()) if line != ""]
|
||||
|
||||
for comment in comment_lines:
|
||||
parts = comment.split("=", 2)
|
||||
if len(parts) != 2:
|
||||
continue
|
||||
|
||||
command = parts[0].strip()
|
||||
value = parts[1].strip()
|
||||
|
||||
match command:
|
||||
case "json_rename":
|
||||
self.name_change = value
|
||||
case "json_dont_fail_on_deserialization":
|
||||
self.dont_fail_on_deserialization = bool(value.lower() == "true")
|
||||
case "json_transform":
|
||||
self.name_transform = value
|
||||
case "json_inner":
|
||||
self.inner_root = value
|
||||
pass
|
||||
case "json_tag":
|
||||
self.tag = value
|
||||
case other:
|
||||
log.warning(f"Unknown comment command found: {other} with value {value}")
|
||||
|
||||
def apply_name_transform(self, input_json_name: str) -> str:
|
||||
if self.name_change is not None:
|
||||
return self.name_change
|
||||
|
||||
match self.name_transform:
|
||||
case "snake_case":
|
||||
return re.sub(r"(?<![A-Z])\B[A-Z]", r"_\g<0>", input_json_name).lower()
|
||||
|
||||
case other:
|
||||
log.warning(f"Unknown transformation '{other}', ignoring")
|
||||
return input_json_name
|
||||
|
||||
|
||||
def json_transform(input_str: str, transformation: str) -> str:
|
||||
|
||||
@@ -17,7 +17,7 @@ class Enum:
|
||||
self.name = name
|
||||
self.constants: List[EnumConstant] = []
|
||||
self.parent: str = ""
|
||||
self.comment_commands: CommentCommands = []
|
||||
self.comment_commands = CommentCommands()
|
||||
self.inner_root: str = ""
|
||||
self.namespace = namespace
|
||||
|
||||
@@ -47,18 +47,4 @@ class Enum:
|
||||
return env.get_template("enum-definition.tmpl").render(enum=self)
|
||||
|
||||
def apply_comment_commands(self, comment_commands: CommentCommands) -> None:
|
||||
for command, value in comment_commands:
|
||||
match command:
|
||||
case "json_rename":
|
||||
# Do nothing on enums
|
||||
pass
|
||||
case "json_dont_fail_on_deserialization":
|
||||
# Do nothing on enums
|
||||
pass
|
||||
case "json_transform":
|
||||
# Do nothing on enums
|
||||
pass
|
||||
case "json_inner":
|
||||
self.inner_root = value
|
||||
case other:
|
||||
log.warning(f"Unknown comment command found: {other} with value {value}")
|
||||
self.inner_root = comment_commands.inner_root
|
||||
|
||||
@@ -6,7 +6,7 @@ import logging
|
||||
|
||||
import clang.cindex
|
||||
|
||||
from .comment_commands import CommentCommands, json_transform, parse_comment_commands
|
||||
from .comment_commands import CommentCommands
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -23,31 +23,15 @@ class EnumConstant:
|
||||
self.dont_fail_on_deserialization: bool = False
|
||||
|
||||
def apply_comment_commands(self, comment_commands: CommentCommands) -> None:
|
||||
for command, value in comment_commands:
|
||||
match command:
|
||||
case "json_rename":
|
||||
# Rename the key that this field will use in json terms
|
||||
log.debug(f"Rename json key from {self.json_name} to {value}")
|
||||
self.json_name = value
|
||||
case "json_dont_fail_on_deserialization":
|
||||
# Don't fail when an optional object exists and its data is bad
|
||||
log.debug(f"Don't fail on deserialization for {self.name}")
|
||||
self.dont_fail_on_deserialization = bool(value.lower() == "true")
|
||||
case "json_transform":
|
||||
# Transform the key from whatever-case to case specified by `value`
|
||||
self.json_name = json_transform(self.json_name, value)
|
||||
case "json_inner":
|
||||
# Do nothing on members
|
||||
pass
|
||||
case "json_tag":
|
||||
# Rename the key that this field will use in json terms
|
||||
log.debug(f"Applied json tag on {self.json_name}: {value}")
|
||||
self.tag = value
|
||||
case other:
|
||||
log.warning(f"Unknown comment command found: {other} with value {value}")
|
||||
self.json_name = comment_commands.apply_name_transform(self.json_name)
|
||||
self.tag = comment_commands.tag
|
||||
self.dont_fail_on_deserialization = comment_commands.dont_fail_on_deserialization
|
||||
|
||||
@staticmethod
|
||||
def from_node(node: clang.cindex.Cursor) -> EnumConstant:
|
||||
def from_node(
|
||||
node: clang.cindex.Cursor,
|
||||
comment_commands: CommentCommands,
|
||||
) -> EnumConstant:
|
||||
assert node.type is not None
|
||||
|
||||
name = node.spelling
|
||||
@@ -55,8 +39,9 @@ class EnumConstant:
|
||||
enum = EnumConstant(name)
|
||||
|
||||
if node.raw_comment is not None:
|
||||
comment_commands = parse_comment_commands(node.raw_comment)
|
||||
enum.apply_comment_commands(comment_commands)
|
||||
comment_commands.parse(node.raw_comment)
|
||||
|
||||
enum.apply_comment_commands(comment_commands)
|
||||
|
||||
return enum
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional, List
|
||||
from typing import Optional
|
||||
|
||||
import logging
|
||||
|
||||
import clang.cindex
|
||||
from clang.cindex import CursorKind
|
||||
|
||||
from .comment_commands import CommentCommands, json_transform, parse_comment_commands
|
||||
from .comment_commands import CommentCommands
|
||||
from .membertype import MemberType
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -72,31 +72,16 @@ class Member:
|
||||
self.dont_fail_on_deserialization: bool = False
|
||||
|
||||
def apply_comment_commands(self, comment_commands: CommentCommands) -> None:
|
||||
for command, value in comment_commands:
|
||||
match command:
|
||||
case "json_rename":
|
||||
# Rename the key that this field will use in json terms
|
||||
log.debug(f"Rename json key from {self.json_name} to {value}")
|
||||
self.json_name = value
|
||||
case "json_dont_fail_on_deserialization":
|
||||
# Don't fail when an optional object exists and its data is bad
|
||||
log.debug(f"Don't fail on deserialization for {self.name}")
|
||||
self.dont_fail_on_deserialization = bool(value.lower() == "true")
|
||||
case "json_transform":
|
||||
# Transform the key from whatever-case to case specified by `value`
|
||||
self.json_name = json_transform(self.json_name, value)
|
||||
case "json_inner":
|
||||
# Do nothing on members
|
||||
pass
|
||||
case "json_tag":
|
||||
# Rename the key that this field will use in json terms
|
||||
log.debug(f"Applied json tag on {self.json_name}: {value}")
|
||||
self.tag = value
|
||||
case other:
|
||||
log.warning(f"Unknown comment command found: {other} with value {value}")
|
||||
self.json_name = comment_commands.apply_name_transform(self.json_name)
|
||||
self.tag = comment_commands.tag
|
||||
self.dont_fail_on_deserialization = comment_commands.dont_fail_on_deserialization
|
||||
|
||||
@staticmethod
|
||||
def from_field(node: clang.cindex.Cursor, namespace: tuple[str, ...]) -> Member:
|
||||
def from_field(
|
||||
node: clang.cindex.Cursor,
|
||||
comment_commands: CommentCommands,
|
||||
namespace: tuple[str, ...],
|
||||
) -> Member:
|
||||
assert node.type is not None
|
||||
|
||||
name = node.spelling
|
||||
@@ -155,8 +140,9 @@ class Member:
|
||||
member = Member(name, member_type, type_name, _is_trivially_copyable(node.type))
|
||||
|
||||
if node.raw_comment is not None:
|
||||
comment_commands = parse_comment_commands(node.raw_comment)
|
||||
member.apply_comment_commands(comment_commands)
|
||||
comment_commands.parse(node.raw_comment)
|
||||
|
||||
member.apply_comment_commands(comment_commands)
|
||||
|
||||
return member
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ class Struct:
|
||||
self.name = name
|
||||
self.members: List[Member] = []
|
||||
self.parent: str = ""
|
||||
self.comment_commands: CommentCommands = []
|
||||
self.comment_commands = CommentCommands()
|
||||
self.inner_root: str = ""
|
||||
self.namespace = namespace
|
||||
|
||||
@@ -47,18 +47,4 @@ class Struct:
|
||||
return env.get_template("struct-definition.tmpl").render(struct=self)
|
||||
|
||||
def apply_comment_commands(self, comment_commands: CommentCommands) -> None:
|
||||
for command, value in comment_commands:
|
||||
match command:
|
||||
case "json_rename":
|
||||
# Do nothing on structs
|
||||
pass
|
||||
case "json_dont_fail_on_deserialization":
|
||||
# Do nothing on structs
|
||||
pass
|
||||
case "json_transform":
|
||||
# Do nothing on structs
|
||||
pass
|
||||
case "json_inner":
|
||||
self.inner_root = value
|
||||
case other:
|
||||
log.warning(f"Unknown comment command found: {other} with value {value}")
|
||||
self.inner_root = comment_commands.inner_root
|
||||
|
||||
@@ -6,7 +6,7 @@ import os
|
||||
import clang.cindex
|
||||
from clang.cindex import CursorKind
|
||||
|
||||
from .comment_commands import parse_comment_commands
|
||||
from .comment_commands import CommentCommands
|
||||
from .member import Member
|
||||
from .enum_constant import EnumConstant
|
||||
from .struct import Struct
|
||||
@@ -28,7 +28,7 @@ class Walker:
|
||||
case CursorKind.STRUCT_DECL:
|
||||
new_struct = Struct(node.spelling, self.namespace)
|
||||
if node.raw_comment is not None:
|
||||
new_struct.comment_commands = parse_comment_commands(node.raw_comment)
|
||||
new_struct.comment_commands.parse(node.raw_comment)
|
||||
new_struct.apply_comment_commands(new_struct.comment_commands)
|
||||
if struct is not None:
|
||||
new_struct.parent = struct.full_name
|
||||
@@ -43,7 +43,7 @@ class Walker:
|
||||
case CursorKind.ENUM_DECL:
|
||||
new_enum = Enum(node.spelling, self.namespace)
|
||||
if node.raw_comment is not None:
|
||||
new_enum.comment_commands = parse_comment_commands(node.raw_comment)
|
||||
new_enum.comment_commands.parse(node.raw_comment)
|
||||
new_enum.apply_comment_commands(new_enum.comment_commands)
|
||||
if struct is not None:
|
||||
new_enum.parent = struct.full_name
|
||||
@@ -60,8 +60,7 @@ class Walker:
|
||||
# log.warning(
|
||||
# f"enum constant decl {node.spelling} - enum comments: {enum.comment_commands} - node comments: {node.raw_comment}"
|
||||
# )
|
||||
constant = EnumConstant.from_node(node)
|
||||
constant.apply_comment_commands(enum.comment_commands)
|
||||
constant = EnumConstant.from_node(node, CommentCommands(enum.comment_commands))
|
||||
enum.constants.append(constant)
|
||||
|
||||
case CursorKind.FIELD_DECL:
|
||||
@@ -72,8 +71,7 @@ class Walker:
|
||||
|
||||
# log.debug(f"{struct}: {type.spelling} {node.spelling} ({type.kind})")
|
||||
if struct:
|
||||
member = Member.from_field(node, self.namespace)
|
||||
member.apply_comment_commands(struct.comment_commands)
|
||||
member = Member.from_field(node, CommentCommands(struct.comment_commands), self.namespace)
|
||||
struct.members.append(member)
|
||||
|
||||
case CursorKind.NAMESPACE:
|
||||
|
||||
Reference in New Issue
Block a user