From 3a41ab17677e381e47b9f2b06cbd6fb8526b4f8c Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Tue, 12 Aug 2025 17:00:58 -0700 Subject: [PATCH] server: much improved xmls pretty printer --- Cargo.lock | 472 +++----------------------------------------- server/Cargo.toml | 2 +- server/src/error.rs | 2 + server/src/nm.rs | 65 ++++-- 4 files changed, 72 insertions(+), 469 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e9a3ab0..b0a1ea2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1131,12 +1131,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core-foundation" version = "0.9.4" @@ -1361,23 +1355,6 @@ dependencies = [ "url", ] -[[package]] -name = "cssparser" -version = "0.28.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db8599a9761b371751fbf13e076fa03c6e1a78f8c5288e6ab9467f10a2322c1" -dependencies = [ - "cssparser-macros", - "dtoa-short", - "itoa 0.4.8", - "matches", - "phf 0.8.0", - "proc-macro2", - "quote", - "smallvec 1.15.1", - "syn 1.0.109", -] - [[package]] name = "cssparser" version = "0.34.0" @@ -1387,7 +1364,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa 1.0.15", - "phf 0.11.3", + "phf", "smallvec 1.15.1", ] @@ -1400,7 +1377,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa 1.0.15", - "phf 0.11.3", + "phf", "smallvec 1.15.1", ] @@ -1569,10 +1546,8 @@ version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ - "convert_case", "proc-macro2", "quote", - "rustc_version 0.4.1", "syn 2.0.104", ] @@ -3251,7 +3226,7 @@ dependencies = [ "mailparse", "maplit", "memmap", - "quick-xml 0.38.1", + "quick-xml", "regex", "reqwest", "scraper", @@ -3265,7 +3240,6 @@ dependencies = [ "tracing", "url", "urlencoding", - "xmlem", "xtracing", "zip", ] @@ -3554,8 +3528,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7a7213d12e1864c0f002f52c2923d4556935a43dec5e71355c2760e0f6e7a18" dependencies = [ "log", - "phf 0.11.3", - "phf_codegen 0.11.3", + "phf", + "phf_codegen", "string_cache", "string_cache_codegen", "tendril", @@ -3603,12 +3577,6 @@ dependencies = [ "regex-automata 0.1.10", ] -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "matchit" version = "0.7.3" @@ -3881,12 +3849,6 @@ dependencies = [ "libc", ] -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - [[package]] name = "nom" version = "7.1.3" @@ -4340,35 +4302,14 @@ dependencies = [ "sha2 0.10.9", ] -[[package]] -name = "phf" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12" -dependencies = [ - "phf_macros 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", -] - [[package]] name = "phf" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros 0.11.3", - "phf_shared 0.11.3", -] - -[[package]] -name = "phf_codegen" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", + "phf_macros", + "phf_shared", ] [[package]] @@ -4377,18 +4318,8 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", -] - -[[package]] -name = "phf_generator" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526" -dependencies = [ - "phf_shared 0.8.0", - "rand 0.7.3", + "phf_generator", + "phf_shared", ] [[package]] @@ -4397,53 +4328,30 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared 0.11.3", + "phf_shared", "rand 0.8.5", ] -[[package]] -name = "phf_macros" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6fde18ff429ffc8fe78e2bf7f8b7a5a5a6e2a8b58bc5a9ac69198bbda9189c" -dependencies = [ - "phf_generator 0.8.0", - "phf_shared 0.8.0", - "proc-macro-hack", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "phf_macros" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator", + "phf_shared", "proc-macro2", "quote", "syn 2.0.104", ] -[[package]] -name = "phf_shared" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" -dependencies = [ - "siphasher 0.3.11", -] - [[package]] name = "phf_shared" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher 1.0.1", + "siphasher", ] [[package]] @@ -4616,12 +4524,6 @@ dependencies = [ "syn 2.0.104", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" version = "1.0.96" @@ -4663,48 +4565,6 @@ dependencies = [ "prost", ] -[[package]] -name = "qname" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fac552bc5de28a2d8ebb5d93d157b1eb3d1ca58f88ba2d1e2b931951cb52b29" -dependencies = [ - "qname-impl", - "qname-macro", -] - -[[package]] -name = "qname-impl" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28cd0334d1ed0cb075cde40d45b9ee648fe79e69a18c3e2891f79c3ec788304" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "qname-macro" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e673fdaa2dcfd19fac443c4a361cc2635b2304592cdc5352ef76bb8dd38333c" -dependencies = [ - "proc-macro2", - "qname-impl", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "quick-xml" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b" -dependencies = [ - "memchr", -] - [[package]] name = "quick-xml" version = "0.38.1" @@ -4802,20 +4662,6 @@ dependencies = [ "scheduled-thread-pool", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", - "rand_pcg", -] - [[package]] name = "rand" version = "0.8.5" @@ -4837,16 +4683,6 @@ dependencies = [ "rand_core 0.9.3", ] -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - [[package]] name = "rand_chacha" version = "0.3.1" @@ -4867,15 +4703,6 @@ dependencies = [ "rand_core 0.9.3", ] -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - [[package]] name = "rand_core" version = "0.6.4" @@ -4904,24 +4731,6 @@ dependencies = [ "rand 0.8.5", ] -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - [[package]] name = "rayon" version = "1.10.0" @@ -5562,24 +5371,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "selectors" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdea87c686be721aab36607728047801ee21561bfdbd6bf0da7ace2536d5879f" -dependencies = [ - "bitflags 1.3.2", - "cssparser 0.28.1", - "derive_more 0.99.20", - "fxhash", - "log", - "phf 0.8.0", - "phf_codegen 0.8.0", - "precomputed-hash", - "servo_arc 0.1.1", - "smallvec 1.15.1", -] - [[package]] name = "selectors" version = "0.26.0" @@ -5592,10 +5383,10 @@ dependencies = [ "fxhash", "log", "new_debug_unreachable", - "phf 0.11.3", - "phf_codegen 0.11.3", + "phf", + "phf_codegen", "precomputed-hash", - "servo_arc 0.4.1", + "servo_arc", "smallvec 1.15.1", ] @@ -5611,10 +5402,10 @@ dependencies = [ "fxhash", "log", "new_debug_unreachable", - "phf 0.11.3", - "phf_codegen 0.11.3", + "phf", + "phf_codegen", "precomputed-hash", - "servo_arc 0.4.1", + "servo_arc", "smallvec 1.15.1", ] @@ -5737,16 +5528,6 @@ dependencies = [ "serde", ] -[[package]] -name = "servo_arc" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98238b800e0d1576d8b6e3de32827c2d74bee68bb97748dcf5071fb53965432" -dependencies = [ - "nodrop", - "stable_deref_trait", -] - [[package]] name = "servo_arc" version = "0.4.1" @@ -5855,12 +5636,6 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" -[[package]] -name = "siphasher" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" - [[package]] name = "siphasher" version = "1.0.1" @@ -6169,7 +5944,7 @@ checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", "parking_lot 0.12.4", - "phf_shared 0.11.3", + "phf_shared", "precomputed-hash", "serde", ] @@ -6180,8 +5955,8 @@ version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ - "phf_generator 0.11.3", - "phf_shared 0.11.3", + "phf_generator", + "phf_shared", "proc-macro2", "quote", ] @@ -7218,189 +6993,6 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" -[[package]] -name = "unic-char-property" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" -dependencies = [ - "unic-char-range", -] - -[[package]] -name = "unic-char-range" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" - -[[package]] -name = "unic-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" - -[[package]] -name = "unic-ucd" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625b18f7601e1127504a20ae731dc3c7826d0e86d5f7fe3434f8137669240efd" -dependencies = [ - "unic-ucd-age", - "unic-ucd-bidi", - "unic-ucd-block", - "unic-ucd-case", - "unic-ucd-category", - "unic-ucd-common", - "unic-ucd-hangul", - "unic-ucd-ident", - "unic-ucd-name", - "unic-ucd-name_aliases", - "unic-ucd-normal", - "unic-ucd-segment", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-age" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8cfdfe71af46b871dc6af2c24fcd360e2f3392ee4c5111877f2947f311671c" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-bidi" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1d568b51222484e1f8209ce48caa6b430bf352962b877d592c29ab31fb53d8c" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-block" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b2a16f2d7ecd25325a1053ca5a66e7fa1b68911a65c5e97f8d2e1b236b6f1d7" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-case" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d98d6246a79bac6cf66beee01422bda7c882e11d837fa4969bfaaba5fdea6d3" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-category" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8d4591f5fcfe1bd4453baaf803c40e1b1e69ff8455c47620440b46efef91c0" -dependencies = [ - "matches", - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-common" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9b78b910beafa1aae5c59bf00877c6cece1c5db28a1241ad801e86cecdff4ad" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-hangul" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1dc690e19010e1523edb9713224cba5ef55b54894fe33424439ec9a40c0054" -dependencies = [ - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-ident" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-name" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8fc55a45b2531089dc1773bf60c1f104b38e434b774ffc37b9c29a9b0f492e" -dependencies = [ - "unic-char-property", - "unic-ucd-hangul", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-name_aliases" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b7674212643087699ba247a63dd05f1204c7e4880ec9342e545a7cffcc6a46f" -dependencies = [ - "unic-char-property", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-normal" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86aed873b8202d22b13859dda5fe7c001d271412c31d411fd9b827e030569410" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-category", - "unic-ucd-hangul", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-segment" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2079c122a62205b421f499da10f3ee0f7697f012f55b675e002483c73ea34700" -dependencies = [ - "unic-char-property", - "unic-char-range", - "unic-ucd-version", -] - -[[package]] -name = "unic-ucd-version" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" -dependencies = [ - "unic-common", -] - [[package]] name = "unicase" version = "2.8.1" @@ -7738,8 +7330,8 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414" dependencies = [ - "phf 0.11.3", - "phf_codegen 0.11.3", + "phf", + "phf_codegen", "string_cache", "string_cache_codegen", ] @@ -8139,22 +7731,6 @@ version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" -[[package]] -name = "xmlem" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d7eadd176b9e994da5c27acd46001a82256afa9e49b1b56b37e117e63a0aee" -dependencies = [ - "cssparser 0.28.1", - "indexmap 1.9.3", - "once_cell", - "qname", - "quick-xml 0.22.0", - "selectors 0.23.0", - "slotmap", - "unic-ucd", -] - [[package]] name = "xtracing" version = "0.3.2" diff --git a/server/Cargo.toml b/server/Cargo.toml index 5e35c01..ef719f9 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -54,7 +54,7 @@ urlencoding = "2.1.3" #xtracing = { path = "../../xtracing" } xtracing = { version = "0.3.2", registry = "xinu" } zip = "4.3.0" -xmlem = "0.1.0" + [build-dependencies] build-info-build = "0.0.41" diff --git a/server/src/error.rs b/server/src/error.rs index d48c64d..f868da2 100644 --- a/server/src/error.rs +++ b/server/src/error.rs @@ -43,4 +43,6 @@ pub enum ServerError { AskamaError(#[from] askama::Error), #[error("xml error: {0}")] XmlError(#[from] quick_xml::Error), + #[error("xml encoding error: {0}")] + XmlEncodingError(#[from] quick_xml::encoding::EncodingError), } diff --git a/server/src/nm.rs b/server/src/nm.rs index 52f9126..1ee9d7b 100644 --- a/server/src/nm.rs +++ b/server/src/nm.rs @@ -2,7 +2,7 @@ use std::{ collections::{HashMap, HashSet}, fs::File, io::{Cursor, Read}, - str::FromStr, + }; use askama::Template; @@ -14,7 +14,7 @@ use memmap::MmapOptions; use quick_xml::de::from_str as xml_from_str; use sqlx::{types::Json, PgPool}; use tracing::{error, info, info_span, instrument, warn}; -use xmlem::{display, Document}; + use zip::ZipArchive; use crate::{ @@ -407,15 +407,12 @@ pub async fn thread( } else { // DMARC reports are XML // Pretty print XML - let doc_result = Document::from_str(&raw_content); - if let Ok(doc) = doc_result { - doc.to_string_pretty_with_config(&display::Config::default_pretty()) - } else { - error!( - "Failed to parse XML for pretty printing: {:?}", - doc_result.unwrap_err() - ); - raw_content + match pretty_print_xml_with_trimming(&raw_content) { + Ok(pretty_xml) => pretty_xml, + Err(e) => { + error!("Failed to pretty print XML: {:?}", e); + raw_content + } } }; current_html.push_str(&format!( @@ -897,15 +894,12 @@ fn extract_mixed(m: &ParsedMail, part_addr: &mut Vec) -> Result pretty_xml, + Err(e) => { + error!("Failed to pretty print XML: {:?}", e); + xml + } } }; parts.push(Body::html(format!("\n
{}
", html_escape::encode_text(&pretty_printed_content)))); @@ -1864,6 +1858,37 @@ pub fn parse_dmarc_report(xml: &str) -> Result { Ok(html) } +fn pretty_print_xml_with_trimming(xml_input: &str) -> Result { + use quick_xml::events::{Event, BytesText}; + use quick_xml::reader::Reader; + use quick_xml::writer::Writer; + use std::io::Cursor; + + let mut reader = Reader::from_str(xml_input); + reader.config_mut().trim_text(true); + + let mut writer = Writer::new_with_indent(Cursor::new(Vec::new()), b' ', 4); + + let mut buf = Vec::new(); + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Eof) => break, + Ok(Event::Text(e)) => { + let trimmed_text = e.decode()?.trim().to_string(); + writer.write_event(Event::Text(BytesText::new(&trimmed_text)))?; + }, + Ok(event) => { + writer.write_event(event)?; + }, + Err(e) => return Err(ServerError::StringError(format!("XML parsing error: {}", e))), + } + buf.clear(); + } + + let result = writer.into_inner().into_inner(); + Ok(String::from_utf8(result)?) +} + #[cfg(test)] mod tests { use super::*;