-
-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
WIP: Custom derive macro for rust_xlsxwriter
specific serialization options
#66
Comments
I asked a question about this on users.rust-lang.org. Some interesting suggestions there. |
Just an update on progress on this. You can now mix and match Like this: se rust_xlsxwriter::{ExcelSerialize, Workbook, XlsxError};
use rust_xlsxwriter_derive::ExcelSerialize;
use serde::Serialize;
fn main() -> Result<(), XlsxError> {
let mut workbook = Workbook::new();
// Add a worksheet to the workbook.
let worksheet = workbook.add_worksheet();
#[derive(ExcelSerialize, Serialize)]
#[allow(dead_code)]
struct Produce {
#[rust_xlsxwriter(rename = "Item")]
fruit: &'static str,
#[rust_xlsxwriter(rename = "Price")]
#[rust_xlsxwriter(num_format = "$0.00")]
cost: f64,
#[serde(skip)]
in_stock: bool,
}
// Create some data instances.
let items = [
Produce {
fruit: "Peach",
cost: 1.05,
in_stock: true,
},
Produce {
fruit: "Plum",
cost: 0.15,
in_stock: false,
},
Produce {
fruit: "Pear",
cost: 0.75,
in_stock: true,
},
];
worksheet.set_serialize_headers::<Produce>(0, 0)?;
worksheet.serialize(&items)?;
// Save the file.
workbook.save("serialize.xlsx")?;
Ok(())
} Output: There is still quite a bit of work to make this robust but at least it demonstrates that it is feasible. |
Can we make the attribute name |
That is a good suggestion. There is a different xlsxwriter library that I didn't write but wraps the libxlxwriter library which I did write. I would have preferred to use just So the shorted attribute "path" of I'll definitely consider it though. @lucatrv your 2 cents? |
I think that using |
Is it also possible to rely on |
Yes. That is the goal. But it is also the hard part. :-) Hence my question on users.rust-lang.org. |
Naming things is hard. I had also registered excelwriter and made an earlier attempt to move the code to that but in the end I didn't. |
That's a nice name indeed, and there's already an eminent homonymous ;) |
Well, actually it's not that hard... Here's the MVP/POV code: https://github.com/zjp-CN/parse-serde-macro/blob/main/_impl/src/lib.rs // key implementation
if ident == "rust_xlsxwriter" || ident == "xlsxwriter" || ident == "serde" {
// e.g. #[ident(one)] or #[ident(one, two)] or #[ident(one, two, ...)]
let parsed = Punctuated::<AttributeTypes, Token![,]>::parse_separated_nonempty
.parse2(list.tokens)?;
return Ok(parsed.into_iter().collect());
} // field `b` => [Rename(LitStr { token: "serde rename for b" })]
// field `c` => [Skip]
// field `d` => [Rename(LitStr { token: "rust_xlsxwriter rename for d" }), NumFormat(LitStr { token: "$0.00" })]
// field `e` => [Rename(LitStr { token: "xlsxwriter rename for d" }), NumFormat(LitStr { token: "$0.00" })]
// field `f` => [Skip]
#[derive(_impl::ExcelSerialize, serde::Serialize)]
pub struct A {
#[serde(rename = "serde rename for b")]
b: (),
#[serde(skip)]
c: (),
#[rust_xlsxwriter(rename = "rust_xlsxwriter rename for d", num_format = "$0.00")]
d: (),
#[xlsxwriter(rename = "xlsxwriter rename for d", num_format = "$0.00")]
e: (),
#[xlsxwriter(skip)]
f: (),
}
// error: `not_exist` is not supported by ExcelSerialize derive macro
// --> src/main.rs:40:18
// |
// 40 | #[xlsxwriter(not_exist)]
// | ^^^^^^^^^
#[derive(_impl::ExcelSerialize, serde::Serialize)]
pub struct B {
#[xlsxwriter(not_exist)]
f: (),
} |
IMHO once the integration with Serde is completed, so that Serde's |
@jmcnamara, the more I think about this, the more I would suggest to change name into |
@zjp-CN Thank you. I really appreciate that. I actually got the In terms of difficultly I was thinking more about the container attribute Your code looks much cleaner and more generic. Could you have a look at my implementation and make any suggestions that you have. One issue that I have and haven't managed to come up with a solution for is that I would like to an #[derive(ExcelSerialize, Serialize)]
struct Produce {
fruit: &'static str,
#[rust_xlsxwriter(value_format = Format::new().set_bold())]
cost: f64,
in_stock: bool,
} Is that possible to handle non-primitives as an attribute value? Anything that I have tried so far end up with a literal string or a parse error. I would also need to modify the token stream to get Any thoughts on whether that is possible and if so how? |
@lucatrv I think you are right. Could you open a separate Feature Request for that so we can have the discussion separately to this thread. I think it is worth doing. |
You can simply add it as follows: @@ -57,6 +57,7 @@ enum AttributeTypes {
Skip,
Rename(LitStr),
NumFormat(LitStr),
+ FormatObj(Expr),
}
@@ -73,6 +74,9 @@ fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
} else if ident == "num_format" {
let _ = input.parse::<Token![=]>()?;
Ok(Self::NumFormat(input.parse()?))
+ } else if ident == "value_format" {
+ let _ = input.parse::<Token![=]>()?;
+ Ok(Self::FormatObj(input.parse()?))
} else {
Err(syn::Error::new(
ident.span(),
Yes, and I've already wrote the code partially based on yours. I can open a PR for your derive branch, but I don't know if there are tests for the feature. |
Thank you. I'll try that.
There are integration test to cover it. I normally run the integration tests like the following to avoid the long compilation and test time for the examples: cargo test --test '*' Currently the following tests cover this functionality. I'll add more once I get the
Just some notes:
Thanks once more. |
Thanks. I'll go with
I know they bring different meanings, but I think we need both of
I checked the code in |
I've updated the code to handle all of the Serde I'm moving on to the Serde Update 1: I have never used this option before but it turns out that |
Good news for a small audience. I got all of the major functionality working including the format support. Here is an example: use rust_xlsxwriter::{ExcelSerialize, Workbook, XlsxError};
use serde::Serialize;
fn main() -> Result<(), XlsxError> {
let mut workbook = Workbook::new();
// Add a worksheet to the workbook.
let worksheet = workbook.add_worksheet();
// Create a serializable struct.
#[derive(ExcelSerialize, Serialize)]
#[rust_xlsxwriter(header_format = Format::new()
.set_bold()
.set_border(FormatBorder::Thin)
.set_background_color("C6EFCE"))]
#[serde(rename_all = "PascalCase")]
struct Produce {
fruit: &'static str,
#[rust_xlsxwriter(value_format = Format::new().set_num_format("$0.00"))]
cost: f64,
}
// Create some data instances.
let item1 = Produce {
fruit: "Peach",
cost: 1.05,
};
let item2 = Produce {
fruit: "Plum",
cost: 0.15,
};
let item3 = Produce {
fruit: "Pear",
cost: 0.75,
};
// Set the serialization location and headers.
worksheet.set_serialize_headers::<Produce>(0, 0)?;
// Serialize the data.
worksheet.serialize(&item1)?;
worksheet.serialize(&item2)?;
worksheet.serialize(&item3)?;
// Save the file to disk.
workbook.save("serialize.xlsx")?;
Ok(())
} Which generates this output: The header format is a bit long in this example but I wanted to replicate the previous example. The diff with the previous example looks like this: diff --git a/examples/app_serialize.rs b/examples/app_serialize.rs
index 1b4810a..3e7980b 100644
--- a/examples/app_serialize.rs
+++ b/examples/app_serialize.rs
@@ -5,10 +5,8 @@
//! Example of serializing Serde derived structs to an Excel worksheet using
//! `rust_xlsxwriter`.
-use rust_xlsxwriter::{
- CustomSerializeField, Format, FormatBorder, SerializeFieldOptions, Workbook, XlsxError,
-};
-use serde::{Deserialize, Serialize};
+use rust_xlsxwriter::{ExcelSerialize, Workbook, XlsxError};
+use serde::Serialize;
fn main() -> Result<(), XlsxError> {
let mut workbook = Workbook::new();
@@ -16,19 +14,17 @@ fn main() -> Result<(), XlsxError> {
// Add a worksheet to the workbook.
let worksheet = workbook.add_worksheet();
- // Set some formats.
- let header_format = Format::new()
+ // Create a serializable struct.
+ #[derive(ExcelSerialize, Serialize)]
+ #[rust_xlsxwriter(header_format = Format::new()
.set_bold()
.set_border(FormatBorder::Thin)
- .set_background_color("C6EFCE");
-
- let value_format = Format::new().set_num_format("$0.00");
-
- // Create a serializable struct.
- #[derive(Deserialize, Serialize)]
+ .set_background_color("C6EFCE"))]
#[serde(rename_all = "PascalCase")]
struct Produce {
fruit: &'static str,
+
+ #[rust_xlsxwriter(value_format = Format::new().set_num_format("$0.00"))]
cost: f64,
}
@@ -48,13 +44,8 @@ fn main() -> Result<(), XlsxError> {
cost: 0.75,
};
- // Set the custom headers.
- let header_options = SerializeFieldOptions::new()
- .set_header_format(&header_format)
- .set_custom_headers(&[CustomSerializeField::new("Cost").set_value_format(&value_format)]);
-
// Set the serialization location and headers.
- worksheet.deserialize_headers_with_options::<Produce>(0, 0, &header_options)?;
+ worksheet.set_serialize_headers::<Produce>(0, 0)?;
// Serialize the data.
worksheet.serialize(&item1)?; The nice thing about it is that you get standard rustc error messages: error[E0599]: no method named `set_scold` found for struct `Format` in the current scope
--> examples/app_serialize.rs:20:10
|
19 | #[rust_xlsxwriter(header_format = Format::new()
| _______________________________________-
20 | | .set_scold()
| | -^^^^^^^^^ help: there is a method with a similar name: `set_bold`
| |_________|
|
// or
error: unknown rust_xlsxwriter attribute: `skipper`
--> examples/app_serialize.rs:27:27
|
27 | #[rust_xlsxwriter(skipper)]
| ^^^^^^^ In case anyone is interested, here is what the expanded derive output for the example above looks like: #[doc(hidden)]
const _: () = {
#[allow(unused_imports)]
use rust_xlsxwriter::{
Color, Format, FormatAlign, FormatBorder, FormatDiagonalBorder,
FormatPattern, FormatScript, FormatUnderline,
};
impl ExcelSerialize for Produce {
fn to_serialize_field_options() -> rust_xlsxwriter::SerializeFieldOptions {
let mut custom_headers: Vec<rust_xlsxwriter::CustomSerializeField> = ::alloc::vec::Vec::new();
custom_headers.push(rust_xlsxwriter::CustomSerializeField::new("Fruit"));
custom_headers
.push(
rust_xlsxwriter::CustomSerializeField::new("Cost")
.set_value_format(Format::new().set_num_format("$0.00")),
);
rust_xlsxwriter::SerializeFieldOptions::new()
.set_header_format(
Format::new()
.set_bold()
.set_border(FormatBorder::Thin)
.set_background_color("C6EFCE"),
)
.set_struct_name("Produce")
.set_custom_headers(&custom_headers)
}
}
}; I stole the Overall I'm very happy with the way this turned out. I may drop/deprecate/hide the existing serialize/deserialize way of setting up the headers and only document this. I now have a heartfelt appreciation for the functionality of the I'll move this back onto main shortly and fix up the feature flags so that folks can try it out. Thanks @zjp-CN and @lucatrv for the help and feedback. |
Thanks @jmcnamara , I'm looking forward to testing this out! |
Add support for ExcelSerialize trait via a proc macro. Support rust_xlsxwriter attributes and also Serde attributes. See #66
Make rust_xlsxwriter_derive a sub-dependency of the serde feature. It is enabled with the serde flag. Feature #66
Make rust_xlsxwriter_derive a sub-dependency of the serde feature. It is enabled with the serde flag. Feature #66
I've merged this back to main. The You should add the following to your [dependencies]
rust_xlsxwriter = { git = "https://github.com/jmcnamara/rust_xlsxwriter.git", version = "0.60.0", features = ["serde"] }
serde = { version = "1.0.195", features = ["derive"] } I seem to have broken installation via
I'm not sure if this is a |
That tells us how to do it right by sepecifying the package in the workshop:
|
I had tried that but it can't seem to process the feature flag: $ cargo add --git https://github.com/jmcnamara/rust_xlsxwriter.git rust_xlsxwriter -F serde
Updating git repository `https://github.com/jmcnamara/rust_xlsxwriter.git`
Adding rust_xlsxwriter (git) to dependencies.
error: unrecognized feature for crate rust_xlsxwriter: serde I'll see if moving to a workspace improves things. |
How come??? It worked on my machine with $ cargo add --git https://github.com/jmcnamara/rust_xlsxwriter.git rust_xlsxwriter -F serde
Updating git repository `https://github.com/jmcnamara/rust_xlsxwriter.git`
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 10 (delta 6), reused 8 (delta 6), pack-reused 0
Unpacking objects: 100% (10/10), 991 bytes | 20.00 KiB/s, done.
From https://github.com/jmcnamara/rust_xlsxwriter
9465a21..1f648f6 -> origin/HEAD
Adding rust_xlsxwriter (git) to dependencies.
Features:
+ serde
- chrono
- js-sys
- polars
- test-resave
- wasm
- wasm-bindgen
- zlib |
I would consider this point, because formats can get pretty long and they may need to be applied to several struct fields. |
As @zjp-CN points out the format parts need to be const/non-dynamic since they will end up in a However you could use functions to give you re-use and reduce verbosity. Something like this: use rust_xlsxwriter::{ExcelSerialize, Format, Workbook, XlsxError, FormatBorder};
use serde::Serialize;
fn main() -> Result<(), XlsxError> {
let mut workbook = Workbook::new();
// Add a worksheet to the workbook.
let worksheet = workbook.add_worksheet();
// Set up some formats.
fn my_header_format() -> Format {
Format::new()
.set_bold()
.set_border(FormatBorder::Thin)
.set_background_color("C6EFCE")
}
fn my_red_num_format(s: &str) -> Format {
Format::new().set_font_color("FF0000").set_num_format(s)
}
// Create a serializable struct.
#[derive(ExcelSerialize, Serialize)]
#[rust_xlsxwriter(header_format = my_header_format())]
#[serde(rename_all = "PascalCase")]
struct Produce {
fruit: &'static str,
#[rust_xlsxwriter(value_format = my_red_num_format("$0.00"))]
cost: f64,
}
// Create some data instances.
let item1 = Produce {
fruit: "Peach",
cost: 1.05,
};
let item2 = Produce {
fruit: "Plum",
cost: 0.15,
};
let item3 = Produce {
fruit: "Pear",
cost: 0.75,
};
// Set the serialization location and headers.
worksheet.set_serialize_headers::<Produce>(0, 0)?;
// Serialize the data.
worksheet.serialize(&item1)?;
worksheet.serialize(&item2)?;
worksheet.serialize(&item3)?;
// Save the file to disk.
workbook.save("serialize.xlsx")?;
Ok(())
} |
This works, hurrah! #![feature(lazy_cell)]
use std::sync::LazyLock;
static CENTER_FORMAT: LazyLock<Format> = LazyLock::new(|| {
Format::new().set_align(FormatAlign::Center)
});
#[derive(Deserialize, Serialize, ExcelSerialize)]
#[serde(rename_all = "UPPERCASE")]
#[rust_xlsxwriter(header_format = &*CENTER_FORMAT)]
struct Record<'a> {
#[rust_xlsxwriter(column_format = &*CENTER_FORMAT)]
articolo: &'a str,
descrizione: &'a str,
#[rust_xlsxwriter(column_format = &*CENTER_FORMAT)]
stato: u8,
} I think this is the best way to apply formats, so I would document it in the examples. You need rustc nightly to compile this, waiting for |
I don't think so... I think I should withdraw the first improvement because defining a custom format function is an awesome solution: function is flexible, IDE friendly, and repeatable. As for your static variable + For my second suggestion
I would like to implement it if you guys like the idea. The API in design // column format: header + value format
#[derive(ExcelSerialize, Serialize)]
#[column_format(align = "center", font_name = "...", border = "thin")] // applies for all columns
struct Data { ... }
#[derive(ExcelSerialize, Serialize)]
struct Data { #[column_format(align = "center", border = "thin")] field: i32 } // applies only for one field column
// header format
#[derive(ExcelSerialize, Serialize)]
#[header_format(align = "center", font_size = 45, bold, italic)] // applies for all headers
struct Data { ... }
#[derive(ExcelSerialize, Serialize)]
struct Data { #[header_format(bold) field: i32 } // applies only for one field header
// value format
#[derive(ExcelSerialize, Serialize)]
#[value_format(num_format = "0.00")] // applies for all values of all fields
struct Data { ... }
#[derive(ExcelSerialize, Serialize)]
struct Data { #[value_format(num_format = "0.00")] field: i32 } // applies only for values of one field
// combined formats
#[derive(ExcelSerialize, Serialize)]
struct Data {
#[header_format(border = "thick")]
#[value_format(border = "thin", num_format = "0.00")]
field: i32
}
// overridden formats
#[derive(ExcelSerialize, Serialize)]
#[column_format(background_color= "#123456")]
struct Data {
#[header_format(background_color= "#7890AB")]
#[value_format(background_color= "#ABCDEF")]
field: i32
} |
@zjp-CN Could you hold off on this. I'll admit that is what I thought the API would look like initially because I wasn't sure if it was possible to parse an expression. However I'd prefer not to get into having to document and test a secondary interface to I think a better overall formatting solution would be to add support for Worksheet Tables to get something like this: That is what the Polars folks use as the default format on output dataframes. I am also currently working on some of the refactoring to make that happen as part of the fix for #71. |
Sorry, I'm not familiar with polars, what does it mean? |
I agree with you if in my example above
I think the best of both words would be to define a let custom_format = format![align = "center", font_name = "...", border = "thin"]; Then the same macro could also be used within the Serde struct annotations. Finally the documentation examples should suggest the best way to define a format in one place and apply it to several fields, for instance using functions: fn custom_format() -> Format {
format![align = "center", font_name = "...", border = "thin"]
} |
Great! I like the design. Why not make let custom_format = format![align = "center", font_name = "...", border = "thin"];
// expansion
let custom_format = {
fn custom_format() -> Format { ... }
custom_format
};
// then custom_format is copyable ~ Update: this results in
Update2: wait, since most structs are defined under modules instead of scopes of functions, it would be better for |
@zjp-CN Sorry, I should have added a link there. Polars is a Rust/Python Dataframe library. They implemented Xlsx output based on the Python version of rust_xlsxwriter and use a default table format for each dataframe. There are some visual examples here: pola-rs/polars#5568 and here. |
Folks, before anyone gets too far into this I want to say that I'm not in favour of a secondary interface to Format. Not at this point in time. If someone wants to open a Feature Request for it we can look at it once the basic serde support is complete. For now I think the function wrapper suggested above is a reasonable workaround. At the same time I'm not completely against the header_format = workbook.add_format(
{
"bold": True,
"text_wrap": True,
"valign": "top",
"fg_color": "#D7E4BC",
"border": 1,
}
) However, that will need a fair amount of documentation and tests and it loses enum validation for the property values. At the moment that feels like one too many rabbit holes. So let's punt that out until later. |
This does not seem to be working: #[rust_xlsxwriter(header_format = Format::new().set_bold(), column_width = 10.0)] It only applies the last annotation. In this case it applies only |
Good catch. Fixed on main, with a test. |
Now it works, thanks! |
@jmcnamara, please notice that something in main broke the serialization support for Result<T, E>, which works in v0.60.0. |
@lucatrv The tests for that feature are still passing. Could you add a small working sample application that demonstrates the break in the #64 thread. |
Sorry yesterday it was too late... now I see that my code does not work with v0.60.0 neither, I'll try to dig into this. |
This code does not work to me: use rust_xlsxwriter::{Format, FormatBorder, Workbook, XlsxError};
use serde::{Serialize, Deserialize};
fn main() -> Result<(), XlsxError> {
let mut workbook = Workbook::new();
// Add a worksheet to the workbook.
let worksheet = workbook.add_worksheet();
// Add some formats to use with the serialization data.
let header_format = Format::new()
.set_bold()
.set_border(FormatBorder::Thin)
.set_background_color("C6E0B4");
// Create a serializable struct.
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
struct Student<'a> {
name: &'a str,
age: Result<f64, String>,
id: Result<f64, String>,
}
let students = [
Student {
name: "Aoife",
age: Ok(1.0),
id: Err(String::from("564351")),
},
Student {
name: "Caoimhe",
age: Err(String::new()),
id: Ok(443287.0),
},
];
// Set up the start location and headers of the data to be serialized.
worksheet.deserialize_headers_with_format::<Student>(1, 3, &header_format)?;
// Serialize the data.
worksheet.serialize(&students)?;
// Save the file.
workbook.save("serialize.xlsx")?;
Ok(())
} I get empty fields for |
@lucatrv I'll check when I'm online later but do the |
@jmcnamara, I confirm that the following works, but I thought you could add serialization support for use rust_xlsxwriter::{Format, FormatBorder, Workbook, XlsxError};
use serde::{Serialize, Serializer, Deserialize};
fn main() -> Result<(), XlsxError> {
let mut workbook = Workbook::new();
// Add a worksheet to the workbook.
let worksheet = workbook.add_worksheet();
// Add some formats to use with the serialization data.
let header_format = Format::new()
.set_bold()
.set_border(FormatBorder::Thin)
.set_background_color("C6E0B4");
// Create a serializable struct.
#[derive(Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
struct Student<'a> {
name: &'a str,
#[serde(serialize_with = "se_f64_or_string")]
age: Result<f64, String>,
#[serde(serialize_with = "se_f64_or_string")]
id: Result<f64, String>,
}
fn se_f64_or_string<S>(
f64_or_string: &Result<f64, String>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match f64_or_string {
Ok(f64) => serializer.serialize_f64(*f64),
Err(string) => serializer.serialize_str(string),
}
}
let students = [
Student {
name: "Aoife",
age: Ok(1.0),
id: Err(String::from("564351")),
},
Student {
name: "Caoimhe",
age: Err(String::new()),
id: Ok(443287.0),
},
];
// Set up the start location and headers of the data to be serialized.
worksheet.deserialize_headers_with_format::<Student>(1, 3, &header_format)?;
// Serialize the data.
worksheet.serialize(&students)?;
// Save the file.
workbook.save("serialize.xlsx")?;
Ok(())
} |
Correct. |
Folks, the derive/attribute feature is now released in v0.61.0 on crates.io. Thanks for all the input, testing and suggestions. See the updates docs at: https://docs.rs/rust_xlsxwriter/latest/rust_xlsxwriter/serializer/index.html#setting-serialization-headers |
I thought it could be seen as a Serde enum. |
@lucatrv You are right, it is handled by Serde but not |
Note the This is currently on main. I'll release it by or on the weekend. use rust_xlsxwriter::{Workbook, XlsxError, XlsxSerialize};
use serde::Serialize;
fn main() -> Result<(), XlsxError> {
let mut workbook = Workbook::new();
// Add a worksheet to the workbook.
let worksheet = workbook.add_worksheet();
// Create a serializable struct.
#[derive(XlsxSerialize, Serialize)]
#[xlsx(table_default)]
struct Produce {
fruit: &'static str,
cost: f64,
}
// Create some data instances.
let items = [
Produce {
fruit: "Peach",
cost: 1.05,
},
Produce {
fruit: "Plum",
cost: 0.15,
},
Produce {
fruit: "Pear",
cost: 0.75,
},
];
// Set the serialization location and headers.
worksheet.set_serialize_headers::<Produce>(0, 0)?;
// Serialize the data.
worksheet.serialize(&items)?;
// Save the file to disk.
workbook.save("serialize.xlsx")?;
Ok(())
} Output: |
In a comment on #63 @claudiofsr said:
I've started work on implementing a custom derive macro to enable this functionality on the
derive
branch.Here is the feature set to be implemented
SerializeFieldOptions
:CustomSerializeField
Serde Container attributes:
#[serde(rename = "name")]
#[serde(rename_all = "...")]
#[serde(rename_all(serialize = "..."))]
Serde Field attributes:
#[serde(rename = "name")]
#[serde(skip)]
#[serde(skip_serializing)]
Packaging:
As a start, you can now do this:
Output:
Note the
ExcelSerialize
derived trait. Theset_serialize_headers()
doesn't use Serde serialization or deserialization (for the headers). Instead the macro generates code a custom impl for the type inline like this:It should be straight forward to support attributes like
#[rust_xlsxwriter(set_num_format="dd/mm/yyyy")]
. However, interacting (correctly) with Serde attributes will be a little trickier.I'll continue to work on this for the next few weeks and post updates when there is enough functionality to try it out with a more realistic use case.
@lucatrv for information.
The text was updated successfully, but these errors were encountered: