Repository: zhiburt/tabled
Pull request #493 (2025-01-31 11:14:53)

Add `#[tabled(display_type)]`
CONTINUATION OF #491


Patch:
diff --git a/README.md b/README.md
index 391b89a..5ca63e1 100644
--- a/README.md
+++ b/README.md
@@ -1549,7 +1549,7 @@ However, this may be often not the case for example when a field uses the `Optio
 - Implement `Tabled` trait manually for a type.
 - Wrap `Option` to something like `DisplayedOption<T>(Option<T>)` and implement a Display trait for it.
 
-Alternatively, you can use the `#[tabled(display_with = "func")]` attribute for the field to specify a display function.
+Alternatively, you can use the `#[tabled(display = "func")]` attribute for the field to specify a display function.
 
 ```rust
 use tabled::Tabled;
@@ -1557,7 +1557,7 @@ use tabled::Tabled;
 #[derive(Tabled)]
 pub struct MyRecord {
     pub id: i64,
-    #[tabled(display_with = "display_option")]
+    #[tabled(display = "display_option")]
     pub valid: Option<bool>
 }
 
@@ -1570,7 +1570,7 @@ fn display_option(o: &Option<bool>) -> String {
 ```
 
 You can send an argument to a function like this (it also possible to use `&self`),
-using `#[tabled(display_with("some_function", "arg1", 2, self))]`
+using `#[tabled(display("some_function", "arg1", 2, self))]`
 
 ```rust
 use tabled::Tabled;
@@ -1578,7 +1578,7 @@ use tabled::Tabled;
 #[derive(Tabled)]
 pub struct MyRecord {
     pub id: i64,
-    #[tabled(display_with("Self::display_valid", self, 1))]
+    #[tabled(display("Self::display_valid", self, 1))]
     pub valid: Option<bool>
 }
 
diff --git a/tabled/Cargo.toml b/tabled/Cargo.toml
index 458d345..41a14ff 100644
--- a/tabled/Cargo.toml
+++ b/tabled/Cargo.toml
@@ -68,6 +68,11 @@ name = "derive_display_with"
 path = "examples/derive/display_with.rs"
 required-features = ["derive"]
 
+[[example]]
+name = "derive_display_type"
+path = "examples/derive/display_type.rs"
+required-features = ["derive"]
+
 [[example]]
 name = "derive_crate_override"
 path = "examples/derive/crate_override.rs"
@@ -332,7 +337,7 @@ macros = ["std"]
 
 [dependencies]
 papergrid = { path = "../papergrid", default-features = false }
-tabled_derive = { version = "0.9", optional = true }
+tabled_derive = { path = "../tabled_derive", optional = true }
 ansi-str = { version = "0.9", optional = true }
 ansitok = { version = "0.3", optional = true }
 
diff --git a/tabled/examples/derive/display_type.rs b/tabled/examples/derive/display_type.rs
new file mode 100644
index 0000000..5796422
--- /dev/null
+++ b/tabled/examples/derive/display_type.rs
@@ -0,0 +1,43 @@
+use tabled::Tabled;
+
+#[derive(Tabled)]
+#[tabled(display(Option, "display_option", "UNKNOWN"))]
+pub struct Country {
+    name: String,
+    capital: Option<String>,
+    currency: Option<String>,
+}
+
+fn display_option<T>(opt: &Option<T>, default: &str) -> String
+where
+    T: ToString,
+{
+    match opt {
+        Some(val) => val.to_string().to_uppercase(),
+        None => default.to_string(),
+    }
+}
+
+fn main() {
+    let data = vec![
+        Country {
+            name: String::from("France"),
+            capital: Some(String::from("Paris")),
+            currency: Some(String::from("EUR")),
+        },
+        Country {
+            name: String::from("Germany"),
+            capital: Some(String::from("Berlin")),
+            currency: None,
+        },
+        Country {
+            name: String::from("Unknown"),
+            capital: None,
+            currency: Some(String::from("01")),
+        },
+    ];
+
+    let table = tabled::Table::new(data);
+
+    println!("{}", table);
+}
diff --git a/tabled/examples/derive/display_with.rs b/tabled/examples/derive/display_with.rs
index c1e89b5..f12b160 100644
--- a/tabled/examples/derive/display_with.rs
+++ b/tabled/examples/derive/display_with.rs
@@ -10,42 +10,45 @@
 //! * Attribute arguments can be directly overridden with static values, effectively ignoring the
 //!   augmented fields natural value entirely. Even an entire object can be passed as context with `self`.
 
-use std::borrow::Cow;
-
 use tabled::{Table, Tabled};
 
 #[derive(Tabled)]
 struct Country {
     name: String,
-    #[tabled(display_with = "str::to_uppercase")]
+    #[tabled(display = "str::to_uppercase")]
     capital: String,
-    #[tabled(display_with("display_perimeter", self))]
+    #[tabled(display("perimeter", self, false))]
     area_km2: f32,
+    #[tabled(display("tabled::derive::display::option", "unknown"))]
+    currency: Option<String>,
 }
 
-impl Country {
-    fn new(name: &str, capital: &str, area_km2: f32) -> Self {
-        Self {
-            name: name.to_string(),
-            capital: capital.to_string(),
-            area_km2,
-        }
-    }
-}
-
-fn display_perimeter(country: &Country) -> Cow<'_, str> {
-    if country.area_km2 > 1_000_000.0 {
-        "BIG".into()
-    } else {
-        "small".into()
-    }
+fn perimeter<'a>(area: &f32, country: &Country, _milies: bool) -> String {
+    let is_big = *area > 1_000_000.0f32;
+    let big_sign = if is_big { "B" } else { "" };
+    format!("{} {}", country.area_km2, big_sign)
 }
 
 fn main() {
     let data = [
-        Country::new("Afghanistan", "Kabul", 652867.0),
-        Country::new("Angola", "Luanda", 1246700.0),
-        Country::new("Canada", "Ottawa", 9984670.0),
+        Country {
+            name: String::from("Afghanistan"),
+            capital: String::from("Kabul"),
+            area_km2: 652867.0,
+            currency: Some(String::from("Afghan afghani (AFN)")),
+        },
+        Country {
+            name: String::from("Angola"),
+            capital: String::from("Luanda"),
+            area_km2: 1246700.0,
+            currency: None,
+        },
+        Country {
+            name: String::from("Canada"),
+            capital: String::from("Ottawa"),
+            area_km2: 9984670.0,
+            currency: None,
+        },
     ];
 
     let table = Table::new(data);
diff --git a/tabled/examples/nested_table_2.rs b/tabled/examples/nested_table_2.rs
index 4301145..b62744b 100644
--- a/tabled/examples/nested_table_2.rs
+++ b/tabled/examples/nested_table_2.rs
@@ -25,9 +25,9 @@ fn main() {
 #[derive(Tabled)]
 struct Vendor {
     name: String,
-    #[tabled(display_with = "display_distribution")]
+    #[tabled(display = "display_distribution")]
     main_os: Dist,
-    #[tabled(display_with = "display_distribution")]
+    #[tabled(display = "display_distribution")]
     switch_os: Dist,
 }
 
diff --git a/tabled/examples/target_content.rs b/tabled/examples/target_content.rs
index 452b1ee..d919dbd 100644
--- a/tabled/examples/target_content.rs
+++ b/tabled/examples/target_content.rs
@@ -8,7 +8,7 @@ use tabled::{
 #[derive(Tabled)]
 struct Job {
     title: String,
-    #[tabled(display_with = "JobStatus::as_string")]
+    #[tabled(display = "JobStatus::as_string")]
     status: JobStatus,
 }
 
diff --git a/tabled/src/derive/display/mod.rs b/tabled/src/derive/display/mod.rs
new file mode 100644
index 0000000..36ea8f0
--- /dev/null
+++ b/tabled/src/derive/display/mod.rs
@@ -0,0 +1,142 @@
+//! Module contains a list of helpers for work with display.
+
+use core::fmt::Debug;
+
+/// A function which is usefull in conjuntion with
+/// `#[tabled(display)]` and `#[tabled(display)]`.
+///
+/// It can be used with any [`Option`] type.
+/// You must provide a second argument which represents a value be printed in case of [`None`].
+///
+/// # Example
+///
+/// ```
+/// use tabled::Tabled;
+/// use tabled::derive::display;
+/// use testing_table::assert_table;
+///
+/// #[derive(Tabled)]
+/// #[tabled(display(Option, "display::option", "Unknown"))]
+/// pub struct ZKP<'a> {
+///     application: &'a str,
+///     state: Option<&'a str>
+/// }
+///
+/// let data = vec![
+///     ZKP { application: "Decentralized Identity", state: Some("Proved") },
+///     ZKP { application: "Voting Systems", state: Some("Investigation") },
+///     ZKP { application: "Privacy-Preserving Transactions", state: None },
+/// ];
+///
+/// let table = tabled::Table::new(data);
+///
+/// assert_table!(
+///     table,
+///     "+---------------------------------+---------------+"
+///     "| application                     | state         |"
+///     "+---------------------------------+---------------+"
+///     "| Decentralized Identity          | Proved        |"
+///     "+---------------------------------+---------------+"
+///     "| Voting Systems                  | Investigation |"
+///     "+---------------------------------+---------------+"
+///     "| Privacy-Preserving Transactions | Unknown       |"
+///     "+---------------------------------+---------------+"
+/// );
+/// ```
+pub fn option<T>(value: &Option<T>, default: &str) -> String
+where
+    T: ToString,
+{
+    match value {
+        Some(val) => val.to_string(),
+        None => default.to_string(),
+    }
+}
+
+/// A function which is usefull in conjuntion with
+/// `#[tabled(display)]` and `#[tabled(display)]`.
+///
+/// It can be used with any type which implements a [`Debug`].
+/// So rather then [`std::fmt::Display`] usage we will be using a debug implementation.
+///
+/// ```
+/// use tabled::Tabled;
+/// use tabled::derive::display;
+/// use testing_table::assert_table;
+///
+/// #[derive(Tabled)]
+/// #[tabled(display(Option, "display::debug"))]
+/// pub struct ZKP<'a> {
+///     application: &'a str,
+///     state: Option<&'a str>
+/// }
+///
+/// let data = vec![
+///     ZKP { application: "Decentralized Identity", state: Some("Proved") },
+///     ZKP { application: "Voting Systems", state: Some("Investigation") },
+///     ZKP { application: "Privacy-Preserving Transactions", state: None },
+/// ];
+///
+/// let table = tabled::Table::new(data);
+///
+/// assert_table!(
+///     table,
+///     r#"+---------------------------------+-----------------------+"#
+///     r#"| application                     | state                 |"#
+///     r#"+---------------------------------+-----------------------+"#
+///     r#"| Decentralized Identity          | Some("Proved")        |"#
+///     r#"+---------------------------------+-----------------------+"#
+///     r#"| Voting Systems                  | Some("Investigation") |"#
+///     r#"+---------------------------------+-----------------------+"#
+///     r#"| Privacy-Preserving Transactions | None                  |"#
+///     r#"+---------------------------------+-----------------------+"#
+/// );
+/// ```
+pub fn debug<T>(value: &T) -> String
+where
+    T: Debug,
+{
+    format!("{:?}", value)
+}
+
+/// A function which is usefull in conjuntion with
+/// `#[tabled(display)]` and `#[tabled(display)]`.
+///
+/// It just returns an empty string.
+///
+/// ```
+/// use tabled::Tabled;
+/// use tabled::derive::display;
+/// use testing_table::assert_table;
+///
+/// #[derive(Tabled)]
+/// pub struct ZKP<'a> {
+///     application: &'a str,
+///     #[tabled(display = "display::empty")]
+///     state: Option<&'a str>
+/// }
+///
+/// let data = vec![
+///     ZKP { application: "Decentralized Identity", state: Some("Proved") },
+///     ZKP { application: "Voting Systems", state: Some("Investigation") },
+///     ZKP { application: "Privacy-Preserving Transactions", state: None },
+/// ];
+///
+/// let table = tabled::Table::new(data);
+///
+/// assert_table!(
+///     table,
+///     r#"+---------------------------------+-------+"#
+///     r#"| application                     | state |"#
+///     r#"+---------------------------------+-------+"#
+///     r#"| Decentralized Identity          |       |"#
+///     r#"+---------------------------------+-------+"#
+///     r#"| Voting Systems                  |       |"#
+///     r#"+---------------------------------+-------+"#
+///     r#"| Privacy-Preserving Transactions |       |"#
+///     r#"+---------------------------------+-------+"#
+/// );
+/// ```
+pub fn empty<T>(_value: &T) -> String {
+    String::new()
+}
diff --git a/tabled/src/derive.rs b/tabled/src/derive/mod.rs
similarity index 91%
rename from tabled/src/derive.rs
rename to tabled/src/derive/mod.rs
index 06114a6..3209a4b 100644
--- a/tabled/src/derive.rs
+++ b/tabled/src/derive/mod.rs
@@ -1,3 +1,7 @@
+//! Module contains a list of helpers for work with derive.
+
+pub mod display;
+
 /// A derive macro to implement a [`Tabled`] trait.
 ///
 /// The macro available only when `derive` feature in turned on (and it is by default).
