From 3823ae670c5644bb92c7fed7936bcadf19693ed0 Mon Sep 17 00:00:00 2001 From: Joel Natividad <1980690+jqnatividad@users.noreply.github.com> Date: Sat, 2 Sep 2023 09:22:06 -0400 Subject: [PATCH 1/4] use upstream version of calamine --- Cargo.lock | 9 ++++----- Cargo.toml | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80509109f..01a27f83a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -912,8 +912,7 @@ checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" [[package]] name = "calamine" version = "0.21.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95f023f9ae2c2f017564b7eca85660c3c09477bb037f2fbfbaaba732b5642a32" +source = "git+https://github.com/tafia/calamine?rev=a54bd9c#a54bd9cddc61a4a30475c4454efbbb2eecf9f975" dependencies = [ "byteorder", "chrono", @@ -921,7 +920,7 @@ dependencies = [ "encoding_rs", "log", "once_cell", - "quick-xml 0.28.2", + "quick-xml 0.30.0", "serde", "zip", ] @@ -4101,9 +4100,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.28.2" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce5e73202a820a31f8a0ee32ada5e21029c81fd9e3ebf668a40832e4219d9d1" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" dependencies = [ "encoding_rs", "memchr", diff --git a/Cargo.toml b/Cargo.toml index bdddced48..f52597c2d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -226,6 +226,7 @@ serial_test = { version = "2.0", features = ["file_locks"] } [patch.crates-io] geosuggest-core = { git = "https://github.com/estin/geosuggest", rev = "5c6b08b" } geosuggest-utils = { git = "https://github.com/estin/geosuggest", rev = "5c6b08b" } +calamine = { git = "https://github.com/tafia/calamine", rev = "a54bd9c" } [features] default = ["mimalloc"] From 1f310ae9dd7f95b4d222c236aba62ae1a232c739 Mon Sep 17 00:00:00 2001 From: Joel Natividad <1980690+jqnatividad@users.noreply.github.com> Date: Sat, 2 Sep 2023 09:25:47 -0400 Subject: [PATCH 2/4] `excel`: moar metadata; moar examples! - now retrieves SheetType (Worksheet, DialogSheet, MacroSheet, ChartSheet, VBA) - now retrieves Visibility: (Visible, Hidden, VeryHidden) - returns an error when trying to export a non Worksheet SheetType sheet - added examples beyond just referring to test suite --- src/cmd/excel.rs | 72 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 68 insertions(+), 4 deletions(-) diff --git a/src/cmd/excel.rs b/src/cmd/excel.rs index 828582c06..01c06e09d 100644 --- a/src/cmd/excel.rs +++ b/src/cmd/excel.rs @@ -2,7 +2,50 @@ static USAGE: &str = r#" Exports a specified Excel/ODS sheet to a CSV file. The first row of a sheet is assumed to be the header row. -For examples, see https://github.com/jqnatividad/qsv/blob/master/tests/test_excel.rs. +Examples: + +Export the first sheet of an Excel file to a CSV file: + qsv excel input.xlsx > output.csv + qsv excel input.xlsx --output output.csv + +Export the first sheet of an ODS file to a CSV file: + qsv excel input.ods > output.csv + qsv excel input.ods -o output.csv + +Export the first sheet of an Excel file to a CSV file with different delimiters: + # semicolon + qsv excel input.xlsx -d ";" > output.csv + # tab + qsv excel input.xlsx -d "\t" > output.tsv + +Export a sheet by name (case-insensitive): + qsv excel --sheet "Sheet 3" input.xlsx + +Export a sheet by index: + # this exports the 3nd sheet (0-based index) + qsv excel -s 2 input.xlsx + +Export the last sheet (negative index)): + qsv excel -s -1 input.xlsx + +Export the second to last sheet: + qsv excel -s -2 input.xls + +Export a range of cells in the first sheet: + qsv excel --range C3:T25 input.xlsx + +Export a range of cells in the second sheet: + qsv excel --range C3:T25 -s 1 input.xlsx + +Export metadata for all sheets in CSV format: + qsv excel --metadata c input.xlsx + +Export metadata for all sheets in JSON format: + qsv excel --metadata j input.xlsx + # pretty-printed JSON + qsv excel --metadata J input.xlsx + +For more examples, see https://github.com/jqnatividad/qsv/blob/master/tests/test_excel.rs. Usage: qsv excel [options] [] @@ -53,7 +96,7 @@ Common options: use std::{cmp, fmt::Write, path::PathBuf}; -use calamine::{open_workbook_auto, DataType, Range, Reader}; +use calamine::{open_workbook_auto, DataType, Range, Reader, SheetType}; use indicatif::HumanCount; #[cfg(any(feature = "feature_capable", feature = "lite"))] use indicatif::{ProgressBar, ProgressDrawTarget}; @@ -92,6 +135,8 @@ enum MetadataMode { struct SheetMetadata { index: usize, name: String, + typ: String, + visible: String, headers: Vec, num_columns: usize, num_rows: usize, @@ -251,7 +296,13 @@ pub fn run(argv: &[&str]) -> CliResult<()> { match result { Ok(result) => result, Err(e) => { - return fail_clierror!("Cannot retrieve range from {sheet_name}: {e}."); + let sheet_type = workbook.sheets_metadata()[i].typ; + if sheet_type == SheetType::ChartSheet { + // return an empty range for ChartSheet + Range::empty() + } else { + return fail_clierror!("Cannot retrieve range from {sheet_name}: {e}."); + } }, } } else { @@ -308,6 +359,8 @@ pub fn run(argv: &[&str]) -> CliResult<()> { let sheetmetadata_struct = SheetMetadata { index: i, name: sheet_name.to_string(), + typ: format!("{:?}", workbook.sheets_metadata()[i].typ), + visible: format!("{:?}", workbook.sheets_metadata()[i].visible), headers: header_vec, num_columns, num_rows, @@ -326,6 +379,8 @@ pub fn run(argv: &[&str]) -> CliResult<()> { metadata_fields.extend_from_slice(&[ "index", "sheet_name", + "type", + "visible", "headers", "num_columns", "num_rows", @@ -344,6 +399,8 @@ pub fn run(argv: &[&str]) -> CliResult<()> { sheetmetadata.index.to_string(), sheetmetadata.name, format!("{:?}", sheetmetadata.headers), + sheetmetadata.typ, + sheetmetadata.visible, sheetmetadata.num_columns.to_string(), sheetmetadata.num_rows.to_string(), format!("{:?}", sheetmetadata.safe_headers), @@ -433,6 +490,13 @@ pub fn run(argv: &[&str]) -> CliResult<()> { return fail_clierror!("Cannot get sheet index for {sheet}"); }; + let sheet_type = workbook.sheets_metadata()[sheet_index].typ; + if sheet_type != SheetType::WorkSheet { + return fail_incorrectusage_clierror!( + "Can only export Worksheets. {sheet} is a {sheet_type:?}." + ); + } + let mut range = if let Some(result) = workbook.worksheet_range_at(sheet_index) { match result { Ok(result) => result, @@ -648,7 +712,7 @@ pub fn run(argv: &[&str]) -> CliResult<()> { wtr.write_record(&record)?; } // end of main processing loop } else { - return fail_clierror!("\"{sheet}\" sheet is empty"); + return fail_clierror!("\"{sheet}\" sheet is empty."); } wtr.flush()?; From 5291ec367c76db95e031add5b767799cbb2f795d Mon Sep 17 00:00:00 2001 From: Joel Natividad <1980690+jqnatividad@users.noreply.github.com> Date: Sat, 2 Sep 2023 09:26:59 -0400 Subject: [PATCH 3/4] `excel`: added tests for sheet type and visibility metadata --- resources/test/any_sheets.ods | Bin 0 -> 5813 bytes resources/test/any_sheets.xls | Bin 0 -> 40448 bytes resources/test/any_sheets.xlsb | Bin 0 -> 14837 bytes resources/test/any_sheets.xlsx | Bin 0 -> 14804 bytes tests/test_excel.rs | 401 ++++++++++++++++++++++++++++++++- 5 files changed, 399 insertions(+), 2 deletions(-) create mode 100644 resources/test/any_sheets.ods create mode 100644 resources/test/any_sheets.xls create mode 100644 resources/test/any_sheets.xlsb create mode 100644 resources/test/any_sheets.xlsx diff --git a/resources/test/any_sheets.ods b/resources/test/any_sheets.ods new file mode 100644 index 0000000000000000000000000000000000000000..fcce70d7010760231f4b515b657bd23906b647c7 GIT binary patch literal 5813 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 literal 0 HcmV?d00001 diff --git a/resources/test/any_sheets.xls b/resources/test/any_sheets.xls new file mode 100644 index 0000000000000000000000000000000000000000..c9838e8cdcc32904ad5f1e6bc6fa57eba7ba9673 GIT binary patch literal 40448 zcmeHw2S60Z_y6p1z=5ELAY#K4EU1(_4iF2{iv>j`##n*}9Eu`G4zNUt*rLW>Vq%Fg zDzU{9ONvItn8cW1G{qQWikfC(iYBI-LQ($;@8Ngpp*>{fzTLH7Gw)l3c4~@%U^j>3U|0xUrbuF?!)t36z-F#vJmbDcOx6m z^7(v>7m&AcZOUXE>7kg1h9spNp2-zr=CPqH1)7k@a@b%t5OtjnUC70AD!U7qTZItD zaU3F1o1nJgC|qljz?WMrzch}(U5dHAdj%}ASaxc=j)J<4AorZ{!ml)rmjcrBaA(5@ zp7J~iYwm$Ye+j>P@r{^E2HxDHRwxJ19TO|6N?LVg{uGEkizFd$59??A>ag# zN-Be$lC!sgJIFO5dz3r2xcqK|aeThPFn%3g`@!H)5~u~P{`_?}Pviyunt(T^={Gpj z^pe43MyNv@CQIVuE#j{s{IxNz9D9~e^FEK!z>Hl(d<9!d^)0Jo?p!idx0vZz3Cz&{ z-rHdIs)R30GndNqEJmXt{u&t6{fLoh&Y9yFLCCf17&W(`(U1~)xs*i~dh6xnZ5K8c@{X%7udhv=snD?$voUNe zOs=Rqz3O8-(yRa%Z)NCiU1?>E%eUSx-}skWwrXCl=J6&PY0 z%Z8dcYBvltFqFuntaMR{IWs?w4C4_aJNGmrLLx$tue`9NJaCD`I0Yk>yfA6HE-GoF zE-I@Oi5SgbTq0#Al)#!FpEU`~#6%0zI1Gv~{HbRurmyEKCgxd7Q)<~}$e#;E9zyY_ zhJ{x_N{3Y;7$=Qn5y08M36>|Y8dQsuWFyd7>RBNh<$^#WdlFS9ArJ}PuDIL250>{~ zTaZ(iB%I~Mp4feCE7~ZRhZ1*z1RRx`-U%{s9YdrMg7_g4c?#+2nuIY$1Phjjvu!lG z&5ICqC8Fm@$vxpoQzCliMI_}#1j}cz?HI8-$fRv~1Xfgi)>(BNk783iA;XEVz7{1A zDA85SbPU_UFl?*OH0hco-@>Cg>(DfF<}dP70>*wcsbJW}P^%(5O)Pl)jXU^%LJlNF z4)s|dWHxf9Ap!;YT5I{&<99p3&v$}f;{^Y>6a023_@|uUo#khhllVKF;GNMI;?v}o zmw&w+g!uM&AwTwbAwTwbXY`-u^JyRdc_(;h@eeqO|Ev@IJ}3AiPVhp0XhLr-pPhVs zn(XlSm41GYG*1^5X>y!sCBI1U`uRxy=cFyA%9Yhj?;2 z@bqo$2{|4d;mI+<ANT$)fcG_Jt8VR;}|Swrd4w7j8l$@SLIxSqEWmxSF=y3{i_G%i`O zhQ=igZ)jZ7)`rF!jm#l!yq5jHwoz@qT#)u=#Dur!&YE`b& z^UfXf2-QA4SzXuZNwb{ur`GN|J!zZs^kj`(r{|ly)BIB}<2pTQrE~sd{avN6>+f3r zWMy5acP)Riey-EImOu4IuG71gKlLiE)4P^G^^&gByOuw>_ATl0P;b8l{g2kVmOm{E zxK8g{{+Yc?jxGKM}FyuUa z`&*`WE&mR;Oz&F$9dDW5wfsBXGQDg0ht;R25yv{_VG_qLr2m97op{4!k>5g_{-ZNu zc<}uP#r>^{*`@#f`|s=1dzxuZpFZ7$2|W32@)XuM@$y|)wxUKFYnw)yD- ztdJiX6;ZG)PfbmYU49Z16YcV2##!Zu$JykE#|f;EAKUR^SG;l77u&`Szm2#Nw-Gn; zHsaC}mylnoPpT(Te)eTk*T*GU(hohi9;BsjOZs(sdp$8-T_;Iz z+y3F2mb8EAd`#3clc}ygy=ag2?%iwG{>?b6_Rr&N+CPs2R|@v^L(BKl`bo5_A4m4_ zf6pG#RsxG2O660fh_JEkwB^8Yn`w-75Uxs*W0QdPC%8GBOpu^$8>v;pHFpN~je+(% zSPYU0{gK!hR>+!SCkG}`(drJW2vu@Hfxfgtvs{tImh*ik5>i@uu*nz8BWmo>P{A>* z!kT#j{`CB=0l_SBSdsOV>x(|T`m@QM?ny4-L>&BVIYirY2ywwdFkCzbcYV?CpT29y zp{Xqgtv!d1E;tCLjpyK@FZ$-2Z|peu+j5Ap=g`Fk2f_I996a?!rtjoh_&Ys z>4F29M_Ras>=pW=^OwH25DFZ_r4v6K;jVNT9?{_Lrep7 zsjmOwbKlx=Xl~2Ffi8u*;2_ndy8geszSE9FkSzxXy41-92dOUA^}lf8f*pqzwj3Pj zQdbuoq`Fks-}L!vI}R;vIXKXzC>I>0x>VPH|0`eEacE`B!GSJmU2u@6H>{1(B4i0pwy$cRfU5eJ;*fZOXL$ECe2f7sI zf`e3-qO~Vap0wl8)|P_L8?p9TGRO=I}R#a4i0ol?Sg|;m!h?cx0&06q#oMY za&VwaF)lbrbtziA?aX4EGWcLVgehcw@7BwvcWYzvblRgaHe&5rJ9?oOde9kLdhKlJ zN%e%WZ>ltQ^x9kKK|^fmb+Dl))e6RFGRF6~^%C_#KV4s5=z}f2&NlRXJHEUoc*ksxK*7uDIVOOU+H*$kJF0#R{;IQiXdxGFD=hAzK9zGejd~kNOM=Phy6I zCox09(+ZiKb**+YEmx?lbK&lW0;fU{6Y!@4Z^kHYc(n)OhS|jRz{YY*TooGu^F69Q zO+>XEX*$}Zk((4qQ;8jKMt;y)VBV-oTq3JIks{J2g{R5KE`>F(X4umJ&wcvjvJzdX zuEJoPqRQ1zu2`-}u_h&pH7T(#bTalqQjM4LwNCq1r$F4f$j)SF0gbQ4I;VQ3jH#== zkg*IKYF?(M*cfZzE6KnnA$=j}WPw^4?v20~gU9%Kik?u)hX)!#(F+@XFu~3=7@+2H z)?~SsCONh(({^5KMJJDdm@(uWWEx{24}VscURPLLI!1-TzDXySpcNa2enWJ{U=4U7#i|Qh;>U|3$b0_zM)aFcrt$di%ZAr z3(c0sR&j%1{Ak}IDW{;c7F#2$SAH^hqKP9fg$E?GKDA6WC~9!WR0+c4DRoNl@)yL z8f}<11L=W$$?fY`+K~trSm-+F-jWI7Nz8=sBxXW*B8l~?c8A2;TXT1JhvaimTx0!Y zo&-f2nLrwuTU=2BL20a0xivw3HB z9DFjfgxp4G2Sba?i}OqLb5O<`aQ1M}j;!LsLVf8RY)_9(CUJVRp?c#K5$nr7PeDo< zy4bNgW5pUp5L+Y3t)Se(2RfUup?{O)9dTR;jpy*Ui<8rxyg4?-ifN8vWBoBa=%&fo z?`#l{r*|2`=6cm6^X__8fuXcQS6o`Iicyu1)$1$Dd#KFi2v-UD3|Cq5tkRXjicn@v zD2sx%fR`IJ#tIaH0LqjQ0?ofNwn}lJ(JILsdXdx5O&$R05V{@v@}nk4QCX}tjRFfmFYic{XXlnh`#IgCMi06IrGHg6C0=1zPh@7 z;K}#)Zn^8`&;8%ZP1&jnED1la{ds-QPmG7#dLB6b=(;U^cYmMKwf)srnWJhx?l$oJ z&Y|;Xv<&K{t7-D+je|#C2(e{^@?mw6qH{hwhsft{lA0sz+o&UtCdtS`_wZ)z*_v&NcjNST?>ib3un;zfeGe7V27*&gi&yV(8 zKmY3s@7`1L!iS6ZE}j;5Z{2`nrhw5uCSLJ>yKef3>5zyCqg-ow)x5jqi(1Gj4)qDs z&hm`-N)u6YI2X&YjRkJiTYEu=v6gGWqNUIC2u3i{; z!o&NJV)o|me%_Yw=dK06URwEuDsatqkK>n96JH;DX=1|B=`&Bx*}SELV#MUK2RBW< za3Jr6oHsZ1I;Ckswrm z>;LwTfrHO=8@+4E){~}v$JKS~2OgWgJN&bBd1<$m{fj@%e`0IYCqoV|-KyXIPVkun z&;9!DA1I1_LB~&76*vn8d92tdj8(mcOCkPrLJq4d}6~}k6$?V*(bBQz439t*|q(A?+IV$>HCoWROGy8uT2Zz z(rWAbS*>3VFS&H0+2>zQ4m!Dik^jeiUM(L~6?%5*oSg?gRK0tk>)CeSg@1VO^X zPmaxrxzaW4^k`M`+TLk%M;52|&HdeNQ`Um_st;d&K>K^V_NMRi2k-wzmv-2#&Ruuk zsi=`BZw4=(8m_v&;q#Bb+28xUYYFWJwN|~BKk|nY!+tsU%tzC|>+=4}c8m9T{`B;F zX|ahbqtDlUme%CKWzW6XwDs}By+83=er;J3BbPb%Vz-UYADDJz-2&zOs;CUV=N_BZ z?N|NxH$Q#v(An=+1poAG(VuGrI?k`jaZ7mZ!fQR(x2U>#d91P5l-^T%bbsKwV%Oux zO{e!yP=|*#(gaq%cGxX#eDD5k_8f2gKv>n1kam|cPMyBmHu2fY*tv0AGIDqAiT!%` zZ)3)Fi&Z>0{{8{IuN18aTu_rgrRNX3x!lN4);wJF{5@~{Ud+Dwar9@Mo4-A#c+0`J z!?K_D2s>QyOKEP)tL}{qzvSgzeqq$$qk;F%8f<#G!?YcD%?%qq*|+cYnhw*R9(K=; z<`FM=)co>&(<942E)D34SSfukUuxPTugmUpV|JWURb@FVpRuEYm(6v-y`P8&C8J8UJVI#k1X1QI99|?0*0K#v4~pesZEy zW@7di@9qjeG%BD;|3l|Kp8n_0=a#&1^npJ=|8YgsTicpnfAOWGzg=H(_t8JDcl>$J zA-?s}d&zNfL9ev*w3@hcYJ;cUjD>_!l`vrJMQY-zVQC)&OfZ-0$Y-Dc z8yz|Qb*Hqd2YYRptUvgjUrEi)igwR6x;$?1n&$HkYA-JydGCq3?H~X2=udv{c5ypg zyZpwym$rO$bLQSh{%oV4^wd>j%!l8+_u6aS&rVzG|AqFzp$(UIl$nx0E8Q??TC3y7 z_kQsFM_xboy!4al=NAuubw}w8@atxtB(82+!pU&bYk`IkG{I6%|?0L@9ufsTg=;bb;h-( z9WS=tF!!+|&DJm8SQWVWi}NYTkBu6l@ymMS#1Azkt=|ZaPJg8958C0M-@C8Pm3==h ztlpc@#H%2B?Mnw|rRn3g&fGlkdu5EL*WtbWbb~7QyqELsm~F%Jxp($HRh#kJtZ(i< z71ivReBs4;bHWj;2eleo z(#3tz;p1!a?u|-5c_U}=4>zv`v&c>IahY3v)f?R=fBf!>UR4*PHimmO-#a7iQIC-e z25k56TlVPGm1(hg5sCfW79DtSZ0Vd>{=>6X>kJg{eSo-b6pw{<~>T!9Ja^ZPZy z8lKQ(>(5QQv~_$~o6H!mH}`LPG=Kez2Ud=$xUnVN{3C6|U4urwy)*w^pM7zAl&>#{oigie_)8m#i{=ijnL5b0 zL%Vatdz}hL$z#Yuw$2h2a;vvAMonTrkGzG*gQS--ICj_n7BMQR>3Hty7S#kw;O<^Ec| zD}K|Gp`V6qdNTRdLuaynuB_T{eJwZb<@Sg3l~d2M7OQgBnD+e<-*Wz#sJ4Nl{v4EC zetwR7*v41tE{3k%-e3OOh=T`T`WOWhiq6R*uuAG|%!p``;9vxq)6bfK?I6mMS`B$h zhE%o*Z3iWmge@>ylU9PQ_-!fKju0%A-gQKYA1~O_uvG#Jjc)J+Nps^uC35KeL9BU;5cf4Q=fm?gL7{jbR!`+hVrR+y z6Ig6;14Wv1ahP#YNws`NARioF8V$n&n-l_1}nXcTf^^X0Ww+PjH4UmTl~uQr11DMzj6~zca{LZg z=fI~KCRRzQYEnW-_t@yz5LJP$EFq)_%fGmtP*#`_QeHYHM5QYkgYpzq z7;njli@IppZ2o=@cLRfp2agfmx%_n=@QlspuMhFp1^jg(e_h01 z7xUL8{FOfqgDvClm-E*Z{BWO1SVVPT zX@Dv%otM>LtUju3*VRWc1Wj;WeWoI^4<bF5UN4(Z9_*;akpx{q#{Jy%_Z%p|1#|i(B@dO zlBNK618BN?6P?0y0BKIiXZVK`M-WSKs%D zNyWa0dN2w@VdaKGScPT@1uKJrb>q;V^6#xLiFhQrd9Zf4TK9g`Pi|I>v+2oZaHIWt zFCUBy{Tq6+QUi5kLbvhX-$Sie1he&mqVv_n`wlQ(k@4q95^6m*3Q1!KMv|5+g=I-O z8(gm|Vt*%U2DJv#JW-c;7W{=YudT9+1#yeJw%qM&6_KP4&Q+W(~aUPR2%QxW;^x(T7>W02Ju<-FQFMW{MA+pm?Z&iMq z*!cUE-*pN8T+iXO1A9zMRTbF0P^2pWC2Tw`&Nmhd69v7_*_(oL z$8Ru;5DLRjAtAO6B@mc8?m8?V=vo=aY@>`i100fV~Nu7l(rHTIg<1o zr*cQC5oUg(%*FXRr4k)27B8J1C6zI{V*Y1#6pItyGl;C#)pcG&LlmYGo7y;#g z3P2@bB483=GGGeee!x_~G(Z*L0l;)XHGm654c@>FEhWE)Upaok%k&|BHExU@2T?y<;;Kqn=Q?9*Q9TQjObAM81i6LKC!qp8?l<5k%r5U=Ca+$tSeyUNg ztI)~wO9Fy(jQT=-RzZo5dGsk{J9Fj6N}nWrPh~(v|5myB3HY+AxuW1 zD$6Sj6N>M5lc&riwYpvV)9(YSlRNh3AJVmd%KnfQvRD}D-TwvqBNzgyZE+@kX`DOu zN3=iKoKtPyor7^hGz`#EGC)h~4bXehY8W6n*&kW!3r*MDC{=9^--W~hnO#N>gf5-laBBQ=g zyqEs+eI)-*eD5qKrINeII6S-6=@&PIf6c(MxNwW80+ z$(MJ#K?%fJ+d{t3Q61@HVL-`{LF|K1wH`~Fnq{NsIpN#y+F zeSg*D{NsIpX>k7S$>n{2DsukuzCT9JKl1$r@ykqV}mnyLe z&k-KES1K$O+)D@IKvBXg)7DR%l3+!H5bQCseG5ER6Ny z)yx1vjmCS5NfjcbP?+y%HH3|B*Iki3b27*`5!ykh)4|I7F_RZWNTBQ_ z8VAw~+FhE&b-~JOmeq<`4Ayxw{zi`(igh#kZ}md4?iPx*=wz(L(%EpuSjV-T2w09c z**wmOQEoY)2;*!Of07)XoLmALIzD9-)}QGNK{|Jeu9R|!d1Ow94MB8#JUZB_0`GFo zXT_=bAZ;~GgD=&&fU z25Gh45;7#RPQz?07OTDK_@l92I%E~k>#zoQ8q{3`o`o2+{t^vt6eAVs(r}l8zZk6b zhT@H&x&KC47x71GsgXlhQl+yK>43L#NKYtDLuJabqI#L8LWTq>&^!$!6#-gmNRaNj zL1r<$PAB6^a*aUTZjfnryotwE4bTFkaUj#XkTW_^joRWk$}jpqqod=wW*(Ph?*xglTTEH>Di-6;RmjEvVDD4USz6v;L zetsRlb%0ZVH_W&*=HLHx2mE*VTiQ}2v|=*!GSp$8Wr=tDZ4O)GBh!7CTx_YF6Db?L zFuM^dDyzO7MniKm(5A>r#h{f%ptYp{Ll#Z2Z7H}*!TWT+9n#K~Wc<-@4E_XsbbULF z|Cjb7WUmajuxta-N{je*u7_>A{T{=zai4bIDp*qtBLe_|GL8iSS^`=Ff^krU3cn%d z@jVn7LrLtYd+ufteNbQ@Xf#DTd*yiRIEEz+N91h6jr|@N06W`l8i2aYgmsNW>GSay z&tFZZ&NAm|OeWfl#s`#;oy54$WNKZ%k=4H#=6@H48bd>KOe)sUxQ^mcYjA{jh8wOo^qj899yb50G2x{dpl{Xwvr}?%r?31$#J`b$wfXJ^$=V~GPd~f!KtZ;M|KL#T^Ghygdp*4K&qE)* z@^+?(U%RAZK#nm@`OKuCMI)Z5>nP&sR|!6IPbYL=c2T#TZ(4Z%s{D9Y?>7g2d-TH1 zofm>esHO39P_}Bs!LDeo1@quuiohmVdHUo6eMxm!C3BMxO-cUwvJ$wvi%T_e#~v`> zYNI`;n{VS{Cgavko~%X$?840+l7S-^%2S&t;o88#1;qvWW{Bt`$DTrQ zOX2uwpD39Z3Se^MSYA2~TBwBEFX+6IHI`eS%k$=!b~x=@_BhQM;F`#7z7ttE&n|Jy z+lc5XhmV0=4EcUa)=X|Wf)@S-(YP(!cXQ z7MI9HL*ruQZR#J@FI+-=|Jq(gYcw5YZn$;^9UrqMN1zAxVNrP7m}&5~3De5t*i|p2 z?Iv?4#|9r7BPQ*!4@6EljQ0OMfg7=}n*1!Zbz42NFE(}2KtYz(#f#Mjcx1WJrg)XI zlbm_8r-r65QM)#}(^mmWGH(x=_wWRnx2Md=1t>h4dU3L*!XU&aUDmgk%sUX(QGF7qms+YN{ZpJq5tZkJPH%|0;8eaP=r}I-}oZooGFXq4;f~q z73EPwaGWvI7Y?Z`D>J|k6P1fE_f(j^=Nom#Vm)S5MH8ackrgH3`JtZeX6CAxRyFX! z{24Q62TqB?2MDGzh+#R!#4GLKMwW7w`*#pYVZB}8KkAD^a< zNsWkAq+}!?t4@hXNt36?vSG=|*iGCrrdAehs(!tCLb+kq!s_E)RGPR*wa<(N)t#$Z z6Iq=H^M8?3+}PhDC6){ef1Fpo!7yHt$%1OVXw>*naqV+=ryLs3@@u&b2An_N@{LB( z?>E}my0$6CFDaZx_WtmbNk0as$T;`#7uy|sXH7_oqH7A*;>Gtb`6UTQgW*e&r!rYP zuNuGd4gPwS`U)1k+p}nuf4hnu9;>2{c$oZUqnq~o;CKJ++CTiY*Zt{wx%oCzrFZWDdaMiy@lhIvLtJmiDywo!<=Dkc7JVVBM>S}j*_I%pC7uL^{ z0+gQ7Z9TO|12zVzuI&hT@u;CN2&SsG)A6HN_NL?BOOgh19ToEE=ARz-k{kNHT$XoZ zVY|-sc}zi`GT+fi+Pdva?Xc6~we9(6l0k9~zFrxKFI>nMl8diKCRmfdtd(EsN_|II=X z=4rxzAYl2wU4+>7*D&xWzO;_IDeD5#u(1LG5R1AvsIT{kEJJw(I!~O+U#K^#`ji$K zM)`Wz3s{pdRU5C3PK%3CN2F$SS4U_xsW=iW9i5w6ovcpnuFhc z0F95S93%%S_cQ>FoM!__9^^NmvXifv~dsGUdB>1)Q zlcB;T*ivcym{tV?pjFjp0CE)mBd#*@suYskR%i+d5^pgyB=KoPDr!i?2F4M2({BTl j{G-wS+xa(!G=vEcKLd`8XE(alryoP%soA2p~uxARs~@v}4+-MPMKx48X^iK#(Asf;QHU zM%IoxN^Z7B4sU2&tt<&L!9d8rcL!(D>Bu(|1!+mjNy{5dlZu?PE0X2FlNu*Eg zB`Si-8Jgra3dTofzICt&y$;O+pT>&QDWH3zVB!4@1l{AHR!Q`YDPS_gRIsVA9t6X) z_X6MTXWG!Nd?oO);JT_|PZ}-z^0q@=9am`P1tJ68Dp*@DGx0cW6KCwaTWr4-2UthJ zt}tw~ew^qN8D8q{B~FMA_3*%c6>TVjDbGxDux@!Tj6mg{tq3I4GJJE)BG&eDSn8 zv6AnoC~ns_i6tuuw#m*k4;WS)tG7(&`BQhOyu-!*uO@iyUyVhcm8P$yh*^7v; z5td1;v8%(fi21HA4axGEYH;t5&PLLj7KbMCTiApsKl?bz`{?I&sZA>%k2QTxI=Upl`?lzP)ld z#heMgAMo?N08{^0Ke7CJbV&fno(5n*Kv+PKz^<0Gzg)$|#@<5T#>Rq1-^}_SZUYQR zqJZ!I-+na3EXw%NA^IOldjuLE=Ctmb55_jrlfEt5169KCtQd_NzV4WxUxw%`-iJHM z?UU+Y*9l-;wB$|FXv)g!#{!o}^x=!1D`xQbiMwB$#D&ziVtAwJ3(Y}1LmG#kL{`jv z8=haRfvnvX;S*Kg92Yf?j4kbLHFlk}&>&Jt`-V_61<%o&0luT!k(Zeu!8GJeZtFn> zYS6oB_pP%zmXRV9e9P}s=_@bMPCFVvKLzazS5roy!cs@w550CRf$g23tCeh*E5J1# z*}p3`152#h(p@Fd3!o1r5Zq%+aD^xRZW3<_Z`@1_Yo~fUir{_od28P$w#wv-1Y5&Z z&i)AbapQ%mLTQ|#*gJ)r>7~$S1FisGA~IRQQ}9387mcY#FCD;UG65F?z;FQe^}A(h zMQ}@k@}mfk%!eWoFZ&3~AqpA1{!k|ZBTTKI;h*l$ZuS0{L_PrBibZ7=@0{se2x$|tta;Rg;*AS3HFtzUqJ3u z!6g~COCjD~=AxH>5DjPScznm}!1XRCpC^(;u12>355-v4ptsULd|U?h{&9Kg5==ag9@I)_0 z-5L5xx2o2y6QJFSSmPGGyyXPH+&MRi#9WH&F!RCG!u!>_7CB@iGV1C4Rm9=%NM5^RLLPY#Ih**obUxe@AiHRLs+I18xD1yg%bxn=});cAvbvB3Q zSu0sdiwoRGp$Vz*=oxZf?|nYpX|XmCeS2^)Ak@(3HeI(jo#gLqf;iRYEPyJj2)6Nf8zVDOcljSj2 z)j_n&w^Ab>s^NgHK&?RiNi+4yAuY)OjnD!N<=<$A;fH3-4GrT5^IfC-gWJ4;VIKs6 zDI>^+^xIZ@S3C6w>HOt^h{6aoF)=wXH#X^B31PGvXA?jT>MHR^zL20N67PtA6AEh; z3R`Fy3M=IS3#Z5)uZH;*BbDLoeR6wSI_MNbWTYPQs}4srvLMn343;cik#Y- zUn%Tq#ebT=gCY{}67OZlNL9$-*&G>3uXXy~jyT(F;G_Yp6@+Jz?v*Gg75rK%(w}tc;2V zDoL>;Xi*LW9z#mH+K`|RuVy}2DPvm77R^(0kTe_)0V|t}mEz6S0RPoU_Tm(u(={op zvw(>O0ynR3{!yv9BrTQx=oRN&p}$+M{_>W~3^Vh4Joo$a3ELNc(m?PvTkHQy13y>x z9~$@(03endmpT3F=XVbIg%W985#2vs0CyYCK_n$afSG+;T5#-IJMB^qERS$UWV`xofIvga6e076A zsuy22~fzK(p{QoNrF#dBB1F(VLHQ3ZypTtwm;+#R-Z)`4?3Jb7C3*?CNf|rg|A|T84#Fk z>lr!DumIj?n<+}`{&NCAEiL3~2Iz(o;046Lyx>;?_!aX0=mvh_L(uJXh<-n^z@_!L zJ7Q$6w$SnG3*cD8TVE^WijzDW8yX*rWQmmIrHTo=?j2%4HV_r}jXA>o z5~#4*o4zaYPPtg;j|KoQbpEXgaEIh62pHPyxtLj-{38SY_3KWij>>fq@#nkO_Xu&vvLYwB$AzEw`Nsh*(Z1tSZHz1X?K*=?|&p<%e1nyk7c6j zl>0OoRvvo6G0)6F5?5v!?r}9 zG117hbf!=s*>OrPJ&K4jl|QAvFj}+xmRT-nH=MxEi1VFArNcEO=RuMd14^5ryDgoF zX_5m!AR{D?xMPgEA31bn+p;99h?j{?cnq~UW4L3q24ZTSFhoZMR)2NlFg&^TT|3#_Mmlb%sDhA`Mw*VfCtC+=z1Z z-VjN<`wh1gWet}tRv&$;qg5{pfg!&4vQzyIBy|*~1y{QPNIBGr9YafEh~t~C8vN7d z{sqXJi;ybP;+G@RSLeD!F1fN>Z%^+>&6_0S8JkH5O9&>rvLf+|YWCPp3hZH7@=q1^ z4idqn!i6i|yT9^habl2jy9<`c5mnnUp~_%zkq45<4m-~#5F%&_-6W5PzL~2^IO3-W zl$E246KR~}Wu$_Fp25fYdT?6k_0Au}3cta3N%uX7`lF%NBiI>6>}x+Bay&dHE_9rt zJI`ExawiHIe|&$U(ojAF544ygv~VNI45`YsLhb-o72&#l5=+~oMFA%8DJ3_rFNZs) zc6lD|qPMgz`&@MX#RtCC#ewtYdG{ZuJ7)($%ixEBiq$tf!>E&k6b~ z5jKqJhQVtC2!)(&%3!%~a;hWkHcC-StNXBmwNsL51orRJVK;S7uA4>-KOx)B#|!Gl zZ$7T2DtHVd({FuQLv2?i4kGKB9MTFebVIO1ZE6&==X1it&GW?Q2D$}O){+*C6t`AQc z{1Ny2i!wh@Kemi0Oa94dhv>!OGKP!Tq_kG80Dkmazei8eLmbZAk4$WgiyGK+CiKzM z&J%Bo=PF6`Kf>8DN1RL?=Q12c23T?BITCvo?5-G-^E6nmR=jB7i3KAQdQcc`5Lgl1 z>+i=Kt$;{06mYfI;4iy>0EB_5P{rBto@A(Z&eNbGV?|+_xrKoPJ(Ufc`M2`$o1;%}YP8LDt07IW$GM!6cElc9 zK#g1TSPe8*H@R_mMA&u8Q0ragPQ2(jcz2=ASc7w@jaQzC_61W8=7QXjUHI}SiP{lQ z7rw>Cc5QpS(*{y2ZPXN!0CqQSVE)3)zqIl$o9vQ1SIL4Fc2Vr)OK9RHdnz#Q!L|kn zG_O-aWVp_`2kVyY+eNg@G{?HBN3ul-kOG6^gr#6a8UB{K>%sN8gM#@ z`nTw4U}I@x@9;A`CP(vI`_dr_m-=`B!4hYnLeCn$aTG0 zw!Anv?d4@mj6bF-mbjohKF(XuG24)YHJwT_6w+$w_@hRd5Hs@tqcexV zr0lEVITF@UW>6tL!QtyK8s+H+XjS(<&T6CmG9SpAy%iBrX4Plm3cn4(Fo(nSQ?(3t zcqQcq)o-~SpXaMm;OD!x9OR>(to2S@-v?ZGK#WW*Cv+bLT!u)Ce1#riJ7w~;9Fco- zbJi^L=X~lsZ=RY6P?`@wX@8Xo|IDYqXVPTFAqh}=#1@zq!QMA6nmKbh^F%Rd{GGl$ zt?@(7zDb4&*-p(jh&(~dUA_qAEs>R83X%v4AcTz3H>79pqWg-!vd@b-_9w<8$OsN+ zBeBREqn-uccJ+~%4Eu@`)QWW933Koy;o(d7gJqepii!Yr3mbNGowWq{v<6F=+ve3o z1Y{Nn!=*}TfkU-3^6znB1gwzTf=h^Lk>;e;A}y`70h?mQtW0bg9dBE7-Ki_$;>(?f(TP9-K?B8`S#-EUg8$L#D!)1Wam2#w9ve;O|h%};0y^Gu|f*k z*Sn)Wi@RHV<=p(dmqC_^hsQwjBOsBASKrDe_%6g3Eq3n?*IRbED{_fV# zYka_zwvCnb4RvjGfk0^pDBAVg^jq}@SAmGF-jBZT^2e1g6-R8^cJHz)ISiMT(xt#FQQ$yceJw?@XLRwNl|*a zd^Z3m6JM}?bRvJ&q>iRWRz|cxpMP!0)ECXMMNwL4&v9d$RnKdS@L#fvQBqhNkP#{< z*t188%mtgcbPe}@g!Eq4y2#Cmh+RUNTRP8-!AcyL!}<|C7~*&Svj zB`#@MrkZt>e6yKc`7L3w-ebd)uY?&qx4W=uD1jh~Tv~P8aa%F@5T~DE-`v zFTg`_(EbuUe8g2Hh~UeY+w)mz;hvlvRo0$PnTW|db5nsx*fh>m&Uf7_}NMp`=-?x5+#=ltTv z9?qihs$aHDdB5S5tXdLIq>+T(gbUL!n&ac1QeuJ^RJsT5bb<5h6p2k)kWpJmO4+)6 zo<)`BNoU1k5D6N%mibBN`ur8t#|`FTY1KP(S15@)PK#^nXeaw3crKRXU3V6=V z!HhPOANqLHWd}u%uAWigWf7t@1d?NRcFvK$#D}&EX%A|%%~0A#>pxb#<2aQNfwa=i zR)a}x4K0C#@J zyH_MSmB#z-{(Ik6-`677+*O_Xg1o$4NFt_5;ia8@=59#lYH)wqa{ar zu!fRQ{BfjhgX&3IJ~-2}6d%$MYG}Fi(tT88+MIf$UyVo~kSzf0ZLtFqbS| zM8(7plW}$-NUvF9ywtlrexgNi%ll&}`z-U#f{1)J(fruwtt?&h_Diwtq8Wr4ni%p5 zYmo)8Z^7rBLLLEJ>BpUp-w4$tuaf+iS%Qir%+v{1lhiGHHgb6-8WxpATIMcz_xI8+WdPo8U1Uo)345A$w&`thqY2fNF3 z){7N|g;~0cfTT{bIW@aZUZRDg8&hcPhfL~ZDYaeq_F}WnWp+Mz>~}C+Dd3!ASM5M4M8?J3b-7B-cWZM!>H=(ihah7DKv+n$SGpYW53<)xsmIU%|U_%J{$lM?k zz5TRi{mb&P)xP@O3KveP0aa5`mPJb0P9| z4>=7Cx zXWx+~s-8=~X4QE|ysY4e%nS+HQ!Xq%TGn-1$uufw9<;7P(C?u}n=2U?G^JWV2(h-8neazf zbWBa1>OHA`nWTGicrZ=}eHzIX)F-)m+L^01E@K(xi&aEBY+(7}{We#AOv8nJvvS|$ zz>xK@S*>|}gR0ra9BS#@VGVb!$`Mah2{)8;qR~i!qj|l=*qOad4#D>HZhfKRYvvU5 z$|<`YvMsKCC=|>g%40O?Xy4Zc43x8!0Ocf*-1-FK6yo#Qc=EaeX z*-Si_s_It(nJgAPrsrX1^{IM3K)WI;eydO)`TM3sT&I8{?VLa9^l}r*fpuPVO-eKy z8$r0O95PMP&3B8+J1f@HCqBdq!LG?Hh-4AiufR1}r!=35l$fiR5vgjlJ6nd*nz5k8 znn8WGC!rR?JyrHp?v~6f-7N%PQ7P-iL|<5{GwR6_JWkPh-7D(Y&GS`@kulV4yiuC} zCHak{Kwt+TC ze5zWX6<-3@!Wx=I=wrB{u$Xp>LPc&4;shm8El3Oz2Etm{gYNcC2a$tvev<=Z(k8PF z?m;Cxhr(ASREIW(uiZPV%X>O!6f=Rx#Y4f3zqOBORIx96{=(-it@o^>DbMU)HEfF=Ls5|aTqMLP%?$uqnNpa&7{ZIGc5%@8Prl*xS#4! z6F@Z(w(ta>q?)XM8v}gE9hBsD>eh~r1CU}M+R(+I{wFbL5~@UNcW+1qD=!dB@E>MNXaLf_UU@~LHjFc^9< zhg{1~D+M4W(5dPWVj*_CYTx=P=%NOeaLyl>6)q#w-@88qQ0qR%X6y@+w|Zk#77sA8 z^yJ-abF+Go&d`o@k3_+GUeXt5rExp2flV3K4p< zpv>X}=5{x3$u40#H*RIMQIm!Fez^

