Struct Optimizer

Source
pub struct Optimizer { /* private fields */ }
Expand description

Optimizes a MetaTypes structure by reducing redundant or verbose type definitions.

The Optimizer performs various semantic transformations on a type graph, such as flattening unions, removing empty enums, simplifying typedef chains, and reducing nested complex structures.

It is typically used after schema interpretation and before code generation, to ensure that only necessary and well-structured types are preserved in the final output.

Optimization is performed lazily; the resulting MetaTypes can be retrieved using finish.

Implementations§

Source§

impl Optimizer

Source

pub fn convert_dynamic_to_choice(self) -> Self

This will use a enum that contains all known variants of the dynamic type instead of a dynamic box.

§Examples

Consider the following XML schema.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com"
    targetNamespace="http://example.com">

    <xs:element name="Abstract" abstract="true" />

    <xs:element name="First" substitutionGroup="tns:Abstract">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="a" type="xs:string" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="Second" substitutionGroup="tns:Abstract">
        <xs:simpleType>
            <xs:restriction base="xs:token">
                <xs:enumeration value="Var1" />
                <xs:enumeration value="Var2" />
            </xs:restriction>
        </xs:simpleType>
    </xs:element>
</xs:schema>

Without this optimization this will result in the following code:

use xsd_parser::AsAny;
pub struct Abstract(pub Box<dyn AbstractTrait>);
pub trait AbstractTrait: AsAny {}
pub struct FirstType {
    pub a: String,
}
impl AbstractTrait for FirstType {}
pub enum SecondType {
    Var1,
    Var2,
}
impl AbstractTrait for SecondType {}

With this optimization the following code is generated:

pub struct Abstract {
    pub content: AbstractContent,
}
pub enum AbstractContent {
    First(FirstType),
    Second(SecondType),
}
pub struct FirstType {
    pub a: String,
}
pub enum SecondType {
    Var1,
    Var2,
}
Source§

impl Optimizer

Source

pub fn remove_empty_enum_variants(self) -> Self

This will remove any enum variant that has an empty string as name.

§Examples

Consider the following XML schema:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com"
    targetNamespace="http://example.com">

    <xs:simpleType name="MyEnum">
        <xs:restriction base="xs:string">
            <xs:enumeration value="" />
            <xs:enumeration value="Var1" />
            <xs:enumeration value="Var2" />
        </xs:restriction>
    </xs:simpleType>
</xs:schema>

Without this optimization this will result in the following code:

pub enum MyEnumType {
    Unknown1,
    Var1,
    Var2,
}

With this optimization the following code is generated:

pub enum MyEnumType {
    Var1,
    Var2,
}
Source

pub fn remove_empty_enums(self) -> Self

This will replace the enum with a type reference to the enums base type if the enum does not have any variant.

This optimization is usually used in combination with remove_empty_enum_variants.

§Examples

Consider the following XML schema:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com"
    targetNamespace="http://example.com">

    <xs:simpleType name="MyEnum">
        <xs:restriction base="xs:string">
            <xs:enumeration value="" />
        </xs:restriction>
    </xs:simpleType>
</xs:schema>

Without this optimization this will result in the following code:

pub enum MyEnumType {
    Unknown1,
}

With this optimization (and the remove_empty_enum_variants) the following code is generated:

pub type MyEnumType = String;
Source§

impl Optimizer

Source

pub fn remove_duplicate_union_variants(self) -> Self

This will remove variants of an union, if the type of the variant resolves to the same type as an other variant. In other words, the variant will be removed if the types are identical.

§Examples

Consider the following XML schema. xs:language and xs:string are both type definitions for String.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com"
    targetNamespace="http://example.com">

    <xs:simpleType name="MyUnion">
        <xs:union memberTypes="xs:language xs:string" />
    </xs:simpleType>
</xs:schema>

Without this optimization this will result in the following code:

pub enum MyUnionType {
    Language(LanguageType),
    String(StringType),
}
pub type LanguageType = String;
pub type StringType = String;

With this optimization the following code is generated:

pub enum MyUnionType {
    Language(LanguageType),
}
pub type LanguageType = String;
Source

pub fn remove_empty_unions(self) -> Self

This will replace an union with a simple type definition, if the union has only one variant.

§Examples

Consider the following XML schema.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com"
    targetNamespace="http://example.com">

    <xs:simpleType name="MyUnion">
        <xs:union memberTypes="xs:string" />
    </xs:simpleType>
</xs:schema>

Without this optimization this will result in the following code:

pub enum MyUnionType {
    String(String),
}

With this optimization the following code is generated:

pub type MyUnionType = String;
Source§

impl Optimizer

Source

pub fn flatten_complex_type(self, ident: Ident) -> Result<Self, Error>

This will flatten the nested groups (xs::all, xs::choice or xs::sequence) of the complex type identified by ident to one type instead of rendering nested structures.

§Errors

Returns an error if the passed ident could not be found, the referenced type is not complex type or the complex type has no content.

§Examples

Consider the following XML schema.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com"
    targetNamespace="http://example.com">

    <xs:complexType name="MyComplexType">
        <xs:sequence>
            <xs:element name="ElementA" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
            <xs:element name="ElementB" type="xs:int" minOccurs="0" maxOccurs="unbounded" />
            <xs:choice>
                <xs:element name="ElementC" type="xs:boolean" />
                <xs:sequence minOccurs="0">
                    <xs:element name="ElementD" type="xs:string" />
                    <xs:element name="ElementE" type="xs:int" />
                </xs:sequence>
                <xs:sequence>
                    <xs:element name="ElementF" type="xs:string" />
                    <xs:element name="ElementG" type="xs:int" maxOccurs="unbounded" />
                </xs:sequence>
            </xs:choice>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

Without this optimization this will result in the following code:

pub struct MyComplexType {
    pub element_a: Vec<String>,
    pub element_b: Vec<i32>,
    pub content_2: MyComplexContent2Type,
}
pub struct MyComplexContent2Type {
    pub content: MyComplexContent2TypeContent,
}
pub enum MyComplexContent2TypeContent {
    ElementC(bool),
    Content3(Option<MyComplexContent3Type>),
    Content4(MyComplexContent4Type),
}
pub struct MyComplexContent3Type {
    pub element_d: String,
    pub element_e: i32,
}
pub struct MyComplexContent4Type {
    pub element_f: String,
    pub element_g: Vec<i32>,
}

With this optimization the following code is generated:

pub struct MyComplexType {
    pub content: Vec<MyComplexTypeContent>,
}
pub enum MyComplexTypeContent {
    ElementA(String),
    ElementB(i32),
    ElementC(bool),
    ElementD(String),
    ElementE(i32),
    ElementF(String),
    ElementG(i32),
}
Source

pub fn flatten_complex_types(self) -> Self

This will flatten all complex types.

For details see flatten_complex_type.

Source§

impl Optimizer

Source

pub fn flatten_union(self, ident: Ident) -> Result<Self, Error>

This will flatten the union identified by ident to one single union.

§Errors

Returns an error if the passed ident could not be found, or is not an union.

§Examples

Consider the following XML schema.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com"
    targetNamespace="http://example.com">

    <xs:simpleType name="MyBaseUnion">
        <xs:union memberTypes="xs:int">
            <xs:simpleType>
                <xs:list itemType="xs:unsignedInt" />
            </xs:simpleType>
        </xs:union>
    </xs:simpleType>

    <xs:simpleType name="MyEnum">
        <xs:restriction base="xs:string">
            <xs:enumeration value="Var1" />
            <xs:enumeration value="Var2" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="MyUnion">
        <xs:union memberTypes="xs:string tns:MyEnum tns:MyBaseUnion" />
    </xs:simpleType>
