lerouxrgd / rsgen-avro

Command line and library for generating Rust types from Avro schemas
MIT License
36 stars 29 forks source link

Array of Records not supported ? #5

Closed 0x003e closed 4 years ago

0x003e commented 4 years ago

Hi,

Thank you for making this project.

However this schema is not supported:

{
  "type": "record",
  "name": "Object",
  "namespace": "",
  "doc": "",
  "fields": [
    {
      "name":  "field",
      "default": null,
      "type": [
        "null", {
          "type": "array",
          "items": {
            "name": "Variable",
            "type": "record",
            "fields": [
              {"name": "key", "type": "int", "doc":  ""},
              {"name": "val",  "type":  "string", "doc":  ""}
            ]
          }
        }
      ]
    }
  ]
}
thread 'main' panicked at 'called `Result::unwrap()` on an `Err`

value: Template("Didn\'t find schema 
Array(Record { name: Name { name: \"Variable\", namespace: None, aliases: None }, doc: None, fields: [
RecordField { name: \"key\", doc: Some(\"\"), default: None, schema: Int, order: Ascending, position: 0 }, 
RecordField { name: \"val\", doc: Some(\"\"), default: None, schema: String, order: Ascending, position: 1 }
], 

lookup: {\"key\": 0, \"val\": 1} }) in state GenState({})")', src/libcore/result.rs:1165:5

I think that we need to adjust this function, right? https://github.com/lerouxrgd/rsgen-avro/blob/master/src/templates.rs#L673

lerouxrgd commented 4 years ago

Hi, thanks for reporting this! Indeed it looks like the case of a Option<Vec<Something>> is not working, I'll investigate.

lerouxrgd commented 4 years ago

Ok it should be fixed in 0.6.1, let me know how it goes for you.

0x003e commented 4 years ago

Unfortunately last commit https://github.com/lerouxrgd/rsgen-avro/commit/a8510af520cb2952d1842c3244dc601333e4579d cause a regression (duplicate struct) with this more complex example:

{
  "name": "Snmp",
  "type": "record",
  "fields": [
    {"name":  "v1", "default":  null, "type":
      [
        "null",
        {
          "name": "V1", "type": "record", "doc": "An SNMPv1 or SNMPv2c message",
          "fields": [
            {"name":  "pdu", "default": null, "type":  [
                "null",
                { "name": "TrapV1",
                  "type": "record",
                  "fields": [
                    {"name":  "var", "default": null, "type": ["null",
                      {"type": "array", "items":
                      {"name": "Variable", "type": "record", "fields": [
                        {"name": "oid", "default":  null, "type": ["null", {"type":"array", "items": "long"}], "doc":  ""},
                        {"name": "val", "default":  null, "type": ["null", "string"], "doc":  ""}
                      ]}
                      }
                    ]}
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Rust file generated with duplicate TrapV1 and V1 struct :

use serde::{Deserialize, Serialize};

#[serde(default)]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct Variable {
    pub oid: Option<Vec<i64>>,
    pub val: Option<String>,
}

impl Default for Variable {
    fn default() -> Variable {
        Variable {
            oid: None,
            val: None,
        }
    }
}

#[serde(default)]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct TrapV1 {
    pub var: Option<Vec<Variable>>,
}

impl Default for TrapV1 {
    fn default() -> TrapV1 {
        TrapV1 {
            var: None,
        }
    }
}

#[serde(default)]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct TrapV1 {
    pub var: Option<Vec<Variable>>,
}

impl Default for TrapV1 {
    fn default() -> TrapV1 {
        TrapV1 {
            var: None,
        }
    }
}

/// An SNMPv1 or SNMPv2c message
#[serde(default)]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct V1 {
    pub pdu: Option<TrapV1>,
}

impl Default for V1 {
    fn default() -> V1 {
        V1 {
            pdu: None,
        }
    }
}

/// An SNMPv1 or SNMPv2c message
#[serde(default)]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct V1 {
    pub pdu: Option<TrapV1>,
}

impl Default for V1 {
    fn default() -> V1 {
        V1 {
            pdu: None,
        }
    }
}

#[serde(default)]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct Snmp {
    pub v1: Option<V1>,
}

impl Default for Snmp {
    fn default() -> Snmp {
        Snmp {
            v1: None,
        }
    }
}
lerouxrgd commented 4 years ago

Nice catch, how about 0.6.3 ?

0x003e commented 4 years ago

We almost made it !

Just one more thing: (duplicate) on enum type

  "name": "Snmp",
  "type": "record",
  "fields": [
    {"name":  "v1", "default":  null, "type":
      [
        "null",
        {
          "name": "V1", "type": "record", "doc": "An SNMPv1 or SNMPv2c message",
          "fields": [
            {"name":  "version", "type": ["null","int"], "default":  null },
            {"name":  "community", "type":   ["null","string"], "default":  null },
            {"name":  "pdu", "default": null, "type":  [
                "null",
                { "name": "TrapV1",
                  "type": "record",
                  "fields": [
                    {"name":  "generic_trap",  "type": ["null", { "type": "enum", "name": "TrapType", "symbols": ["COLD_START", "WARM_START", "LINK_DOWN", "LINK_UP", "AUTHENTICATION_FAILURE", "EGP_NEIGHBOR_LOSS", "ENTERPRISE_SPECIFIC"]}], "default":  null, "doc":  "trap type"},
                    {"name":  "var", "default": null, "type": ["null",
                      {"type": "array", "items":
                      {"name": "Variable", "type": "record", "fields": [
                        {"name": "oid", "default":  null, "type": ["null", {"type":"array", "items": "long"}], "doc":  ""},
                        {"name": "val", "default":  null, "type": ["null", "string"], "doc":  ""}
                      ]}
                      }
                    ]}
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Generated rust file with TrapType enum duplicated

use serde::{Deserialize, Serialize};

/// La clé
#[serde(default)]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct SnmpKey {
    pub host: String,
    pub port: i64,
    #[serde(rename = "senderIp")]
    pub sender_ip: String,
}

impl Default for SnmpKey {
    fn default() -> SnmpKey {
        SnmpKey {
            host: String::default(),
            port: 0,
            sender_ip: String::default(),
        }
    }
}

#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Deserialize, Serialize)]
pub enum TrapType {
    #[serde(rename = "COLD_START")]
    ColdStart,
    #[serde(rename = "WARM_START")]
    WarmStart,
    #[serde(rename = "LINK_DOWN")]
    LinkDown,
    #[serde(rename = "LINK_UP")]
    LinkUp,
    #[serde(rename = "AUTHENTICATION_FAILURE")]
    AuthenticationFailure,
    #[serde(rename = "EGP_NEIGHBOR_LOSS")]
    EgpNeighborLoss,
    #[serde(rename = "ENTERPRISE_SPECIFIC")]
    EnterpriseSpecific,
}

#[derive(Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Clone, Deserialize, Serialize)]
pub enum TrapType {
    #[serde(rename = "COLD_START")]
    ColdStart,
    #[serde(rename = "WARM_START")]
    WarmStart,
    #[serde(rename = "LINK_DOWN")]
    LinkDown,
    #[serde(rename = "LINK_UP")]
    LinkUp,
    #[serde(rename = "AUTHENTICATION_FAILURE")]
    AuthenticationFailure,
    #[serde(rename = "EGP_NEIGHBOR_LOSS")]
    EgpNeighborLoss,
    #[serde(rename = "ENTERPRISE_SPECIFIC")]
    EnterpriseSpecific,
}

#[serde(default)]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct TrapV1 {
    pub generic_trap: Option<TrapType>,
}

impl Default for TrapV1 {
    fn default() -> TrapV1 {
        TrapV1 {
            generic_trap: None,
        }
    }
}

/// An SNMPv1 or SNMPv2c message
#[serde(default)]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct V1 {
    pub version: Option<i32>,
    pub community: Option<String>,
    pub pdu: Option<TrapV1>,
}

impl Default for V1 {
    fn default() -> V1 {
        V1 {
            version: None,
            community: None,
            pdu: None,
        }
    }
}

#[serde(default)]
#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
pub struct Snmp {
    pub v1: Option<V1>,
}

impl Default for Snmp {
    fn default() -> Snmp {
        Snmp {
            v1: None,
        }
    }
}
lerouxrgd commented 4 years ago

Are you sure that you are testing with 0.6.3 ? It looks good for me. (if you use 0.6.2 I think it is possible to see this duplicate)

0x003e commented 4 years ago

Sorry, I made a copy-paste git hash mistake; now 0.6.3 everything works well. thank you ! 👍