diff --git a/README.md b/README.md index c149399..55a109c 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ function writeExcel(header: string[], rows: (string | number)[][]): Buffer { headerStyle .setAlign(FormatAlign.Top) .setTextWrap() - .setBackgroundColor(Color.red()); + .setBackgroundColor("Red"); // Write sheet header worksheet.writeRowWithFormat(0, 0, header, headerStyle); diff --git a/examples/nodejs/main.ts b/examples/nodejs/main.ts index 536ce5c..844b6d1 100644 --- a/examples/nodejs/main.ts +++ b/examples/nodejs/main.ts @@ -1,5 +1,4 @@ import { - Color, DocProperties, Format, FormatAlign, @@ -31,7 +30,7 @@ function writeExcel(header: string[], rows: (string | number)[][]): Buffer { headerStyle .setAlign(FormatAlign.Top) .setTextWrap() - .setBackgroundColor(Color.red()); + .setBackgroundColor("Red"); // Write sheet header worksheet.writeRowWithFormat(0, 0, header, headerStyle); diff --git a/rust/Cargo.lock b/rust/Cargo.lock index da2eebe..7861e65 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -120,6 +120,19 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "hashbrown" version = "0.15.2" @@ -159,6 +172,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "js-sys" version = "0.3.80" @@ -262,26 +281,66 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[package]] name = "serde" -version = "1.0.210" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "0dca6411025b24b60bfa7ec1fe1f8e710ac09782dca409ee8237ba74b51295fd" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.226" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2ba63999edb9dac981fb34b3e5c0d111a69b0924e253ed29d83f7c99e966a4" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.226" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "8db53ae22f34573731bafa1db20f04027b2d25e02d8205921b569171699cdb33" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + [[package]] name = "shlex" version = "1.3.0" @@ -305,6 +364,31 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tsify" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ec91b85e6c6592ed28636cb1dd1fac377ecbbeb170ff1d79f97aac5e38926d" +dependencies = [ + "gloo-utils", + "serde", + "serde_json", + "tsify-macros", + "wasm-bindgen", +] + +[[package]] +name = "tsify-macros" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a324606929ad11628a19206d7853807481dcaecd6c08be70a235930b8241955" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn", +] + [[package]] name = "unicode-ident" version = "1.0.13" @@ -378,6 +462,18 @@ dependencies = [ "console_error_panic_hook", "js-sys", "rust_xlsxwriter", + "serde", + "tsify", + "wasm-bindgen", +] + +[[package]] +name = "web-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbe734895e869dc429d78c4b433f8d17d95f8d05317440b4fad5ab2d33e596dc" +dependencies = [ + "js-sys", "wasm-bindgen", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index bf3c35b..42f21f4 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -13,4 +13,6 @@ chrono = "0.4.42" console_error_panic_hook = "0.1.7" js-sys = "0.3.80" rust_xlsxwriter = { version = "0.90.1", features = ["wasm", "chrono"] } +serde = { version = "1.0.219", features = ["derive"] } +tsify = "0.5.5" wasm-bindgen = "0.2.94" diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 92e3807..68aa04e 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -1,4 +1,5 @@ #![allow(clippy::new_without_default)] mod error; +mod macros; mod wrapper; diff --git a/rust/src/macros.rs b/rust/src/macros.rs new file mode 100644 index 0000000..3b58a2a --- /dev/null +++ b/rust/src/macros.rs @@ -0,0 +1,12 @@ +#[macro_export] +macro_rules! impl_method { + ($self:ident.$method:ident($($arg:expr),*)) => { + let mut lock = $self.lock(); + let mut inner = std::mem::take(&mut *lock); + inner = inner.$method($($arg),*); + let _ = std::mem::replace(&mut *lock, inner); + return Self { + inner: Arc::clone(&$self.inner), + } + }; +} diff --git a/rust/src/wrapper/chart/chart_font.rs b/rust/src/wrapper/chart/chart_font.rs index be27b27..eede818 100644 --- a/rust/src/wrapper/chart/chart_font.rs +++ b/rust/src/wrapper/chart/chart_font.rs @@ -52,8 +52,8 @@ impl ChartFont { /// @param {Color} color - The font color property. /// @return {ChartFont} - The ChartFont instance. #[wasm_bindgen(js_name = "setColor")] - pub fn set_color(&mut self, color: &Color) -> ChartFont { - self.inner.set_color(color.inner); + pub fn set_color(&mut self, color: Color) -> ChartFont { + self.inner.set_color(color); self.clone() } diff --git a/rust/src/wrapper/chart/chart_gradient_stop.rs b/rust/src/wrapper/chart/chart_gradient_stop.rs index 0a65d88..a592b02 100644 --- a/rust/src/wrapper/chart/chart_gradient_stop.rs +++ b/rust/src/wrapper/chart/chart_gradient_stop.rs @@ -11,9 +11,9 @@ pub struct ChartGradientStop { #[wasm_bindgen] impl ChartGradientStop { #[wasm_bindgen(constructor)] - pub fn new(color: &Color, position: u8) -> ChartGradientStop { + pub fn new(color: Color, position: u8) -> ChartGradientStop { ChartGradientStop { - inner: xlsx::ChartGradientStop::new(color.inner, position), + inner: xlsx::ChartGradientStop::new(color, position), } } } diff --git a/rust/src/wrapper/chart/chart_line.rs b/rust/src/wrapper/chart/chart_line.rs index dc0c09d..791a4fc 100644 --- a/rust/src/wrapper/chart/chart_line.rs +++ b/rust/src/wrapper/chart/chart_line.rs @@ -37,8 +37,8 @@ impl ChartLine { } #[wasm_bindgen(js_name = "setColor")] - pub fn set_color(&mut self, color: &Color) -> ChartLine { - self.inner.set_color(color.inner); + pub fn set_color(&mut self, color: Color) -> ChartLine { + self.inner.set_color(color); self.clone() } diff --git a/rust/src/wrapper/chart/chart_pattern_fill.rs b/rust/src/wrapper/chart/chart_pattern_fill.rs index e90d0c7..73b4dd7 100644 --- a/rust/src/wrapper/chart/chart_pattern_fill.rs +++ b/rust/src/wrapper/chart/chart_pattern_fill.rs @@ -25,14 +25,14 @@ impl ChartPatternFill { } #[wasm_bindgen(js_name = "setBackgroundColor")] - pub fn set_background_color(&mut self, color: &Color) -> ChartPatternFill { - self.inner.set_background_color(color.inner); + pub fn set_background_color(&mut self, color: Color) -> ChartPatternFill { + self.inner.set_background_color(color); self.clone() } #[wasm_bindgen(js_name = "setForegroundColor")] - pub fn set_foreground_color(&mut self, color: &Color) -> ChartPatternFill { - self.inner.set_foreground_color(color.inner); + pub fn set_foreground_color(&mut self, color: Color) -> ChartPatternFill { + self.inner.set_foreground_color(color); self.clone() } } diff --git a/rust/src/wrapper/chart/chart_solid_fill.rs b/rust/src/wrapper/chart/chart_solid_fill.rs index 3d03926..e43d67a 100644 --- a/rust/src/wrapper/chart/chart_solid_fill.rs +++ b/rust/src/wrapper/chart/chart_solid_fill.rs @@ -31,8 +31,8 @@ impl ChartSolidFill { /// @param {Color} color - The color property. /// @return {ChartSolidFill} - The ChartSolidFill instance. #[wasm_bindgen(js_name = "setColor")] - pub fn set_color(&mut self, color: &Color) -> ChartSolidFill { - self.inner.set_color(color.inner); + pub fn set_color(&mut self, color: Color) -> ChartSolidFill { + self.inner.set_color(color); self.clone() } diff --git a/rust/src/wrapper/color.rs b/rust/src/wrapper/color.rs index 3ab9724..0a2d1a3 100644 --- a/rust/src/wrapper/color.rs +++ b/rust/src/wrapper/color.rs @@ -1,172 +1,79 @@ + use rust_xlsxwriter as xlsx; use wasm_bindgen::prelude::*; +use tsify::Tsify; +use serde::{Deserialize, Serialize}; /// The `Color` enum defines Excel colors that can be used throughout the /// `rust_xlsxwriter` APIs. -/// +/// /// There are 3 types of colors within the enum: -/// +/// /// 1. Predefined named colors like `Color::Green`. /// 2. User defined RGB colors such as `Color::RGB(0x4F026A)` using a format -/// similar to html colors like `#RRGGBB`, except as an integer. +/// similar to html colors like `#RRGGBB`, except as an integer. /// 3. Theme colors from the standard palette of 60 colors like `Color::Theme(9, -/// 4)`. The theme colors are shown in the image below. -/// -/// -/// -/// The syntax for theme colors in `Color` is `Theme(color, shade)` where -/// `color` is one of the 0-9 values on the top row and `shade` is the -/// variant in the associated column from 0-5. For example "White, background -/// 1" in the top left is `Theme(0, 0)` and "Orange, Accent 6, Darker 50%" in -/// the bottom right is `Theme(9, 5)`. -/// +/// 4)`. The theme colors are shown in the image below. +/// +/// +/// +/// The syntax for theme colors in `Color` is `Theme(color, shade)` where +/// `color` is one of the 0-9 values on the top row and `shade` is the +/// variant in the associated column from 0-5. For example "White, background +/// 1" in the top left is `Theme(0, 0)` and "Orange, Accent 6, Darker 50%" in +/// the bottom right is `Theme(9, 5)`. +/// /// Note, there are no plans to support anything other than the default Excel /// "Office" theme. -#[derive(Debug, Copy, Clone, Default)] -#[wasm_bindgen] -pub struct Color { - pub(crate) inner: xlsx::Color, -} - -impl Color { - pub fn new(inner: xlsx::Color) -> Self { - Color { inner } - } +#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, Tsify)] +#[tsify(into_wasm_abi, from_wasm_abi)] +pub enum Color { + RGB(u32), + Theme(u8, u8), + #[default] + Default, + Automatic, + Black, + Blue, + Brown, + Cyan, + Gray, + Green, + Lime, + Magenta, + Navy, + Orange, + Pink, + Purple, + Red, + Silver, + White, + Yellow, } - -#[wasm_bindgen] -impl Color { - /// The default color for an Excel property. - #[wasm_bindgen(constructor)] - pub fn default() -> Color { - Color::new(xlsx::Color::Default) - } - - /// A user defined RGB color in the range 0x000000 (black) to 0xFFFFFF - /// (white). Any values outside this range will be ignored with a a warning. - #[wasm_bindgen] - pub fn rgb(hex: u32) -> Color { - Color::new(xlsx::Color::RGB(hex)) - } - - /// A theme color on the default palette (see the image above). The syntax - /// for theme colors is `Theme(color, shade)` where `color` is one of the - /// 0-9 values on the top row and `shade` is the variant in the associated - /// column from 0-5. Any values outside these ranges will be ignored with a - /// a warning. - #[wasm_bindgen] - pub fn theme(color: u8, shade: u8) -> Color { - Color::new(xlsx::Color::Theme(color, shade)) - } - - /// The Automatic color for an Excel property. This is usually the same as - /// the `Default` color but can vary according to system settings. - #[wasm_bindgen] - pub fn automatic() -> Color { - Color::new(xlsx::Color::Automatic) - } - - /// Convert from a Html style color string line "#6495ED" into a {@link Color} enum value. - #[wasm_bindgen] - pub fn parse(s: &str) -> Color { - let color = xlsx::Color::from(s); - Color { inner: color } - } - - /// The color Black with a RGB value of 0x000000. - #[wasm_bindgen] - pub fn black() -> Color { - Color::new(xlsx::Color::Black) - } - - /// The color Blue with a RGB value of 0x0000FF. - #[wasm_bindgen] - pub fn blue() -> Color { - Color::new(xlsx::Color::Blue) - } - - /// The color Brown with a RGB value of 0x800000. - #[wasm_bindgen] - pub fn brown() -> Color { - Color::new(xlsx::Color::Brown) - } - - /// The color Cyan with a RGB value of 0x00FFFF. - #[wasm_bindgen] - pub fn cyan() -> Color { - Color::new(xlsx::Color::Cyan) - } - - /// The color Gray with a RGB value of 0x808080. - #[wasm_bindgen] - pub fn gray() -> Color { - Color::new(xlsx::Color::Gray) - } - - /// The color Green with a RGB value of 0x008000. - #[wasm_bindgen] - pub fn green() -> Color { - Color::new(xlsx::Color::Green) - } - - /// The color Lime with a RGB value of 0x00FF00. - #[wasm_bindgen] - pub fn lime() -> Color { - Color::new(xlsx::Color::Lime) - } - - /// The color Magenta with a RGB value of 0xFF00FF. - #[wasm_bindgen] - pub fn magenta() -> Color { - Color::new(xlsx::Color::Magenta) - } - - /// The color Navy with a RGB value of 0x000080. - #[wasm_bindgen] - pub fn navy() -> Color { - Color::new(xlsx::Color::Navy) - } - - /// The color Orange with a RGB value of 0xFF6600. - #[wasm_bindgen] - pub fn orange() -> Color { - Color::new(xlsx::Color::Orange) - } - - /// The color Pink with a RGB value of 0xFFC0CB. - #[wasm_bindgen] - pub fn pink() -> Color { - Color::new(xlsx::Color::Pink) - } - - /// The color Purple with a RGB value of 0x800080. - #[wasm_bindgen] - pub fn purple() -> Color { - Color::new(xlsx::Color::Purple) - } - - /// The color Red with a RGB value of 0xFF0000. - #[wasm_bindgen] - pub fn red() -> Color { - Color::new(xlsx::Color::Red) - } - - /// The color Silver with a RGB value of 0xC0C0C0. - #[wasm_bindgen] - pub fn silver() -> Color { - Color::new(xlsx::Color::Silver) - } - - /// The color White with a RGB value of 0xFFFFFF. - #[wasm_bindgen] - pub fn white() -> Color { - Color::new(xlsx::Color::White) - } - - /// The color Yellow with a RGB value of 0xFFFF00 - #[wasm_bindgen] - pub fn yellow() -> Color { - Color::new(xlsx::Color::Yellow) +impl From:: for xlsx::Color { + fn from(value: Color) -> Self { + match value { + Color::RGB(rgb) => xlsx::Color::RGB(rgb), + Color::Theme(color, shade) => xlsx::Color::Theme(color, shade), + Color::Default => xlsx::Color::Default, + Color::Automatic => xlsx::Color::Automatic, + Color::Black => xlsx::Color::Black, + Color::Blue => xlsx::Color::Blue, + Color::Brown => xlsx::Color::Brown, + Color::Cyan => xlsx::Color::Cyan, + Color::Gray => xlsx::Color::Gray, + Color::Green => xlsx::Color::Green, + Color::Lime => xlsx::Color::Lime, + Color::Magenta => xlsx::Color::Magenta, + Color::Navy => xlsx::Color::Navy, + Color::Orange => xlsx::Color::Orange, + Color::Pink => xlsx::Color::Pink, + Color::Purple => xlsx::Color::Purple, + Color::Red => xlsx::Color::Red, + Color::Silver => xlsx::Color::Silver, + Color::White => xlsx::Color::White, + Color::Yellow => xlsx::Color::Yellow, + } } } diff --git a/rust/src/wrapper/format.rs b/rust/src/wrapper/format.rs index 33e7c36..c4be565 100644 --- a/rust/src/wrapper/format.rs +++ b/rust/src/wrapper/format.rs @@ -313,7 +313,7 @@ impl Format { /// TODO: example omitted #[wasm_bindgen(js_name = "setBorderColor", skip_jsdoc)] pub fn set_border_color(&self, color: Color) -> Format { - impl_method!(self.set_border_color(color.inner)); + impl_method!(self.set_border_color(color)); } /// Set the cell bottom border style. @@ -336,7 +336,7 @@ impl Format { /// @return {Format} - The Format instance. #[wasm_bindgen(js_name = "setBorderBottomColor", skip_jsdoc)] pub fn set_border_bottom_color(&self, color: Color) -> Format { - impl_method!(self.set_border_bottom_color(color.inner)); + impl_method!(self.set_border_bottom_color(color)); } /// Set the cell top border style. @@ -359,7 +359,7 @@ impl Format { /// @return {Format} - The Format instance. #[wasm_bindgen(js_name = "setBorderTopColor", skip_jsdoc)] pub fn set_border_top_color(&self, color: Color) -> Format { - impl_method!(self.set_border_top_color(color.inner)); + impl_method!(self.set_border_top_color(color)); } /// Set the cell left border style. @@ -382,7 +382,7 @@ impl Format { /// @return {Format} - The Format instance. #[wasm_bindgen(js_name = "setBorderLeftColor", skip_jsdoc)] pub fn set_border_left_color(&self, color: Color) -> Format { - impl_method!(self.set_border_left_color(color.inner)); + impl_method!(self.set_border_left_color(color)); } /// Set the cell right border style. @@ -405,7 +405,7 @@ impl Format { /// @return {Format} - The Format instance. #[wasm_bindgen(js_name = "setBorderRightColor", skip_jsdoc)] pub fn set_border_right_color(&self, color: Color) -> Format { - impl_method!(self.set_border_right_color(color.inner)); + impl_method!(self.set_border_right_color(color)); } /// Set the Format border diagonal property. @@ -432,7 +432,7 @@ impl Format { /// @return {Format} - The Format instance. #[wasm_bindgen(js_name = "setBorderDiagonalColor", skip_jsdoc)] pub fn set_border_diagonal_color(&self, color: Color) -> Format { - impl_method!(self.set_border_diagonal_color(color.inner)); + impl_method!(self.set_border_diagonal_color(color)); } /// Set the cell diagonal border direction type. @@ -470,7 +470,7 @@ impl Format { /// TODO: example omitted #[wasm_bindgen(js_name = "setFontColor", skip_jsdoc)] pub fn set_font_color(&self, color: Color) -> Format { - impl_method!(self.set_font_color(color.inner)); + impl_method!(self.set_font_color(color)); } /// Set the Format font family property. @@ -570,7 +570,7 @@ impl Format { /// TODO: example omitted #[wasm_bindgen(js_name = "setForegroundColor", skip_jsdoc)] pub fn set_foreground_color(&self, color: Color) -> Format { - impl_method!(self.set_foreground_color(color.inner)); + impl_method!(self.set_foreground_color(color)); } /// Set the Format pattern background color property. @@ -586,7 +586,7 @@ impl Format { /// TODO: example omitted #[wasm_bindgen(js_name = "setBackgroundColor", skip_jsdoc)] pub fn set_background_color(&self, color: Color) -> Format { - impl_method!(self.set_background_color(color.inner)); + impl_method!(self.set_background_color(color)); } /// Set the number format for a Format. diff --git a/rust/src/wrapper/note.rs b/rust/src/wrapper/note.rs index bf6ea88..4d12d00 100644 --- a/rust/src/wrapper/note.rs +++ b/rust/src/wrapper/note.rs @@ -68,7 +68,7 @@ impl Note { } #[wasm_bindgen(js_name = "setBackgroundColor", skip_jsdoc)] pub fn set_background_color(&self, color: Color) -> Note { - impl_method!(self.set_background_color(color.inner)); + impl_method!(self.set_background_color(color)); } #[wasm_bindgen(js_name = "setFontName", skip_jsdoc)] pub fn set_font_name(&self, font_name: &str) -> Note { diff --git a/rust/tools/rust-toolchain.toml b/rust/tools/rust-toolchain.toml index 74324bb..1c9923c 100644 --- a/rust/tools/rust-toolchain.toml +++ b/rust/tools/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2025-07-16" +channel = "nightly-2025-07-31" components = ["rustc", "cargo"] profile = "minimal" diff --git a/rust/tools/wrapper_generator/Cargo.lock b/rust/tools/wrapper_generator/Cargo.lock new file mode 100644 index 0000000..20c805d --- /dev/null +++ b/rust/tools/wrapper_generator/Cargo.lock @@ -0,0 +1,590 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + +[[package]] +name = "camino" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-manifest" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d8af896b707212cd0e99c112a78c9497dd32994192a463ed2f7419d29bd8c6" +dependencies = [ + "serde", + "thiserror", + "toml", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "clap" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "crate-inspector" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b5a358fe54350c5d7dea8c6d83191dedf33b3ba8fc947a6640d0948c3305e4" +dependencies = [ + "rustdoc-json", + "rustdoc-types", + "serde_json", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "hashbrown" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "ruast" +version = "0.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c1f05cb5b2fd678b0c38e2b97737509f7202e63cfb66278c963c6e4ee8a8c23" +dependencies = [ + "indenter", +] + +[[package]] +name = "rustdoc-json" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2651f0825c500d39b0471dd502356ee74f069b60de1d7c87e9b13bf41c68f8" +dependencies = [ + "cargo-manifest", + "cargo_metadata", + "serde", + "thiserror", + "toml", + "tracing", +] + +[[package]] +name = "rustdoc-types" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "814bc4dddadb32bef41ec20b836b35aa7aaf88356c721a0dd9cda41331394b8b" +dependencies = [ + "serde", + "serde_derive", +] + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +dependencies = [ + "serde", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "toml_write", + "winnow", +] + +[[package]] +name = "toml_write" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +dependencies = [ + "memchr", +] + +[[package]] +name = "wrapper_generator" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "crate-inspector", + "regex", + "ruast", + "rustdoc-types", +] diff --git a/rust/tools/wrapper_generator/Cargo.toml b/rust/tools/wrapper_generator/Cargo.toml new file mode 100644 index 0000000..75550f9 --- /dev/null +++ b/rust/tools/wrapper_generator/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "wrapper_generator" +version = "0.1.0" +edition = "2021" +description = "Tool to automatically generate wasm_xlsxwriter wrapper methods from rust_xlsxwriter" + +[dependencies] +anyhow = "1.0" +clap = { version = "4.4", features = ["derive"] } +crate-inspector = "0.3.0" +ruast = "0.0.20" +rustdoc-types = "0.54.0" +regex = "1.10.2" diff --git a/rust/tools/wrapper_generator/src/common.rs b/rust/tools/wrapper_generator/src/common.rs new file mode 100644 index 0000000..8775270 --- /dev/null +++ b/rust/tools/wrapper_generator/src/common.rs @@ -0,0 +1,37 @@ +use ruast::*; + +/// Generate common use statements for struct wrappers +pub fn generate_use_statements() -> Vec { + vec![ + // use rust_xlsxwriter as xlsx; + Use::from(Path::single("rust_xlsxwriter").chain_use_rename("xlsx")), + // use std::sync::{Arc, Mutex, MutexGuard} + Use::from(Path::single("std").chain("sync").chain_use_group(vec![ + UseTree::from(Path::single(PathSegment::new("Arc", None))), + UseTree::from(Path::single(PathSegment::new("Mutex", None))), + UseTree::from(Path::single(PathSegment::new("MutexGuard", None))), + ])), + // use wasm_bindgen::prelude::*; + Use::from( + Path::single("wasm_bindgen") + .chain("prelude") + .chain_use_glob(), + ), + // use crate::impl_method; + Use::from(Path::single("crate").chain("impl_method")), + ] +} + +/// Generate use statements for enum wrappers +pub fn generate_enum_use_statements() -> Vec { + vec![ + // use rust_xlsxwriter as xlsx; + Use::from(Path::single("rust_xlsxwriter").chain_use_rename("xlsx")), + // use wasm_bindgen::prelude::*; + Use::from( + Path::single("wasm_bindgen") + .chain("prelude") + .chain_use_glob(), + ), + ] +} \ No newline at end of file diff --git a/rust/tools/wrapper_generator/src/enum_wrapper.rs b/rust/tools/wrapper_generator/src/enum_wrapper.rs new file mode 100644 index 0000000..56cd39b --- /dev/null +++ b/rust/tools/wrapper_generator/src/enum_wrapper.rs @@ -0,0 +1,114 @@ +use crate_inspector::{CrateItem, EnumItem}; +use ruast::*; + +use crate::utils::{new_line, process_doc_comment}; + +pub fn generate_enum_wrapper_output( enum_info: &EnumItem) -> Crate { + let mut krate = Crate::new(); + let uses = crate::common::generate_enum_use_statements(); + let enum_ = generate_enum_wrapper(&enum_info); + let from_impl = generate_enum_impl_from(enum_info); + + // Build crate + uses.into_iter().for_each(|use_item| { + krate.add_item(use_item); + }); + krate.add_item(enum_); + krate.add_item(from_impl); + + krate +} + +fn generate_enum_wrapper(enum_info: &EnumItem) -> Item { + let mut enum_def = EnumDef::empty(enum_info.name()); + + // Add variants from original enum with their documentation + for variant in enum_info.variants() { + let variant_name = variant.name(); + let variant_def = Variant::empty(variant_name); + + // TODO: Add documentation for variants when crate_inspector supports it + // Currently VariantItem doesn't expose docs field + + enum_def.add_variant(variant_def); + } + + let mut item = Item::from(enum_def); + item.vis = Visibility::Public; + item.add_attr(new_line()); + + // Add documentation if available + if let Some(doc) = &enum_info.item().docs { + item.add_attr(Attribute::doc_comment(process_doc_comment(doc))); + } + + // Add derive attributes + item.add_attr(Attribute::normal(AttributeItem::new( + "derive", + AttrArgs::Delimited(DelimArgs::parenthesis( + vec![ + Token::Ident("Debug".to_string()).into_joint(), + Token::Comma, + Token::Ident("Clone".to_string()).into_joint(), + Token::Comma, + Token::Ident("Copy".to_string()).into_joint(), + Token::Comma, + Token::Ident("Default".to_string()), + ] + .into_tokens(), + )), + ))); + + // Add wasm_bindgen attribute + item.add_attr(Attribute::normal(AttributeItem::new( + "wasm_bindgen", + AttrArgs::Empty, + ))); + + item +} + +fn generate_enum_impl_from(enum_info: &EnumItem) -> Item { + let enum_name = enum_info.name(); + + // Generate match arms for each variant + let mut variants_match_arms: Vec = vec![]; + + for variant in enum_info.variants() { + let variant_name = variant.name(); + + // Create pattern: EnumName::VariantName + let pattern = Pat::ident(format!("{}::{}", enum_name, variant_name)); + + // Create expression: xlsx::EnumName::VariantName + let expression = Expr::from(ExprKind::Path( + Path::single("xlsx").chain(enum_name).chain(variant_name) + )); + + variants_match_arms.push(Arm::new(pattern, None, expression)); + } + + let match_expr = Match::new( + Expr::from(ExprKind::Path(Path::single("value"))), + variants_match_arms + ); + + let from_impl = Impl::new( + vec![], + Some(Type::Path(Path::single(PathSegment::new("From", Some(vec![GenericArg::Type( + Type::Path(Path::single(PathSegment::new(enum_name, None))), + )]))))), + Type::Path(Path::single(PathSegment::new("xlsx", None)).chain(PathSegment::new(enum_name, None))), + None, + vec![AssocItem::from(AssocItemKind::Fn(Fn::simple( + "from", + FnDecl::regular( + vec![Param::new(Pat::ident("value"), Type::Path(Path::single(enum_name)))], + Some(Type::Path(Path::single("Self"))) + ), + Block::single(ExprKind::Match(match_expr)), + )))], + ); + + Item::from(from_impl) +} \ No newline at end of file diff --git a/rust/tools/wrapper_generator/src/main.rs b/rust/tools/wrapper_generator/src/main.rs new file mode 100644 index 0000000..471fe37 --- /dev/null +++ b/rust/tools/wrapper_generator/src/main.rs @@ -0,0 +1,109 @@ +use anyhow::{Context, Result}; +use clap::Parser; +use crate_inspector::{CrateBuilder, CrateItem}; +use ruast::*; +use std::path::PathBuf; + +mod common; +mod enum_wrapper; +mod method_wrapper; +mod struct_wrapper; +mod utils; + +use crate::enum_wrapper::generate_enum_wrapper_output; +use crate::struct_wrapper::generate_struct_wrapper_output; + +/// Tool to automatically generate wasm_xlsxwriter wrapper methods from rust_xlsxwriter +#[derive(Parser, Debug)] +#[command(author, version, about, long_about = None)] +struct Args { + /// Path to the Cargo.toml of the crate to extract methods from + #[arg(short, long)] + manifest_path: String, + + /// Output directory path + #[arg(short, long)] + output_dir: PathBuf, +} + +fn main() -> Result<()> { + let args = Args::parse(); + + let krate = get_crate_info(&args.manifest_path, false).context("Failed to get crate info")?; + + // Create output directory if it doesn't exist + std::fs::create_dir_all(&args.output_dir) + .context(format!("Failed to create output directory: {:?}", args.output_dir))?; + + for struct_info in krate.all_structs() { + let struct_krate = generate_struct_wrapper_output(&struct_info); + + let module_path = struct_info.module().map(|m| m.name().to_string()).unwrap_or_default(); + let output_path = get_output_path(&args.output_dir, &module_path, &struct_info.name())?; + write_generated_file(&output_path, &struct_krate.to_string())?; + + println!("Generated struct wrapper: {:?} (module: {})", output_path, module_path); + } + + for enum_info in krate.all_enums() { + let enum_krate = generate_enum_wrapper_output(&enum_info); + + let module_path = enum_info.module().map(|m| m.name().to_string()).unwrap_or_default(); + let output_path = get_output_path(&args.output_dir, &module_path, &enum_info.name())?; + write_generated_file(&output_path, &enum_krate.to_string())?; + + println!("Generated enum wrapper: {:?} (module: {})", output_path, module_path); + } + + println!("All wrappers generated in directory: {:?}", args.output_dir); + + Ok(()) +} + + +fn get_crate_info(manifest_path: &str, document_private: bool) -> Result { + Ok(CrateBuilder::default() + .manifest_path(manifest_path) + .document_private_items(document_private) + .build() + .context("Failed to build crate inspector")?) +} + +fn get_output_path(base_dir: &PathBuf, module_path: &str, item_name: &str) -> Result { + // For items in a module, create subdirectory structure + // e.g., format.rs -> FormatAlign should go to format/format_align.rs + let module_dir = if module_path.is_empty() { + base_dir.clone() + } else { + // Create subdirectory named after the module + base_dir.join(module_path) + }; + + // Convert item name like "FormatAlign" to snake_case filename + let filename = format!("{}.rs", to_snake_case(item_name)); + Ok(module_dir.join(filename)) +} + +fn to_snake_case(s: &str) -> String { + let mut result = String::new(); + for (i, c) in s.chars().enumerate() { + if i > 0 && c.is_uppercase() { + result.push('_'); + } + result.push(c.to_lowercase().next().unwrap()); + } + result +} + +fn write_generated_file(path: &PathBuf, content: &str) -> Result<()> { + // Create parent directories if they don't exist + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent) + .context(format!("Failed to create directory: {:?}", parent))?; + } + + std::fs::write(path, content) + .context(format!("Failed to write file: {:?}", path))?; + + Ok(()) +} diff --git a/rust/tools/wrapper_generator/src/method_wrapper.rs b/rust/tools/wrapper_generator/src/method_wrapper.rs new file mode 100644 index 0000000..6c9a4ad --- /dev/null +++ b/rust/tools/wrapper_generator/src/method_wrapper.rs @@ -0,0 +1,389 @@ +use crate::utils::{new_line, process_doc_comment, to_camel_case}; +use crate_inspector::{CrateItem, FunctionItem, StructItem}; +use ruast::*; +/// Generate common methods for the struct +/// new, lock, deep_clone +pub(crate) fn generate_common_methods(struct_info: &StructItem) -> Vec> { + let mut items = vec![]; + let impl_items: Vec<_> = struct_info.associated_impls().collect(); + let existing_new_fn = impl_items.iter() + .filter_map(|impl_item| impl_item.get_function("new")) + .next(); + + if let Some(existing_fn) = existing_new_fn { + let params: Vec = existing_fn + .sig() + .inputs + .iter() + .map(|(name, _)| { + Param::new( + Pat::ident(name), + Type::Path(Path::single("xlsx").chain(struct_info.name())), + ) + }) + .collect(); + let args = params + .iter() + .map(|p| Expr::from(ExprKind::Path(Path::single(p.pat.to_string())))) + .collect(); + + let mut new_fn = Item::from(Fn::simple( + "new", + FnDecl::regular( + params, + Some(Type::Path(Path::single(PathSegment::new( + struct_info.name(), + None, + )))), + ), + Block::single(ExprKind::Struct(Struct::new( + Path::single(struct_info.name()), + vec![ExprField::new( + "inner", + ExprKind::from(ExprKind::call( + ExprKind::Path(Path::single("Arc").chain("new")), + vec![Expr::from(ExprKind::call( + ExprKind::Path(Path::single("Mutex").chain("new")), + vec![Expr::from(ExprKind::call( + ExprKind::Path( + Path::single("xlsx").chain(struct_info.name()).chain("new"), + ), + args, + ))], + ))], + )), + )], + ))), + )); + + new_fn.vis = Visibility::Public; + if let Some(doc) = &existing_fn.item().docs { + new_fn.add_attr(Attribute::doc_comment(process_doc_comment(doc))); + } + // Add wasm_bindgen constructor attribute + new_fn.add_attr(Attribute::normal(AttributeItem::new( + "wasm_bindgen", + AttrArgs::Delimited(DelimArgs::parenthesis( + vec![Token::Ident("constructor".to_string())].into_tokens(), + )), + ))); + + items.push(new_fn); + } + + let mut lock_fn = Item::from(Fn::simple( + "lock", + FnDecl::regular( + vec![Param::ref_self()], + Some(Type::poly_path( + "MutexGuard", + vec![GenericArg::Type(Type::Path( + Path::single("xlsx").chain(struct_info.name()), + ))], + )), + ), + Block::single(ExprKind::method_call0( + ExprKind::from(ExprKind::method_call0( + ExprKind::from(ExprKind::field( + ExprKind::Path(Path::single("self")), + "inner", + )), + "lock", + )), + "unwrap", + )), + )); + + lock_fn.vis = Visibility::crate_(); + lock_fn.add_attr(new_line()); + items.push(lock_fn); + + let mut deep_clone_fn = Item::from(Fn::simple( + "deep_clone", + FnDecl::regular( + vec![Param::ref_self()], + Some(Type::Path(Path::single(PathSegment::new( + struct_info.name(), + None, + )))), + ), + Block::new( + vec![ + Stmt::Local(Local::new( + "inner", + None, + ExprKind::method_call0( + ExprKind::from(ExprKind::method_call0( + ExprKind::from(ExprKind::field( + ExprKind::Path(Path::single("self")), + "inner", + )), + "lock", + )), + "unwrap", + ), + )), + Stmt::from(Expr::from(Struct::new( + Path::single(struct_info.name()), + vec![ExprField::new( + "inner", + ExprKind::from(ExprKind::call( + ExprKind::Path(Path::single("Arc").chain("new")), + vec![Expr::from(ExprKind::call( + ExprKind::Path(Path::single("Mutex").chain("new")), + vec![Expr::from(ExprKind::method_call0( + ExprKind::Path(Path::single("inner")), + "clone", + ))], + ))], + )), + )], + ))), + ], + None, + ), + )); + + deep_clone_fn.vis = Visibility::Public; + deep_clone_fn.add_attr(new_line()); + deep_clone_fn.add_attr(Attribute::doc_comment(format!( + "/// Deep clones a {} object.", + struct_info.name() + ))); + deep_clone_fn.add_attr(Attribute::normal(AttributeItem::new( + "wasm_bindgen", + AttrArgs::Delimited(DelimArgs::parenthesis( + vec![ + Token::Ident("js_name".to_string()), + Token::Eq, + Token::Lit(Lit::str("clone")), + ] + .into_tokens(), + )), + ))); + items.push(deep_clone_fn); + + items +} + +/// Generate wrapper methods for the original struct's methods and functions + +pub(crate) fn generate_wrapper_methods(struct_info: &StructItem) -> Vec> { + let impls = struct_info.associated_impls().collect::>(); + + let all_methods: Vec<_> = impls.iter() + .flat_map(|impl_item| impl_item.functions().filter(|f| f.is_method()).collect::>()) + .collect(); + + let all_functions: Vec<_> = impls.iter() + .flat_map(|impl_item| impl_item.functions().filter(|f| !f.is_method() && f.name() != "new").collect::>()) + .collect(); + + // Map methods and functions to wrapper methods + let wrapped_methods = all_methods.into_iter() + .map(|method| create_wrapper_method(&method, struct_info)); + let wrapped_functions = all_functions.into_iter() + .map(|method| create_wrapper_method(&method, struct_info)); + + wrapped_functions.chain(wrapped_methods).collect() +} + +/// Create a wrapper method for a single function +fn create_wrapper_method(function: &FunctionItem, struct_info: &StructItem) -> Item { + let sig = function.sig(); + let generics = function.generics(); + let mut params = Vec::new(); + let mut call_args = Vec::new(); + + // For methods, the first parameter is always a reference to self + if function.is_method() { + params.push(Param::ref_self()); + } + + for (i, (name, ty)) in sig.inputs.iter().enumerate() { + // Skip the first parameter for methods (self) + if function.is_method() && i == 0 { + continue; + } + + params.push(Param::new(Pat::ident(name), determine_param_type(ty, generics))); + + let arg_expr = match ty { + rustdoc_types::Type::ResolvedPath(_) => Expr::from(ExprKind::method_call0( + ExprKind::Path(Path::single(name)), + "into", + )), + rustdoc_types::Type::BorrowedRef{ type_, .. } => { + if let rustdoc_types::Type::ResolvedPath(_) = &**type_ { + Expr::from(ExprKind::method_call0( + ExprKind::Path(Path::single(name)), + "into", + )) + } else { + Expr::from(ExprKind::Path(Path::single(name))) + } + } + _ => { + // For other types, just pass the name + Expr::from(ExprKind::Path(Path::single(name))) + } + }; + call_args.push(arg_expr); + } + + let method_body_macro = if function.is_method() { + // For methods, create a self.method_name(args) expression + let receiver = Expr::from(Path::single("self")); + let method_segment = PathSegment::simple(function.name()); + let args: Vec = call_args + .iter() + .map(|arg| Expr::from(Path::single(arg.to_string()))) + .collect(); + + let method_call = MethodCall::new(receiver, method_segment, args); + TokenStream::from(method_call) + } else { + // For functions, just use the arguments + let mut tokens = Vec::new(); + for (i, arg) in call_args.iter().enumerate() { + if i > 0 { + tokens.push(Token::Comma); + } + tokens.push(Token::Ident(arg.to_string()).into_joint()); + } + tokens.into_tokens() + }; + + let method_body = if function.is_method() { + Block::new( + vec![Stmt::Semi(Semi(Expr::from(MacCall::new( + Path::single("impl_method"), + DelimArgs::parenthesis(method_body_macro), + ))))], + None, + ) + } else { + Block::new( + vec![ + Stmt::Local(Local::simple( + "result", + ExprKind::Call(Call::new( + Path::single("xlsx") + .chain(struct_info.name()) + .chain(function.name()), + vec![], + )), + )), + Stmt::Expr(Expr::from(ExprKind::Struct(Struct::new( + struct_info.name(), + vec![ExprField::new( + "inner", + ExprKind::from(ExprKind::call( + ExprKind::Path(Path::single("Arc").chain("new")), + vec![Expr::from(ExprKind::call( + ExprKind::Path(Path::single("Mutex").chain("new")), + vec![Expr::from(ExprKind::Path(Path::single("result")))], + ))], + )), + )], + )))), + ], + None, + ) + }; + + // Create the function + let mut wrapper_fn = Item::from(Fn::simple( + function.name(), + FnDecl::regular( + params, + Some(Type::Path(Path::single(PathSegment::new( + struct_info.name(), + None, + )))), + ), + method_body, + )); + + wrapper_fn.vis = Visibility::Public; + wrapper_fn.add_attr(new_line()); + // inherit documentation if available + if let Some(doc) = &function.item().docs { + wrapper_fn.add_attr(Attribute::doc_comment(process_doc_comment(doc))); + } + // Add wasm_bindgen attribute with js_name in camelCase + let js_name = to_camel_case(function.name()); + wrapper_fn.add_attr(Attribute::normal(AttributeItem::new( + "wasm_bindgen", + AttrArgs::Delimited(DelimArgs::parenthesis( + vec![ + Token::Ident("js_name".to_string()), + Token::Eq, + Token::Lit(Lit::str(&js_name)).into_joint(), + Token::Comma, + Token::Ident("skip_jsdoc".to_string()), + ] + .into_tokens(), + )), + ))); + + wrapper_fn +} + +/// Determine the parameter type recursively +fn determine_param_type(ty: &rustdoc_types::Type, generics: &rustdoc_types::Generics) -> Type { + let extract_into_inner = |path: &Vec| { + if let Some(rustdoc_types::GenericBound::TraitBound { trait_, .. }) = path.first() { + if trait_.path == "Into" || trait_.path.ends_with("::Into") { + if let Some(args) = &trait_.args { + if let rustdoc_types::GenericArgs::AngleBracketed { args, .. } = &**args { + if args.len() == 1 { + if let rustdoc_types::GenericArg::Type(inner_type) = &args[0] { + return determine_param_type(inner_type, generics); + } + } + } + } + } + } + eprintln!("Warning: Unsupported type for Into: {:?}", path); + return Type::Path(Path::single("String")); // Fallback to String type + }; + match ty { + rustdoc_types::Type::ResolvedPath(path) => Type::Path(Path::single(&path.path)), + rustdoc_types::Type::Primitive(prim) => Type::Path(Path::single(prim)), + rustdoc_types::Type::BorrowedRef { type_, .. } => determine_param_type(type_, generics), + rustdoc_types::Type::Array { type_, .. } => { + let inner_type = determine_param_type(type_, generics); + Type::poly_path("Vec", vec![GenericArg::Type(inner_type)]) + } + rustdoc_types::Type::ImplTrait(impl_trait) => { + extract_into_inner(impl_trait) + } + rustdoc_types::Type::Generic(name) => { + let generic_param = generics.params.iter() + .find(|param| param.name == *name) + .expect("Generic type not found in generics"); + + for pred in generics.where_predicates.iter() { + if let rustdoc_types::WherePredicate::BoundPredicate{ type_, bounds , .. } = pred { + if let rustdoc_types::Type::Generic(generic_name) = type_ { + if *generic_name == *name { + return extract_into_inner(bounds); + } + } + } + } + + let param_bounds = match &generic_param.kind { + rustdoc_types::GenericParamDefKind::Type{bounds, ..} => bounds, + _ => panic!("Unsupported generic parameter type: {:?}", generic_param), + }; + extract_into_inner(param_bounds) + } + _ => { + eprintln!("Warning: Unsupported type: {:?}", ty); + Type::Path(Path::single("String")) // Fallback to String type + } + } +} diff --git a/rust/tools/wrapper_generator/src/struct_wrapper.rs b/rust/tools/wrapper_generator/src/struct_wrapper.rs new file mode 100644 index 0000000..ff0cfbd --- /dev/null +++ b/rust/tools/wrapper_generator/src/struct_wrapper.rs @@ -0,0 +1,146 @@ +use crate_inspector::{CrateItem, StructItem}; +use crate::method_wrapper::{generate_common_methods, generate_wrapper_methods}; +use ruast::*; + +use crate::utils::{new_line, process_doc_comment}; + +pub fn generate_struct_wrapper_output(struct_info: &StructItem) -> Crate { + let mut krate = Crate::new(); + let uses = crate::common::generate_use_statements(); + let struct_ = generate_struct_wrapper(&struct_info); + + let common_methods = generate_common_methods(&struct_info); + let wrapper_methods = generate_wrapper_methods(&struct_info); + + let from_impls = generate_impl_from(struct_info.name()); + + let mut impl_block = Item::from(Impl::simple( + Type::Path(Path::single(PathSegment::new(struct_info.name(), None))), + common_methods.into_iter().chain(wrapper_methods).collect(), + )); + impl_block.add_attr(new_line()); + impl_block.add_attr(Attribute::normal(AttributeItem::new( + "wasm_bindgen", + AttrArgs::Empty, + ))); + + // Build crate + uses.into_iter().for_each(|use_item| { + krate.add_item(use_item); + }); + krate.add_item(struct_); + from_impls.into_iter().for_each(|from_impl| { + krate.add_item(from_impl); + }); + krate.add_item(impl_block); + + return krate +} + +fn generate_struct_wrapper(struct_info: &StructItem) -> Item { + let mut struct_def = StructDef::empty(struct_info.name()); + struct_def.add_field(FieldDef::new( + Visibility::crate_(), + Some("inner"), + Type::poly_path( + "Arc", + vec![GenericArg::Type(Type::poly_path( + "Mutex", + vec![GenericArg::Type(Type::Path( + Path::single("xlsx").chain(struct_info.name()), + ))], + ))], + ), + )); + + let mut item = Item::from(struct_def); + item.vis = Visibility::Public; + item.add_attr(new_line()); + if let Some(doc) = &struct_info.item().docs { + item.add_attr(Attribute::doc_comment(process_doc_comment(doc))); + } + item.add_attr(Attribute::normal(AttributeItem::new( + "derive", + AttrArgs::Delimited(DelimArgs::parenthesis( + vec![ + Token::Ident("Debug".to_string()).into_joint(), + Token::Comma, + Token::Ident("Clone".to_string()), + ] + .into_tokens(), + )), + ))); + item.add_attr(Attribute::normal(AttributeItem::new( + "wasm_bindgen", + AttrArgs::Empty, + ))); + + item +} + +fn generate_impl_from(struct_name: &str) -> Vec> { + // First implementation: impl From for xlsx::StructName + let from_impl1 = Impl::new( + vec![], + Some(Type::Path(Path::single(PathSegment::new("From", Some(vec![GenericArg::Type( + Type::Path(Path::single(PathSegment::new(struct_name, None))), + )]))))), + Type::Path(Path::single(PathSegment::new("xlsx", None)).chain(PathSegment::new(struct_name, None))), + None, + vec![AssocItem::from(AssocItemKind::Fn(Fn::simple( + "from", + FnDecl::regular( + vec![Param::new(Pat::ident("format"), Type::Path(Path::single(struct_name)))], + Some(Type::Path(Path::single("Self"))) + ), + Block::single( + ExprKind::method_call0( + ExprKind::from(ExprKind::method_call0( + ExprKind::Path(Path::single("format")), + "lock" + )), + "clone" + ) + ), + )))], + ); + + // Second implementation: impl From for &'static xlsx::StructName + let from_impl2 = Impl::new( + vec![], + Some(Type::Path(Path::single(PathSegment::new("From", Some(vec![GenericArg::Type( + Type::Path(Path::single(PathSegment::new(struct_name, None))), + )]))))), + Type::static_ref(Type::Path(Path::single(PathSegment::new("xlsx", None)).chain(PathSegment::new(struct_name, None)))), + None, + vec![AssocItem::from(AssocItemKind::Fn(Fn::simple( + "from", + FnDecl::regular( + vec![Param::new(Pat::ident("format"), Type::Path(Path::single(struct_name)))], + Some(Type::Path(Path::single("Self"))) + ), + Block::single( + ExprKind::call( + ExprKind::Path(Path::single("Box").chain("leak")), + vec![ + Expr::from(ExprKind::call( + ExprKind::Path(Path::single("Box").chain("new")), + vec![ + Expr::from(ExprKind::method_call0( + ExprKind::from(ExprKind::method_call0( + ExprKind::Path(Path::single("format")), + "lock" + )), + "clone" + )) + ] + )) + ] + ) + ), + )))], + ); + + // Return both implementations as a vector + vec![Item::from(from_impl1), Item::from(from_impl2)] +} \ No newline at end of file diff --git a/rust/tools/wrapper_generator/src/utils.rs b/rust/tools/wrapper_generator/src/utils.rs new file mode 100644 index 0000000..3ae41ad --- /dev/null +++ b/rust/tools/wrapper_generator/src/utils.rs @@ -0,0 +1,97 @@ +use regex::Regex; +use ruast::Attribute; + +/// FIXME: Hacky workaround to pretty format source code +pub(crate) fn new_line() -> Attribute { + Attribute::doc_comment("") +} + +/// Add "/// " to the start of each line +fn add_doc_comment_marker(s: &str) -> String { + s.lines() + .map(|line| format!("/// {}", line.trim())) + .collect::>() + .join("\n") +} + +/// Find the first occurrence of "# Example" and return everything before it, +/// because it's not relevant for the generated javascript code +fn omit_after_example(s: &str) -> &str { + if let Some(pos) = s.find("# Example") { + s[..pos].trim() + } else { + s.trim() + } +} + +/// Convert a snake_case string to camelCase +pub(crate) fn to_camel_case(s: &str) -> String { + let mut result = String::new(); + let mut capitalize_next = false; + + for c in s.chars() { + if c == '_' { + capitalize_next = true; + } else if capitalize_next { + result.push(c.to_ascii_uppercase()); + capitalize_next = false; + } else { + result.push(c); + } + } + + result +} + +/// Convert method names inside square brackets to camelCase +/// +/// For example, [`Format::set_italic()`] becomes [`Format::setItalic()`] +fn convert_bracket_methods_to_camel_case(s: &str) -> String { + let re = Regex::new(r"\[\`([^`]+)::([\w_]+)(\([^)]*\))\`\]").unwrap(); + + re.replace_all(s, |caps: ®ex::Captures| { + let module = &caps[1]; + let method = &caps[2]; + let args = &caps[3]; + + format!("[`{}::{}{}`]", module, to_camel_case(method), args) + }) + .to_string() +} + +/// Process documentation comments by: +/// 1. Removing content after "# Example" +/// 2. Converting method names in square brackets to camelCase +/// 3. Adding doc comment markers ("/// ") to each line +/// +/// This function combines the functionality of `omit_after_example`, +/// `convert_bracket_methods_to_camel_case`, and `add_doc_comment_marker`. +pub(crate) fn process_doc_comment(s: &str) -> String { + let trimmed = omit_after_example(s); + let camel_cased = convert_bracket_methods_to_camel_case(trimmed); + add_doc_comment_marker(&camel_cased) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_convert_bracket_methods_to_camel_case() { + let input = "This is a reference to [`Format::set_italic()`] method."; + let expected = "This is a reference to [`Format::setItalic()`] method."; + assert_eq!(convert_bracket_methods_to_camel_case(input), expected); + + // Test with multiple references + let input = "See [`Format::set_italic()`] and [`Chart::add_series()`]."; + let expected = "See [`Format::setItalic()`] and [`Chart::addSeries()`]."; + assert_eq!(convert_bracket_methods_to_camel_case(input), expected); + } + + #[test] + fn test_process_doc_comment() { + let input = "Unset the italic Format property back to its default \"off\" state.\n\nThe opposite of [`Format::set_italic()`].\n\n# Example\nThis should be removed."; + let expected = "/// Unset the italic Format property back to its default \"off\" state.\n/// \n/// The opposite of [`Format::setItalic()`]."; + assert_eq!(process_doc_comment(input), expected); + } +} diff --git a/test/chart.test.ts b/test/chart.test.ts index 42af9e7..a0a2570 100644 --- a/test/chart.test.ts +++ b/test/chart.test.ts @@ -12,7 +12,6 @@ import { ChartMarkerType, ChartSolidFill, ChartFormat, - Color, ChartLine, ChartLayout, ChartGradientFill, @@ -56,8 +55,8 @@ describe("xlsx-wasm test", () => { const chartDataLabel1 = new ChartDataLabel().setFont(chartFont).showValue().setPosition(ChartDataLabelPosition.Left); const chartSeries1 = new ChartSeries(); - const chartLine1 = new ChartLine().setColor(Color.green()); - const chartSolidFill1 = new ChartSolidFill().setColor(Color.green()); + const chartLine1 = new ChartLine().setColor("Green"); + const chartSolidFill1 = new ChartSolidFill().setColor("Green"); const chartFormat1 = new ChartFormat().setLine(chartLine1).setSolidFill(chartSolidFill1); const chartMarker1 = new ChartMarker().setType(ChartMarkerType.Circle).setSize(10).setFormat(chartFormat1); const categoriesRange1 = ChartRange.newFromRange("Sheet1", 1, 0, 6, 0); @@ -82,9 +81,9 @@ describe("xlsx-wasm test", () => { .setName("Score 2") .setCategories(categoriesRange2) .setValues(valuesRange2) - .setFormat(chartFormat2.setLine(chartLine2.setColor(Color.purple())).setSolidFill(chartSolidFill2.setColor(Color.purple()))) + .setFormat(chartFormat2.setLine(chartLine2.setColor("Purple")).setSolidFill(chartSolidFill2.setColor("Purple"))) .setDataLabel(chartDataLabel2.setFont(chartFont).showValue().setPosition(ChartDataLabelPosition.Left)) - .setMarker(chartMarker2.setType(ChartMarkerType.Diamond).setSize(10).setFormat(chartFormat2.setLine(chartLine2.setColor(Color.purple())).setSolidFill(chartSolidFill2.setColor(Color.purple())))); + .setMarker(chartMarker2.setType(ChartMarkerType.Diamond).setSize(10).setFormat(chartFormat2.setLine(chartLine2.setColor("Purple")).setSolidFill(chartSolidFill2.setColor("Purple")))); chart .pushSeries(chartSeries1) .pushSeries(chartSeries2); @@ -123,7 +122,7 @@ describe("xlsx-wasm test", () => { const categoriesRange = ChartRange.newFromRange("Sheet1", 1, 0, 6, 0); const chartSeries1 = new ChartSeries(); - const chartGradientFill1 = new ChartGradientFill().setType(ChartGradientFillType.Linear).setAngle(45).setGradientStops([new ChartGradientStop(Color.green(), 0), new ChartGradientStop(Color.yellow(), 100)]); + const chartGradientFill1 = new ChartGradientFill().setType(ChartGradientFillType.Linear).setAngle(45).setGradientStops([new ChartGradientStop("Green", 0), new ChartGradientStop("Yellow", 100)]); const chartFormat1 = new ChartFormat().setGradientFill(chartGradientFill1); const valuesRange1 = ChartRange.newFromRange("Sheet1", 1, 1, 6, 1); chartSeries1 @@ -133,7 +132,7 @@ describe("xlsx-wasm test", () => { .setFormat(chartFormat1); const chartSeries2 = new ChartSeries(); - const chartPatternFill = new ChartPatternFill().setPattern(ChartPatternFillType.DiagonalBrick).setBackgroundColor(Color.purple()); + const chartPatternFill = new ChartPatternFill().setPattern(ChartPatternFillType.DiagonalBrick).setBackgroundColor("Purple"); const chartFormat2 = new ChartFormat().setPatternFill(chartPatternFill); const valuesRange2 = ChartRange.newFromRange("Sheet1", 1, 2, 6, 2); chartSeries2 diff --git a/test/format.test.ts b/test/format.test.ts index eb9d243..d17d6b9 100644 --- a/test/format.test.ts +++ b/test/format.test.ts @@ -1,5 +1,4 @@ import { - Color, ExcelDateTime, Format, FormatAlign, @@ -108,7 +107,7 @@ describe("xlsx-wasm test", () => { worksheet.writeStringWithFormat(0, 0, "AAA", format1); const format2 = new Format() - .setBorderColor(Color.rgb(0x00ff00)) + .setBorderColor({ RGB: 0x00ff00 }) .setBorderBottom(FormatBorder.Dashed); worksheet.writeStringWithFormat(0, 1, "BBB", format2); @@ -119,7 +118,7 @@ describe("xlsx-wasm test", () => { const format4 = new Format() .setBorderDiagonal(FormatBorder.Dotted) - .setBorderDiagonalColor(Color.rgb(0xff0000)); + .setBorderDiagonalColor({ RGB: 0xff0000 }); worksheet.writeStringWithFormat(0, 3, "DDD", format4); const format5 = new Format() @@ -141,11 +140,11 @@ describe("xlsx-wasm test", () => { const worksheet = workbook.addWorksheet(); // Act - const black = Color.black(); - const purple = Color.purple(); - const blue = Color.rgb(0x0000ff); - const red = Color.parse("#FF0000"); - const yellow = Color.yellow(); + const black = "Black"; + const purple = "Purple"; + const blue = { RGB: 0x0000ff }; + const red = { RGB: 0xff0000 }; + const yellow = "Yellow"; worksheet.writeStringWithFormat( 0, @@ -239,7 +238,7 @@ describe("xlsx-wasm test", () => { const worksheet = workbook.addWorksheet(); const baseFormat = new Format().setBold(); const format1 = baseFormat.clone().setItalic(); - const format2 = baseFormat.clone().setFontColor(Color.red()); + const format2 = baseFormat.clone().setFontColor("Red"); worksheet.writeStringWithFormat(0, 0, "bold", baseFormat); worksheet.writeStringWithFormat(0, 1, "bold italic", format1); diff --git a/test/note.test.ts b/test/note.test.ts index e07a1c9..d4095a7 100644 --- a/test/note.test.ts +++ b/test/note.test.ts @@ -1,4 +1,4 @@ -import { Color, Format, Note, Workbook, ObjectMovement } from "../web"; +import { Format, Note, Workbook, ObjectMovement } from "../web"; import { describe, test, beforeAll, expect } from "vitest"; import { initWasModule, readXlsx, readXlsxFile } from "./common"; @@ -17,14 +17,14 @@ describe("xlsx-wasm test", () => { .addAuthorPrefix(false) .setWidth(200) .setHeight(100) - .setBackgroundColor(Color.purple()) + .setBackgroundColor("Purple") .setFontName("Meiryo UI") .setFontSize(20) .setAltText("Alt text") .setObjectMovement(ObjectMovement.DontMoveOrSizeWithCells); note1.resetText("This is other note"); const format = new Format() - .setBackgroundColor(Color.green()) + .setBackgroundColor("Green") .setFontName("Meiryo UI") .setFontSize(11); const note2 = new Note("This is a note") diff --git a/test/print.test.ts b/test/print.test.ts index 387ac2d..be7fb6f 100644 --- a/test/print.test.ts +++ b/test/print.test.ts @@ -1,4 +1,4 @@ -import { Color, Format, HeaderImagePosition, Image, Workbook } from "../web"; +import { Format, HeaderImagePosition, Image, Workbook } from "../web"; import { describe, test, beforeAll, expect } from "vitest"; import { initWasModule, loadFile, readXlsx, readXlsxFile } from "./common"; @@ -16,8 +16,7 @@ describe("xlsx-wasm test", () => { // Act const worksheet = workbook.addWorksheet(); - const purple = Color.purple(); - const format = new Format().setFontColor(purple); + const format = new Format().setFontColor("Purple"); worksheet.writeWithFormat(0, 0, "Hello, World!", format); worksheet.setLandscape();