iaE(W5@f4nisY%*m*W}IV%=-`m@jlpv@hM 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 N$N=f6?k5HKzX0;%s7(L> literal 0 HcmV?d00001 diff --git a/resources/test/any_sheets.xlsx b/resources/test/any_sheets.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..693894d89566ebfacd8f4807a4a13d6b53837842 GIT binary patch literal 14804 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 LE3jex> = wrk.read_stdout(&mut cmd); + let expected = vec![ + svec![ + "index", + "sheet_name", + "type", + "visible", + "headers", + "num_columns", + "num_rows", + "safe_headers", + "safe_headers_count", + "unsafe_headers", + "unsafe_headers_count", + "duplicate_headers_count" + ], + svec![ + "0", + "Visible", + "[\"1\", \"2\"]", + "WorkSheet", + "Visible", + "2", + "5", + "[]", + "0", + "[\"1\", \"2\"]", + "2", + "0" + ], + svec![ + "1", + "Hidden", + "[\"1\", \"2\"]", + "WorkSheet", + "Hidden", + "2", + "3", + "[]", + "0", + "[\"1\", \"2\"]", + "2", + "0" + ], + svec![ + "2", + "VeryHidden", + "[]", + "WorkSheet", + "VeryHidden", + "0", + "0", + "[]", + "0", + "[]", + "0", + "0" + ], + svec![ + "3", + "Chart", + "[\"1\", \"2\"]", + "ChartSheet", + "Visible", + "2", + "3", + "[]", + "0", + "[\"1\", \"2\"]", + "2", + "0" + ], + ]; + assert_eq!(got, expected); + wrk.assert_success(&mut cmd); +} + +#[test] +fn excel_metadata_sheet_types_xlsx() { + let wrk = Workdir::new("excel_metadata_sheet_types_xlsx"); + + let xlsx_file = wrk.load_test_file("any_sheets.xlsx"); + + let mut cmd = wrk.command("excel"); + cmd.arg("--metadata").arg("csv").arg(xlsx_file); + + let got: Vec> = wrk.read_stdout(&mut cmd); + let expected = vec![ + svec![ + "index", + "sheet_name", + "type", + "visible", + "headers", + "num_columns", + "num_rows", + "safe_headers", + "safe_headers_count", + "unsafe_headers", + "unsafe_headers_count", + "duplicate_headers_count" + ], + svec![ + "0", + "Visible", + "[\"1\", \"2\"]", + "WorkSheet", + "Visible", + "2", + "5", + "[]", + "0", + "[\"1\", \"2\"]", + "2", + "0" + ], + svec![ + "1", + "Hidden", + "[]", + "WorkSheet", + "Hidden", + "0", + "0", + "[]", + "0", + "[]", + "0", + "0" + ], + svec![ + "2", + "VeryHidden", + "[]", + "WorkSheet", + "VeryHidden", + "0", + "0", + "[]", + "0", + "[]", + "0", + "0" + ], + // we don't get metadata for chart sheets in xlsx + svec![ + "3", + "Chart", + "[]", + "ChartSheet", + "Visible", + "0", + "0", + "[]", + "0", + "[]", + "0", + "0" + ], + ]; + assert_eq!(got, expected); + wrk.assert_success(&mut cmd); +} + +#[test] +fn excel_metadata_sheet_types_xlsb() { + let wrk = Workdir::new("excel_metadata_sheet_types_xlsb"); + + let xlsb_file = wrk.load_test_file("any_sheets.xlsb"); + + let mut cmd = wrk.command("excel"); + cmd.arg("--metadata").arg("csv").arg(xlsb_file); + + let got: Vec> = wrk.read_stdout(&mut cmd); + let expected = vec![ + svec![ + "index", + "sheet_name", + "type", + "visible", + "headers", + "num_columns", + "num_rows", + "safe_headers", + "safe_headers_count", + "unsafe_headers", + "unsafe_headers_count", + "duplicate_headers_count" + ], + svec![ + "0", + "Visible", + "[\"1\", \"2\"]", + "WorkSheet", + "Visible", + "2", + "5", + "[]", + "0", + "[\"1\", \"2\"]", + "2", + "0" + ], + svec![ + "1", + "Hidden", + "[]", + "WorkSheet", + "Hidden", + "0", + "0", + "[]", + "0", + "[]", + "0", + "0" + ], + svec![ + "2", + "VeryHidden", + "[]", + "WorkSheet", + "VeryHidden", + "0", + "0", + "[]", + "0", + "[]", + "0", + "0" + ], + // we don't get metadata for chart sheets in xlsb + svec![ + "3", + "Chart", + "[]", + "ChartSheet", + "Visible", + "0", + "0", + "[]", + "0", + "[]", + "0", + "0" + ], + ]; + assert_eq!(got, expected); + wrk.assert_success(&mut cmd); +} + +#[test] +fn excel_metadata_sheet_types_ods() { + let wrk = Workdir::new("excel_metadata_sheet_types_ods"); + + let ods_file = wrk.load_test_file("any_sheets.ods"); + + let mut cmd = wrk.command("excel"); + cmd.arg("--metadata").arg("csv").arg(ods_file); + + let got: Vec> = wrk.read_stdout(&mut cmd); + let expected = vec![ + svec![ + "index", + "sheet_name", + "type", + "visible", + "headers", + "num_columns", + "num_rows", + "safe_headers", + "safe_headers_count", + "unsafe_headers", + "unsafe_headers_count", + "duplicate_headers_count" + ], + svec![ + "0", + "Visible", + "[\"1\", \"2\"]", + "WorkSheet", + "Visible", + "2", + "5", + "[]", + "0", + "[\"1\", \"2\"]", + "2", + "0" + ], + svec![ + "1", + "Hidden", + "[]", + "WorkSheet", + "Hidden", + "0", + "0", + "[]", + "0", + "[]", + "0", + "0" + ], + svec![ + "2", + "VeryHidden", + "[]", + "WorkSheet", + "Hidden", + "0", + "0", + "[]", + "0", + "[]", + "0", + "0" + ], + svec![ + "3", + "Chart", + "[]", + "WorkSheet", + "Visible", + "0", + "0", + "[]", + "0", + "[]", + "0", + "0" + ], + ]; + assert_eq!(got, expected); + wrk.assert_success(&mut cmd); +} + #[test] fn excel_message() { let wrk = Workdir::new("excel_message"); @@ -804,7 +1201,7 @@ fn excel_empty_sheet2_message() { cmd.arg("--sheet").arg("Sheet1").arg(xls_file); let got = wrk.output_stderr(&mut cmd); - assert_eq!(got, "\"Sheet1\" sheet is empty\n"); + assert_eq!(got, "\"Sheet1\" sheet is empty.\n"); wrk.assert_err(&mut cmd); } From 4c2d9fff5274d4ce8867ca62f67f1bee147ffcab Mon Sep 17 00:00:00 2001 From: Joel Natividad <1980690+jqnatividad@users.noreply.github.com> Date: Sat, 2 Sep 2023 09:43:51 -0400 Subject: [PATCH 4/4] add lint allow for clippy::implicit_clone false positive lint warning as we actually need to clone for iter().enumerate() later --- src/cmd/excel.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cmd/excel.rs b/src/cmd/excel.rs index 01c06e09d..20bb0e0cd 100644 --- a/src/cmd/excel.rs +++ b/src/cmd/excel.rs @@ -258,6 +258,7 @@ pub fn run(argv: &[&str]) -> CliResult<()> { return fail!("No sheets found."); } let num_sheets = sheet_names.len(); + #[allow(clippy::redundant_clone)] let sheet_vec = sheet_names.to_owned(); let mut wtr = Config::new(&args.flag_output)