</xs:schema>

Without this optimization this will result in the following code:

pub enum MyUnionType {
    String(String),
    MyEnum(MyEnumType),
    MyBaseUnion(MyBaseUnionType),
}
pub enum MyEnumType {
    Var1,
    Var2,
}
pub enum MyBaseUnionType {
    I32(i32),
    MyBaseUnion1(UnsignedIntList),
}
#[derive(Default)]
pub struct UnsignedIntList(pub Vec<UnsignedIntType>);
pub type UnsignedIntType = u32;

With this optimization the following code is generated:

pub enum MyUnionType {
    String(String),
    MyEnum(MyEnumType),
    I32(i32),
    MyBaseUnion1(UnsignedIntList),
}
pub enum MyEnumType {
    Var1,
    Var2,
}
#[derive(Default)]
pub struct UnsignedIntList(pub Vec<UnsignedIntType>);
pub type UnsignedIntType = u32;
Source

pub fn flatten_unions(self) -> Self

This will flatten all union types.

For details see flatten_union.

Source§

impl Optimizer

Source

pub fn merge_choice_cardinality(self, ident: Ident) -> Result<Self, Error>

This will merge the cardinality of each element of the complex choice type identified by ident with the cardinality of the types content.

§Errors

Returns an error if the passed ident could not be found, the referenced type is not complex type or the complex types content is not a choice.

§Examples

Consider the following XML schema.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com"
    targetNamespace="http://example.com">

    <xs:complexType name="MyComplexType">
        <xs:choice>
            <xs:element name="ElementA" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
            <xs:element name="ElementB" type="xs:int" minOccurs="0" maxOccurs="unbounded" />
            <xs:element name="ElementC" type="xs:boolean" />
            <xs:element name="ElementD" type="xs:string" minOccurs="0" />
            <xs:element name="ElementE" type="xs:int" minOccurs="0" />
            <xs:element name="ElementF" type="xs:string" />
            <xs:element name="ElementG" type="xs:int" maxOccurs="unbounded" />
        </xs:choice>
    </xs:complexType>
</xs:schema>

Without this optimization this will result in the following code:

pub struct MyComplexType {
    pub content: MyComplexTypeContent,
}
pub enum MyComplexTypeContent {
    ElementA(Vec<String>),
    ElementB(Vec<i32>),
    ElementC(bool),
    ElementD(Option<String>),
    ElementE(Option<i32>),
    ElementF(String),
    ElementG(Vec<i32>),
}

With this optimization the following code is generated:

pub struct MyComplexType {
    pub content: Vec<MyComplexTypeContent>,
}
pub enum MyComplexTypeContent {
    ElementA(String),
    ElementB(i32),
    ElementC(bool),
    ElementD(String),
    ElementE(i32),
    ElementF(String),
    ElementG(i32),
}
Source

pub fn merge_choice_cardinalities(self) -> Self

This merge the cardinality of all elements of a choice with the content of the choice for all choice types.

For details see merge_choice_cardinality.

Source§

impl Optimizer

Source

pub fn merge_enum_union(self, ident: Ident) -> Result<Self, Error>

This will flatten the union identified by ident to one single union.

This will merge the nested union and enum types of the union identified by ident to one single enum type.

§Errors

Returns an error if the passed ident could not be found, or is not an union.

§Examples

Consider the following XML schema.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com"
    targetNamespace="http://example.com">

    <xs:simpleType name="MyBaseUnion">
        <xs:union memberTypes="xs:int">
            <xs:simpleType>
                <xs:list itemType="xs:unsignedInt" />
            </xs:simpleType>
        </xs:union>
    </xs:simpleType>

    <xs:simpleType name="MyEnum">
        <xs:restriction base="xs:string">
            <xs:enumeration value="Var1" />
            <xs:enumeration value="Var2" />
        </xs:restriction>
    </xs:simpleType>

    <xs:simpleType name="MyUnion">
        <xs:union memberTypes="xs:string tns:MyEnum tns:MyBaseUnion" />
    </xs:simpleType>
