@model_validator(mode="after")
def determine_value_validator(self):
"""Determines value validator (inner `data_type`)."""
str_type = self.root
self._hashable = not (
"dict" in str_type
or "set" in str_type
or "array" in str_type
or "special" in str_type
or "json" in str_type
or "link" in str_type
)
if str_type in primitive_data_types:
# Primitive type
data_type = primitive_data_types[str_type]
elif m := re.match(re_array, str_type):
# Array
element_type = m.group(1).strip()
value_type = DataType(root=element_type)
if not is_primitive_element_type(value_type):
raise ValueError(f"Data type {element_type} is not supported as an array element")
data_type = list[value_type.data_type]
self._iterable = True
self._elem_type = value_type
elif m := re.match(re_set, str_type):
# Set
element_type = m.group(1).strip()
value_type = DataType(root=element_type)
if not is_primitive_element_type(value_type):
raise ValueError(f"Data type {element_type} is not supported as a set element")
data_type = list[value_type.data_type] # set is not supported by MongoDB
self._iterable = True
self._elem_type = value_type
elif m := re.match(re_link, str_type):
# Link
etype, data, mirrored = m.group("etype"), m.group("data"), m.group("mirror")
self._link_to = etype
self._is_link = True
self._link_data = bool(data)
self._mirror_link = bool(mirrored)
self._mirror_as = mirrored if mirrored else None
self._type_info = f"link<{etype},{data}>"
if etype and data:
value_type = DataType(root=data)
data_type = create_model(
f"Link<{data}>", __base__=Link, data=(value_type.data_type, ...)
)
else:
data_type = Link
elif re.match(re_dict, str_type):
# Dict
dict_spec = {}
key_str = str_type.split("<")[1].split(">")[0]
key_spec = dict(item.strip().split(":") for item in key_str.split(","))
# For each dict key
for k, v in key_spec.items():
if v not in primitive_data_types:
raise ValueError(f"Data type {v} of key {k} is not supported as a dict field")
# Optional subattribute
k_optional = k[-1] == "?"
# Set (type, default value) for the key
if k_optional:
k = k[:-1] # Remove question mark from key
dict_spec[k] = (Optional[primitive_data_types[v]], None)
else:
dict_spec[k] = (primitive_data_types[v], ...)
# Create model for this dict
data_type = create_model(f"{str_type}__inner", **dict_spec)
self._type_info = (
"dict<" + ",".join(f"{k}:{v}" for k, v in sorted(dict_spec.items())) + ">"
)
elif m := re.match(re_category, str_type):
# Category
category_type, category_values = m.group("type"), m.group("vals")
category_type = DataType(root=category_type)
category_values = [
category_type._data_type(value.strip()) for value in category_values.split(",")
]
data_type = Enum(
f"Category<{category_type}>", {str(val): val for val in category_values}
)
else:
raise ValueError(f"Data type '{str_type}' is not supported")
# Set data type
self._data_type = data_type
# Set default type info
if self._type_info is None:
self._type_info = str(data_type)
return self