@@ -76,7 +80,7 @@
 /// - Implement `Tabled` trait manually for a type.
 /// - Wrap `Option` to something like `DisplayedOption<T>(Option<T>)` and implement a Display trait for it.
 ///
-/// Alternatively, you can use the `#[tabled(display_with = "func")]` attribute for the field to specify a display function.
+/// Alternatively, you can use the `#[tabled(display = "func")]` attribute for the field to specify a display function.
 ///
 /// ```rust,no_run
 /// use tabled::Tabled;
@@ -84,7 +88,7 @@
 /// #[derive(Tabled)]
 /// pub struct MyRecord {
 ///     pub id: i64,
-///     #[tabled(display_with = "display_option")]
+///     #[tabled(display = "display_option")]
 ///     pub valid: Option<bool>
 /// }
 ///
@@ -97,7 +101,7 @@
 /// ```
 ///
 /// It's also possible to change function argument to be `&self`,
-/// using `#[tabled(display_with("some_function", self))]`
+/// using `#[tabled(display("some_function", self))]`
 ///
 /// ```rust,no_run
 /// use tabled::Tabled;
@@ -105,13 +109,13 @@
 /// #[derive(Tabled)]
 /// pub struct MyRecord {
 ///     pub id: i64,
-///     #[tabled(display_with("Self::display_valid", self))]
+///     #[tabled(display("Self::display_valid", self))]
 ///     pub valid: Option<bool>
 /// }
 ///
 /// impl MyRecord {
-///     fn display_valid(&self) -> String {
-///         match self.valid {
+///     fn display_valid(_: &Option<bool>, s: &Self) -> String {
+///         match s.valid {
 ///             Some(s) => format!("is valid thing = {}", s),
 ///             None => format!("is not valid"),
 ///         }
diff --git a/tabled/src/lib.rs b/tabled/src/lib.rs
index 36c79ac..a3af2f0 100644
--- a/tabled/src/lib.rs
+++ b/tabled/src/lib.rs
@@ -276,18 +276,18 @@
 )]
 #![allow(clippy::uninlined_format_args)]
 
-mod util;
-
-#[cfg(feature = "derive")]
-mod derive;
 #[cfg(feature = "macros")]
 mod macros;
 #[cfg(feature = "std")]
 mod tabled;
+mod util;
 
 #[cfg(feature = "std")]
 #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
 pub mod builder;
+#[cfg(feature = "derive")]
+#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
+pub mod derive;
 pub mod grid;
 pub mod iter;
 pub mod settings;