</xs:schema>

Without this optimization this will result in the following code:

pub enum MyUnionType {
    String(String),
    MyEnum(MyEnumType),
    MyBaseUnion(MyBaseUnionType),
}
pub enum MyEnumType {
    Var1,
    Var2,
}
pub enum MyBaseUnionType {
    I32(i32),
    MyBaseUnion1(UnsignedIntList),
}
#[derive(Default)]
pub struct UnsignedIntList(pub Vec<UnsignedIntType>);
pub type UnsignedIntType = u32;

With this optimization the following code is generated:

pub enum MyUnionType {
    String(String),
    Var1,
    Var2,
    I32(i32),
    UnsignedIntList(UnsignedIntList),
}
#[derive(Default)]
pub struct UnsignedIntList(pub Vec<UnsignedIntType>);
pub type UnsignedIntType = u32;
Source

pub fn merge_enum_unions(self) -> Self

This will flatten all enumeration and union types.

For details see merge_enum_union.

Source§

impl Optimizer

Source

pub fn remove_duplicates(self) -> Self

If two types are completely equal this optimization will generate the first type complete and just a type definition for the second one.

*Caution*

Be careful with this optimization. This will compare each known type with each other type and check if the types are identical or not. This would result in a type reference for two types, even if the types itself are not logically related to each other.

Furthermore this may result in typedef loops. The code generator should be able to deal with them (using a Box), but it is still risky to use it.

§Examples

Consider the following XML schema.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com"
    targetNamespace="http://example.com">

    <xs:complexType name="First">
        <xs:sequence>
            <xs:element name="a" type="xs:string" />
            <xs:element name="b" type="xs:string" minOccurs="0" />
            <xs:element name="c" type="xs:string" maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="Second">
        <xs:sequence>
            <xs:element name="a" type="xs:string" />
            <xs:element name="b" type="xs:string" minOccurs="0" />
            <xs:element name="c" type="xs:string" maxOccurs="unbounded" />
        </xs:sequence>
    </xs:complexType>
</xs:schema>

Without this optimization this will result in the following code:

pub struct FirstType {
    pub a: String,
    pub b: Option<String>,
    pub c: Vec<String>,
}
pub struct SecondType {
    pub a: String,
    pub b: Option<String>,
    pub c: Vec<String>,
}

With this optimization the following code is generated:

pub struct FirstType {
    pub a: String,
    pub b: Option<String>,
    pub c: Vec<String>,
}
pub type SecondType = FirstType;
Source§

impl Optimizer

Source

pub fn resolve_typedefs(self) -> Self

This will resolve all simple type definitions and use the target type directly.

§Examples

Consider the following XML schema.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com"
    targetNamespace="http://example.com">

    <xs:complexType name="MyComplexType">
        <xs:sequence>
            <xs:element name="ElementA" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
            <xs:element name="ElementB" type="xs:int" minOccurs="0" maxOccurs="unbounded" />
            <xs:choice>
                <xs:element name="ElementC" type="xs:boolean" />
                <xs:sequence minOccurs="0">
                    <xs:element name="ElementD" type="xs:string" />
                    <xs:element name="ElementE" type="xs:int" />
                </xs:sequence>
                <xs:sequence>
                    <xs:element name="ElementF" type="xs:string" />
                    <xs:element name="ElementG" type="xs:int" maxOccurs="unbounded" />
                </xs:sequence>
            </xs:choice>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

Without this optimization this will result in the following code:

pub struct MyComplexType {
    pub element_a: Vec<StringType>,
    pub element_b: Vec<IntType>,
    pub content_2: MyComplexContent2Type,
}
pub type StringType = String;
pub type IntType = i32;
pub struct MyComplexContent2Type {
    pub content: MyComplexContent2TypeContent,
}
pub enum MyComplexContent2TypeContent {
    ElementC(BooleanType),
    Content3(Option<MyComplexContent3Type>),
    Content4(MyComplexContent4Type),
}
pub type BooleanType = bool;
pub struct MyComplexContent3Type {
    pub element_d: StringType,
    pub element_e: IntType,
}
pub struct MyComplexContent4Type {
    pub element_f: StringType,
    pub element_g: Vec<IntType>,
}

With this optimization the following code is generated:

pub struct MyComplexType {
    pub element_a: Vec<String>,
    pub element_b: Vec<i32>,
    pub content_2: MyComplexContent2Type,
}
pub struct MyComplexContent2Type {
    pub content: MyComplexContent2TypeContent,
}
pub enum MyComplexContent2TypeContent {
    ElementC(bool),
    Content3(Option<MyComplexContent3Type>),
    Content4(MyComplexContent4Type),
}
pub struct MyComplexContent3Type {
    pub element_d: String,
    pub element_e: i32,
}
pub struct MyComplexContent4Type {
    pub element_f: String,
    pub element_g: Vec<i32>,
}
Source§

impl Optimizer

Source

pub fn use_unrestricted_base_type(self) -> Self

This will use the unrestricted base type instead the restricted version when ever possible.

This is useful if you want to reduce the amount of different types, because the base type can store the same data than the restricted one. However this is only useful if you want to deserialize the type only. Using this feature for serializing types will cause problems because the type information is dropped during deserialization.

§Examples

Consider the following XML schema.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:tns="http://example.com"
    targetNamespace="http://example.com">

    <xs:complexType name="BaseType">
        <xs:sequence>
            <xs:element minOccurs="0" maxOccurs="unbounded" name="a" type="xs:string" />
            <xs:element minOccurs="0" maxOccurs="unbounded" name="b" type="xs:int" />
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="FirstRestrictedType">
        <xs:complexContent>
            <xs:restriction base="tns:BaseType">
                <xs:sequence>
                    <xs:element minOccurs="1" maxOccurs="1" name="a" type="xs:string" />
                </xs:sequence>
            </xs:restriction>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="SecondRestrictedType">
        <xs:complexContent>
            <xs:restriction base="tns:BaseType">
                <xs:sequence>
                    <xs:element minOccurs="1" maxOccurs="unbounded" name="b" type="xs:int" />
                </xs:sequence>
            </xs:restriction>
        </xs:complexContent>
    </xs:complexType>

    <xs:complexType name="ThirdRestrictedType">
        <xs:complexContent>
            <xs:restriction base="tns:SecondRestrictedType">
                <xs:sequence>
                    <xs:element minOccurs="1" maxOccurs="1" name="b" type="xs:int" />
                </xs:sequence>
            </xs:restriction>
        </xs:complexContent>
    </xs:complexType>
</xs:schema>

Without this optimization this will result in the following code:

pub struct FirstRestrictedType {
    pub a: String,
}
pub struct SecondRestrictedType {
    pub b: Vec<i32>,
}
pub struct ThirdRestrictedType {
    pub b: i32,
}

With this optimization the following code is generated:

pub type FirstRestrictedType = BaseType;
pub struct BaseType {
    pub a: Vec<String>,
    pub b: Vec<i32>,
}
pub type SecondRestrictedType = BaseType;
pub type ThirdRestrictedType = BaseType;
Source§

impl Optimizer

Source

pub fn new(types: MetaTypes) -> Self

Create a new Optimizer instance from the passed types.

Source

pub fn finish(self) -> MetaTypes

Finish the optimization and return the resulting MetaTypes.

Trait Implementations§

Source§

impl Debug for Optimizer

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<X> AsAny for X
where X: 'static,

Source§

fn as_any(&self) -> &(dyn Any + 'static)

Get a reference to the current object as Any.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Get a mutable reference to the current object as Any.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> MaybeSendSync for T