reis 0.1.0

Pure Rust implementation of libei/libeis protocol.
Documentation
{# ei-scanner jinja template, for `` #}
#![allow(unused_parens, clippy::useless_conversion, clippy::double_parens, clippy::match_single_binding)]

// GENERATED FILE

{# TODO handle destructor #}
{# TODO handle context_type #}

{% macro interface_type(interface) -%}
    super::{{interface.plainname}}::{{interface.plainname|camel}}
{%- endmacro %}

{% macro arg_type(arg, owned, generic) -%}
    {% if arg.enum != None %} {{ arg.enum.name|camel }}
    {% elif arg.protocol_type == 'string' and owned %} String
    {% elif arg.protocol_type == 'string' %} &str
    {% elif arg.protocol_type == 'int32' %} i32
    {% elif arg.protocol_type == 'uint32' %} u32
    {% elif arg.protocol_type == 'int64' %} i64
    {% elif arg.protocol_type == 'uint64' %} u64
    {% elif arg.protocol_type == 'object' and owned %} {{interface_type(arg.interface)}}
    {% elif arg.protocol_type == 'object' %} &{{interface_type(arg.interface)}}
    {% elif arg.protocol_type == 'new_id' and arg.interface %} {{interface_type(arg.interface)}}
    {% elif arg.protocol_type == 'new_id' and arg.interface_arg and generic %} {{arg.interface_arg.name|camel}}
    {% elif arg.protocol_type == 'new_id' and arg.interface_arg %} crate::Object
    {% elif arg.protocol_type == 'float' %} f32
    {% elif arg.protocol_type == 'fd' and owned %} std::os::unix::io::OwnedFd
    {% elif arg.protocol_type == 'fd' %} std::os::unix::io::BorrowedFd
    {% else %} unhandled_arg_type_{{arg.protocol_type}}
    {% endif %}
{%- endmacro %}

{% macro return_type(arguments) -%}
{%- endmacro %}

{% macro incoming_enum() -%}
    {% if extra.eis %}
    Request
    {% else %}
    Event
    {% endif %}
{%- endmacro %}

{% macro module() -%}
    {% if extra.eis %}
    crate::eis
    {% else %}
    crate::ei
    {% endif %}
{%- endmacro %}

{% for interface in interfaces %}
/** {{interface.description.text|ei_escape_names}} */
pub mod {{interface.plainname}} {
    #[derive(Clone, Debug)]
    pub struct {{interface.plainname|camel}}(pub(crate) crate::Object);

    impl crate::private::Sealed for {{interface.plainname|camel}} {}

    impl crate::Interface for {{interface.plainname|camel}} {
        const NAME: &'static str = "{{interface.protocol_name}}";
        const VERSION: u32 = {{interface.version}};
        {% if extra.eis %}
        const CLIENT_SIDE: bool = false;
        {% else %}
        const CLIENT_SIDE: bool = true;
        {% endif %}
        type Incoming = {{incoming_enum()}};

        fn new_unchecked(object: crate::Object) -> Self {
            Self(object)
        }

        fn as_arg(&self) -> crate::Arg<'_> {
            self.0.as_arg()
        }
    }

    impl {{module()}}::Interface for {{interface.plainname|camel}} {}

    impl {{interface.plainname|camel}} {
        {% for outgoing in interface.outgoing %}
        /** {{outgoing.description.text|ei_escape_names}} */
        pub fn {{outgoing.name}}<
            {%- for arg in outgoing.arguments %}
            {% if arg.interface_arg_for %}
            {{arg.name|camel}}: {{module()}}::Interface
            {% endif %}
            {% endfor -%}
        >(
            &self,
            {%- for arg in outgoing.arguments %}
            {% if arg.protocol_type != 'new_id' and not arg.interface_arg_for %}
            {{arg.name}}: {{arg_type(arg, false, false)}},
            {% endif %}
            {% endfor -%}
            ) -> (
                {%- for arg in outgoing.arguments %}
                {% if arg.protocol_type == 'new_id' %}
                {{arg_type(arg, true, true)}}
                {% endif %}
                {% endfor -%}
            ) {
            {%- for arg in outgoing.arguments %}
            {% if arg.protocol_type == 'new_id' and arg.interface_arg %}
            let {{arg.name}} = self.0.backend().new_id({{arg.interface_arg.name|camel}}::NAME.to_string(), {{arg.version_arg.name}});
            {% elif arg.protocol_type == 'new_id' %}
            let {{arg.name}} = self.0.backend().new_id("{{arg.interface.protocol_name}}".to_string(), {{arg.version_arg.name}});
            {% endif -%}
            {% endfor -%}

            let args = &[
            {%- for arg in outgoing.arguments %}
            {% if arg.interface_arg_for %}
                crate::Arg::{{arg.protocol_type|camel}}({{arg.name|camel}}::NAME),
            {% else %}
                crate::Arg::{{arg.protocol_type|camel}}({{arg.name}}.into()),
            {% endif %}
            {% endfor -%}
            ];

            self.0.request({{outgoing.opcode}}, args);
            {% if outgoing.is_destructor %}
                self.0.backend().remove_id(self.0.id());
            {% endif %}

            (
            {%- for arg in outgoing.arguments %}
            {% if arg.protocol_type == 'new_id' %}
            {% if arg.interface %}
                {{arg_type(arg, true, false)}}(crate::Object::new(
                    self.0.backend().clone(), {{arg.name}},
                    {% if extra.eis %}
                    false
                    {% else %}
                    true
                    {% endif %}
                    ))
            {% elif arg.interface_arg %}
                crate::Object::new(self.0.backend().clone(), {{arg.name}},
                {% if extra.eis %}
                false
                {% else %}
                true
                {% endif %}
                ).downcast_unchecked()
            {% else -%}
                unreachable
            {% endif -%}
            {% endif -%}
            {% endfor -%}
            )
        }

        {% endfor %}
    }

    {% for enum in interface.enums %}
     /** {{enum.description.text|ei_escape_names}} */
    #[derive(Clone, Copy, Debug)]
    pub enum {{enum.camel_name}} {
        {% for entry in enum.entries %}
        /** {{entry.summary}} */
        {{entry.name|camel}} = {{entry.value}},
        {% endfor %}
    }

    impl From<{{enum.camel_name}}> for u32 {
        fn from(value: {{enum.camel_name}}) -> u32 {
            value as u32
        }
    }

    impl crate::OwnedArg for {{enum.camel_name}} {
        fn parse(buf: &mut crate::ByteStream) -> Result<Self, crate::ParseError> {
            match u32::parse(buf)? {
            {% for entry in enum.entries %}
                {{entry.value}} => Ok(Self::{{entry.name|camel}}),
            {% endfor %}
        variant => Err(crate::ParseError::InvalidVariant("{{enum.camel_name}}", variant)),
            }
        }

        fn as_arg(&self) -> crate::Arg<'_> {
            crate::Arg::Uint32(*self as u32)
        }

        fn enum_name(&self) -> Option<(&'static str, &'static str)> {
            Some(("{{enum.name}}", match self {
                {% for entry in enum.entries %}
                Self::{{entry.name|camel}} => "{{entry.name}}",
                {% endfor %}
            }
            ))
        }
    }
    {% endfor %}

    #[non_exhaustive]
    #[derive(Debug)]
    pub enum {{incoming_enum()}} {
        {% for incoming in interface.incoming %}
            /** {{incoming.description.text|ei_escape_names}} */
            {{ incoming.name|camel }}
                {% if incoming.arguments %}
                { {% for arg in incoming.arguments %}
                    {% if not (arg.version_arg_for or arg.interface_arg_for) %}
                    /** {{arg.summary}} */
                    {{arg.name}}: {{arg_type(arg, true, false)}},
                    {% endif %}
                {% endfor %} }
                {% endif %},
        {% endfor %}
    }

    impl {{incoming_enum()}} {
        pub(super) fn op_name(operand: u32) -> Option<&'static str> {
            match operand {
            {% for incoming in interface.incoming %}
                {{incoming.opcode}} => Some("{{incoming.name}}"),
            {% endfor %}
                _ => None
            }
        }

        pub(super) fn parse(operand: u32, _bytes: &mut crate::ByteStream) -> Result<Self, crate::ParseError> {
            match operand {
            {% for incoming in interface.incoming %}
                {{incoming.opcode}} => {
                    {% for arg in incoming.arguments %}
                        let {{arg.name}} = _bytes.read_arg()?;
                    {% endfor %}

                    Ok(Self::{{ incoming.name|camel }}
                    {% if incoming.arguments %} {
                    {% for arg in incoming.arguments %}
                        {% if arg.version_arg_for or arg.interface_arg_for %}
                        {% elif arg.protocol_type == 'new_id' and arg.interface_arg %}
                        {{arg.name}}: _bytes.backend().new_peer_object({{arg.name}}, {{arg.interface_arg.name}}, {{arg.version_arg.name}})?,
                        {% elif arg.protocol_type == 'new_id' %}
                        {{arg.name}}: _bytes.backend().new_peer_interface({{arg.name}}, {{arg.version_arg.name}})?,
                        {% else %}
                        {{arg.name}},
                        {% endif %}
                    {% endfor %} }
                    {% endif %} )
                }
            {% endfor %}
         opcode => Err(crate::ParseError::InvalidOpcode("{{interface.plainname}}", opcode)),
            }
        }

        #[allow(unused_imports, unused_mut, unused_variables, unreachable_code, unreachable_patterns)]
        pub(super) fn args(&self) -> Vec<crate::Arg<'_>> {
            use crate::{arg::OwnedArg, Interface};
            let mut args = Vec::new();
            match self {
            {% for incoming in interface.incoming %}
                {% if incoming.arguments %}
                Self::{{ incoming.name|camel }} {
                    {% for arg in incoming.arguments %}
                    {% if not (arg.version_arg_for or arg.interface_arg_for) %}
                    {{arg.name}},
                    {% endif %}
                    {% endfor %}
                } => {
                    {% for arg in incoming.arguments %}
                    {% if not (arg.version_arg_for or arg.interface_arg_for) %}
                    args.push({{arg.name}}.as_arg());
                    {% endif %}
                    {% endfor %}
                }
                {% else %}
                Self::{{ incoming.name|camel }} => {}
                {% endif %}
            {% endfor %}
                _ => unreachable!()
            }
            args
        }
    }
}

pub use {{interface.plainname}}::{{interface.plainname|camel}};

{% endfor %}

#[non_exhaustive]
#[derive(Debug)]
pub enum {{incoming_enum()}} {
    {% for interface in interfaces %}
        {{interface.plainname|camel}}({{interface.plainname}}::{{interface.plainname|camel}}, {{interface.plainname}}::{{incoming_enum()}}),
    {% endfor %}
}

impl {{incoming_enum()}} {
    pub(crate) fn op_name(interface: &str, operand: u32) -> Option<&'static str> {
        match interface {
            {% for interface in interfaces %}
                "{{interface.protocol_name}}" =>
                    {{interface.plainname}}::{{incoming_enum()}}::op_name(operand),
            {% endfor %}
            _ => None,
        }
    }

    pub(crate) fn parse(id: u64, interface: &str, operand: u32, bytes: &mut crate::ByteStream) -> Result<Self, crate::ParseError> {
        match interface {
            {% for interface in interfaces %}
                "{{interface.protocol_name}}" => Ok(Self::{{interface.plainname|camel}}(
                    crate::Object::new(bytes.backend().clone(), id,
                        {% if extra.eis %}
                        false
                        {% else %}
                        true
                        {% endif %}
                    ).downcast_unchecked(), 
                    {{interface.plainname}}::{{incoming_enum()}}::parse(operand, bytes)?)
                ),
            {% endfor %}
            _ => Err(crate::ParseError::InvalidInterface),
        }
    }

}

impl crate::MessageEnum for {{incoming_enum()}} {
    fn args(&self) -> Vec<crate::Arg<'_>> {
        match self {
            {% for interface in interfaces %}
            Self::{{interface.plainname|camel}}(_, x) => x.args(),
            {% endfor %}
        }
    }
}