stellar / rs-stellar-xdr

Rust lib for Stellar XDR.
Apache License 2.0
20 stars 27 forks source link

Add schemars::JsonSchema derives to all types #347

Closed leighmcculloch closed 5 months ago

leighmcculloch commented 8 months ago

What

Add schemars::JsonSchema derives to all types

Why

See https://github.com/stellar/xdrgen/pull/193.

Close https://github.com/stellar/rs-stellar-xdr/issues/348

Todo

willemneal commented 8 months ago

@leighmcculloch I updated https://github.com/stellar/rs-stellar-xdr/pull/349

Feel free to move the impls where ever you think is best, I'm pretty happy with them now. The next issue is enum representations. See here the option is unnamed Option 1 and the current doc strings are redundant.

Here is the site where you can view the forms generated by the schema: https://rjsf-team.github.io/react-jsonschema-form/

Unfortunately you can't share an example prefilled in, but here is a picture of the memo section of the transaction envelope.

image
  "Memo": {
    "description": "Memo is an XDR Union defines as:\n\n```text union Memo switch (MemoType type) { case MEMO_NONE: void; case MEMO_TEXT: string text<28>; case MEMO_ID: uint64 id; case MEMO_HASH: Hash hash; // the hash of what to pull from the content server case MEMO_RETURN: Hash retHash; // the hash of the tx you are rejecting }; ```",
"oneOf": [
    {
      "type": "string",
      "enum": [
        "none"
      ]
    },
    {
      "type": "object",
      "required": [
        "text"
      ],
      "properties": {
        "text": {
          "type": "string",
          "maxLength": 28
        }
      },
      "additionalProperties": false
    },
    {
      "type": "object",
      "required": [
        "id"
      ],
      "properties": {
        "id": {
          "type": "integer",
          "format": "uint64",
          "minimum": 0.0
        }
      },
      "additionalProperties": false
    },
    {
      "type": "object",
      "required": [
        "hash"
      ],
      "properties": {
        "hash": {
          "$ref": "#/definitions/Hash"
        }
      },
      "additionalProperties": false
    },
    {
      "type": "object",
      "required": [
        "return"
      ],
      "properties": {
        "return": {
          "$ref": "#/definitions/Hash"
        }
      },
      "additionalProperties": false
    }
  ]
}
willemneal commented 8 months ago

Oh one other thing to note are byte arrays. Currently they are treated as Vec<u8>, but a base64 string would work better for JSON. Now it's pretty painful. Here is a Hash type with one input per 32 bytes.

image
leighmcculloch commented 8 months ago

Byte arrays are rendered in the JSON as hex, not as arrays, so I'm surprised the JSON schema is rendering that as an array.

willemneal commented 8 months ago

I update BytesM to use a hex string, but pub struct Hash(pub [u8; 32]) is a default impl of schemars which is the same as Vec<u8> with a fixed length.

leighmcculloch commented 8 months ago

Interesting. Hash does render to hex because it has the below custom serialize/deserialize based on the display and fromstr impls:

#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
#[cfg_attr(
    all(feature = "serde", feature = "alloc"),
    derive(
        serde_with::SerializeDisplay,
        serde_with::DeserializeFromStr
    )
)]
pub struct Hash(pub [u8; 32]);
impl core::fmt::Display for Hash {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        let v = &self.0;
        for b in v {
            write!(f, "{b:02x}")?;
        }
        Ok(())
    }
}
#[cfg(feature = "alloc")]
impl core::str::FromStr for Hash {
    type Err = Error;
    fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
        hex::decode(s).map_err(|_| Error::InvalidHex)?.try_into()
    }
}
willemneal commented 8 months ago

The JsonSchema type is implemented for arrays here: https://github.com/GREsau/schemars/blob/e04e3a3a8191bac1f218539733110fcc26bdbf7c/schemars/src/json_schema_impls/array.rs#L31

Not sure how to get around other than manually implementing Hash?

leighmcculloch commented 8 months ago

Not sure how to get around other than manually implementing Hash?

Yup, I think we can generate the manual impl in xdrgen for any type that we generate hex encoding for.