mirror of
https://github.com/divkit/divkit.git
synced 2026-05-07 20:02:32 +00:00
Added untyped template resolver
commit_hash:299c999f2d6caad74d8568059e890770d7a6b1f2
This commit is contained in:
@@ -16419,6 +16419,7 @@
|
||||
"client/ios/DivKit/Templates/TemplateError.swift":"divkit/public/client/ios/DivKit/Templates/TemplateError.swift",
|
||||
"client/ios/DivKit/Templates/TemplateToType.swift":"divkit/public/client/ios/DivKit/Templates/TemplateToType.swift",
|
||||
"client/ios/DivKit/Templates/TemplateValue.swift":"divkit/public/client/ios/DivKit/Templates/TemplateValue.swift",
|
||||
"client/ios/DivKit/Templates/UntypedDivTemplateResolver.swift":"divkit/public/client/ios/DivKit/Templates/UntypedDivTemplateResolver.swift",
|
||||
"client/ios/DivKit/Timers/DivTimerAction.swift":"divkit/public/client/ios/DivKit/Timers/DivTimerAction.swift",
|
||||
"client/ios/DivKit/Timers/DivTimerController.swift":"divkit/public/client/ios/DivKit/Timers/DivTimerController.swift",
|
||||
"client/ios/DivKit/Timers/DivTimerStorage.swift":"divkit/public/client/ios/DivKit/Timers/DivTimerStorage.swift",
|
||||
@@ -16982,6 +16983,7 @@
|
||||
"client/ios/DivKitTests/Actions/ScrollActionHandlerTests.swift":"divkit/public/client/ios/DivKitTests/Actions/ScrollActionHandlerTests.swift",
|
||||
"client/ios/DivKitTests/Actions/SetStoredValueActionHandlerTests.swift":"divkit/public/client/ios/DivKitTests/Actions/SetStoredValueActionHandlerTests.swift",
|
||||
"client/ios/DivKitTests/Actions/SubmitActionHandlerTests.swift":"divkit/public/client/ios/DivKitTests/Actions/SubmitActionHandlerTests.swift",
|
||||
"client/ios/DivKitTests/DictionarySerializationBehaviorTests.swift":"divkit/public/client/ios/DivKitTests/DictionarySerializationBehaviorTests.swift",
|
||||
"client/ios/DivKitTests/DivBlockModelingContextTests.swift":"divkit/public/client/ios/DivKitTests/DivBlockModelingContextTests.swift",
|
||||
"client/ios/DivKitTests/DivBlockStateStorageTests.swift":"divkit/public/client/ios/DivKitTests/DivBlockStateStorageTests.swift",
|
||||
"client/ios/DivKitTests/DivDataParsingTests.swift":"divkit/public/client/ios/DivKitTests/DivDataParsingTests.swift",
|
||||
|
||||
@@ -150,8 +150,11 @@ class SwiftGenerator(Generator):
|
||||
def __entity_enumeration_extensions_declaration(self, entity_enumeration: EntityEnumeration) -> List[str]:
|
||||
access_modifier = self._access_level.value
|
||||
args_decl = swift_template_deserializable_args_decl(entity_enumeration.mode)
|
||||
context_arg = '' if entity_enumeration.mode.is_template else ', context: ParsingContext'
|
||||
deserializable_extension = Text(f'extension {entity_enumeration.prefixed_declaration} {{')
|
||||
deserializable_extension += f' {access_modifier}init(dictionary: [String: Any]{args_decl}) throws {{'
|
||||
deserializable_extension += (
|
||||
f' {access_modifier}init(dictionary: [String: Any]{args_decl}{context_arg}) throws {{'
|
||||
)
|
||||
body = Text()
|
||||
if entity_enumeration.mode.is_template:
|
||||
body += 'let receivedType = try dictionary.getField("type") as String'
|
||||
@@ -164,8 +167,9 @@ class SwiftGenerator(Generator):
|
||||
obj_t = entity[1].prefixed_declaration if entity[1] is not None else utils.capitalize_camel_case(entity[0])
|
||||
low_name = utils.lower_camel_case(name)
|
||||
args = swift_template_deserializable_args(entity_enumeration.mode)
|
||||
context_param = '' if entity_enumeration.mode.is_template else ', context: context'
|
||||
body += f'case {obj_t}.type:'
|
||||
body += f' self = .{low_name}(try {obj_t}(dictionary: dictionary{args}))'
|
||||
body += f' self = .{low_name}(try {obj_t}(dictionary: dictionary{args}{context_param}))'
|
||||
body += 'default:'
|
||||
args = f'field: "{entity_enumeration.name}", representation: dictionary'
|
||||
body += f' throw DeserializationError.invalidFieldRepresentation({args})'
|
||||
@@ -194,7 +198,10 @@ class SwiftGenerator(Generator):
|
||||
equatable_extension = str(equatable_extension)
|
||||
|
||||
if self.generate_serialization:
|
||||
if entity_enumeration.mode is GenerationMode.NORMAL_WITHOUT_TEMPLATES:
|
||||
if entity_enumeration.mode in [
|
||||
GenerationMode.NORMAL_WITHOUT_TEMPLATES,
|
||||
GenerationMode.NORMAL_WITH_TEMPLATES,
|
||||
]:
|
||||
result = [deserializable_extension, equatable_extension]
|
||||
elif entity_enumeration.mode.is_template:
|
||||
result = [deserializable_extension]
|
||||
@@ -252,8 +259,12 @@ class SwiftGenerator(Generator):
|
||||
result += validator_decl.indented()
|
||||
result += EMPTY
|
||||
|
||||
if self.generate_serialization and (entity.generation_mode is GenerationMode.NORMAL_WITHOUT_TEMPLATES or
|
||||
entity.generation_mode.is_template):
|
||||
if self.generate_serialization and (
|
||||
entity.generation_mode in [
|
||||
GenerationMode.NORMAL_WITHOUT_TEMPLATES,
|
||||
GenerationMode.NORMAL_WITH_TEMPLATES,
|
||||
] or entity.generation_mode.is_template
|
||||
):
|
||||
result += entity.deserializing_constructor_declaration.indented()
|
||||
result += EMPTY
|
||||
|
||||
|
||||
@@ -122,16 +122,21 @@ class SwiftEntity(Entity):
|
||||
def deserializing_constructor_declaration(self) -> Text:
|
||||
props = self.instance_properties_swift
|
||||
args_decl = swift_template_deserializable_args_decl(self.generation_mode)
|
||||
context_arg = '' if self.generation_mode.is_template else ', context: ParsingContext'
|
||||
if not props:
|
||||
return Text(f'public init(dictionary: [String: Any]{args_decl}) throws {{}}')
|
||||
result = Text(f'public convenience init(dictionary: [String: Any]{args_decl}) throws {{')
|
||||
return Text(f'public init(dictionary: [String: Any]{args_decl}{context_arg}) throws {{}}')
|
||||
result = Text(f'public convenience init(dictionary: [String: Any]{args_decl}{context_arg}) throws {{')
|
||||
lines = Text('self.init(')
|
||||
for prop in props:
|
||||
tail = '' if prop == props[-1] else ','
|
||||
if prop.name == PARENT_PROPERTY.name:
|
||||
lines += f' {prop.declaration_name}: dictionary["type"] as? String{tail}'
|
||||
else:
|
||||
lines += f' {prop.declaration_name}: dictionary.{prop.deserialization_expression}{tail}'
|
||||
throw_prefix = '' if self.generation_mode.is_template else 'try '
|
||||
lines += (
|
||||
f' {prop.declaration_name}: '
|
||||
f'{throw_prefix}dictionary.{prop.deserialization_expression_for_mode(self.generation_mode)}{tail}'
|
||||
)
|
||||
lines += ')'
|
||||
result += lines.indented()
|
||||
result += '}'
|
||||
@@ -587,19 +592,54 @@ class SwiftProperty(Property):
|
||||
|
||||
@property
|
||||
def deserialization_expression(self) -> str:
|
||||
return self.deserialization_expression_for_mode(self.mode)
|
||||
|
||||
def deserialization_expression_for_mode(self, mode: GenerationMode) -> str:
|
||||
is_normal_mode = mode in [
|
||||
GenerationMode.NORMAL_WITH_TEMPLATES,
|
||||
GenerationMode.NORMAL_WITHOUT_TEMPLATES,
|
||||
]
|
||||
if is_normal_mode and self.property_type.can_be_templated:
|
||||
validator_arg_string = self.validator_arg(entity_name='Self', mode=mode)
|
||||
declaration_mode = SwiftProperty.SwiftMode(value=mode, use_expressions=False)
|
||||
|
||||
if isinstance(self.property_type, Array):
|
||||
item_type = cast(SwiftPropertyType, self.property_type.property_type).prefixed_declaration(
|
||||
declaration_mode
|
||||
)
|
||||
optional_suffix = 'Optional' if self.parsed_value_is_optional else ''
|
||||
expr = (
|
||||
f'"{self.dict_field}", transform: '
|
||||
f'{{ (dict: [String: Any]) in try? {item_type}(dictionary: dict, context: context) }}'
|
||||
f'{validator_arg_string}'
|
||||
)
|
||||
return f'get{optional_suffix}Array({expr})'
|
||||
|
||||
value_type = cast(SwiftPropertyType, self.property_type).prefixed_declaration(
|
||||
declaration_mode
|
||||
)
|
||||
optional_suffix = 'Optional' if self.parsed_value_is_optional else ''
|
||||
expr = (
|
||||
f'"{self.dict_field}", transform: '
|
||||
f'{{ (dict: [String: Any]) in try {value_type}(dictionary: dict, context: context) }}'
|
||||
f'{validator_arg_string}'
|
||||
)
|
||||
return f'get{optional_suffix}Field({expr})'
|
||||
|
||||
if isinstance(self.property_type, Array):
|
||||
type_suffix = 'Array'
|
||||
else:
|
||||
type_suffix = 'Field'
|
||||
if self.property_type.can_be_templated:
|
||||
template_to_type_arg = swift_template_deserializable_args(self.mode)
|
||||
template_to_type_arg = swift_template_deserializable_args(mode)
|
||||
else:
|
||||
template_to_type_arg = ''
|
||||
optional_suffix = 'Optional' if self.parsed_value_is_optional or self.mode.is_template else ''
|
||||
optional_suffix = 'Optional' if self.parsed_value_is_optional or mode.is_template else ''
|
||||
expression_suffix = 'Expression' if self.supports_expressions else ''
|
||||
validator_arg_string = self.validator_arg(entity_name='Self', mode=self.mode)
|
||||
validator_arg_string = self.validator_arg(entity_name='Self', mode=mode)
|
||||
transformed = cast(SwiftPropertyType, self.property_type).transform_arg
|
||||
expr = f'"{self.dict_field}"{template_to_type_arg}{transformed}{validator_arg_string}'
|
||||
context_arg = ', context: context' if is_normal_mode else ''
|
||||
expr = f'"{self.dict_field}"{template_to_type_arg}{transformed}{validator_arg_string}{context_arg}'
|
||||
return f'get{optional_suffix}{expression_suffix}{type_suffix}({expr})'
|
||||
|
||||
def add_default_value_to(self, declaration: str, public_default_value: bool = False) -> str:
|
||||
|
||||
@@ -71,47 +71,47 @@ public enum Entity: Sendable {
|
||||
}
|
||||
|
||||
extension Entity {
|
||||
public init(dictionary: [String: Any]) throws {
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
let blockType = try dictionary.getField("type") as String
|
||||
switch blockType {
|
||||
case EntityWithArray.type:
|
||||
self = .entityWithArray(try EntityWithArray(dictionary: dictionary))
|
||||
self = .entityWithArray(try EntityWithArray(dictionary: dictionary, context: context))
|
||||
case EntityWithArrayOfEnums.type:
|
||||
self = .entityWithArrayOfEnums(try EntityWithArrayOfEnums(dictionary: dictionary))
|
||||
self = .entityWithArrayOfEnums(try EntityWithArrayOfEnums(dictionary: dictionary, context: context))
|
||||
case EntityWithArrayOfExpressions.type:
|
||||
self = .entityWithArrayOfExpressions(try EntityWithArrayOfExpressions(dictionary: dictionary))
|
||||
self = .entityWithArrayOfExpressions(try EntityWithArrayOfExpressions(dictionary: dictionary, context: context))
|
||||
case EntityWithArrayOfNestedItems.type:
|
||||
self = .entityWithArrayOfNestedItems(try EntityWithArrayOfNestedItems(dictionary: dictionary))
|
||||
self = .entityWithArrayOfNestedItems(try EntityWithArrayOfNestedItems(dictionary: dictionary, context: context))
|
||||
case EntityWithArrayWithTransform.type:
|
||||
self = .entityWithArrayWithTransform(try EntityWithArrayWithTransform(dictionary: dictionary))
|
||||
self = .entityWithArrayWithTransform(try EntityWithArrayWithTransform(dictionary: dictionary, context: context))
|
||||
case EntityWithComplexProperty.type:
|
||||
self = .entityWithComplexProperty(try EntityWithComplexProperty(dictionary: dictionary))
|
||||
self = .entityWithComplexProperty(try EntityWithComplexProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithComplexPropertyWithDefaultValue.type:
|
||||
self = .entityWithComplexPropertyWithDefaultValue(try EntityWithComplexPropertyWithDefaultValue(dictionary: dictionary))
|
||||
self = .entityWithComplexPropertyWithDefaultValue(try EntityWithComplexPropertyWithDefaultValue(dictionary: dictionary, context: context))
|
||||
case EntityWithEntityProperty.type:
|
||||
self = .entityWithEntityProperty(try EntityWithEntityProperty(dictionary: dictionary))
|
||||
self = .entityWithEntityProperty(try EntityWithEntityProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithOptionalComplexProperty.type:
|
||||
self = .entityWithOptionalComplexProperty(try EntityWithOptionalComplexProperty(dictionary: dictionary))
|
||||
self = .entityWithOptionalComplexProperty(try EntityWithOptionalComplexProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithOptionalProperty.type:
|
||||
self = .entityWithOptionalProperty(try EntityWithOptionalProperty(dictionary: dictionary))
|
||||
self = .entityWithOptionalProperty(try EntityWithOptionalProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithOptionalStringEnumProperty.type:
|
||||
self = .entityWithOptionalStringEnumProperty(try EntityWithOptionalStringEnumProperty(dictionary: dictionary))
|
||||
self = .entityWithOptionalStringEnumProperty(try EntityWithOptionalStringEnumProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithPropertyWithDefaultValue.type:
|
||||
self = .entityWithPropertyWithDefaultValue(try EntityWithPropertyWithDefaultValue(dictionary: dictionary))
|
||||
self = .entityWithPropertyWithDefaultValue(try EntityWithPropertyWithDefaultValue(dictionary: dictionary, context: context))
|
||||
case EntityWithRawArray.type:
|
||||
self = .entityWithRawArray(try EntityWithRawArray(dictionary: dictionary))
|
||||
self = .entityWithRawArray(try EntityWithRawArray(dictionary: dictionary, context: context))
|
||||
case EntityWithRequiredProperty.type:
|
||||
self = .entityWithRequiredProperty(try EntityWithRequiredProperty(dictionary: dictionary))
|
||||
self = .entityWithRequiredProperty(try EntityWithRequiredProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithSimpleProperties.type:
|
||||
self = .entityWithSimpleProperties(try EntityWithSimpleProperties(dictionary: dictionary))
|
||||
self = .entityWithSimpleProperties(try EntityWithSimpleProperties(dictionary: dictionary, context: context))
|
||||
case EntityWithStringArrayProperty.type:
|
||||
self = .entityWithStringArrayProperty(try EntityWithStringArrayProperty(dictionary: dictionary))
|
||||
self = .entityWithStringArrayProperty(try EntityWithStringArrayProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithStringEnumProperty.type:
|
||||
self = .entityWithStringEnumProperty(try EntityWithStringEnumProperty(dictionary: dictionary))
|
||||
self = .entityWithStringEnumProperty(try EntityWithStringEnumProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithStringEnumPropertyWithDefaultValue.type:
|
||||
self = .entityWithStringEnumPropertyWithDefaultValue(try EntityWithStringEnumPropertyWithDefaultValue(dictionary: dictionary))
|
||||
self = .entityWithStringEnumPropertyWithDefaultValue(try EntityWithStringEnumPropertyWithDefaultValue(dictionary: dictionary, context: context))
|
||||
case EntityWithoutProperties.type:
|
||||
self = .entityWithoutProperties(try EntityWithoutProperties(dictionary: dictionary))
|
||||
self = .entityWithoutProperties(try EntityWithoutProperties(dictionary: dictionary, context: context))
|
||||
default:
|
||||
throw DeserializationError.invalidFieldRepresentation(field: "entity", representation: dictionary)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@ public final class EntityWithArray: Sendable {
|
||||
static let arrayValidator: AnyArrayValueValidator<Entity> =
|
||||
makeArrayValidator(minItems: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
array: try dictionary.getArray("array", transform: { (dict: [String: Any]) in try? Entity(dictionary: dict, context: context) }, validator: Self.arrayValidator)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
array: [Entity]
|
||||
) {
|
||||
|
||||
@@ -17,6 +17,12 @@ public final class EntityWithArrayOfEnums: Sendable {
|
||||
static let itemsValidator: AnyArrayValueValidator<EntityWithArrayOfEnums.Item> =
|
||||
makeArrayValidator(minItems: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
items: try dictionary.getArray("items", validator: Self.itemsValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
items: [Item]
|
||||
) {
|
||||
|
||||
@@ -15,6 +15,12 @@ public final class EntityWithArrayOfExpressions: Sendable {
|
||||
static let itemsValidator: AnyArrayValueValidator<Expression<String>> =
|
||||
makeArrayValidator(minItems: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
items: try dictionary.getExpressionArray("items", validator: Self.itemsValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
items: [Expression<String>]
|
||||
) {
|
||||
|
||||
@@ -13,6 +13,13 @@ public final class EntityWithArrayOfNestedItems: Sendable {
|
||||
resolver.resolveString(property)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
entity: try dictionary.getField("entity", transform: { (dict: [String: Any]) in try Entity(dictionary: dict, context: context) }),
|
||||
property: try dictionary.getExpressionField("property", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
entity: Entity,
|
||||
property: Expression<String>
|
||||
@@ -28,6 +35,12 @@ public final class EntityWithArrayOfNestedItems: Sendable {
|
||||
static let itemsValidator: AnyArrayValueValidator<EntityWithArrayOfNestedItems.Item> =
|
||||
makeArrayValidator(minItems: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
items: try dictionary.getArray("items", transform: { (dict: [String: Any]) in try? EntityWithArrayOfNestedItems.Item(dictionary: dict, context: context) }, validator: Self.itemsValidator)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
items: [Item]
|
||||
) {
|
||||
|
||||
@@ -15,6 +15,12 @@ public final class EntityWithArrayWithTransform: Sendable {
|
||||
static let arrayValidator: AnyArrayValueValidator<Expression<Color>> =
|
||||
makeArrayValidator(minItems: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
array: try dictionary.getExpressionArray("array", transform: Color.color(withHexString:), validator: Self.arrayValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
array: [Expression<Color>]
|
||||
) {
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class EntityWithComplexProperty: Sendable {
|
||||
resolver.resolveUrl(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getExpressionField("value", transform: URL.makeFromNonEncodedString, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<URL>
|
||||
) {
|
||||
@@ -22,6 +28,12 @@ public final class EntityWithComplexProperty: Sendable {
|
||||
public static let type: String = "entity_with_complex_property"
|
||||
public let property: Property
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getField("property", transform: { (dict: [String: Any]) in try EntityWithComplexProperty.Property(dictionary: dict, context: context) })
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Property
|
||||
) {
|
||||
|
||||
+12
@@ -12,6 +12,12 @@ public final class EntityWithComplexPropertyWithDefaultValue: Sendable {
|
||||
resolver.resolveString(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getExpressionField("value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<String>
|
||||
) {
|
||||
@@ -22,6 +28,12 @@ public final class EntityWithComplexPropertyWithDefaultValue: Sendable {
|
||||
public static let type: String = "entity_with_complex_property_with_default_value"
|
||||
public let property: Property // default value: EntityWithComplexPropertyWithDefaultValue.Property(value: .value("Default text"))
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getOptionalField("property", transform: { (dict: [String: Any]) in try EntityWithComplexPropertyWithDefaultValue.Property(dictionary: dict, context: context) })
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Property? = nil
|
||||
) {
|
||||
|
||||
@@ -8,6 +8,12 @@ public final class EntityWithEntityProperty: Sendable {
|
||||
public static let type: String = "entity_with_entity_property"
|
||||
public let entity: Entity // default value: .entityWithStringEnumProperty(EntityWithStringEnumProperty(property: .value(.second)))
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
entity: try dictionary.getOptionalField("entity", transform: { (dict: [String: Any]) in try Entity(dictionary: dict, context: context) })
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
entity: Entity? = nil
|
||||
) {
|
||||
|
||||
@@ -8,6 +8,12 @@ public final class EntityWithJsonProperty: @unchecked Sendable {
|
||||
public static let type: String = "entity_with_json_property"
|
||||
public let jsonProperty: [String: Any] // default value: { "key": "value", "items": [ "value" ] }
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
jsonProperty: try dictionary.getOptionalField("json_property", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
jsonProperty: [String: Any]? = nil
|
||||
) {
|
||||
|
||||
+12
@@ -12,6 +12,12 @@ public final class EntityWithOptionalComplexProperty: Sendable {
|
||||
resolver.resolveUrl(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getExpressionField("value", transform: URL.makeFromNonEncodedString, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<URL>
|
||||
) {
|
||||
@@ -22,6 +28,12 @@ public final class EntityWithOptionalComplexProperty: Sendable {
|
||||
public static let type: String = "entity_with_optional_complex_property"
|
||||
public let property: Property?
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getOptionalField("property", transform: { (dict: [String: Any]) in try EntityWithOptionalComplexProperty.Property(dictionary: dict, context: context) })
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Property? = nil
|
||||
) {
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class EntityWithOptionalProperty: Sendable {
|
||||
resolver.resolveString(property)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getOptionalExpressionField("property", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Expression<String>? = nil
|
||||
) {
|
||||
|
||||
+6
@@ -18,6 +18,12 @@ public final class EntityWithOptionalStringEnumProperty: Sendable {
|
||||
resolver.resolveEnum(property)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getOptionalExpressionField("property", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Expression<Property>? = nil
|
||||
) {
|
||||
|
||||
+16
@@ -28,6 +28,14 @@ public final class EntityWithPropertyWithDefaultValue: Sendable {
|
||||
static let urlValidator: AnyValueValidator<URL> =
|
||||
makeURLValidator(schemes: ["https"])
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
int: try dictionary.getOptionalExpressionField("int", validator: Self.intValidator, context: context),
|
||||
nonOptional: try dictionary.getExpressionField("non_optional", context: context),
|
||||
url: try dictionary.getOptionalExpressionField("url", transform: URL.makeFromNonEncodedString, validator: Self.urlValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
int: Expression<Int>? = nil,
|
||||
nonOptional: Expression<String>,
|
||||
@@ -58,6 +66,14 @@ public final class EntityWithPropertyWithDefaultValue: Sendable {
|
||||
static let urlValidator: AnyValueValidator<URL> =
|
||||
makeURLValidator(schemes: ["https"])
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
int: try dictionary.getOptionalExpressionField("int", validator: Self.intValidator, context: context),
|
||||
nested: try dictionary.getOptionalField("nested", transform: { (dict: [String: Any]) in try EntityWithPropertyWithDefaultValue.Nested(dictionary: dict, context: context) }),
|
||||
url: try dictionary.getOptionalExpressionField("url", transform: URL.makeFromNonEncodedString, validator: Self.urlValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
int: Expression<Int>? = nil,
|
||||
nested: Nested? = nil,
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class EntityWithRawArray: @unchecked Sendable {
|
||||
resolver.resolveArray(array)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
array: try dictionary.getExpressionField("array", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
array: Expression<[Any]>
|
||||
) {
|
||||
|
||||
@@ -15,6 +15,12 @@ public final class EntityWithRequiredProperty: Sendable {
|
||||
static let propertyValidator: AnyValueValidator<String> =
|
||||
makeStringValidator(minLength: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getExpressionField("property", validator: Self.propertyValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Expression<String>
|
||||
) {
|
||||
|
||||
@@ -51,6 +51,20 @@ public final class EntityWithSimpleProperties: EntityProtocol, Sendable {
|
||||
static let positiveIntegerValidator: AnyValueValidator<Int> =
|
||||
makeValueValidator(valueValidator: { $0 > 0 })
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
boolean: try dictionary.getOptionalExpressionField("boolean", context: context),
|
||||
booleanInt: try dictionary.getOptionalExpressionField("boolean_int", context: context),
|
||||
color: try dictionary.getOptionalExpressionField("color", transform: Color.color(withHexString:), context: context),
|
||||
double: try dictionary.getOptionalExpressionField("double", context: context),
|
||||
id: try dictionary.getOptionalField("id", context: context),
|
||||
integer: try dictionary.getOptionalExpressionField("integer", context: context),
|
||||
positiveInteger: try dictionary.getOptionalExpressionField("positive_integer", validator: Self.positiveIntegerValidator, context: context),
|
||||
string: try dictionary.getOptionalExpressionField("string", context: context),
|
||||
url: try dictionary.getOptionalExpressionField("url", transform: URL.makeFromNonEncodedString, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
boolean: Expression<Bool>? = nil,
|
||||
booleanInt: Expression<Bool>? = nil,
|
||||
|
||||
@@ -15,6 +15,12 @@ public final class EntityWithStringArrayProperty: Sendable {
|
||||
static let arrayValidator: AnyArrayValueValidator<Expression<String>> =
|
||||
makeArrayValidator(minItems: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
array: try dictionary.getExpressionArray("array", validator: Self.arrayValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
array: [Expression<String>]
|
||||
) {
|
||||
|
||||
@@ -18,6 +18,12 @@ public final class EntityWithStringEnumProperty: Sendable {
|
||||
resolver.resolveEnum(property)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getExpressionField("property", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Expression<Property>
|
||||
) {
|
||||
|
||||
+6
@@ -19,6 +19,12 @@ public final class EntityWithStringEnumPropertyWithDefaultValue: Sendable {
|
||||
resolver.resolveEnum(value) ?? Value.second
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getOptionalExpressionField("value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<Value>? = nil
|
||||
) {
|
||||
|
||||
@@ -7,6 +7,8 @@ import Serialization
|
||||
public final class EntityWithoutProperties: Sendable {
|
||||
public static let type: String = "entity_without_properties"
|
||||
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {}
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,13 @@ public enum EnumWithDefaultType: Sendable {
|
||||
}
|
||||
|
||||
extension EnumWithDefaultType {
|
||||
public init(dictionary: [String: Any]) throws {
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
let blockType = try dictionary.getField("type") as String
|
||||
switch blockType {
|
||||
case WithDefault.type:
|
||||
self = .withDefault(try WithDefault(dictionary: dictionary))
|
||||
self = .withDefault(try WithDefault(dictionary: dictionary, context: context))
|
||||
case WithoutDefault.type:
|
||||
self = .withoutDefault(try WithoutDefault(dictionary: dictionary))
|
||||
self = .withoutDefault(try WithoutDefault(dictionary: dictionary, context: context))
|
||||
default:
|
||||
throw DeserializationError.invalidFieldRepresentation(field: "enum_with_default_type", representation: dictionary)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import Serialization
|
||||
public final class WithDefault: Sendable {
|
||||
public static let type: String = "default"
|
||||
|
||||
public init(dictionary: [String: Any]) throws {}
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {}
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import Serialization
|
||||
public final class WithoutDefault: Sendable {
|
||||
public static let type: String = "non_default"
|
||||
|
||||
public init(dictionary: [String: Any]) throws {}
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {}
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,54 @@ public enum Entity: Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
extension Entity {
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
let blockType = try dictionary.getField("type") as String
|
||||
switch blockType {
|
||||
case EntityWithArray.type:
|
||||
self = .entityWithArray(try EntityWithArray(dictionary: dictionary, context: context))
|
||||
case EntityWithArrayOfEnums.type:
|
||||
self = .entityWithArrayOfEnums(try EntityWithArrayOfEnums(dictionary: dictionary, context: context))
|
||||
case EntityWithArrayOfExpressions.type:
|
||||
self = .entityWithArrayOfExpressions(try EntityWithArrayOfExpressions(dictionary: dictionary, context: context))
|
||||
case EntityWithArrayOfNestedItems.type:
|
||||
self = .entityWithArrayOfNestedItems(try EntityWithArrayOfNestedItems(dictionary: dictionary, context: context))
|
||||
case EntityWithArrayWithTransform.type:
|
||||
self = .entityWithArrayWithTransform(try EntityWithArrayWithTransform(dictionary: dictionary, context: context))
|
||||
case EntityWithComplexProperty.type:
|
||||
self = .entityWithComplexProperty(try EntityWithComplexProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithComplexPropertyWithDefaultValue.type:
|
||||
self = .entityWithComplexPropertyWithDefaultValue(try EntityWithComplexPropertyWithDefaultValue(dictionary: dictionary, context: context))
|
||||
case EntityWithEntityProperty.type:
|
||||
self = .entityWithEntityProperty(try EntityWithEntityProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithOptionalComplexProperty.type:
|
||||
self = .entityWithOptionalComplexProperty(try EntityWithOptionalComplexProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithOptionalProperty.type:
|
||||
self = .entityWithOptionalProperty(try EntityWithOptionalProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithOptionalStringEnumProperty.type:
|
||||
self = .entityWithOptionalStringEnumProperty(try EntityWithOptionalStringEnumProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithPropertyWithDefaultValue.type:
|
||||
self = .entityWithPropertyWithDefaultValue(try EntityWithPropertyWithDefaultValue(dictionary: dictionary, context: context))
|
||||
case EntityWithRawArray.type:
|
||||
self = .entityWithRawArray(try EntityWithRawArray(dictionary: dictionary, context: context))
|
||||
case EntityWithRequiredProperty.type:
|
||||
self = .entityWithRequiredProperty(try EntityWithRequiredProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithSimpleProperties.type:
|
||||
self = .entityWithSimpleProperties(try EntityWithSimpleProperties(dictionary: dictionary, context: context))
|
||||
case EntityWithStringArrayProperty.type:
|
||||
self = .entityWithStringArrayProperty(try EntityWithStringArrayProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithStringEnumProperty.type:
|
||||
self = .entityWithStringEnumProperty(try EntityWithStringEnumProperty(dictionary: dictionary, context: context))
|
||||
case EntityWithStringEnumPropertyWithDefaultValue.type:
|
||||
self = .entityWithStringEnumPropertyWithDefaultValue(try EntityWithStringEnumPropertyWithDefaultValue(dictionary: dictionary, context: context))
|
||||
case EntityWithoutProperties.type:
|
||||
self = .entityWithoutProperties(try EntityWithoutProperties(dictionary: dictionary, context: context))
|
||||
default:
|
||||
throw DeserializationError.invalidFieldRepresentation(field: "entity", representation: dictionary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
extension Entity: Equatable {
|
||||
public static func ==(lhs: Entity, rhs: Entity) -> Bool {
|
||||
|
||||
@@ -11,6 +11,12 @@ public final class EntityWithArray: Sendable {
|
||||
static let arrayValidator: AnyArrayValueValidator<Entity> =
|
||||
makeArrayValidator(minItems: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
array: try dictionary.getArray("array", transform: { (dict: [String: Any]) in try? Entity(dictionary: dict, context: context) }, validator: Self.arrayValidator)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
array: [Entity]
|
||||
) {
|
||||
|
||||
@@ -17,6 +17,12 @@ public final class EntityWithArrayOfEnums: Sendable {
|
||||
static let itemsValidator: AnyArrayValueValidator<EntityWithArrayOfEnums.Item> =
|
||||
makeArrayValidator(minItems: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
items: try dictionary.getArray("items", validator: Self.itemsValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
items: [Item]
|
||||
) {
|
||||
|
||||
@@ -15,6 +15,12 @@ public final class EntityWithArrayOfExpressions: Sendable {
|
||||
static let itemsValidator: AnyArrayValueValidator<Expression<String>> =
|
||||
makeArrayValidator(minItems: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
items: try dictionary.getExpressionArray("items", validator: Self.itemsValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
items: [Expression<String>]
|
||||
) {
|
||||
|
||||
@@ -13,6 +13,13 @@ public final class EntityWithArrayOfNestedItems: Sendable {
|
||||
resolver.resolveString(property)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
entity: try dictionary.getField("entity", transform: { (dict: [String: Any]) in try Entity(dictionary: dict, context: context) }),
|
||||
property: try dictionary.getExpressionField("property", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
entity: Entity,
|
||||
property: Expression<String>
|
||||
@@ -28,6 +35,12 @@ public final class EntityWithArrayOfNestedItems: Sendable {
|
||||
static let itemsValidator: AnyArrayValueValidator<EntityWithArrayOfNestedItems.Item> =
|
||||
makeArrayValidator(minItems: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
items: try dictionary.getArray("items", transform: { (dict: [String: Any]) in try? EntityWithArrayOfNestedItems.Item(dictionary: dict, context: context) }, validator: Self.itemsValidator)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
items: [Item]
|
||||
) {
|
||||
|
||||
@@ -15,6 +15,12 @@ public final class EntityWithArrayWithTransform: Sendable {
|
||||
static let arrayValidator: AnyArrayValueValidator<Expression<Color>> =
|
||||
makeArrayValidator(minItems: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
array: try dictionary.getExpressionArray("array", transform: Color.color(withHexString:), validator: Self.arrayValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
array: [Expression<Color>]
|
||||
) {
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class EntityWithComplexProperty: Sendable {
|
||||
resolver.resolveUrl(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getExpressionField("value", transform: URL.makeFromNonEncodedString, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<URL>
|
||||
) {
|
||||
@@ -22,6 +28,12 @@ public final class EntityWithComplexProperty: Sendable {
|
||||
public static let type: String = "entity_with_complex_property"
|
||||
public let property: Property
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getField("property", transform: { (dict: [String: Any]) in try EntityWithComplexProperty.Property(dictionary: dict, context: context) })
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Property
|
||||
) {
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class EntityWithComplexPropertyWithDefaultValue: Sendable {
|
||||
resolver.resolveString(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getExpressionField("value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<String>
|
||||
) {
|
||||
@@ -22,6 +28,12 @@ public final class EntityWithComplexPropertyWithDefaultValue: Sendable {
|
||||
public static let type: String = "entity_with_complex_property_with_default_value"
|
||||
public let property: Property // default value: EntityWithComplexPropertyWithDefaultValue.Property(value: .value("Default text"))
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getOptionalField("property", transform: { (dict: [String: Any]) in try EntityWithComplexPropertyWithDefaultValue.Property(dictionary: dict, context: context) })
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Property? = nil
|
||||
) {
|
||||
|
||||
@@ -8,6 +8,12 @@ public final class EntityWithEntityProperty: Sendable {
|
||||
public static let type: String = "entity_with_entity_property"
|
||||
public let entity: Entity // default value: .entityWithStringEnumProperty(EntityWithStringEnumProperty(property: .value(.second)))
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
entity: try dictionary.getOptionalField("entity", transform: { (dict: [String: Any]) in try Entity(dictionary: dict, context: context) })
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
entity: Entity? = nil
|
||||
) {
|
||||
|
||||
@@ -8,6 +8,12 @@ public final class EntityWithJsonProperty: @unchecked Sendable {
|
||||
public static let type: String = "entity_with_json_property"
|
||||
public let jsonProperty: [String: Any] // default value: { "key": "value", "items": [ "value" ] }
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
jsonProperty: try dictionary.getOptionalField("json_property", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
jsonProperty: [String: Any]? = nil
|
||||
) {
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class EntityWithOptionalComplexProperty: Sendable {
|
||||
resolver.resolveUrl(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getExpressionField("value", transform: URL.makeFromNonEncodedString, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<URL>
|
||||
) {
|
||||
@@ -22,6 +28,12 @@ public final class EntityWithOptionalComplexProperty: Sendable {
|
||||
public static let type: String = "entity_with_optional_complex_property"
|
||||
public let property: Property?
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getOptionalField("property", transform: { (dict: [String: Any]) in try EntityWithOptionalComplexProperty.Property(dictionary: dict, context: context) })
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Property? = nil
|
||||
) {
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class EntityWithOptionalProperty: Sendable {
|
||||
resolver.resolveString(property)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getOptionalExpressionField("property", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Expression<String>? = nil
|
||||
) {
|
||||
|
||||
@@ -18,6 +18,12 @@ public final class EntityWithOptionalStringEnumProperty: Sendable {
|
||||
resolver.resolveEnum(property)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getOptionalExpressionField("property", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Expression<Property>? = nil
|
||||
) {
|
||||
|
||||
@@ -28,6 +28,14 @@ public final class EntityWithPropertyWithDefaultValue: Sendable {
|
||||
static let urlValidator: AnyValueValidator<URL> =
|
||||
makeURLValidator(schemes: ["https"])
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
int: try dictionary.getOptionalExpressionField("int", validator: Self.intValidator, context: context),
|
||||
nonOptional: try dictionary.getExpressionField("non_optional", context: context),
|
||||
url: try dictionary.getOptionalExpressionField("url", transform: URL.makeFromNonEncodedString, validator: Self.urlValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
int: Expression<Int>? = nil,
|
||||
nonOptional: Expression<String>,
|
||||
@@ -58,6 +66,14 @@ public final class EntityWithPropertyWithDefaultValue: Sendable {
|
||||
static let urlValidator: AnyValueValidator<URL> =
|
||||
makeURLValidator(schemes: ["https"])
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
int: try dictionary.getOptionalExpressionField("int", validator: Self.intValidator, context: context),
|
||||
nested: try dictionary.getOptionalField("nested", transform: { (dict: [String: Any]) in try EntityWithPropertyWithDefaultValue.Nested(dictionary: dict, context: context) }),
|
||||
url: try dictionary.getOptionalExpressionField("url", transform: URL.makeFromNonEncodedString, validator: Self.urlValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
int: Expression<Int>? = nil,
|
||||
nested: Nested? = nil,
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class EntityWithRawArray: @unchecked Sendable {
|
||||
resolver.resolveArray(array)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
array: try dictionary.getExpressionField("array", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
array: Expression<[Any]>
|
||||
) {
|
||||
|
||||
@@ -15,6 +15,12 @@ public final class EntityWithRequiredProperty: Sendable {
|
||||
static let propertyValidator: AnyValueValidator<String> =
|
||||
makeStringValidator(minLength: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getExpressionField("property", validator: Self.propertyValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Expression<String>
|
||||
) {
|
||||
|
||||
@@ -51,6 +51,20 @@ public final class EntityWithSimpleProperties: EntityProtocol, Sendable {
|
||||
static let positiveIntegerValidator: AnyValueValidator<Int> =
|
||||
makeValueValidator(valueValidator: { $0 > 0 })
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
boolean: try dictionary.getOptionalExpressionField("boolean", context: context),
|
||||
booleanInt: try dictionary.getOptionalExpressionField("boolean_int", context: context),
|
||||
color: try dictionary.getOptionalExpressionField("color", transform: Color.color(withHexString:), context: context),
|
||||
double: try dictionary.getOptionalExpressionField("double", context: context),
|
||||
id: try dictionary.getOptionalField("id", context: context),
|
||||
integer: try dictionary.getOptionalExpressionField("integer", context: context),
|
||||
positiveInteger: try dictionary.getOptionalExpressionField("positive_integer", validator: Self.positiveIntegerValidator, context: context),
|
||||
string: try dictionary.getOptionalExpressionField("string", context: context),
|
||||
url: try dictionary.getOptionalExpressionField("url", transform: URL.makeFromNonEncodedString, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
boolean: Expression<Bool>? = nil,
|
||||
booleanInt: Expression<Bool>? = nil,
|
||||
|
||||
@@ -15,6 +15,12 @@ public final class EntityWithStringArrayProperty: Sendable {
|
||||
static let arrayValidator: AnyArrayValueValidator<Expression<String>> =
|
||||
makeArrayValidator(minItems: 1)
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
array: try dictionary.getExpressionArray("array", validator: Self.arrayValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
array: [Expression<String>]
|
||||
) {
|
||||
|
||||
@@ -18,6 +18,12 @@ public final class EntityWithStringEnumProperty: Sendable {
|
||||
resolver.resolveEnum(property)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
property: try dictionary.getExpressionField("property", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
property: Expression<Property>
|
||||
) {
|
||||
|
||||
+6
@@ -19,6 +19,12 @@ public final class EntityWithStringEnumPropertyWithDefaultValue: Sendable {
|
||||
resolver.resolveEnum(value) ?? Value.second
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getOptionalExpressionField("value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<Value>? = nil
|
||||
) {
|
||||
|
||||
@@ -7,6 +7,8 @@ import Serialization
|
||||
public final class EntityWithoutProperties: Sendable {
|
||||
public static let type: String = "entity_without_properties"
|
||||
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {}
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,20 @@ public enum EnumWithDefaultType: Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
extension EnumWithDefaultType {
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
let blockType = try dictionary.getField("type") as String
|
||||
switch blockType {
|
||||
case WithDefault.type:
|
||||
self = .withDefault(try WithDefault(dictionary: dictionary, context: context))
|
||||
case WithoutDefault.type:
|
||||
self = .withoutDefault(try WithoutDefault(dictionary: dictionary, context: context))
|
||||
default:
|
||||
throw DeserializationError.invalidFieldRepresentation(field: "enum_with_default_type", representation: dictionary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
extension EnumWithDefaultType: Equatable {
|
||||
public static func ==(lhs: EnumWithDefaultType, rhs: EnumWithDefaultType) -> Bool {
|
||||
|
||||
@@ -7,6 +7,8 @@ import Serialization
|
||||
public final class WithDefault: Sendable {
|
||||
public static let type: String = "default"
|
||||
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {}
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ import Serialization
|
||||
public final class WithoutDefault: Sendable {
|
||||
public static let type: String = "non_default"
|
||||
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {}
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
/// performance issues, crashes, or other problems. By default, only features that have testing are
|
||||
/// included in the framework.
|
||||
/// You can access the default `DivFlagsInfo` instance using the static property `default`.
|
||||
public struct DivFlagsInfo: Encodable, Equatable {
|
||||
public struct DivFlagsInfo: Encodable, Equatable, Sendable {
|
||||
/// The default instance of `DivFlagsInfo`.
|
||||
public static let `default` = DivFlagsInfo()
|
||||
|
||||
@@ -53,6 +53,14 @@ public struct DivFlagsInfo: Encodable, Equatable {
|
||||
/// `font_weight_value` is specified.
|
||||
public let variationFontWeightOverrideEnabled: Bool
|
||||
|
||||
/// Enables untyped template resolving pipeline for card parsing.
|
||||
///
|
||||
/// `false` - typed template resolving is used (`*Template.swift` entities).
|
||||
///
|
||||
/// `true` - templates are resolved by untyped resolver and then parsed with generated
|
||||
/// non-template deserializers.
|
||||
public let useUntypedTemplateResolver: Bool
|
||||
|
||||
/// Creates an instance of `DivFlagsInfo`.
|
||||
public init(
|
||||
useUrlHandlerForVisibilityActions: Bool = false,
|
||||
@@ -62,7 +70,8 @@ public struct DivFlagsInfo: Encodable, Equatable {
|
||||
initializeTriggerOnSet: Bool = true,
|
||||
defaultTextAutoEllipsize: Bool = true,
|
||||
fontCacheEnabled: Bool = true,
|
||||
variationFontWeightOverrideEnabled: Bool = true
|
||||
variationFontWeightOverrideEnabled: Bool = true,
|
||||
useUntypedTemplateResolver: Bool = false
|
||||
) {
|
||||
self.useUrlHandlerForVisibilityActions = useUrlHandlerForVisibilityActions
|
||||
self.imageBlurPreferMetal = imageBlurPreferMetal
|
||||
@@ -72,6 +81,7 @@ public struct DivFlagsInfo: Encodable, Equatable {
|
||||
self.defaultTextAutoEllipsize = defaultTextAutoEllipsize
|
||||
self.fontCacheEnabled = fontCacheEnabled
|
||||
self.variationFontWeightOverrideEnabled = variationFontWeightOverrideEnabled
|
||||
self.useUntypedTemplateResolver = useUntypedTemplateResolver
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +95,7 @@ extension DivFlagsInfo: Decodable {
|
||||
case defaultTextAutoEllipsize
|
||||
case fontCacheEnabled
|
||||
case variationFontWeightOverrideEnabled
|
||||
case useUntypedTemplateResolver
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
@@ -129,5 +140,10 @@ extension DivFlagsInfo: Decodable {
|
||||
Bool.self,
|
||||
forKey: .variationFontWeightOverrideEnabled
|
||||
) ?? Self.default.variationFontWeightOverrideEnabled
|
||||
|
||||
self.useUntypedTemplateResolver = try container.decodeIfPresent(
|
||||
Bool.self,
|
||||
forKey: .useUntypedTemplateResolver
|
||||
) ?? Self.default.useUntypedTemplateResolver
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,7 +265,8 @@ public final class DivKitComponents {
|
||||
let rawDivData = try RawDivData(dictionary: jsonDict)
|
||||
let result = DivData.resolve(
|
||||
card: rawDivData.card,
|
||||
templates: rawDivData.templates
|
||||
templates: rawDivData.templates,
|
||||
flagsInfo: flagsInfo
|
||||
).asCardResult(cardId: cardId)
|
||||
if let divData = result.value {
|
||||
setCardData(divData: divData, cardId: cardId)
|
||||
|
||||
@@ -43,3 +43,245 @@ extension [String: Any] {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension [String: Any] {
|
||||
func getExpressionField<T: RawRepresentable>(
|
||||
_ key: String,
|
||||
validator: AnyValueValidator<T>? = nil
|
||||
) throws -> Expression<T> where T.RawValue == String {
|
||||
try getExpressionField(
|
||||
key,
|
||||
transform: T.init(rawValue:),
|
||||
validator: validator
|
||||
)
|
||||
}
|
||||
|
||||
func getExpressionField<T: ValidSerializationValue>(
|
||||
_ key: String,
|
||||
validator: AnyValueValidator<T>? = nil
|
||||
) throws -> Expression<T> {
|
||||
try getExpressionField(
|
||||
key,
|
||||
transform: { $0 as T },
|
||||
validator: validator
|
||||
)
|
||||
}
|
||||
|
||||
func getExpressionField<T, U>(
|
||||
_ key: String,
|
||||
transform: (U) -> T?,
|
||||
validator: AnyValueValidator<T>? = nil
|
||||
) throws -> Expression<T> {
|
||||
try getField(
|
||||
key,
|
||||
transform: { expressionTransform($0, transform: transform, validator: validator) }
|
||||
)
|
||||
}
|
||||
|
||||
func getOptionalExpressionField<T: RawRepresentable>(
|
||||
_ key: String,
|
||||
validator: AnyValueValidator<T>? = nil
|
||||
) throws -> Expression<T>? where T.RawValue == String {
|
||||
try getOptionalExpressionField(
|
||||
key,
|
||||
transform: T.init(rawValue:),
|
||||
validator: validator
|
||||
)
|
||||
}
|
||||
|
||||
func getOptionalExpressionField<T: ValidSerializationValue>(
|
||||
_ key: String,
|
||||
validator: AnyValueValidator<T>? = nil
|
||||
) throws -> Expression<T>? {
|
||||
try getOptionalExpressionField(
|
||||
key,
|
||||
transform: { $0 as T },
|
||||
validator: validator
|
||||
)
|
||||
}
|
||||
|
||||
func getOptionalExpressionField<T, U>(
|
||||
_ key: String,
|
||||
transform: (U) -> T?,
|
||||
validator: AnyValueValidator<T>? = nil
|
||||
) throws -> Expression<T>? {
|
||||
try getOptionalField(
|
||||
key,
|
||||
transform: { expressionTransform($0, transform: transform, validator: validator) }
|
||||
)
|
||||
}
|
||||
|
||||
func getExpressionArray<T: ValidSerializationValue>(
|
||||
_ key: String,
|
||||
validator: AnyArrayValueValidator<Expression<T>>? = nil
|
||||
) throws -> [Expression<T>] {
|
||||
try getExpressionArray(
|
||||
key,
|
||||
transform: { $0 as T },
|
||||
validator: validator
|
||||
)
|
||||
}
|
||||
|
||||
func getExpressionArray<T, U: ValidSerializationValue>(
|
||||
_ key: String,
|
||||
transform: (U) -> T?,
|
||||
validator: AnyArrayValueValidator<Expression<T>>? = nil
|
||||
) throws -> [Expression<T>] {
|
||||
try getArray(
|
||||
key,
|
||||
transform: { (value: U) in
|
||||
expressionTransform(value, transform: transform)
|
||||
},
|
||||
validator: validator
|
||||
)
|
||||
}
|
||||
|
||||
func getOptionalExpressionArray<T: ValidSerializationValue>(
|
||||
_ key: String,
|
||||
validator: AnyArrayValueValidator<Expression<T>>? = nil
|
||||
) throws -> [Expression<T>]? {
|
||||
try getOptionalExpressionArray(
|
||||
key,
|
||||
transform: { $0 as T },
|
||||
validator: validator
|
||||
)
|
||||
}
|
||||
|
||||
func getOptionalExpressionArray<T, U: ValidSerializationValue>(
|
||||
_ key: String,
|
||||
transform: (U) -> T?,
|
||||
validator: AnyArrayValueValidator<Expression<T>>? = nil
|
||||
) throws -> [Expression<T>]? {
|
||||
try getOptionalArray(
|
||||
key,
|
||||
transform: { (value: U) in
|
||||
expressionTransform(value, transform: transform)
|
||||
},
|
||||
validator: validator
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension [String: Any] {
|
||||
func getExpressionField<T: RawRepresentable>(
|
||||
_ key: String,
|
||||
validator: AnyValueValidator<T>? = nil,
|
||||
context: ParsingContext
|
||||
) throws -> Expression<T> where T.RawValue == String {
|
||||
try getExpressionField(
|
||||
key,
|
||||
transform: T.init(rawValue:),
|
||||
validator: validator,
|
||||
context: context
|
||||
)
|
||||
}
|
||||
|
||||
func getExpressionField<T: ValidSerializationValue>(
|
||||
_ key: String,
|
||||
validator: AnyValueValidator<T>? = nil,
|
||||
context: ParsingContext
|
||||
) throws -> Expression<T> {
|
||||
try getExpressionField(key, transform: { $0 as T }, validator: validator, context: context)
|
||||
}
|
||||
|
||||
func getExpressionField<T, U>(
|
||||
_ key: String,
|
||||
transform: (U) -> T?,
|
||||
validator: AnyValueValidator<T>? = nil,
|
||||
context: ParsingContext
|
||||
) throws -> Expression<T> {
|
||||
try getField(
|
||||
key,
|
||||
transform: { expressionTransform($0, transform: transform, validator: validator) },
|
||||
context: context
|
||||
)
|
||||
}
|
||||
|
||||
func getOptionalExpressionField<T: RawRepresentable>(
|
||||
_ key: String,
|
||||
validator: AnyValueValidator<T>? = nil,
|
||||
context: ParsingContext
|
||||
) throws -> Expression<T>? where T.RawValue == String {
|
||||
try getOptionalExpressionField(
|
||||
key,
|
||||
transform: T.init(rawValue:),
|
||||
validator: validator,
|
||||
context: context
|
||||
)
|
||||
}
|
||||
|
||||
func getOptionalExpressionField<T: ValidSerializationValue>(
|
||||
_ key: String,
|
||||
validator: AnyValueValidator<T>? = nil,
|
||||
context: ParsingContext
|
||||
) throws -> Expression<T>? {
|
||||
try getOptionalExpressionField(
|
||||
key,
|
||||
transform: { $0 as T },
|
||||
validator: validator,
|
||||
context: context
|
||||
)
|
||||
}
|
||||
|
||||
func getOptionalExpressionField<T, U>(
|
||||
_ key: String,
|
||||
transform: (U) -> T?,
|
||||
validator: AnyValueValidator<T>? = nil,
|
||||
context: ParsingContext
|
||||
) throws -> Expression<T>? {
|
||||
try getOptionalField(
|
||||
key,
|
||||
transform: { expressionTransform($0, transform: transform, validator: validator) },
|
||||
context: context
|
||||
)
|
||||
}
|
||||
|
||||
func getExpressionArray<T: ValidSerializationValue>(
|
||||
_ key: String,
|
||||
validator: AnyArrayValueValidator<Expression<T>>? = nil,
|
||||
context: ParsingContext
|
||||
) throws -> [Expression<T>] {
|
||||
try getExpressionArray(key, transform: { $0 as T }, validator: validator, context: context)
|
||||
}
|
||||
|
||||
func getExpressionArray<T, U: ValidSerializationValue>(
|
||||
_ key: String,
|
||||
transform: (U) -> T?,
|
||||
validator: AnyArrayValueValidator<Expression<T>>? = nil,
|
||||
context: ParsingContext
|
||||
) throws -> [Expression<T>] {
|
||||
try getArray(
|
||||
key,
|
||||
transform: { (value: U) in expressionTransform(value, transform: transform) },
|
||||
validator: validator,
|
||||
context: context
|
||||
)
|
||||
}
|
||||
|
||||
func getOptionalExpressionArray<T: ValidSerializationValue>(
|
||||
_ key: String,
|
||||
validator: AnyArrayValueValidator<Expression<T>>? = nil,
|
||||
context: ParsingContext
|
||||
) throws -> [Expression<T>]? {
|
||||
try getOptionalExpressionArray(
|
||||
key,
|
||||
transform: { $0 as T },
|
||||
validator: validator,
|
||||
context: context
|
||||
)
|
||||
}
|
||||
|
||||
func getOptionalExpressionArray<T, U: ValidSerializationValue>(
|
||||
_ key: String,
|
||||
transform: (U) -> T?,
|
||||
validator: AnyArrayValueValidator<Expression<T>>? = nil,
|
||||
context: ParsingContext
|
||||
) throws -> [Expression<T>]? {
|
||||
try getOptionalArray(
|
||||
key,
|
||||
transform: { (value: U) in expressionTransform(value, transform: transform) },
|
||||
validator: validator,
|
||||
context: context
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,8 +69,11 @@ extension Field {
|
||||
case let .link(link):
|
||||
return context.templateData.getArray(
|
||||
link,
|
||||
transform: { (value: U) in
|
||||
expressionTransform(value, transform: transform)
|
||||
transform: { (value: U) -> DeserializationResult<Expression<E>> in
|
||||
guard let transformed = expressionTransform(value, transform: transform) else {
|
||||
return .noValue
|
||||
}
|
||||
return .success(transformed)
|
||||
},
|
||||
validator: validator
|
||||
)
|
||||
|
||||
@@ -95,8 +95,59 @@ extension DivData: DivBlockModeling {
|
||||
extension DivData {
|
||||
public static func resolve(
|
||||
card cardDict: [String: Any],
|
||||
templates templatesDict: [String: Any]?
|
||||
templates templatesDict: [String: Any]?,
|
||||
flagsInfo: DivFlagsInfo = .default
|
||||
) -> DeserializationResult<DivData> {
|
||||
if flagsInfo.useUntypedTemplateResolver {
|
||||
var resolver = UntypedDivTemplateResolver(templates: templatesDict)
|
||||
let resolvedCardResult = resolver.resolve(card: cardDict)
|
||||
let parsingContext = ParsingContext()
|
||||
|
||||
guard let resolvedCard = resolvedCardResult.value else {
|
||||
return .failure(resolvedCardResult.errorsOrWarnings ?? NonEmptyArray(.generic))
|
||||
}
|
||||
|
||||
let divDataResult: DeserializationResult<DivData>
|
||||
do {
|
||||
divDataResult = try .success(DivData(dictionary: resolvedCard, context: parsingContext))
|
||||
} catch let error as DeserializationError {
|
||||
divDataResult = .failure(NonEmptyArray(error))
|
||||
} catch {
|
||||
divDataResult =
|
||||
.failure(NonEmptyArray(.unexpectedError(message: String(describing: error))))
|
||||
}
|
||||
|
||||
let contextWarnings: NonEmptyArray<DeserializationError>? = NonEmptyArray(parsingContext
|
||||
.warnings
|
||||
)
|
||||
let contextErrors: NonEmptyArray<DeserializationError>? = NonEmptyArray(parsingContext.errors)
|
||||
let resolverWarnings = resolvedCardResult.warnings
|
||||
|
||||
let allIssues: NonEmptyArray<DeserializationError>? = NonEmptyArray(
|
||||
mergeErrors(contextWarnings, contextErrors, resolverWarnings)
|
||||
)
|
||||
|
||||
switch divDataResult {
|
||||
case let .success(value):
|
||||
if let warnings = allIssues {
|
||||
return .partialSuccess(value, warnings: warnings)
|
||||
}
|
||||
return .success(value)
|
||||
case let .partialSuccess(value, warnings):
|
||||
if let mergedWarnings = NonEmptyArray(mergeErrors(warnings, allIssues)) {
|
||||
return .partialSuccess(value, warnings: mergedWarnings)
|
||||
}
|
||||
return .success(value)
|
||||
case let .failure(errors):
|
||||
return .failure(NonEmptyArray(mergeErrors(errors, allIssues))!)
|
||||
case .noValue:
|
||||
if let warnings = allIssues {
|
||||
return .failure(warnings)
|
||||
}
|
||||
return .failure(NonEmptyArray(.generic))
|
||||
}
|
||||
}
|
||||
|
||||
let divTemplates = templatesDict.map(DivTemplates.init) ?? .empty
|
||||
return divTemplates.parseValue(type: DivDataTemplate.self, from: cardDict)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ public struct RawDivData: Deserializable, @unchecked Sendable {
|
||||
templates = try dictionary.getOptionalField("templates") ?? [:]
|
||||
}
|
||||
|
||||
public func resolve() -> DeserializationResult<DivData> {
|
||||
DivData.resolve(card: card, templates: templates)
|
||||
public func resolve(flagsInfo: DivFlagsInfo = .default) -> DeserializationResult<DivData> {
|
||||
DivData.resolve(card: card, templates: templates, flagsInfo: flagsInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
import Foundation
|
||||
import Serialization
|
||||
import VGSL
|
||||
|
||||
struct UntypedDivTemplateResolver {
|
||||
private enum Origin {
|
||||
case instance
|
||||
case template
|
||||
}
|
||||
|
||||
private struct OriginValue {
|
||||
let value: Any
|
||||
let origin: Origin
|
||||
}
|
||||
|
||||
private let templates: [TemplateName: Any]
|
||||
private let templateToType: [TemplateName: String]
|
||||
private var resolvedTemplateCache: [TemplateName: [String: Any]] = [:]
|
||||
private var currentlyResolvingTemplates = Set<TemplateName>()
|
||||
|
||||
init(templates: [String: Any]?) {
|
||||
let templates = templates ?? [:]
|
||||
self.templates = templates
|
||||
templateToType = calculateTemplateToType(in: templates)
|
||||
}
|
||||
|
||||
mutating func resolve(card: [String: Any]) -> DeserializationResult<[String: Any]> {
|
||||
resolveDictionary(
|
||||
card,
|
||||
linkSource: card,
|
||||
origin: .instance
|
||||
)
|
||||
}
|
||||
|
||||
private mutating func resolveDictionary(
|
||||
_ dictionary: [String: Any],
|
||||
linkSource: [String: Any],
|
||||
origin: Origin
|
||||
) -> DeserializationResult<[String: Any]> {
|
||||
var values: [String: OriginValue] = [:]
|
||||
|
||||
if let templateName = dictionary["type"] as? String, templates[templateName] != nil {
|
||||
let resolvedTemplate = resolveTemplate(named: templateName)
|
||||
guard let templateValue = resolvedTemplate.value else {
|
||||
return .failure(normalizedErrors(from: resolvedTemplate.errorsOrWarnings))
|
||||
}
|
||||
|
||||
values = templateValue.mapValues { OriginValue(value: $0, origin: .template) }
|
||||
for (key, value) in dictionary {
|
||||
values[key] = OriginValue(value: value, origin: .instance)
|
||||
}
|
||||
values["type"] = OriginValue(
|
||||
value: templateToType[templateName] ?? templateName,
|
||||
origin: .template
|
||||
)
|
||||
} else {
|
||||
values = dictionary.mapValues { OriginValue(value: $0, origin: .instance) }
|
||||
}
|
||||
|
||||
let currentLinkSource = origin == .instance ? dictionary : linkSource
|
||||
substituteLinks(values: &values, linkSource: currentLinkSource)
|
||||
|
||||
var resolved: [String: Any] = [:]
|
||||
var errors: [DeserializationError] = []
|
||||
|
||||
for (key, value) in values {
|
||||
guard !key.hasPrefix("$") else { continue }
|
||||
|
||||
let childResult = resolveAny(
|
||||
value.value,
|
||||
linkSource: currentLinkSource,
|
||||
origin: value.origin
|
||||
)
|
||||
if let resolvedChild = childResult.value {
|
||||
resolved[key] = resolvedChild
|
||||
}
|
||||
if let childErrors = childResult.errorsOrWarnings {
|
||||
errors.append(contentsOf: childErrors.map { .nestedObjectError(field: key, error: $0) })
|
||||
}
|
||||
}
|
||||
|
||||
if let warnings = NonEmptyArray(errors) {
|
||||
return .partialSuccess(resolved, warnings: warnings)
|
||||
}
|
||||
return .success(resolved)
|
||||
}
|
||||
|
||||
private mutating func resolveArray(
|
||||
_ array: [Any],
|
||||
linkSource: [String: Any],
|
||||
origin: Origin
|
||||
) -> DeserializationResult<[Any]> {
|
||||
var result: [Any] = []
|
||||
var errors: [DeserializationError] = []
|
||||
result.reserveCapacity(array.count)
|
||||
|
||||
for index in array.indices {
|
||||
let child = array[index]
|
||||
let childResult: DeserializationResult<Any>
|
||||
if let dictionary = child as? [String: Any] {
|
||||
let childLinkSource = origin == .instance ? dictionary : linkSource
|
||||
childResult = resolveDictionary(
|
||||
dictionary,
|
||||
linkSource: childLinkSource,
|
||||
origin: origin == .instance ? .instance : .template
|
||||
).map { $0 as Any }
|
||||
} else if let nestedArray = child as? [Any] {
|
||||
childResult = resolveArray(
|
||||
nestedArray,
|
||||
linkSource: linkSource,
|
||||
origin: origin
|
||||
).map { $0 as Any }
|
||||
} else {
|
||||
childResult = .success(child)
|
||||
}
|
||||
|
||||
if let value = childResult.value {
|
||||
result.append(value)
|
||||
}
|
||||
if let childErrors = childResult.errorsOrWarnings {
|
||||
errors
|
||||
.append(contentsOf: childErrors.map { .nestedObjectError(field: "\(index)", error: $0) })
|
||||
}
|
||||
}
|
||||
|
||||
if let warnings = NonEmptyArray(errors) {
|
||||
return .partialSuccess(result, warnings: warnings)
|
||||
}
|
||||
return .success(result)
|
||||
}
|
||||
|
||||
private mutating func resolveAny(
|
||||
_ value: Any,
|
||||
linkSource: [String: Any],
|
||||
origin: Origin
|
||||
) -> DeserializationResult<Any> {
|
||||
if let dictionary = value as? [String: Any] {
|
||||
let childLinkSource = origin == .instance ? dictionary : linkSource
|
||||
return resolveDictionary(
|
||||
dictionary,
|
||||
linkSource: childLinkSource,
|
||||
origin: origin == .instance ? .instance : .template
|
||||
).map { $0 as Any }
|
||||
}
|
||||
if let array = value as? [Any] {
|
||||
return resolveArray(array, linkSource: linkSource, origin: origin).map { $0 as Any }
|
||||
}
|
||||
return .success(value)
|
||||
}
|
||||
|
||||
private mutating func resolveTemplate(named templateName: TemplateName)
|
||||
-> DeserializationResult<[String: Any]> {
|
||||
if let cached = resolvedTemplateCache[templateName] {
|
||||
return .success(cached)
|
||||
}
|
||||
|
||||
guard !currentlyResolvingTemplates.contains(templateName) else {
|
||||
return .failure(NonEmptyArray(.unknownType(type: templateName)))
|
||||
}
|
||||
|
||||
guard let templateDict = templates[templateName] as? [String: Any] else {
|
||||
return .failure(NonEmptyArray(.unknownType(type: templateName)))
|
||||
}
|
||||
|
||||
currentlyResolvingTemplates.insert(templateName)
|
||||
defer { currentlyResolvingTemplates.remove(templateName) }
|
||||
|
||||
var result: [String: Any] = [:]
|
||||
|
||||
if let parentName = templateDict["type"] as? String, templates[parentName] != nil {
|
||||
let parentResult = resolveTemplate(named: parentName)
|
||||
guard let parentTemplate = parentResult.value else {
|
||||
return .failure(normalizedErrors(from: parentResult.errorsOrWarnings))
|
||||
}
|
||||
result = parentTemplate
|
||||
}
|
||||
|
||||
for (key, value) in templateDict {
|
||||
result[key] = value
|
||||
}
|
||||
result["type"] = templateToType[templateName] ??
|
||||
(templateDict["type"] as? String ?? templateName)
|
||||
|
||||
resolvedTemplateCache[templateName] = result
|
||||
return .success(result)
|
||||
}
|
||||
|
||||
private func substituteLinks(values: inout [String: OriginValue], linkSource: [String: Any]) {
|
||||
let linkValues = values.filter { $0.key.hasPrefix("$") }
|
||||
for (linkKey, linkValue) in linkValues {
|
||||
let key = String(linkKey.dropFirst())
|
||||
guard values[key] == nil else { continue }
|
||||
guard let linkName = linkValue.value as? String else { continue }
|
||||
guard let value = linkSource[linkName] else { continue }
|
||||
values[key] = OriginValue(value: value, origin: .instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func normalizedErrors(
|
||||
from errors: NonEmptyArray<DeserializationError>?
|
||||
) -> NonEmptyArray<DeserializationError> {
|
||||
errors ?? NonEmptyArray(.generic)
|
||||
}
|
||||
|
||||
extension DeserializationResult {
|
||||
fileprivate func map<U>(_ transform: (T) -> U) -> DeserializationResult<U> {
|
||||
switch self {
|
||||
case let .success(value):
|
||||
.success(transform(value))
|
||||
case let .partialSuccess(value, warnings):
|
||||
.partialSuccess(transform(value), warnings: warnings)
|
||||
case let .failure(errors):
|
||||
.failure(errors)
|
||||
case .noValue:
|
||||
.noValue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,13 +305,13 @@ final class DivBlockProvider {
|
||||
cardId: DivCardID
|
||||
) throws -> DeserializationResult<DivData> {
|
||||
let rawDivData = try RawDivData(dictionary: jsonDict)
|
||||
let templates = try measurements.templateParsingTime.updateMeasure {
|
||||
DivTemplates(dictionary: rawDivData.templates)
|
||||
}
|
||||
return try measurements.divDataParsingTime.updateMeasure {
|
||||
templates
|
||||
.parseValue(type: DivDataTemplate.self, from: rawDivData.card)
|
||||
.asCardResult(cardId: cardId)
|
||||
DivData.resolve(
|
||||
card: rawDivData.card,
|
||||
templates: rawDivData.templates,
|
||||
flagsInfo: divKitComponents.flagsInfo
|
||||
)
|
||||
.asCardResult(cardId: cardId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,16 +321,17 @@ final class DivBlockProvider {
|
||||
) async throws -> DeserializationResult<DivData> {
|
||||
let rawDivData = try RawDivData(dictionary: jsonDict)
|
||||
let measurements = measurements
|
||||
let templates = try measurements.templateParsingTime.updateMeasure {
|
||||
DivTemplates(dictionary: rawDivData.templates)
|
||||
}
|
||||
let flagsInfo = divKitComponents.flagsInfo
|
||||
let result = try await withCheckedThrowingContinuation { continuation in
|
||||
DispatchQueue.global().async { [measurements] in
|
||||
do {
|
||||
let result = try measurements.divDataParsingTime.updateMeasure {
|
||||
templates
|
||||
.parseValue(type: DivDataTemplate.self, from: rawDivData.card)
|
||||
.asCardResult(cardId: cardId)
|
||||
DivData.resolve(
|
||||
card: rawDivData.card,
|
||||
templates: rawDivData.templates,
|
||||
flagsInfo: flagsInfo
|
||||
)
|
||||
.asCardResult(cardId: cardId)
|
||||
}
|
||||
continuation.resume(returning: result)
|
||||
} catch {
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class ArrayValue: @unchecked Sendable {
|
||||
resolver.resolveArray(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getExpressionField("value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<[Any]>
|
||||
) {
|
||||
|
||||
@@ -13,6 +13,13 @@ public final class ArrayVariable: @unchecked Sendable {
|
||||
resolver.resolveArray(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
name: try dictionary.getField("name", context: context),
|
||||
value: try dictionary.getExpressionField("value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
name: String,
|
||||
value: Expression<[Any]>
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class BooleanValue: Sendable {
|
||||
resolver.resolveNumeric(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getExpressionField("value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<Bool>
|
||||
) {
|
||||
|
||||
@@ -13,6 +13,13 @@ public final class BooleanVariable: Sendable {
|
||||
resolver.resolveNumeric(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
name: try dictionary.getField("name", context: context),
|
||||
value: try dictionary.getExpressionField("value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
name: String,
|
||||
value: Expression<Bool>
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class ColorValue: Sendable {
|
||||
resolver.resolveColor(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getExpressionField("value", transform: Color.color(withHexString:), context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<Color>
|
||||
) {
|
||||
|
||||
@@ -13,6 +13,13 @@ public final class ColorVariable: Sendable {
|
||||
resolver.resolveColor(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
name: try dictionary.getField("name", context: context),
|
||||
value: try dictionary.getExpressionField("value", transform: Color.color(withHexString:), context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
name: String,
|
||||
value: Expression<Color>
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class ContentText: Sendable {
|
||||
resolver.resolveString(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getExpressionField("value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<String>
|
||||
) {
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class ContentUrl: Sendable {
|
||||
resolver.resolveUrl(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getExpressionField("value", transform: URL.makeFromNonEncodedString, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<URL>
|
||||
) {
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class DictValue: @unchecked Sendable {
|
||||
resolver.resolveDict(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getExpressionField("value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: Expression<[String: Any]>
|
||||
) {
|
||||
|
||||
@@ -13,6 +13,13 @@ public final class DictVariable: @unchecked Sendable {
|
||||
resolver.resolveDict(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
name: try dictionary.getField("name", context: context),
|
||||
value: try dictionary.getExpressionField("value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
name: String,
|
||||
value: Expression<[String: Any]>
|
||||
|
||||
@@ -103,6 +103,50 @@ public enum Div: Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
extension Div {
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
let blockType = try dictionary.getField("type") as String
|
||||
switch blockType {
|
||||
case DivImage.type:
|
||||
self = .divImage(try DivImage(dictionary: dictionary, context: context))
|
||||
case DivGifImage.type:
|
||||
self = .divGifImage(try DivGifImage(dictionary: dictionary, context: context))
|
||||
case DivText.type:
|
||||
self = .divText(try DivText(dictionary: dictionary, context: context))
|
||||
case DivSeparator.type:
|
||||
self = .divSeparator(try DivSeparator(dictionary: dictionary, context: context))
|
||||
case DivContainer.type:
|
||||
self = .divContainer(try DivContainer(dictionary: dictionary, context: context))
|
||||
case DivGrid.type:
|
||||
self = .divGrid(try DivGrid(dictionary: dictionary, context: context))
|
||||
case DivGallery.type:
|
||||
self = .divGallery(try DivGallery(dictionary: dictionary, context: context))
|
||||
case DivPager.type:
|
||||
self = .divPager(try DivPager(dictionary: dictionary, context: context))
|
||||
case DivTabs.type:
|
||||
self = .divTabs(try DivTabs(dictionary: dictionary, context: context))
|
||||
case DivState.type:
|
||||
self = .divState(try DivState(dictionary: dictionary, context: context))
|
||||
case DivCustom.type:
|
||||
self = .divCustom(try DivCustom(dictionary: dictionary, context: context))
|
||||
case DivIndicator.type:
|
||||
self = .divIndicator(try DivIndicator(dictionary: dictionary, context: context))
|
||||
case DivSlider.type:
|
||||
self = .divSlider(try DivSlider(dictionary: dictionary, context: context))
|
||||
case DivSwitch.type:
|
||||
self = .divSwitch(try DivSwitch(dictionary: dictionary, context: context))
|
||||
case DivInput.type:
|
||||
self = .divInput(try DivInput(dictionary: dictionary, context: context))
|
||||
case DivSelect.type:
|
||||
self = .divSelect(try DivSelect(dictionary: dictionary, context: context))
|
||||
case DivVideo.type:
|
||||
self = .divVideo(try DivVideo(dictionary: dictionary, context: context))
|
||||
default:
|
||||
throw DeserializationError.invalidFieldRepresentation(field: "div", representation: dictionary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
extension Div: Equatable {
|
||||
public static func ==(lhs: Div, rhs: Div) -> Bool {
|
||||
|
||||
@@ -38,6 +38,15 @@ public final class DivAbsoluteEdgeInsets: Sendable {
|
||||
static let topValidator: AnyValueValidator<Int> =
|
||||
makeValueValidator(valueValidator: { $0 >= 0 })
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
bottom: try dictionary.getOptionalExpressionField("bottom", validator: Self.bottomValidator, context: context),
|
||||
left: try dictionary.getOptionalExpressionField("left", validator: Self.leftValidator, context: context),
|
||||
right: try dictionary.getOptionalExpressionField("right", validator: Self.rightValidator, context: context),
|
||||
top: try dictionary.getOptionalExpressionField("top", validator: Self.topValidator, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
bottom: Expression<Int>? = nil,
|
||||
left: Expression<Int>? = nil,
|
||||
|
||||
@@ -60,6 +60,18 @@ public final class DivAccessibility: Sendable {
|
||||
resolver.resolveString(stateDescription)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
description: try dictionary.getOptionalExpressionField("description", context: context),
|
||||
hint: try dictionary.getOptionalExpressionField("hint", context: context),
|
||||
isChecked: try dictionary.getOptionalExpressionField("is_checked", context: context),
|
||||
mode: try dictionary.getOptionalExpressionField("mode", context: context),
|
||||
muteAfterAction: try dictionary.getOptionalExpressionField("mute_after_action", context: context),
|
||||
stateDescription: try dictionary.getOptionalExpressionField("state_description", context: context),
|
||||
type: try dictionary.getOptionalField("type", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
description: Expression<String>? = nil,
|
||||
hint: Expression<String>? = nil,
|
||||
|
||||
@@ -14,6 +14,14 @@ public final class DivAction: @unchecked Sendable {
|
||||
resolver.resolveString(text)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
action: try dictionary.getOptionalField("action", transform: { (dict: [String: Any]) in try DivAction(dictionary: dict, context: context) }),
|
||||
actions: try dictionary.getOptionalArray("actions", transform: { (dict: [String: Any]) in try? DivAction(dictionary: dict, context: context) }),
|
||||
text: try dictionary.getExpressionField("text", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
action: DivAction? = nil,
|
||||
actions: [DivAction]? = nil,
|
||||
@@ -56,6 +64,21 @@ public final class DivAction: @unchecked Sendable {
|
||||
resolver.resolveUrl(url)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
downloadCallbacks: try dictionary.getOptionalField("download_callbacks", transform: { (dict: [String: Any]) in try DivDownloadCallbacks(dictionary: dict, context: context) }),
|
||||
isEnabled: try dictionary.getOptionalExpressionField("is_enabled", context: context),
|
||||
logId: try dictionary.getExpressionField("log_id", context: context),
|
||||
logUrl: try dictionary.getOptionalExpressionField("log_url", transform: URL.makeFromNonEncodedString, context: context),
|
||||
menuItems: try dictionary.getOptionalArray("menu_items", transform: { (dict: [String: Any]) in try? DivAction.MenuItem(dictionary: dict, context: context) }),
|
||||
payload: try dictionary.getOptionalField("payload", context: context),
|
||||
referer: try dictionary.getOptionalExpressionField("referer", transform: URL.makeFromNonEncodedString, context: context),
|
||||
scopeId: try dictionary.getOptionalField("scope_id", context: context),
|
||||
typed: try dictionary.getOptionalField("typed", transform: { (dict: [String: Any]) in try DivActionTyped(dictionary: dict, context: context) }),
|
||||
url: try dictionary.getOptionalExpressionField("url", transform: URL.makeFromNonEncodedString, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
downloadCallbacks: DivDownloadCallbacks? = nil,
|
||||
isEnabled: Expression<Bool>? = nil,
|
||||
|
||||
@@ -37,6 +37,19 @@ public final class DivActionAnimatorStart: Sendable {
|
||||
static let startDelayValidator: AnyValueValidator<Int> =
|
||||
makeValueValidator(valueValidator: { $0 >= 0 })
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
animatorId: try dictionary.getField("animator_id", context: context),
|
||||
direction: try dictionary.getOptionalExpressionField("direction", context: context),
|
||||
duration: try dictionary.getOptionalExpressionField("duration", validator: Self.durationValidator, context: context),
|
||||
endValue: try dictionary.getOptionalField("end_value", transform: { (dict: [String: Any]) in try DivTypedValue(dictionary: dict, context: context) }),
|
||||
interpolator: try dictionary.getOptionalExpressionField("interpolator", context: context),
|
||||
repeatCount: try dictionary.getOptionalField("repeat_count", transform: { (dict: [String: Any]) in try DivCount(dictionary: dict, context: context) }),
|
||||
startDelay: try dictionary.getOptionalExpressionField("start_delay", validator: Self.startDelayValidator, context: context),
|
||||
startValue: try dictionary.getOptionalField("start_value", transform: { (dict: [String: Any]) in try DivTypedValue(dictionary: dict, context: context) })
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
animatorId: String,
|
||||
direction: Expression<DivAnimationDirection>? = nil,
|
||||
|
||||
@@ -8,6 +8,12 @@ public final class DivActionAnimatorStop: Sendable {
|
||||
public static let type: String = "animator_stop"
|
||||
public let animatorId: String
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
animatorId: try dictionary.getField("animator_id", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
animatorId: String
|
||||
) {
|
||||
|
||||
@@ -18,6 +18,14 @@ public final class DivActionArrayInsertValue: Sendable {
|
||||
resolver.resolveString(variableName)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
index: try dictionary.getOptionalExpressionField("index", context: context),
|
||||
value: try dictionary.getField("value", transform: { (dict: [String: Any]) in try DivTypedValue(dictionary: dict, context: context) }),
|
||||
variableName: try dictionary.getExpressionField("variable_name", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
index: Expression<Int>? = nil,
|
||||
value: DivTypedValue,
|
||||
|
||||
@@ -17,6 +17,13 @@ public final class DivActionArrayRemoveValue: Sendable {
|
||||
resolver.resolveString(variableName)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
index: try dictionary.getExpressionField("index", context: context),
|
||||
variableName: try dictionary.getExpressionField("variable_name", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
index: Expression<Int>,
|
||||
variableName: Expression<String>
|
||||
|
||||
@@ -18,6 +18,14 @@ public final class DivActionArraySetValue: Sendable {
|
||||
resolver.resolveString(variableName)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
index: try dictionary.getExpressionField("index", context: context),
|
||||
value: try dictionary.getField("value", transform: { (dict: [String: Any]) in try DivTypedValue(dictionary: dict, context: context) }),
|
||||
variableName: try dictionary.getExpressionField("variable_name", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
index: Expression<Int>,
|
||||
value: DivTypedValue,
|
||||
|
||||
@@ -7,6 +7,8 @@ import VGSL
|
||||
public final class DivActionClearFocus: Sendable {
|
||||
public static let type: String = "clear_focus"
|
||||
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {}
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,12 @@ public final class DivActionCopyToClipboard: Sendable {
|
||||
public static let type: String = "copy_to_clipboard"
|
||||
public let content: DivActionCopyToClipboardContent
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
content: try dictionary.getField("content", transform: { (dict: [String: Any]) in try DivActionCopyToClipboardContent(dictionary: dict, context: context) })
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
content: DivActionCopyToClipboardContent
|
||||
) {
|
||||
|
||||
@@ -19,6 +19,20 @@ public enum DivActionCopyToClipboardContent: Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
extension DivActionCopyToClipboardContent {
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
let blockType = try dictionary.getField("type") as String
|
||||
switch blockType {
|
||||
case ContentText.type:
|
||||
self = .contentText(try ContentText(dictionary: dictionary, context: context))
|
||||
case ContentUrl.type:
|
||||
self = .contentUrl(try ContentUrl(dictionary: dictionary, context: context))
|
||||
default:
|
||||
throw DeserializationError.invalidFieldRepresentation(field: "div-action-copy-to-clipboard-content", representation: dictionary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
extension DivActionCopyToClipboardContent: Equatable {
|
||||
public static func ==(lhs: DivActionCopyToClipboardContent, rhs: DivActionCopyToClipboardContent) -> Bool {
|
||||
|
||||
@@ -7,6 +7,8 @@ import VGSL
|
||||
public final class DivActionCustom: Sendable {
|
||||
public static let type: String = "custom"
|
||||
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {}
|
||||
|
||||
init() {}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,14 @@ public final class DivActionDictSetValue: Sendable {
|
||||
resolver.resolveString(variableName)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
key: try dictionary.getExpressionField("key", context: context),
|
||||
value: try dictionary.getOptionalField("value", transform: { (dict: [String: Any]) in try DivTypedValue(dictionary: dict, context: context) }),
|
||||
variableName: try dictionary.getExpressionField("variable_name", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
key: Expression<String>,
|
||||
value: DivTypedValue? = nil,
|
||||
|
||||
@@ -14,6 +14,14 @@ public final class DivActionDownload: Sendable {
|
||||
resolver.resolveUrl(url)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
onFailActions: try dictionary.getOptionalArray("on_fail_actions", transform: { (dict: [String: Any]) in try? DivAction(dictionary: dict, context: context) }),
|
||||
onSuccessActions: try dictionary.getOptionalArray("on_success_actions", transform: { (dict: [String: Any]) in try? DivAction(dictionary: dict, context: context) }),
|
||||
url: try dictionary.getExpressionField("url", transform: URL.makeFromNonEncodedString, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
onFailActions: [DivAction]? = nil,
|
||||
onSuccessActions: [DivAction]? = nil,
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class DivActionFocusElement: Sendable {
|
||||
resolver.resolveString(elementId)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
elementId: try dictionary.getExpressionField("element_id", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
elementId: Expression<String>
|
||||
) {
|
||||
|
||||
@@ -12,6 +12,12 @@ public final class DivActionHideTooltip: Sendable {
|
||||
resolver.resolveString(id)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
id: try dictionary.getExpressionField("id", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
id: Expression<String>
|
||||
) {
|
||||
|
||||
@@ -38,6 +38,16 @@ public final class DivActionScrollBy: Sendable {
|
||||
resolver.resolveEnum(overflow) ?? Overflow.clamp
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
animated: try dictionary.getOptionalExpressionField("animated", context: context),
|
||||
id: try dictionary.getExpressionField("id", context: context),
|
||||
itemCount: try dictionary.getOptionalExpressionField("item_count", context: context),
|
||||
offset: try dictionary.getOptionalExpressionField("offset", context: context),
|
||||
overflow: try dictionary.getOptionalExpressionField("overflow", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
animated: Expression<Bool>? = nil,
|
||||
id: Expression<String>,
|
||||
|
||||
@@ -25,6 +25,24 @@ public enum DivActionScrollDestination: Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
extension DivActionScrollDestination {
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
let blockType = try dictionary.getField("type") as String
|
||||
switch blockType {
|
||||
case OffsetDestination.type:
|
||||
self = .offsetDestination(try OffsetDestination(dictionary: dictionary, context: context))
|
||||
case IndexDestination.type:
|
||||
self = .indexDestination(try IndexDestination(dictionary: dictionary, context: context))
|
||||
case StartDestination.type:
|
||||
self = .startDestination(try StartDestination(dictionary: dictionary, context: context))
|
||||
case EndDestination.type:
|
||||
self = .endDestination(try EndDestination(dictionary: dictionary, context: context))
|
||||
default:
|
||||
throw DeserializationError.invalidFieldRepresentation(field: "div-action-scroll-destination", representation: dictionary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
extension DivActionScrollDestination: Equatable {
|
||||
public static func ==(lhs: DivActionScrollDestination, rhs: DivActionScrollDestination) -> Bool {
|
||||
|
||||
@@ -18,6 +18,14 @@ public final class DivActionScrollTo: Sendable {
|
||||
resolver.resolveString(id)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
animated: try dictionary.getOptionalExpressionField("animated", context: context),
|
||||
destination: try dictionary.getField("destination", transform: { (dict: [String: Any]) in try DivActionScrollDestination(dictionary: dict, context: context) }),
|
||||
id: try dictionary.getExpressionField("id", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
animated: Expression<Bool>? = nil,
|
||||
destination: DivActionScrollDestination,
|
||||
|
||||
@@ -17,6 +17,13 @@ public final class DivActionSetState: Sendable {
|
||||
resolver.resolveNumeric(temporary) ?? true
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
stateId: try dictionary.getExpressionField("state_id", context: context),
|
||||
temporary: try dictionary.getOptionalExpressionField("temporary", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
stateId: Expression<String>,
|
||||
temporary: Expression<Bool>? = nil
|
||||
|
||||
@@ -18,6 +18,14 @@ public final class DivActionSetStoredValue: Sendable {
|
||||
resolver.resolveString(name)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
lifetime: try dictionary.getExpressionField("lifetime", context: context),
|
||||
name: try dictionary.getExpressionField("name", context: context),
|
||||
value: try dictionary.getField("value", transform: { (dict: [String: Any]) in try DivTypedValue(dictionary: dict, context: context) })
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
lifetime: Expression<Int>,
|
||||
name: Expression<String>,
|
||||
|
||||
@@ -13,6 +13,13 @@ public final class DivActionSetVariable: Sendable {
|
||||
resolver.resolveString(variableName)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
value: try dictionary.getField("value", transform: { (dict: [String: Any]) in try DivTypedValue(dictionary: dict, context: context) }),
|
||||
variableName: try dictionary.getExpressionField("variable_name", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
value: DivTypedValue,
|
||||
variableName: Expression<String>
|
||||
|
||||
@@ -17,6 +17,13 @@ public final class DivActionShowTooltip: Sendable {
|
||||
resolver.resolveNumeric(multiple)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
id: try dictionary.getExpressionField("id", context: context),
|
||||
multiple: try dictionary.getOptionalExpressionField("multiple", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
id: Expression<String>,
|
||||
multiple: Expression<Bool>? = nil
|
||||
|
||||
@@ -29,6 +29,13 @@ public final class DivActionSubmit: Sendable {
|
||||
resolver.resolveString(value)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
name: try dictionary.getExpressionField("name", context: context),
|
||||
value: try dictionary.getExpressionField("value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
name: Expression<String>,
|
||||
value: Expression<String>
|
||||
@@ -50,6 +57,14 @@ public final class DivActionSubmit: Sendable {
|
||||
resolver.resolveUrl(url)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
headers: try dictionary.getOptionalArray("headers", transform: { (dict: [String: Any]) in try? DivActionSubmit.Request.Header(dictionary: dict, context: context) }),
|
||||
method: try dictionary.getOptionalExpressionField("method", context: context),
|
||||
url: try dictionary.getExpressionField("url", transform: URL.makeFromNonEncodedString, context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
headers: [Header]? = nil,
|
||||
method: Expression<Method>? = nil,
|
||||
@@ -71,6 +86,15 @@ public final class DivActionSubmit: Sendable {
|
||||
resolver.resolveString(containerId)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
containerId: try dictionary.getExpressionField("container_id", context: context),
|
||||
onFailActions: try dictionary.getOptionalArray("on_fail_actions", transform: { (dict: [String: Any]) in try? DivAction(dictionary: dict, context: context) }),
|
||||
onSuccessActions: try dictionary.getOptionalArray("on_success_actions", transform: { (dict: [String: Any]) in try? DivAction(dictionary: dict, context: context) }),
|
||||
request: try dictionary.getField("request", transform: { (dict: [String: Any]) in try DivActionSubmit.Request(dictionary: dict, context: context) })
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
containerId: Expression<String>,
|
||||
onFailActions: [DivAction]? = nil,
|
||||
|
||||
@@ -27,6 +27,13 @@ public final class DivActionTimer: Sendable {
|
||||
resolver.resolveString(id)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
action: try dictionary.getExpressionField("action", context: context),
|
||||
id: try dictionary.getExpressionField("id", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
action: Expression<Action>,
|
||||
id: Expression<String>
|
||||
|
||||
@@ -79,6 +79,60 @@ public enum DivActionTyped: Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
extension DivActionTyped {
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
let blockType = try dictionary.getField("type") as String
|
||||
switch blockType {
|
||||
case DivActionAnimatorStart.type:
|
||||
self = .divActionAnimatorStart(try DivActionAnimatorStart(dictionary: dictionary, context: context))
|
||||
case DivActionAnimatorStop.type:
|
||||
self = .divActionAnimatorStop(try DivActionAnimatorStop(dictionary: dictionary, context: context))
|
||||
case DivActionArrayInsertValue.type:
|
||||
self = .divActionArrayInsertValue(try DivActionArrayInsertValue(dictionary: dictionary, context: context))
|
||||
case DivActionArrayRemoveValue.type:
|
||||
self = .divActionArrayRemoveValue(try DivActionArrayRemoveValue(dictionary: dictionary, context: context))
|
||||
case DivActionArraySetValue.type:
|
||||
self = .divActionArraySetValue(try DivActionArraySetValue(dictionary: dictionary, context: context))
|
||||
case DivActionClearFocus.type:
|
||||
self = .divActionClearFocus(try DivActionClearFocus(dictionary: dictionary, context: context))
|
||||
case DivActionCopyToClipboard.type:
|
||||
self = .divActionCopyToClipboard(try DivActionCopyToClipboard(dictionary: dictionary, context: context))
|
||||
case DivActionDictSetValue.type:
|
||||
self = .divActionDictSetValue(try DivActionDictSetValue(dictionary: dictionary, context: context))
|
||||
case DivActionDownload.type:
|
||||
self = .divActionDownload(try DivActionDownload(dictionary: dictionary, context: context))
|
||||
case DivActionFocusElement.type:
|
||||
self = .divActionFocusElement(try DivActionFocusElement(dictionary: dictionary, context: context))
|
||||
case DivActionHideTooltip.type:
|
||||
self = .divActionHideTooltip(try DivActionHideTooltip(dictionary: dictionary, context: context))
|
||||
case DivActionScrollBy.type:
|
||||
self = .divActionScrollBy(try DivActionScrollBy(dictionary: dictionary, context: context))
|
||||
case DivActionScrollTo.type:
|
||||
self = .divActionScrollTo(try DivActionScrollTo(dictionary: dictionary, context: context))
|
||||
case DivActionSetState.type:
|
||||
self = .divActionSetState(try DivActionSetState(dictionary: dictionary, context: context))
|
||||
case DivActionSetStoredValue.type:
|
||||
self = .divActionSetStoredValue(try DivActionSetStoredValue(dictionary: dictionary, context: context))
|
||||
case DivActionSetVariable.type:
|
||||
self = .divActionSetVariable(try DivActionSetVariable(dictionary: dictionary, context: context))
|
||||
case DivActionShowTooltip.type:
|
||||
self = .divActionShowTooltip(try DivActionShowTooltip(dictionary: dictionary, context: context))
|
||||
case DivActionSubmit.type:
|
||||
self = .divActionSubmit(try DivActionSubmit(dictionary: dictionary, context: context))
|
||||
case DivActionTimer.type:
|
||||
self = .divActionTimer(try DivActionTimer(dictionary: dictionary, context: context))
|
||||
case DivActionUpdateStructure.type:
|
||||
self = .divActionUpdateStructure(try DivActionUpdateStructure(dictionary: dictionary, context: context))
|
||||
case DivActionVideo.type:
|
||||
self = .divActionVideo(try DivActionVideo(dictionary: dictionary, context: context))
|
||||
case DivActionCustom.type:
|
||||
self = .divActionCustom(try DivActionCustom(dictionary: dictionary, context: context))
|
||||
default:
|
||||
throw DeserializationError.invalidFieldRepresentation(field: "div-action-typed", representation: dictionary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
extension DivActionTyped: Equatable {
|
||||
public static func ==(lhs: DivActionTyped, rhs: DivActionTyped) -> Bool {
|
||||
|
||||
@@ -21,6 +21,14 @@ public final class DivActionUpdateStructure: Sendable {
|
||||
static let pathValidator: AnyValueValidator<String> =
|
||||
makeStringValidator(regex: "^(?!/)(.+)(?<!/)$")
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
path: try dictionary.getExpressionField("path", validator: Self.pathValidator, context: context),
|
||||
value: try dictionary.getField("value", transform: { (dict: [String: Any]) in try DivTypedValue(dictionary: dict, context: context) }),
|
||||
variableName: try dictionary.getExpressionField("variable_name", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
path: Expression<String>,
|
||||
value: DivTypedValue,
|
||||
|
||||
@@ -23,6 +23,13 @@ public final class DivActionVideo: Sendable {
|
||||
resolver.resolveString(id)
|
||||
}
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
action: try dictionary.getExpressionField("action", context: context),
|
||||
id: try dictionary.getExpressionField("id", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
action: Expression<Action>,
|
||||
id: Expression<String>
|
||||
|
||||
@@ -54,6 +54,19 @@ public final class DivAnimation: Sendable {
|
||||
static let startDelayValidator: AnyValueValidator<Int> =
|
||||
makeValueValidator(valueValidator: { $0 >= 0 })
|
||||
|
||||
public convenience init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
self.init(
|
||||
duration: try dictionary.getOptionalExpressionField("duration", validator: Self.durationValidator, context: context),
|
||||
endValue: try dictionary.getOptionalExpressionField("end_value", context: context),
|
||||
interpolator: try dictionary.getOptionalExpressionField("interpolator", context: context),
|
||||
items: try dictionary.getOptionalArray("items", transform: { (dict: [String: Any]) in try? DivAnimation(dictionary: dict, context: context) }),
|
||||
name: try dictionary.getExpressionField("name", context: context),
|
||||
repeatCount: try dictionary.getOptionalField("repeat", transform: { (dict: [String: Any]) in try DivCount(dictionary: dict, context: context) }),
|
||||
startDelay: try dictionary.getOptionalExpressionField("start_delay", validator: Self.startDelayValidator, context: context),
|
||||
startValue: try dictionary.getOptionalExpressionField("start_value", context: context)
|
||||
)
|
||||
}
|
||||
|
||||
init(
|
||||
duration: Expression<Int>? = nil,
|
||||
endValue: Expression<Double>? = nil,
|
||||
|
||||
@@ -28,6 +28,20 @@ public enum DivAnimator: Sendable {
|
||||
}
|
||||
}
|
||||
|
||||
extension DivAnimator {
|
||||
public init(dictionary: [String: Any], context: ParsingContext) throws {
|
||||
let blockType = try dictionary.getField("type") as String
|
||||
switch blockType {
|
||||
case DivColorAnimator.type:
|
||||
self = .divColorAnimator(try DivColorAnimator(dictionary: dictionary, context: context))
|
||||
case DivNumberAnimator.type:
|
||||
self = .divNumberAnimator(try DivNumberAnimator(dictionary: dictionary, context: context))
|
||||
default:
|
||||
throw DeserializationError.invalidFieldRepresentation(field: "div-animator", representation: dictionary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
extension DivAnimator: Equatable {
|
||||
public static func ==(lhs: DivAnimator, rhs: DivAnimator) -> Bool {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user