This is an automated email from the ASF dual-hosted git repository.
yuxia pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss-rust.git
The following commit(s) were added to refs/heads/main by this push:
new 0046471 feat: implement Display trait for DataType and related types
(#50)
0046471 is described below
commit 004647119478997469c14e45725b8225a7f5d85b
Author: Pavlos-Petros Tournaris <[email protected]>
AuthorDate: Sat Nov 29 18:16:27 2025 +0200
feat: implement Display trait for DataType and related types (#50)
Fixes #38
---
crates/fluss/src/metadata/datatype.rs | 474 +++++++++++++++++++++++++++++++++-
rust-toolchain.toml | 2 +-
2 files changed, 474 insertions(+), 2 deletions(-)
diff --git a/crates/fluss/src/metadata/datatype.rs
b/crates/fluss/src/metadata/datatype.rs
index 09ca0c2..c7f9326 100644
--- a/crates/fluss/src/metadata/datatype.rs
+++ b/crates/fluss/src/metadata/datatype.rs
@@ -93,6 +93,32 @@ impl DataType {
}
}
+impl Display for DataType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ match self {
+ DataType::Boolean(v) => write!(f, "{}", v),
+ DataType::TinyInt(v) => write!(f, "{}", v),
+ DataType::SmallInt(v) => write!(f, "{}", v),
+ DataType::Int(v) => write!(f, "{}", v),
+ DataType::BigInt(v) => write!(f, "{}", v),
+ DataType::Float(v) => write!(f, "{}", v),
+ DataType::Double(v) => write!(f, "{}", v),
+ DataType::Char(v) => write!(f, "{}", v),
+ DataType::String(v) => write!(f, "{}", v),
+ DataType::Decimal(v) => write!(f, "{}", v),
+ DataType::Date(v) => write!(f, "{}", v),
+ DataType::Time(v) => write!(f, "{}", v),
+ DataType::Timestamp(v) => write!(f, "{}", v),
+ DataType::TimestampLTz(v) => write!(f, "{}", v),
+ DataType::Bytes(v) => write!(f, "{}", v),
+ DataType::Binary(v) => write!(f, "{}", v),
+ DataType::Array(v) => write!(f, "{}", v),
+ DataType::Map(v) => write!(f, "{}", v),
+ DataType::Row(v) => write!(f, "{}", v),
+ }
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct BooleanType {
nullable: bool,
@@ -118,6 +144,16 @@ impl BooleanType {
}
}
+impl Display for BooleanType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "BOOLEAN")?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct TinyIntType {
nullable: bool,
@@ -143,6 +179,16 @@ impl TinyIntType {
}
}
+impl Display for TinyIntType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "TINYINT")?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct SmallIntType {
nullable: bool,
@@ -168,6 +214,16 @@ impl SmallIntType {
}
}
+impl Display for SmallIntType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "SMALLINT")?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct IntType {
nullable: bool,
@@ -193,6 +249,16 @@ impl IntType {
}
}
+impl Display for IntType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "INT")?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct BigIntType {
nullable: bool,
@@ -218,6 +284,16 @@ impl BigIntType {
}
}
+impl Display for BigIntType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "BIGINT")?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct FloatType {
nullable: bool,
@@ -243,6 +319,16 @@ impl FloatType {
}
}
+impl Display for FloatType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "FLOAT")?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct DoubleType {
nullable: bool,
@@ -268,6 +354,16 @@ impl DoubleType {
}
}
+impl Display for DoubleType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "DOUBLE")?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct CharType {
nullable: bool,
@@ -327,6 +423,16 @@ impl StringType {
}
}
+impl Display for StringType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "STRING")?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct DecimalType {
nullable: bool,
@@ -370,6 +476,16 @@ impl DecimalType {
}
}
+impl Display for DecimalType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "DECIMAL({}, {})", self.precision, self.scale)?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct DateType {
nullable: bool,
@@ -395,6 +511,16 @@ impl DateType {
}
}
+impl Display for DateType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "DATE")?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash,
Serialize, Deserialize)]
pub struct TimeType {
nullable: bool,
@@ -434,6 +560,16 @@ impl TimeType {
}
}
+impl Display for TimeType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "TIME({})", self.precision)?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct TimestampType {
nullable: bool,
@@ -473,6 +609,16 @@ impl TimestampType {
}
}
+impl Display for TimestampType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "TIMESTAMP({})", self.precision)?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct TimestampLTzType {
nullable: bool,
@@ -512,6 +658,16 @@ impl TimestampLTzType {
}
}
+impl Display for TimestampLTzType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "TIMESTAMP_LTZ({})", self.precision)?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct BytesType {
nullable: bool,
@@ -537,6 +693,16 @@ impl BytesType {
}
}
+impl Display for BytesType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "BYTES")?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize,
Deserialize)]
pub struct BinaryType {
nullable: bool,
@@ -567,6 +733,16 @@ impl BinaryType {
}
}
+impl Display for BinaryType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "BINARY({})", self.length)?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ArrayType {
nullable: bool,
@@ -597,6 +773,16 @@ impl ArrayType {
}
}
+impl Display for ArrayType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "ARRAY<{}>", self.element_type)?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)]
pub struct MapType {
nullable: bool,
@@ -634,6 +820,16 @@ impl MapType {
}
}
+impl Display for MapType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "MAP<{}, {}>", self.key_type, self.value_type)?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, Hash)]
pub struct RowType {
nullable: bool,
@@ -658,6 +854,23 @@ impl RowType {
}
}
+impl Display for RowType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "ROW<")?;
+ for (i, field) in self.fields.iter().enumerate() {
+ if i > 0 {
+ write!(f, ", ")?;
+ }
+ write!(f, "{}", field)?;
+ }
+ write!(f, ">")?;
+ if !self.nullable {
+ write!(f, " NOT NULL")?;
+ }
+ Ok(())
+ }
+}
+
pub struct DataTypes;
impl DataTypes {
@@ -823,4 +1036,263 @@ impl DataField {
}
}
-// todo: implement display for datatype
+impl Display for DataField {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{} {}", self.name, self.data_type)
+ }
+}
+
+#[test]
+fn test_boolean_display() {
+ assert_eq!(BooleanType::new().to_string(), "BOOLEAN");
+ assert_eq!(
+ BooleanType::with_nullable(false).to_string(),
+ "BOOLEAN NOT NULL"
+ );
+}
+
+#[test]
+fn test_tinyint_display() {
+ assert_eq!(TinyIntType::new().to_string(), "TINYINT");
+ assert_eq!(
+ TinyIntType::with_nullable(false).to_string(),
+ "TINYINT NOT NULL"
+ );
+}
+
+#[test]
+fn test_smallint_display() {
+ assert_eq!(SmallIntType::new().to_string(), "SMALLINT");
+ assert_eq!(
+ SmallIntType::with_nullable(false).to_string(),
+ "SMALLINT NOT NULL"
+ );
+}
+
+#[test]
+fn test_int_display() {
+ assert_eq!(IntType::new().to_string(), "INT");
+ assert_eq!(IntType::with_nullable(false).to_string(), "INT NOT NULL");
+}
+
+#[test]
+fn test_bigint_display() {
+ assert_eq!(BigIntType::new().to_string(), "BIGINT");
+ assert_eq!(
+ BigIntType::with_nullable(false).to_string(),
+ "BIGINT NOT NULL"
+ );
+}
+
+#[test]
+fn test_float_display() {
+ assert_eq!(FloatType::new().to_string(), "FLOAT");
+ assert_eq!(
+ FloatType::with_nullable(false).to_string(),
+ "FLOAT NOT NULL"
+ );
+}
+
+#[test]
+fn test_double_display() {
+ assert_eq!(DoubleType::new().to_string(), "DOUBLE");
+ assert_eq!(
+ DoubleType::with_nullable(false).to_string(),
+ "DOUBLE NOT NULL"
+ );
+}
+
+#[test]
+fn test_string_display() {
+ assert_eq!(StringType::new().to_string(), "STRING");
+ assert_eq!(
+ StringType::with_nullable(false).to_string(),
+ "STRING NOT NULL"
+ );
+}
+
+#[test]
+fn test_date_display() {
+ assert_eq!(DateType::new().to_string(), "DATE");
+ assert_eq!(DateType::with_nullable(false).to_string(), "DATE NOT NULL");
+}
+
+#[test]
+fn test_bytes_display() {
+ assert_eq!(BytesType::new().to_string(), "BYTES");
+ assert_eq!(
+ BytesType::with_nullable(false).to_string(),
+ "BYTES NOT NULL"
+ );
+}
+
+#[test]
+fn test_char_display() {
+ assert_eq!(CharType::new(10).to_string(), "CHAR(10)");
+ assert_eq!(
+ CharType::with_nullable(20, false).to_string(),
+ "CHAR(20) NOT NULL"
+ );
+}
+
+#[test]
+fn test_decimal_display() {
+ assert_eq!(DecimalType::new(10, 2).to_string(), "DECIMAL(10, 2)");
+ assert_eq!(
+ DecimalType::with_nullable(false, 38, 10).to_string(),
+ "DECIMAL(38, 10) NOT NULL"
+ );
+}
+
+#[test]
+fn test_time_display() {
+ assert_eq!(TimeType::new(0).to_string(), "TIME(0)");
+ assert_eq!(TimeType::new(3).to_string(), "TIME(3)");
+ assert_eq!(
+ TimeType::with_nullable(false, 9).to_string(),
+ "TIME(9) NOT NULL"
+ );
+}
+
+#[test]
+fn test_timestamp_display() {
+ assert_eq!(TimestampType::new(6).to_string(), "TIMESTAMP(6)");
+ assert_eq!(TimestampType::new(0).to_string(), "TIMESTAMP(0)");
+ assert_eq!(
+ TimestampType::with_nullable(false, 9).to_string(),
+ "TIMESTAMP(9) NOT NULL"
+ );
+}
+
+#[test]
+fn test_timestamp_ltz_display() {
+ assert_eq!(TimestampLTzType::new(6).to_string(), "TIMESTAMP_LTZ(6)");
+ assert_eq!(TimestampLTzType::new(3).to_string(), "TIMESTAMP_LTZ(3)");
+ assert_eq!(
+ TimestampLTzType::with_nullable(false, 9).to_string(),
+ "TIMESTAMP_LTZ(9) NOT NULL"
+ );
+}
+
+#[test]
+fn test_binary_display() {
+ assert_eq!(BinaryType::new(100).to_string(), "BINARY(100)");
+ assert_eq!(
+ BinaryType::with_nullable(false, 256).to_string(),
+ "BINARY(256) NOT NULL"
+ );
+}
+
+#[test]
+fn test_array_display() {
+ let array_type = ArrayType::new(DataTypes::int());
+ assert_eq!(array_type.to_string(), "ARRAY<INT>");
+
+ let array_type_non_null = ArrayType::with_nullable(false,
DataTypes::string());
+ assert_eq!(array_type_non_null.to_string(), "ARRAY<STRING> NOT NULL");
+
+ let nested_array = ArrayType::new(DataTypes::array(DataTypes::int()));
+ assert_eq!(nested_array.to_string(), "ARRAY<ARRAY<INT>>");
+}
+
+#[test]
+fn test_map_display() {
+ let map_type = MapType::new(DataTypes::string(), DataTypes::int());
+ assert_eq!(map_type.to_string(), "MAP<STRING, INT>");
+
+ let map_type_non_null =
+ MapType::with_nullable(false, DataTypes::int(), DataTypes::string());
+ assert_eq!(map_type_non_null.to_string(), "MAP<INT, STRING> NOT NULL");
+
+ let nested_map = MapType::new(
+ DataTypes::string(),
+ DataTypes::map(DataTypes::int(), DataTypes::boolean()),
+ );
+ assert_eq!(nested_map.to_string(), "MAP<STRING, MAP<INT, BOOLEAN>>");
+}
+
+#[test]
+fn test_row_display() {
+ let fields = vec![
+ DataTypes::field("id".to_string(), DataTypes::int()),
+ DataTypes::field("name".to_string(), DataTypes::string()),
+ ];
+ let row_type = RowType::new(fields);
+ assert_eq!(row_type.to_string(), "ROW<id INT, name STRING>");
+
+ let fields_non_null = vec![DataTypes::field("age".to_string(),
DataTypes::bigint())];
+ let row_type_non_null = RowType::with_nullable(false, fields_non_null);
+ assert_eq!(row_type_non_null.to_string(), "ROW<age BIGINT> NOT NULL");
+}
+
+#[test]
+fn test_datatype_display() {
+ assert_eq!(DataTypes::boolean().to_string(), "BOOLEAN");
+ assert_eq!(DataTypes::int().to_string(), "INT");
+ assert_eq!(DataTypes::string().to_string(), "STRING");
+ assert_eq!(DataTypes::char(50).to_string(), "CHAR(50)");
+ assert_eq!(DataTypes::decimal(10, 2).to_string(), "DECIMAL(10, 2)");
+ assert_eq!(DataTypes::time_with_precision(3).to_string(), "TIME(3)");
+ assert_eq!(
+ DataTypes::timestamp_with_precision(6).to_string(),
+ "TIMESTAMP(6)"
+ );
+ assert_eq!(
+ DataTypes::timestamp_ltz_with_precision(9).to_string(),
+ "TIMESTAMP_LTZ(9)"
+ );
+ assert_eq!(DataTypes::array(DataTypes::int()).to_string(), "ARRAY<INT>");
+ assert_eq!(
+ DataTypes::map(DataTypes::string(), DataTypes::int()).to_string(),
+ "MAP<STRING, INT>"
+ );
+}
+
+#[test]
+fn test_datafield_display() {
+ let field = DataTypes::field("user_id".to_string(), DataTypes::bigint());
+ assert_eq!(field.to_string(), "user_id BIGINT");
+
+ let field2 = DataTypes::field("email".to_string(), DataTypes::string());
+ assert_eq!(field2.to_string(), "email STRING");
+
+ let field3 = DataTypes::field("score".to_string(), DataTypes::decimal(10,
2));
+ assert_eq!(field3.to_string(), "score DECIMAL(10, 2)");
+}
+
+#[test]
+fn test_complex_nested_display() {
+ let row_type = DataTypes::row(vec![
+ DataTypes::field("id".to_string(), DataTypes::int()),
+ DataTypes::field("tags".to_string(),
DataTypes::array(DataTypes::string())),
+ DataTypes::field(
+ "metadata".to_string(),
+ DataTypes::map(DataTypes::string(), DataTypes::string()),
+ ),
+ ]);
+ assert_eq!(
+ row_type.to_string(),
+ "ROW<id INT, tags ARRAY<STRING>, metadata MAP<STRING, STRING>>"
+ );
+}
+
+#[test]
+fn test_non_nullable_datatype() {
+ let nullable_int = DataTypes::int();
+ assert_eq!(nullable_int.to_string(), "INT");
+
+ let non_nullable_int = nullable_int.as_non_nullable();
+ assert_eq!(non_nullable_int.to_string(), "INT NOT NULL");
+}
+
+#[test]
+fn test_deeply_nested_types() {
+ let nested = DataTypes::array(DataTypes::map(
+ DataTypes::string(),
+ DataTypes::row(vec![
+ DataTypes::field("x".to_string(), DataTypes::int()),
+ DataTypes::field("y".to_string(), DataTypes::int()),
+ ]),
+ ));
+ assert_eq!(nested.to_string(), "ARRAY<MAP<STRING, ROW<x INT, y INT>>>");
+}
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index 56c3bf5..870d7eb 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -17,4 +17,4 @@
[toolchain]
channel = "stable"
-components = ["rustfmt", "clippy"]
\ No newline at end of file
+components = ["rustfmt", "clippy"]