diff --git a/tabled/src/settings/span/mod.rs b/tabled/src/settings/span/mod.rs
index 59e8bd5..c3f568e 100644
--- a/tabled/src/settings/span/mod.rs
+++ b/tabled/src/settings/span/mod.rs
@@ -55,7 +55,7 @@ impl Span {
     ///     * == 1 - which is a default span so can be used for removal of spans
     ///     * > 1 - which means to spread a cell by given number of columns right
     ///     * < 0 - which means to spread a cell by given number of columns left
-    /// 
+    ///
     /// # Example
     ///
     /// ```
@@ -93,7 +93,7 @@ impl Span {
     ///     * == 1 - which is a default span so can be used for removal of spans
     ///     * > 1 - which means to spread a cell by given number of rows bottom
     ///     * < 0 - which means to spread a cell by given number of rows top
-    /// 
+    ///
     /// # Example
     ///
     /// ```
diff --git a/tabled/src/util/string.rs b/tabled/src/util/string.rs
index d857020..ff7443c 100644
--- a/tabled/src/util/string.rs
+++ b/tabled/src/util/string.rs
@@ -233,42 +233,36 @@ mod tests {
     fn strip_color_test() {
         let numbers = "\u{1b}[31;100m123456\u{1b}[39m\u{1b}[49m";
 
-        assert_eq!(cut_str(&numbers, 0), "\u{1b}[31;100m\u{1b}[39m\u{1b}[49m");
+        assert_eq!(cut_str(numbers, 0), "\u{1b}[31;100m\u{1b}[39m\u{1b}[49m");
+        assert_eq!(cut_str(numbers, 3), "\u{1b}[31;100m123\u{1b}[39m\u{1b}[49m");
         assert_eq!(
-            cut_str(&numbers, 3),
-            "\u{1b}[31;100m123\u{1b}[39m\u{1b}[49m"
-        );
-        assert_eq!(
-            cut_str(&numbers, 10),
+            cut_str(numbers, 10),
             "\u{1b}[31;100m123456\u{1b}[39m\u{1b}[49m"
         );
 
         let emojies = "\u{1b}[31;100m😳😳😳😳😳\u{1b}[39m\u{1b}[49m";
 
-        assert_eq!(cut_str(&emojies, 0), "\u{1b}[31;100m\u{1b}[39m\u{1b}[49m");
-        assert_eq!(
-            cut_str(&emojies, 3),
-            "\u{1b}[31;100m😳\u{1b}[39m\u{1b}[49m�"
-        );
+        assert_eq!(cut_str(emojies, 0), "\u{1b}[31;100m\u{1b}[39m\u{1b}[49m");
+        assert_eq!(cut_str(emojies, 3), "\u{1b}[31;100m😳\u{1b}[39m\u{1b}[49m�");
         assert_eq!(
-            cut_str(&emojies, 4),
+            cut_str(emojies, 4),
             "\u{1b}[31;100m😳😳\u{1b}[39m\u{1b}[49m"
         );
         assert_eq!(
-            cut_str(&emojies, 20),
+            cut_str(emojies, 20),
             "\u{1b}[31;100m😳😳😳😳😳\u{1b}[39m\u{1b}[49m"
         );
 
         let emojies = "\u{1b}[31;100m🏳️🏳️\u{1b}[39m\u{1b}[49m";
 
-        assert_eq!(cut_str(&emojies, 0), "\u{1b}[31;100m\u{1b}[39m\u{1b}[49m");
-        assert_eq!(cut_str(&emojies, 1), "\u{1b}[31;100m🏳\u{1b}[39m\u{1b}[49m");
+        assert_eq!(cut_str(emojies, 0), "\u{1b}[31;100m\u{1b}[39m\u{1b}[49m");
+        assert_eq!(cut_str(emojies, 1), "\u{1b}[31;100m🏳\u{1b}[39m\u{1b}[49m");
         assert_eq!(
-            cut_str(&emojies, 2),
+            cut_str(emojies, 2),
             "\u{1b}[31;100m🏳\u{fe0f}🏳\u{1b}[39m\u{1b}[49m"
         );
         assert_eq!(
-            get_line_width(&emojies),
+            get_line_width(emojies),
             get_line_width("\u{1b}[31;100m🏳\u{fe0f}🏳\u{fe0f}\u{1b}[39m\u{1b}[49m")
         );
     }
@@ -278,7 +272,7 @@ mod tests {
     fn test_color_strip() {
         let s = "\u{1b}[5;33;48;2;12;200;100mCollored string\u{1b}[0m";
         assert_eq!(
-            cut_str(&s, 1),
+            cut_str(s, 1),
             "\u{1b}[5;33;48;2;12;200;100mC\u{1b}[25m\u{1b}[39m\u{1b}[49m"
         )
     }
diff --git a/tabled_derive/Cargo.toml b/tabled_derive/Cargo.toml
index 2c45bdb..9e81837 100644
--- a/tabled_derive/Cargo.toml
+++ b/tabled_derive/Cargo.toml
@@ -11,7 +11,7 @@ license = "MIT"
 proc-macro = true
 
 [dependencies]
-syn = { version = "2", features = ["full", "visit-mut"] }
+syn = { version = "2", features = ["full", "visit-mut", "extra-traits"] }
 quote = "1"
 proc-macro2 = "1"
 heck = "0.5"
diff --git a/tabled_derive/src/attributes/type_attr.rs b/tabled_derive/src/attributes/type_attr.rs
index 53ccbc9..2987aca 100644
--- a/tabled_derive/src/attributes/type_attr.rs
+++ b/tabled_derive/src/attributes/type_attr.rs
@@ -1,17 +1,19 @@
-use syn::Attribute;
+use syn::{Attribute, TypePath};
 
 use crate::{
+    attributes::FormatArg,
     casing_style::CasingStyle,
     error::Error,
     parse::type_attr::{parse_type_attributes, TypeAttr, TypeAttrKind},
 };
 
-#[derive(Debug, Default)]
+#[derive(Default)]
 pub struct TypeAttributes {
     pub rename_all: Option<CasingStyle>,
     pub inline: bool,
     pub inline_value: Option<String>,
     pub crate_name: Option<String>,
+    pub display_types: Vec<(TypePath, String, Vec<FormatArg>)>,
 }
 
 impl TypeAttributes {
@@ -53,6 +55,10 @@ impl TypeAttributes {
             TypeAttrKind::RenameAll(lit) => {
                 self.rename_all = Some(CasingStyle::from_lit(&lit)?);
             }
+            TypeAttrKind::DisplayType(type_name, func, args) => {
+                let args = args.into_iter().map(FormatArg::new).collect();
+                self.display_types.push((type_name, func.value(), args));
+            }
         }
 
         Ok(())
diff --git a/tabled_derive/src/lib.rs b/tabled_derive/src/lib.rs
index e8f90a6..1977334 100644
--- a/tabled_derive/src/lib.rs
+++ b/tabled_derive/src/lib.rs
@@ -18,7 +18,7 @@ use std::{collections::HashMap, str};
 use syn::visit_mut::VisitMut;
 use syn::{
     parse_macro_input, token, Data, DataEnum, DataStruct, DeriveInput, ExprPath, Field, Fields,
-    Ident, Index, PathSegment, Type, Variant,
+    Ident, Index, PathSegment, Type, TypePath, Variant,
 };
 
 use crate::attributes::{FieldAttributes, TypeAttributes};
@@ -223,7 +223,14 @@ fn info_from_fields(
         headers.push(header);
 
         let field_name_result = field_name(i, field);
-        let value = get_field_fields(&field_name_result, &attributes, fields, field_name);
+        let value = get_field_fields(
+            &field_name_result,
+            &field.ty,
+            &attributes,
+            fields,
+            field_name,
+            attrs,
+        );
         values.push(value);
     }
 
@@ -410,43 +417,24 @@ fn info_from_variant(
     let variant_name = variant_name(variant, attr);
     let value = if let Some(func) = &attr.display_with {
         let args = match &attr.display_with_args {
-            None => None,
-            Some(args) => match args.is_empty() {
-                true => None,
-                false => {
-                    let args = args
-                        .iter()
-                        .map(|arg| fnarg_tokens(arg, &Fields::Unit, struct_field_name))
-                        .collect::<Vec<_>>();
-                    Some(quote!( #(#args,)* ))
-                }
-            },
+            Some(args) => {
+                args_to_tokens_with(&Fields::Unit, &quote!(self), struct_field_name, args)
+            }
+            None => quote!(&self),
         };
 
-        let result = match args {
-            Some(args) => use_function(&args, func),
-            None => use_function_no_args(func),
-        };
+        let result = use_function(&args, func);
 
-        quote! { ::std::borrow::Cow::from(format!("{}", #result)) }
-    } else if let Some(custom_format) = &attr.format {
-        let args = match &attr.format_with_args {
-            None => None,
-            Some(args) => match args.is_empty() {
-                true => None,
-                false => {
-                    let args = args
-                        .iter()
-                        .map(|arg| fnarg_tokens(arg, &Fields::Unit, struct_field_name))
-                        .collect::<Vec<_>>();
-                    Some(quote!( #(#args,)* ))
-                }
-            },
-        };
+        quote! { ::std::borrow::Cow::from(#result) }
+    } else if let Some(fmt) = &attr.format {
+        let args = attr
+            .format_with_args
+            .as_ref()
+            .and_then(|args| args_to_tokens(&Fields::Unit, struct_field_name, args));
 
         let call = match args {
-            Some(args) => quote!(format!(#custom_format, #args)),
-            None => quote!(format!(#custom_format)),
+            Some(args) => use_format(fmt, &args),
+            None => use_format_no_args(fmt),
         };
 
         quote! { ::std::borrow::Cow::from(#call) }
@@ -490,9 +478,11 @@ fn get_type_headers(
 
 fn get_field_fields(
     field: &TokenStream,
+    field_type: &Type,
     attr: &FieldAttributes,
     fields: &Fields,
     field_name: FieldNameFn,
+    type_attrs: &TypeAttributes,
 ) -> TokenStream {
     if attr.inline {
         return quote! { #field.fields() };
@@ -500,85 +490,137 @@ fn get_field_fields(
 
     if let Some(func) = &attr.display_with {
         let args = match &attr.display_with_args {
-            None => Some(quote!(&#field)),
-            Some(args) => match args.is_empty() {
-                true => None,
-                false => {
-                    let args = args
-                        .iter()
-                        .map(|arg| fnarg_tokens(arg, fields, field_name))
-                        .collect::<Vec<_>>();
-                    Some(quote!( #(#args,)* ))
-                }
-            },
+            Some(args) => args_to_tokens_with(fields, field, field_name, args),
+            None => quote!(&#field),
         };
 
-        let result = match args {
-            Some(args) => use_function(&args, func),
-            None => use_function_no_args(func),
-        };
+        let result = use_function(&args, func);
 
-        return quote!(vec![::std::borrow::Cow::from(format!("{}", #result))]);
-    } else if let Some(custom_format) = &attr.format {
-        let args = match &attr.format_with_args {
-            None => None,
-            Some(args) => match args.is_empty() {
-                true => None,
-                false => {
-                    let args = args
-                        .iter()
-                        .map(|arg| fnarg_tokens(arg, fields, field_name))
-                        .collect::<Vec<_>>();
-                    Some(quote!( #(#args,)* ))
-                }
-            },
-        };
+        return quote!(vec![::std::borrow::Cow::from(#result)]);
+    } else if let Some(fmt) = &attr.format {
+        let args = attr
+            .format_with_args
+            .as_ref()
+            .and_then(|args| args_to_tokens(fields, field_name, args));
 
         let call = match args {
-            Some(args) => use_format(&args, custom_format),
-            None => use_format_no_args(custom_format, field),
+            Some(args) => use_format(fmt, &args),
+            None => use_format_with_one_arg(fmt, field),
         };
 
         return quote!(vec![::std::borrow::Cow::Owned(#call)]);
     }
 
+    if let Some(i) = find_display_type(field_type, &type_attrs.display_types) {
+        let (_, func, args) = &type_attrs.display_types[i];
+        let args = args_to_tokens_with(fields, field, field_name, args);
+        let func = use_function(&args, func);
+
+        return quote!(vec![::std::borrow::Cow::from(#func)]);
+    }
+
     quote!(vec![::std::borrow::Cow::Owned(format!("{}", #field))])
 }
 
-fn use_function(args: &TokenStream, function: &str) -> TokenStream {
-    let path: syn::Result<syn::ExprPath> = syn::parse_str(function);
-    match path {
-        Ok(path) => {
-            quote! { #path(#args) }
+fn args_to_tokens(
+    fields: &Fields,
+    field_name: fn(usize, &Field) -> TokenStream,
+    args: &[FormatArg],
+) -> Option<TokenStream> {
+    if args.is_empty() {
+        return None;
+    }
+
+    let args = args
+        .iter()
+        .map(|arg| fnarg_tokens(arg, fields, field_name))
+        .collect::<Vec<_>>();
+    Some(quote!( #(#args,)* ))
+}
+
+fn args_to_tokens_with(
+    fields: &Fields,
+    field: &TokenStream,
+    field_name: fn(usize, &Field) -> TokenStream,
+    args: &[FormatArg],
+) -> TokenStream {
+    if args.is_empty() {
+        return quote!(&#field);
+    }
+
+    let mut out = vec![quote!(&#field)];
+    for arg in args {
+        let arg = fnarg_tokens(arg, fields, field_name);
+        out.push(arg);
+    }
+
+    quote!( #(#out,)* )
+}
+
+fn find_display_type(ty: &Type, types: &[(TypePath, String, Vec<FormatArg>)]) -> Option<usize> {
+    let p: &TypePath = match ty {
+        Type::Path(path) => path,
+        _ => return None,
+    };
+
+    // NOTICE:
+    // We do iteration in a back order to satisfy a later set argument first.
+    //
+    // TODO: Maybe we shall change the data structure for it rather then doing a reverse iteration?
+    // I am just saying it's dirty a little.
+    let args = types.iter().enumerate().rev();
+    for (i, (arg, _, _)) in args {
+        if arg.path == p.path {
+            return Some(i);
         }
-        Err(_) => {
-            let function = Ident::new(function, proc_macro2::Span::call_site());
-            quote! { #function(#args) }
+
+        // NOTICE:
+        // There's a specical case where we wanna match a type without a generic,
+        // e.g. 'Option' with which we wanna match all 'Option's.
+        //
+        // Because in the scope we can only have 1 type name, it's considered to be good,
+        // and nothing must be broken.
+        let arg_segment = arg.path.segments.last();
+        let type_segment = p.path.segments.last();
+        if let Some(arg) = arg_segment {
+            if arg.arguments.is_empty() {
+                if let Some(p) = type_segment {
+                    if p.ident == arg.ident {
+                        return Some(i);
+                    }
+                }
+            }
         }
     }
+
+    None
 }
 
-fn use_function_no_args(function: &str) -> TokenStream {
+fn use_function(args: &TokenStream, function: &str) -> TokenStream {
     let path: syn::Result<syn::ExprPath> = syn::parse_str(function);
     match path {
         Ok(path) => {
-            quote! { #path() }
+            quote! { #path(#args) }
         }
         Err(_) => {
             let function = Ident::new(function, proc_macro2::Span::call_site());
-            quote! { #function() }
+            quote! { #function(#args) }
         }
     }
 }
 
-fn use_format(args: &TokenStream, custom_format: &str) -> TokenStream {
+fn use_format(custom_format: &str, args: &TokenStream) -> TokenStream {
     quote! { format!(#custom_format, #args) }
 }
 
-fn use_format_no_args(custom_format: &str, field: &TokenStream) -> TokenStream {
+fn use_format_with_one_arg(custom_format: &str, field: &TokenStream) -> TokenStream {
     quote! { format!(#custom_format, #field) }
 }
 
+fn use_format_no_args(custom_format: &str) -> TokenStream {
+    quote! { format!(#custom_format) }
+}
+
 fn struct_field_name(index: usize, field: &Field) -> TokenStream {
     let f = field.ident.as_ref().map_or_else(
         || Index::from(index).to_token_stream(),
@@ -783,7 +825,7 @@ impl syn::visit_mut::VisitMut for ExprSelfReplace<'_> {
                 // ```
                 // some_macro! {
                 //     struct Something {
-                //         #[tabled(display_with("_", format!("", self.f1)))]
+                //         #[tabled(display("_", format!("", self.f1)))]
                 //         field: Option<sstr>,
                 //         f1: usize,
                 //     }
diff --git a/tabled_derive/src/parse/field_attr.rs b/tabled_derive/src/parse/field_attr.rs
index 1ef5e0b..f21d534 100644
--- a/tabled_derive/src/parse/field_attr.rs
+++ b/tabled_derive/src/parse/field_attr.rs
@@ -51,9 +51,7 @@ impl Parse for FieldAttr {
                 match name_str.as_str() {
                     "rename" => return Ok(Self::new(Rename(lit))),
                     "rename_all" => return Ok(Self::new(RenameAll(lit))),
-                    "display_with" => {
-                        return Ok(Self::new(DisplayWith(lit, None, Punctuated::new())))
-                    }
+                    "display" => return Ok(Self::new(DisplayWith(lit, None, Punctuated::new()))),
                     "format" => return Ok(Self::new(FormatWith(lit, None, Punctuated::new()))),
                     _ => {}
                 }
@@ -91,7 +89,7 @@ impl Parse for FieldAttr {
                 let lit = nested.parse::<LitStr>()?;
 
                 match name_str.as_str() {
-                    "format" | "display_with" => {
+                    "format" | "display" => {
                         let mut args = Punctuated::new();
                         let mut comma = None;
                         if nested.peek(Token![,]) {
diff --git a/tabled_derive/src/parse/type_attr.rs b/tabled_derive/src/parse/type_attr.rs
index 6abe222..1004b5e 100644
--- a/tabled_derive/src/parse/type_attr.rs
+++ b/tabled_derive/src/parse/type_attr.rs
@@ -1,7 +1,7 @@
 use proc_macro2::{Ident, Span};
 use syn::{
     parenthesized, parse::Parse, punctuated::Punctuated, spanned::Spanned, token, Attribute,
-    LitBool, LitStr, Token,
+    LitBool, LitStr, Token, TypePath,
 };
 
 pub fn parse_type_attributes(
@@ -29,6 +29,7 @@ pub enum TypeAttrKind {
     Inline(LitBool, Option<LitStr>),
     RenameAll(LitStr),
     Crate(LitStr),
+    DisplayType(TypePath, LitStr, Punctuated<syn::Expr, Token!(,)>),
 }
 
 impl Parse for TypeAttr {
@@ -52,7 +53,7 @@ impl Parse for TypeAttr {
             if input.peek(LitStr) {
                 let lit = input.parse::<LitStr>()?;
 
-                if let "rename_all" = name_str.as_str() {
+                if name_str.as_str() == "rename_all" {
                     return Ok(Self::new(RenameAll(lit)));
                 }
             }
@@ -60,7 +61,7 @@ impl Parse for TypeAttr {
             if input.peek(LitBool) {
                 let lit = input.parse::<LitBool>()?;
 
-                if let "inline" = name_str.as_str() {
+                if name_str.as_str() == "inline" {
                     return Ok(Self::new(Inline(lit, None)));
                 }
             }
@@ -78,7 +79,7 @@ impl Parse for TypeAttr {
             if nested.peek(LitStr) {
                 let lit = nested.parse::<LitStr>()?;
 
-                if let "inline" = name_str.as_str() {
+                if name_str.as_str() == "inline" {
                     return Ok(Self::new(Inline(
                         LitBool::new(true, Span::call_site()),
                         Some(lit),
@@ -86,13 +87,35 @@ impl Parse for TypeAttr {
                 }
             }
 
+            if name_str.as_str() == "display" {
+                let path = nested.parse::<TypePath>()?;
+                let _comma = nested.parse::<Token![,]>()?;
+                let lit = nested.parse::<LitStr>()?;
+
+                let mut args: Punctuated<syn::Expr, token::Comma> = Punctuated::new();
+                if nested.peek(Token![,]) {
+                    _ = nested.parse::<Token![,]>()?;
+                    while !nested.is_empty() {
+                        let val = nested.parse()?;
+                        args.push_value(val);
+                        if nested.is_empty() {
+                            break;
+                        }
+                        let punct = nested.parse()?;
+                        args.push_punct(punct);
+                    }
+                }
+
+                return Ok(Self::new(DisplayType(path, lit, args)));
+            }
+
             return Err(syn::Error::new(
                 _paren.span.span(),
                 "expected a `string literal` in parenthesis",
             ));
         }
 
-        if let "inline" = name_str.as_str() {
+        if name_str.as_str() == "inline" {
             return Ok(Self::new(Inline(
                 LitBool::new(true, Span::call_site()),
                 None,


Test patch:
diff --git a/tabled/tests/derive/derive_test.rs b/tabled/tests/derive/derive_test.rs
index ece9778..f5bfa57 100644
--- a/tabled/tests/derive/derive_test.rs
+++ b/tabled/tests/derive/derive_test.rs
@@ -1,7 +1,10 @@
 #![cfg(feature = "derive")]
 #![cfg(feature = "std")]
 
-use tabled::Tabled;
+use std::fmt::Display;
+
+use tabled::{Table, Tabled};
+use testing_table::test_table;
 
 // https://users.rust-lang.org/t/create-a-struct-from-macro-rules/19829
 macro_rules! test_tuple {
@@ -155,7 +158,7 @@ mod tuple {
 
     test_tuple!(
         display_option,
-        { { u8 #[tabled(display_with = "display_option")] Option<sstr> } },
+        { { u8 #[tabled(display = "display_option")] Option<sstr> } },
         { 0 Some("v2") },
         { ["0", "1"], ["0", "some v2"] },
         pre: {
@@ -170,11 +173,11 @@ mod tuple {
 
     test_tuple!(
         display_option_args,
-        { { u8 #[tabled(display_with("display_option", 1, "234"))] Option<sstr> } },
+        { { u8 #[tabled(display("display_option", 1, "234"))] Option<sstr> } },
         { 0 Some("v2") },
         { ["0", "1"], ["0", "some 1 234"] },
         pre: {
-            fn display_option(val: usize, text: &str) -> String {
+            fn display_option(_opt: &Option<sstr>, val: usize, text: &str) -> String {
                 format!("some {val} {text}")
             }
         }
@@ -182,7 +185,7 @@ mod tuple {
 
     test_tuple!(
         display_option_self,
-        { { u8 #[tabled(display_with = "Self::display_option")] Option<sstr> } },
+        { { u8 #[tabled(display = "Self::display_option")] Option<sstr> } },
         { 0 Some("v2") },
         { ["0", "1"], ["0", "some v2"] },
         pre: {
@@ -199,12 +202,12 @@ mod tuple {
 
     test_tuple!(
         display_option_self_2,
-        { { u8 #[tabled(display_with("Self::display_option", self))] Option<sstr> } },
+        { { u8 #[tabled(display("Self::display_option", self))] Option<sstr> } },
         { 0 Some("v2") },
         { ["0", "1"], ["0", "some v2"] },
         pre: {
             impl TestType {
-                fn display_option(o: &TestType) -> String {
+                fn display_option(_opt: &Option<sstr>, o: &TestType) -> String {
                     match o.1 {
                         Some(s) => format!("some {s}"),
                         None => "none".to_string(),
@@ -216,11 +219,11 @@ mod tuple {
 
     test_tuple!(
         display_option_self_3,
-        { { u8 #[tabled(display_with("display_option", self))] Option<sstr> } },
+        { { u8 #[tabled(display("display_option", self))] Option<sstr> } },
         { 0 Some("v2") },
         { ["0", "1"], ["0", "some v2"] },
         pre: {
-            fn display_option(o: &TestType) -> String {
+            fn display_option(_opt: &Option<sstr>, o: &TestType) -> String {
                 match o.1 {
                     Some(s) => format!("some {s}"),
                     None => "none".to_string(),
@@ -231,11 +234,11 @@ mod tuple {
 
     test_tuple!(
         display_option_self_4,
-        { { u8 #[tabled(display_with("display_option", self.0, self.0))] Option<sstr> } },
+        { { u8 #[tabled(display("display_option", self.0, self.0))] Option<sstr> } },
         { 0 Some("v2") },
         { ["0", "1"], ["0", "some 0.0"] },
         pre: {
-            fn display_option(o1: u8, o2: u8) -> String {
+            fn display_option(_opt: &Option<sstr>, o1: u8, o2: u8) -> String {
                 format!("some {o1}.{o2}")
             }
         }
@@ -332,7 +335,7 @@ mod enum_ {
             {
                 #[tabled(inline("backend::"))]
                 Backend {
-                    #[tabled(display_with = "display", rename = "name")]
+                    #[tabled(display = "display", rename = "name")]
                     value: sstr
                 }
                 Frontend
@@ -357,14 +360,14 @@ mod enum_ {
                 #[tabled(inline("backend::"))]
                 Backend {
                     #[tabled()]
-                    #[tabled(display_with("display", self), rename = "name")]
+                    #[tabled(display("display", self), rename = "name")]
                     value: sstr
                 }
                 Frontend
             }
         },
         {
-            fn display<T>(_: &T) -> String {
+            fn display<T>(_opt: &sstr, _: &T) -> String {
                 "asd".to_string()
             }
         },
@@ -381,7 +384,7 @@ mod enum_ {
             {
                 #[tabled(inline)]
                 A(
-                    #[tabled(display_with = "format::<4>")]
+                    #[tabled(display = "format::<4>")]
                     sstr
                 )
                 B
@@ -405,7 +408,7 @@ mod enum_ {
             {
                 #[tabled(inline)]
                 A(
-                    #[tabled(display_with("Self::format::<4>", self))]
+                    #[tabled(display("Self::format::<4>", self))]
                     sstr
                 )
                 B
@@ -413,7 +416,7 @@ mod enum_ {
         },
         {
             impl TestType {
-                fn format<const ID: usize>(&self) -> String {
+                fn format<const ID: usize>(_opt: &sstr, _: &Self) -> String {
                     ID.to_string()
                 }
             }
@@ -431,7 +434,7 @@ mod enum_ {
             {
                 #[tabled(inline)]
                 A(
-                    #[tabled(display_with("Self::format::<4>", self, self.0))]
+                    #[tabled(display("Self::format::<4>", self, self.0))]
                     sstr
                 )
                 B
@@ -439,7 +442,7 @@ mod enum_ {
         },
         {
             impl TestType {
-                fn format<const ID: usize>(&self, _: sstr) -> String {
+                fn format<const ID: usize>(_opt: &sstr, _: &Self, _: sstr) -> String {
                     ID.to_string()
                 }
             }
@@ -457,7 +460,7 @@ mod enum_ {
             {
                 #[tabled(inline)]
                 A{
-                    #[tabled(display_with("Self::format::<4>", self, self.asd))]
+                    #[tabled(display("Self::format::<4>", self, self.asd))]
                     asd: sstr
                 }
                 B
@@ -465,7 +468,7 @@ mod enum_ {
         },
         {
             impl TestType {
-                fn format<const ID: usize>(&self, _: sstr) -> String {
+                fn format<const ID: usize>(_opt: &sstr, _: &Self, _: sstr) -> String {
                     ID.to_string()
                 }
             }
@@ -709,36 +712,27 @@ mod enum_ {
         enum_display_with_variant,
         {
             {
-                #[tabled(display_with = "display_variant1")]
+                #[tabled(display = "display_variant1")]
                 AbsdEgh { a: u8, b: i32 }
-                #[tabled(display_with = "display_variant2::<200>")]
+                #[tabled(display = "display_variant2::<200>")]
                 B(String)
-                #[tabled(display_with = "some::bar::display_variant1")]
                 K
             }
         },
         {
-            fn display_variant1() -> &'static str {
+            fn display_variant1(_: &TestType) -> &'static str {
                 "Hello World"
             }
 
-            fn display_variant2<const VAL: usize>() -> String {
+            fn display_variant2<const VAL: usize>(_: &TestType) -> String {
                 format!("asd {VAL}")
             }
-
-            pub mod some {
-                pub mod bar {
-                    pub fn display_variant1() -> &'static str {
-                        "Hello World 123"
-                    }
-                }
-            }
         },
         { ["AbsdEgh", "B", "K"] },
         {
             AbsdEgh { a: 0, b: 0 }  => ["Hello World", "", ""],
             B(String::new()) => ["", "asd 200", ""],
-            K => ["", "", "Hello World 123"],
+            K => ["", "", "+"],
         }
     );
 
@@ -746,36 +740,27 @@ mod enum_ {
         enum_display_with_self_variant,
         {
             {
-                #[tabled(display_with("display_variant1", self))]
+                #[tabled(display("display_variant1", self))]
                 AbsdEgh { a: u8, b: i32 }
-                #[tabled(display_with("display_variant2::<200, _>", self))]
+                #[tabled(display("display_variant2::<200, _>", self))]
                 B(String)
-                #[tabled(display_with("some::bar::display_variant1", self))]
                 K
             }
         },
         {
-            fn display_variant1<D>(_: &D) -> &'static str {
+            fn display_variant1<D>(_: &TestType, _: &D) -> &'static str {
                 "Hello World"
             }
 
-            fn display_variant2<const VAL: usize, D>(_: &D) -> String {
+            fn display_variant2<const VAL: usize, D>(_: &TestType, _: &D) -> String {
                 format!("asd {VAL}")
             }
-
-            pub mod some {
-                pub mod bar {
-                    pub fn display_variant1<D>(_: &D) -> &'static str {
-                        "Hello World 123"
-                    }
-                }
-            }
         },
         { ["AbsdEgh", "B", "K"] },
         {
             AbsdEgh { a: 0, b: 0 }  => ["Hello World", "", ""],
             B(String::new()) => ["", "asd 200", ""],
-            K => ["", "", "Hello World 123"],
+            K => ["", "", "+"],
         }
     );
 
@@ -783,20 +768,20 @@ mod enum_ {
         enum_display_with_arguments,
         {
             {
-                #[tabled(display_with("display1", 1, 2, self))]
+                #[tabled(display("display1", 1, 2, self))]
                 AbsdEgh { a: u8, b: i32 }
-                #[tabled(display_with("display2::<200>", "Hello World"))]
+                #[tabled(display("display2::<200>", "Hello World"))]
                 B(String)
-                #[tabled(display_with("display1", 100, 200, self))]
+                #[tabled(display("display1", 100, 200, self))]
                 K
             }
         },
         {
-            fn display1<D>(val: usize, val2: usize, _: &D) -> String {
+            fn display1<D>(_: &TestType, val: usize, val2: usize, _: &D) -> String {
                 format!("{val} {val2}")
             }
 
-            fn display2<const VAL: usize>(val: &str) -> String {
+            fn display2<const VAL: usize>(_: &TestType, val: &str) -> String {
                 format!("asd {VAL} {val}")
             }
         },
@@ -1039,7 +1024,7 @@ mod structure {
         {
             {
                 f1: u8,
-                #[tabled(display_with = "display_option")]
+                #[tabled(display = "display_option")]
                 f2: Option<sstr>,
             }
         }
@@ -1059,12 +1044,12 @@ mod structure {
         {
             {
                 f1: u8,
-                #[tabled(display_with("display_option", 1, 2, 3))]
+                #[tabled(display("display_option", 1, 2, 3))]
                 f2: Option<sstr>,
             }
         }
         {
-            fn display_option(v1: usize, v2: usize, v3: usize) -> String {
+            fn display_option(_opt: &Option<sstr>, v1: usize, v2: usize, v3: usize) -> String {
                 format!("{v1} {v2} {v3}")
             }
         }
@@ -1076,12 +1061,12 @@ mod structure {
         {
             {
                 f1: u8,
-                #[tabled(display_with("display_option", &self.f1, 2, 3))]
+                #[tabled(display("display_option", &self.f1, 2, 3))]
                 f2: Option<sstr>,
             }
         }
         {
-            fn display_option(v1: &u8, v2: usize, v3: usize) -> String {
+            fn display_option(_opt: &Option<sstr>, v1: &u8, v2: usize, v3: usize) -> String {
                 format!("{v1} {v2} {v3}")
             }
         }
@@ -1093,7 +1078,7 @@ mod structure {
         {
             {
                 f1: u8,
-                #[tabled(display_with = "Self::display_option")]
+                #[tabled(display = "Self::display_option")]
                 f2: Option<sstr>,
             }
         }
@@ -1115,13 +1100,13 @@ mod structure {
         {
             {
                 f1: u8,
-                #[tabled(display_with("Self::display_option", self))]
+                #[tabled(display("Self::display_option", self))]
                 f2: Option<sstr>,
             }
         }
         {
             impl TestType {
-                fn display_option(o: &TestType) -> String {
+                fn display_option(_opt: &Option<sstr>, o: &TestType) -> String {
                     match o.f2 {
                         Some(s) => format!("some {s}"),
                         None => "none".to_string(),
@@ -1137,14 +1122,14 @@ mod structure {
         {
             {
                 f1: u8,
-                #[tabled(display_with("Self::display_option", self))]
+                #[tabled(display("Self::display_option", self))]
                 f2: Option<sstr>,
             }
         }
         {
             impl TestType {
-                fn display_option(&self) -> String {
-                    match self.f2 {
+                fn display_option(_opt: &Option<sstr>, s: &Self) -> String {
+                    match s.f2 {
                         Some(s) => format!("some {s}"),
                         None => "none".to_string(),
                     }
@@ -1159,12 +1144,12 @@ mod structure {
         {
             {
                 f1: u8,
-                #[tabled(display_with("display_option", self))]
+                #[tabled(display("display_option", self))]
                 f2: Option<sstr>,
             }
         }
         {
-            fn display_option(o: &TestType) -> String {
+            fn display_option(_opt: &Option<sstr>, o: &TestType) -> String {
                 match o.f2 {
                     Some(s) => format!("some {s}"),
                     None => "none".to_string(),
@@ -1180,13 +1165,13 @@ mod structure {
             {
                 #[tabled(skip)]
                 f1: [u8; 4],
-                #[tabled(display_with("display_option", &[self.f1[0], self.f1[1]], ToString::to_string(&self.f3.to_string())))]
+                #[tabled(display("display_option", &[self.f1[0], self.f1[1]], ToString::to_string(&self.f3.to_string())))]
                 f2: Option<sstr>,
                 f3: usize,
             }
         }
         {
-            fn display_option(v1: &[u8; 2], v4: String) -> String {
+            fn display_option(_opt: &Option<sstr>, v1: &[u8; 2], v4: String) -> String {
                 format!("{} {} {v4}", v1[0], v1[1])
             }
         }
@@ -1331,35 +1316,35 @@ test_enum!(
 
 test_struct!(
     ignore_display_with_when_used_with_inline,
-    { { f1: sstr, f2: sstr, #[tabled(display_with = "print", inline)] f3: usize } }
+    { { f1: sstr, f2: sstr, #[tabled(display = "print", inline)] f3: usize } }
     {}
     { f1: "123", f2: "456", f3: 789 }
     { ["f1", "f2", "usize"], ["123", "456", "789"], }
 );
 test_struct!(
     ignore_display_with_when_used_with_inline_2,
-    { { f1: sstr, f2: sstr, #[tabled(display_with = "print", )] #[tabled(inline)] f3: usize } }
+    { { f1: sstr, f2: sstr, #[tabled(display = "print", )] #[tabled(inline)] f3: usize } }
     {}
     { f1: "123", f2: "456", f3: 789 }
     { ["f1", "f2", "usize"], ["123", "456", "789"], }
 );
 test_struct!(
     display_with_and_rename,
-    { { f1: sstr, f2: sstr, #[tabled(display_with = "print", rename = "asd")] f3: usize } }
+    { { f1: sstr, f2: sstr, #[tabled(display = "print", rename = "asd")] f3: usize } }
     { #[allow(dead_code)] fn print<T>(_: T) -> String { String::new() } }
     { f1: "123", f2: "456", f3: 789 }
     { ["f1", "f2", "asd"], ["123", "456", ""], }
 );
 test_struct!(
     display_with_and_rename_2,
-    { { f1: sstr, f2: sstr, #[tabled(display_with = "print")] #[tabled(rename = "asd")] f3: usize } }
+    { { f1: sstr, f2: sstr, #[tabled(display = "print")] #[tabled(rename = "asd")] f3: usize } }
     { #[allow(dead_code)] fn print<T>(_: T) -> String { String::new() } }
     { f1: "123", f2: "456", f3: 789 }
     { ["f1", "f2", "asd"], ["123", "456", ""], }
 );
 test_struct!(
     display_with_and_rename_all,
-    { { f1: sstr, f2: sstr, #[tabled(display_with = "print", rename_all = "UPPERCASE")] f3: usize } }
+    { { f1: sstr, f2: sstr, #[tabled(display = "print", rename_all = "UPPERCASE")] f3: usize } }
     { #[allow(dead_code)] fn print<T>(_: T) -> String { String::new() } }
     { f1: "123", f2: "456", f3: 789 }
     { ["f1", "f2", "F3"], ["123", "456", ""], }
@@ -1525,15 +1510,15 @@ fn test_reimport_trait_by_crate_attribute() {
     );
 }
 
-#[test]
-fn test_display_with_2() {
-    #[derive(tabled::Tabled)]
-    #[allow(dead_code)]
-    struct Struct<'a> {
-        #[tabled(display_with("std::path::Path::display"))]
-        path: &'a std::path::Path,
-    }
-}
+// #[test]
+// fn test_display_with_2() {
+//     #[derive(Tabled)]
+//     #[allow(dead_code)]
+//     struct Struct<'a> {
+//         #[tabled(display("std::path::Path::display"))]
+//         path: &'a std::path::Path,
+//     }
+// }
 
 #[test]
 fn test_format_enum_inline() {
@@ -1570,24 +1555,24 @@ fn test_macros_in_display_with() {
     #[tabled(rename_all = "camelCase")]
     struct Country {
         name: String,
-        #[tabled(display_with("display_capital", format!(".{}", self.capital)))]
+        #[tabled(display("display_capital", format!(".{}", self.capital)))]
         capital: String,
-        #[tabled(display_with("display_perimeter", self))]
+        #[tabled(display("display_perimeter", self))]
         area_km2: f32,
-        #[tabled(display_with = "str::to_lowercase")]
+        #[tabled(display = "str::to_lowercase")]
         national_currency: String,
         national_currency_short: String,
     }
 
-    fn display_perimeter(country: &Country) -> std::borrow::Cow<'_, str> {
+    fn display_perimeter(_area: &f32, country: &Country) -> sstr {
         if country.area_km2 > 1_000_000.0 {
-            "Very Big Land".into()
+            "Very Big Land"
         } else {
-            "Big Land".into()
+            "Big Land"
         }
     }
 
-    fn display_capital(country: String) -> std::borrow::Cow<'static, str> {
+    fn display_capital(_capital: &str, country: String) -> std::borrow::Cow<'static, str> {
         format!("{country}!").into()
     }
 }
@@ -1637,3 +1622,278 @@ mod __ {
         struct __;
     }
 }
+
+test_table!(
+    test_display_type_0,
+    {
+        #[derive(Tabled)]
+        #[tabled(display(Password, "password_fmt"))]
+        struct User {
+            id: usize,
+            pass: Password,
+            mirrow: Password,
+        }
+
+        struct Password([u8; 4]);
+
+        fn password_fmt(p: &Password) -> String {
+            p.0.iter().sum::<u8>().to_string()
+        }
+
+        let data = [
+            User {
+                id: 0,
+                pass: Password([0, 1, 2, 3]),
+                mirrow: Password([1, 1, 1, 1]),
+            },
+            User {
+                id: 1,
+                pass: Password([1, 1, 2, 3]),
+                mirrow: Password([2, 2, 1, 1]),
+            },
+        ];
+
+        Table::new(data)
+    },
+    "+----+------+--------+"
+    "| id | pass | mirrow |"
+    "+----+------+--------+"
+    "| 0  | 6    | 4      |"
+    "+----+------+--------+"
+    "| 1  | 7    | 6      |"
+    "+----+------+--------+"
+);
+
+test_table!(
+    test_display_type_args,
+    {
+        #[derive(Tabled)]
+        #[tabled(display(Password, "password_fmt", self, 0))]
+        struct User {
+            id: usize,
+            pass: Password,
+            mirrow: Password,
+        }
+
+        struct Password([u8; 4]);
+
+        fn password_fmt(p: &Password, _: &User, _: usize) -> String {
+            p.0.iter().sum::<u8>().to_string()
+        }
+
+        let data = [
+            User {
+                id: 0,
+                pass: Password([0, 1, 2, 3]),
+                mirrow: Password([1, 1, 1, 1]),
+            },
+            User {
+                id: 1,
+                pass: Password([1, 1, 2, 3]),
+                mirrow: Password([2, 2, 1, 1]),
+            },
+        ];
+
+        Table::new(data)
+    },
+    "+----+------+--------+"
+    "| id | pass | mirrow |"
+    "+----+------+--------+"
+    "| 0  | 6    | 4      |"
+    "+----+------+--------+"
+    "| 1  | 7    | 6      |"
+    "+----+------+--------+"
+);
+
+test_table!(
+    test_display_type_generic,
+    {
+        #[derive(Tabled)]
+        #[tabled(display(Password, "password_fmt"))]
+        struct User {
+            id: usize,
+            pass: Password<usize>,
+            mirrow: Password<u8>,
+        }
+
+        struct Password<T>([T; 4]);
+
+        fn password_fmt<T>(p: &Password<T>) -> String where T: Display {
+            p.0.iter().map(|s| s.to_string()).collect::<Vec<_>>().join("-")
+        }
+
+        let data = [
+            User {
+                id: 0,
+                pass: Password([0, 1, 2, 3]),
+                mirrow: Password([1, 1, 1, 1]),
+            },
+            User {
+                id: 1,
+                pass: Password([1, 1, 2, 3]),
+                mirrow: Password([2, 2, 1, 1]),
+            },
+        ];
+
+        Table::new(data)
+    },
+    "+----+---------+---------+"
+    "| id | pass    | mirrow  |"
+    "+----+---------+---------+"
+    "| 0  | 0-1-2-3 | 1-1-1-1 |"
+    "+----+---------+---------+"
+    "| 1  | 1-1-2-3 | 2-2-1-1 |"
+    "+----+---------+---------+"
+);
+
+test_table!(
+    test_display_type_generic_use,
+    {
+        #[derive(Tabled)]
+        #[tabled(display(Password<usize>, "password_usize_fmt"))]
+        #[tabled(display(Password<u8>, "password_u8_fmt"))]
+        struct User {
+            id: usize,
+            pass: Password<usize>,
+            mirrow: Password<u8>,
+        }
+
+        struct Password<T>([T; 4]);
+
+        fn password_usize_fmt(p: &Password<usize>) -> String {
+            p.0.iter().sum::<usize>().to_string()
+        }
+
+        fn password_u8_fmt(p: &Password<u8>) -> String {
+            p.0.iter().sum::<u8>().to_string()
+        }
+
+        let data = [
+            User {
+                id: 0,
+                pass: Password([0, 1, 2, 3]),
+                mirrow: Password([1, 1, 1, 1]),
+            },
+            User {
+                id: 1,
+                pass: Password([1, 1, 2, 3]),
+                mirrow: Password([2, 2, 1, 1]),
+            },
+        ];
+
+        Table::new(data)
+    },
+    "+----+------+--------+"
+    "| id | pass | mirrow |"
+    "+----+------+--------+"
+    "| 0  | 6    | 4      |"
+    "+----+------+--------+"
+    "| 1  | 7    | 6      |"
+    "+----+------+--------+"
+);
+
+#[cfg(test)]
+mod test_display_enum {
+    use super::*;
+
+    #[derive(Tabled)]
+    pub enum User {
+        #[tabled(display("some::bar::user_fmt"))]
+        Public { id: usize },
+        #[tabled(display("user_fmt"))]
+        Private { id: usize },
+    }
+
+    fn user_fmt(_: &User) -> String {
+        format!("...")
+    }
+
+    pub mod some {
+        pub mod bar {
+            pub fn user_fmt(_: &super::super::User) -> String {
+                format!("111")
+            }
+        }
+    }
+
+    test_table!(
+        test_display_enum,
+        {
+            let data = [
+                User::Public { id: 0 },
+                User::Private { id: 1 },
+            ];
+
+            Table::new(data)
+        },
+        "+--------+---------+"
+        "| Public | Private |"
+        "+--------+---------+"
+        "| 111    |         |"
+        "+--------+---------+"
+        "|        | ...     |"
+        "+--------+---------+"
+    );
+}
+
+// TODO: Shall we support a 'display_type' for #inlined structs??
+// Seems like we have to?
+// But there's a small issue currently - we treat enum variants as field currently in a parsing stage.
+// And this particular case have no scense applied to struct field.
+// Soooo
+// Yes..
+//
+// test_table!(
+//     test_display_type_enum,
+//     {
+//         #[derive(Tabled)]
+//         enum User {
+//             #[tabled(inline)]
+//             #[tabled(display(Password<usize>, "password_usize_fmt"))]
+//             #[tabled(display(Password<u8>, "password_u8_fmt"))]
+//             Public {
+//                 id: usize,
+//                 pass: Password<usize>,
+//                 mirrow: Password<u8>,
+//             },
+//             #[tabled(display("user_fmt"))]
+//             Private {
+//                 id: usize,
+//             }
+//         }
+
+//         struct Password<T>([T; 4]);
+
+//         fn password_usize_fmt(p: &Password<usize>) -> String {
+//             p.0.iter().sum::<usize>().to_string()
+//         }
+
+//         fn password_u8_fmt(p: &Password<u8>) -> String {
+//             p.0.iter().sum::<u8>().to_string()
+//         }
+
+//         fn user_fmt(p: &User) -> String {
+//             format!("...")
+//         }
+
+//         let data = [
+//             User::Public {
+//                 id: 0,
+//                 pass: Password([0, 1, 2, 3]),
+//                 mirrow: Password([1, 1, 1, 1]),
+//             },
+//             User::Private {
+//                 id: 1,
+//             },
+//         ];
+
+//         Table::new(data)
+//     },
+//     "+----+------+--------+"
+//     "| id | pass | mirrow |"
+//     "+----+------+--------+"
+//     "| 0  | 6    | 4      |"
+//     "+----+------+--------+"
+//     "| 1  | 7    | 6      |"
+//     "+----+------+--------+"
+// );