From 5c3e8cac09ea3d936cd741dd0243cd96be875baa Mon Sep 17 00:00:00 2001 From: loong10k <20489781+loong10k@users.noreply.github.com> Date: Fri, 18 Sep 2026 00:26:41 +0800 Subject: [PATCH] =?UTF-8?q?build(deps):=20quick-xml=200.41=20=E2=86=92=200?= =?UTF-8?q?.42=EF=BC=88String=20=E4=BA=BA=E6=9C=BA=E5=B7=A5=E5=AD=A6?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 0.42 将 Name 类型/事件内容从 &[u8] 改为 &str(Rust 2024 / MSRV 1.86) - sax.rs 常量表 &[u8] = b"..." → &str = "...",全文件比较与 match 判据自动适配 - numbering.rs / image.rs / easydoc-mcp tools.rs 字节字面量 → 字符串字面量, extract_u32_attr / extract_u8_attr 签名 &[u8] → &str - omml_to_latex.rs 去掉多余 from_utf8 包装;BytesText / Attribute::value 直接取 &str / Cow(保持 0.41 的不反转义语义不变) - 全量 1140 测试通过;clippy / fmt 干净 supersede dependabot #4 --- Cargo.lock | 23 ++++-- Cargo.toml | 2 +- crates/easydoc-math/src/omml_to_latex.rs | 13 ++- crates/easydoc-mcp/src/tools.rs | 8 +- crates/easydoc-reader/src/extractor/image.rs | 8 +- .../easydoc-reader/src/extractor/numbering.rs | 34 ++++---- crates/easydoc-reader/src/extractor/sax.rs | 80 +++++++++---------- 7 files changed, 88 insertions(+), 80 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e5a2258..991bc5b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -456,7 +456,7 @@ dependencies = [ "base64 0.22.1", "crc32fast", "image", - "quick-xml", + "quick-xml 0.41.0", "serde", "serde_json", "smallvec", @@ -530,7 +530,7 @@ dependencies = [ "easydoc-ooxml", "easydoc-reader", "easydoc-writer", - "quick-xml", + "quick-xml 0.42.0", "tempfile", "zip", ] @@ -541,7 +541,7 @@ version = "0.1.1" dependencies = [ "easydoc-core", "proptest", - "quick-xml", + "quick-xml 0.42.0", "tempfile", ] @@ -555,7 +555,7 @@ dependencies = [ "easydoc-markdown", "easydoc-reader", "easydoc-writer", - "quick-xml", + "quick-xml 0.42.0", "serde", "serde_json", "tempfile", @@ -580,7 +580,7 @@ dependencies = [ "easydoc-writer", "office_oxide", "proptest", - "quick-xml", + "quick-xml 0.42.0", "tempfile", "zip", ] @@ -981,7 +981,7 @@ dependencies = [ "encoding_rs", "fast-float2", "log", - "quick-xml", + "quick-xml 0.41.0", "serde", "serde_json", "thiserror", @@ -1087,7 +1087,7 @@ checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85" dependencies = [ "base64 0.22.1", "indexmap", - "quick-xml", + "quick-xml 0.41.0", "serde", "time", ] @@ -1224,6 +1224,15 @@ dependencies = [ "serde", ] +[[package]] +name = "quick-xml" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.47" diff --git a/Cargo.toml b/Cargo.toml index 36424b1..8c686c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ tempfile = "3.27.0" thiserror = "2.0.18" uuid = { version = "1.24.0", features = ["v4"] } zip = { version = "8.6.0", default-features = false, features = ["deflate"] } -quick-xml = "0.41" +quick-xml = "0.42" anyhow = "1" tracing = "0.1" proptest = "1.5" diff --git a/crates/easydoc-math/src/omml_to_latex.rs b/crates/easydoc-math/src/omml_to_latex.rs index 80ebd46..961b690 100644 --- a/crates/easydoc-math/src/omml_to_latex.rs +++ b/crates/easydoc-math/src/omml_to_latex.rs @@ -195,7 +195,7 @@ impl OmmlConverter { } } Ok(Event::Text(t)) => { - let text = String::from_utf8_lossy(&t).into_owned(); + let text = t.as_ref().to_owned(); if !text.is_empty() { parts.push(text); } @@ -424,7 +424,7 @@ impl OmmlConverter { } } Ok(Event::Text(t)) if in_text => { - let raw = String::from_utf8_lossy(&t); + let raw = t.as_ref(); let mut mapped = String::with_capacity(raw.len()); for c in raw.chars() { let mut char_buf = [0u8; 4]; @@ -1376,11 +1376,10 @@ fn is_omath(e: &BytesStart) -> bool { /// Get the local name (stripping `m:` prefix) from a start event. fn local_name(e: &BytesStart) -> String { let qname = e.name(); - let raw = qname.as_ref(); - let s = String::from_utf8_lossy(raw); + let s: &str = qname.as_ref(); match s.strip_prefix(OMML_NS_PREFIX) { Some(stripped) => stripped.to_owned(), - None => s.into_owned(), + None => s.to_owned(), } } @@ -1396,9 +1395,9 @@ fn dispatch_empty(stag: &str, e: &BytesStart) -> Option { fn attr_val(e: &BytesStart, name: &str) -> Option { let prefixed = format!("{OMML_NS_PREFIX}{name}"); for attr in e.attributes().flatten() { - let key = String::from_utf8_lossy(attr.key.as_ref()); + let key: &str = attr.key.as_ref(); if key == prefixed || key == name { - return Some(String::from_utf8_lossy(&attr.value).into_owned()); + return Some(attr.value.into_owned()); } } None diff --git a/crates/easydoc-mcp/src/tools.rs b/crates/easydoc-mcp/src/tools.rs index 6960708..a990b77 100644 --- a/crates/easydoc-mcp/src/tools.rs +++ b/crates/easydoc-mcp/src/tools.rs @@ -715,25 +715,25 @@ fn collect_image_entries(xml: &str) -> Vec<(String, String)> { loop { match reader.read_event_into(&mut buf) { Ok(quick_xml::events::Event::Empty(ref tag)) => { - if tag.name().as_ref() == b"Relationship" { + if tag.name().as_ref() == "Relationship" { let mut id = None; let mut target = None; let mut is_image = false; for attr in tag.attributes().flatten() { match attr.key.as_ref() { - b"Id" => { + "Id" => { id = attr .normalized_value(quick_xml::XmlVersion::Implicit1_0) .ok() .map(std::borrow::Cow::into_owned); } - b"Target" => { + "Target" => { target = attr .normalized_value(quick_xml::XmlVersion::Implicit1_0) .ok() .map(std::borrow::Cow::into_owned); } - b"Type" => { + "Type" => { if let Ok(val) = attr.normalized_value(quick_xml::XmlVersion::Implicit1_0) && val.ends_with("/image") diff --git a/crates/easydoc-reader/src/extractor/image.rs b/crates/easydoc-reader/src/extractor/image.rs index a7c0ba1..c705198 100644 --- a/crates/easydoc-reader/src/extractor/image.rs +++ b/crates/easydoc-reader/src/extractor/image.rs @@ -61,26 +61,26 @@ impl Relationships { Ok(Event::Eof) => break, Ok(Event::Empty(ref tag)) => { let name = tag.name(); - if name.as_ref() == b"Relationship" { + if name.as_ref() == "Relationship" { let mut id = None; let mut target = None; let mut rel_type = RelType::Other; for attr in tag.attributes().flatten() { match attr.key.as_ref() { - b"Id" => { + "Id" => { id = attr .normalized_value(quick_xml::XmlVersion::Implicit1_0) .ok() .map(Cow::into_owned); } - b"Target" => { + "Target" => { target = attr .normalized_value(quick_xml::XmlVersion::Implicit1_0) .ok() .map(Cow::into_owned); } - b"Type" => { + "Type" => { if let Ok(val) = attr.normalized_value(quick_xml::XmlVersion::Implicit1_0) { diff --git a/crates/easydoc-reader/src/extractor/numbering.rs b/crates/easydoc-reader/src/extractor/numbering.rs index f8a3be0..d184eea 100644 --- a/crates/easydoc-reader/src/extractor/numbering.rs +++ b/crates/easydoc-reader/src/extractor/numbering.rs @@ -76,26 +76,26 @@ impl Numbering { let name = start.name(); let local = name.as_ref(); match local { - b"w:abstractNum" => { - current_abstract_id = extract_u32_attr(start, b"w:abstractNumId"); + "w:abstractNum" => { + current_abstract_id = extract_u32_attr(start, "w:abstractNumId"); } - b"w:lvl" => { - current_ilvl = extract_u8_attr(start, b"w:ilvl"); + "w:lvl" => { + current_ilvl = extract_u8_attr(start, "w:ilvl"); current_num_fmt = None; current_start = None; } - b"w:numFmt" => { + "w:numFmt" => { current_num_fmt = extract_val_attr(start); } - b"w:start" => { + "w:start" => { current_start = extract_val_attr(start).and_then(|v| v.parse::().ok()); } - b"w:num" => { - current_num_id = extract_u32_attr(start, b"w:numId"); + "w:num" => { + current_num_id = extract_u32_attr(start, "w:numId"); in_num = true; } - b"w:abstractNumId" if in_num => { + "w:abstractNumId" if in_num => { // This is inside -- read the val attribute. if let (Some(abstract_id), Some(num_id)) = (extract_val_attr_u32(start), current_num_id) @@ -110,14 +110,14 @@ impl Numbering { let name = empty.name(); let local = name.as_ref(); match local { - b"w:numFmt" => { + "w:numFmt" => { current_num_fmt = extract_val_attr(empty); } - b"w:start" => { + "w:start" => { current_start = extract_val_attr(empty).and_then(|v| v.parse::().ok()); } - b"w:abstractNumId" if in_num => { + "w:abstractNumId" if in_num => { if let (Some(abstract_id), Some(num_id)) = (extract_val_attr_u32(empty), current_num_id) { @@ -131,7 +131,7 @@ impl Numbering { let name = end.name(); let local = name.as_ref(); match local { - b"w:lvl" => { + "w:lvl" => { // Finalize the level we were parsing. if let (Some(abstract_id), Some(ilvl)) = (current_abstract_id, current_ilvl) @@ -154,7 +154,7 @@ impl Numbering { current_num_fmt = None; current_start = None; } - b"w:num" => { + "w:num" => { current_num_id = None; in_num = false; } @@ -187,7 +187,7 @@ impl Numbering { } /// Extracts a `u32` value from a named attribute (e.g. `w:abstractNumId="0"`). -fn extract_u32_attr(tag: &quick_xml::events::BytesStart, attr_name: &[u8]) -> Option { +fn extract_u32_attr(tag: &quick_xml::events::BytesStart, attr_name: &str) -> Option { for attr in tag.attributes().flatten() { if attr.key.as_ref() == attr_name { let val = attr @@ -200,7 +200,7 @@ fn extract_u32_attr(tag: &quick_xml::events::BytesStart, attr_name: &[u8]) -> Op } /// Extracts a `u8` value from a named attribute (e.g. `w:ilvl="0"`). -fn extract_u8_attr(tag: &quick_xml::events::BytesStart, attr_name: &[u8]) -> Option { +fn extract_u8_attr(tag: &quick_xml::events::BytesStart, attr_name: &str) -> Option { for attr in tag.attributes().flatten() { if attr.key.as_ref() == attr_name { let val = attr @@ -215,7 +215,7 @@ fn extract_u8_attr(tag: &quick_xml::events::BytesStart, attr_name: &[u8]) -> Opt /// Extracts the `w:val` attribute as a `String`. fn extract_val_attr(tag: &quick_xml::events::BytesStart) -> Option { for attr in tag.attributes().flatten() { - if attr.key.as_ref() == b"w:val" { + if attr.key.as_ref() == "w:val" { return attr .normalized_value(quick_xml::XmlVersion::Implicit1_0) .ok() diff --git a/crates/easydoc-reader/src/extractor/sax.rs b/crates/easydoc-reader/src/extractor/sax.rs index e559b1a..22bd281 100644 --- a/crates/easydoc-reader/src/extractor/sax.rs +++ b/crates/easydoc-reader/src/extractor/sax.rs @@ -24,43 +24,43 @@ use quick_xml::events::Event; // OOXML element / attribute name constants (with w: namespace prefix) // --------------------------------------------------------------------------- -const W_P: &[u8] = b"w:p"; -const W_R: &[u8] = b"w:r"; -const W_T: &[u8] = b"w:t"; -const W_PPR: &[u8] = b"w:pPr"; -const W_PSTYLE: &[u8] = b"w:pStyle"; -const W_RPR: &[u8] = b"w:rPr"; -const W_B: &[u8] = b"w:b"; -const W_I: &[u8] = b"w:i"; -const W_STRIKE: &[u8] = b"w:strike"; -const W_TBL: &[u8] = b"w:tbl"; -const W_TR: &[u8] = b"w:tr"; -const W_TC: &[u8] = b"w:tc"; -const W_BR: &[u8] = b"w:br"; -const W_DRAWING: &[u8] = b"w:drawing"; -const W_TCPR: &[u8] = b"w:tcPr"; -const W_GRIDSPAN: &[u8] = b"w:gridSpan"; -const W_VMERGE: &[u8] = b"w:vMerge"; - -const A_BLIP: &[u8] = b"a:blip"; -const WP_DOC_PR: &[u8] = b"wp:docPr"; -const R_EMBED: &[u8] = b"r:embed"; - -const W_VAL: &[u8] = b"w:val"; -const W_TYPE: &[u8] = b"w:type"; +const W_P: &str = "w:p"; +const W_R: &str = "w:r"; +const W_T: &str = "w:t"; +const W_PPR: &str = "w:pPr"; +const W_PSTYLE: &str = "w:pStyle"; +const W_RPR: &str = "w:rPr"; +const W_B: &str = "w:b"; +const W_I: &str = "w:i"; +const W_STRIKE: &str = "w:strike"; +const W_TBL: &str = "w:tbl"; +const W_TR: &str = "w:tr"; +const W_TC: &str = "w:tc"; +const W_BR: &str = "w:br"; +const W_DRAWING: &str = "w:drawing"; +const W_TCPR: &str = "w:tcPr"; +const W_GRIDSPAN: &str = "w:gridSpan"; +const W_VMERGE: &str = "w:vMerge"; + +const A_BLIP: &str = "a:blip"; +const WP_DOC_PR: &str = "wp:docPr"; +const R_EMBED: &str = "r:embed"; + +const W_VAL: &str = "w:val"; +const W_TYPE: &str = "w:type"; // List numbering constants -const W_NUMPR: &[u8] = b"w:numPr"; -const W_NUMID: &[u8] = b"w:numId"; -const W_ILVL: &[u8] = b"w:ilvl"; +const W_NUMPR: &str = "w:numPr"; +const W_NUMID: &str = "w:numId"; +const W_ILVL: &str = "w:ilvl"; // Hyperlink constants -const W_HYPERLINK: &[u8] = b"w:hyperlink"; -const R_ID: &[u8] = b"r:id"; +const W_HYPERLINK: &str = "w:hyperlink"; +const R_ID: &str = "r:id"; // OMML math namespace constants (m: prefix) -const M_OMATH: &[u8] = b"m:oMath"; -const M_OMATHPARA: &[u8] = b"m:oMathPara"; +const M_OMATH: &str = "m:oMath"; +const M_OMATHPARA: &str = "m:oMathPara"; // --------------------------------------------------------------------------- // State machine @@ -726,7 +726,7 @@ impl DocxSaxReader { math_depth += 1; } math_xml_buf.push('<'); - math_xml_buf.push_str(std::str::from_utf8(start.as_ref()).unwrap_or("")); + math_xml_buf.push_str(start.as_ref()); math_xml_buf.push('>'); } Event::End(end) => { @@ -735,7 +735,7 @@ impl DocxSaxReader { // Append closing tag to buffer first. math_xml_buf.push_str("'); // Check whether this end tag closes the root math @@ -761,11 +761,11 @@ impl DocxSaxReader { } Event::Empty(empty) => { math_xml_buf.push('<'); - math_xml_buf.push_str(std::str::from_utf8(empty.as_ref()).unwrap_or("")); + math_xml_buf.push_str(empty.as_ref()); math_xml_buf.push_str("/>"); } Event::Text(text) => { - math_xml_buf.push_str(std::str::from_utf8(text.as_ref()).unwrap_or("")); + math_xml_buf.push_str(text.as_ref()); } _ => {} } @@ -788,7 +788,7 @@ impl DocxSaxReader { math_depth = 1; math_xml_buf.clear(); math_xml_buf.push('<'); - math_xml_buf.push_str(std::str::from_utf8(start.as_ref()).unwrap_or("")); + math_xml_buf.push_str(start.as_ref()); math_xml_buf.push('>'); } else { handle_start(start, &mut state_stack)?; @@ -802,7 +802,7 @@ impl DocxSaxReader { flush_paragraph_runs(sink, &mut state_stack)?; let display = name_bytes == M_OMATHPARA; let mut xml = String::from("<"); - xml.push_str(std::str::from_utf8(empty.as_ref()).unwrap_or("")); + xml.push_str(empty.as_ref()); xml.push_str("/>"); sink.push_block(DocumentBlock::Math { omml: Some(xml), @@ -1143,7 +1143,7 @@ fn handle_empty( WP_DOC_PR => { if let Some(ParseState::Drawing { pending_alt, .. }) = stack.last_mut() { for attr in empty.attributes().flatten() { - if attr.key.as_ref() == b"descr" { + if attr.key.as_ref() == "descr" { let val = attr .normalized_value(quick_xml::XmlVersion::Implicit1_0) .ok() @@ -1165,7 +1165,7 @@ fn handle_empty( fn handle_text(text: &quick_xml::events::BytesText, stack: &mut [ParseState]) -> Result<()> { // Accumulate text into the appropriate buffer depending on current state. // OOXML is always UTF-8, so we can decode the raw bytes directly. - let decoded = std::str::from_utf8(text.as_ref()).unwrap_or("").to_owned(); + let decoded = text.as_ref().to_owned(); if let Some(state) = stack.last_mut() { match state { ParseState::Paragraph { @@ -1506,7 +1506,7 @@ fn extract_bool_attr(tag: &quick_xml::events::BytesStart) -> Option { /// Checks `xml:space="preserve"` on a `` tag. fn has_preserve_space(tag: &quick_xml::events::BytesStart) -> bool { for attr in tag.attributes().flatten() { - if attr.key.as_ref() == b"xml:space" { + if attr.key.as_ref() == "xml:space" { return attr .normalized_value(quick_xml::XmlVersion::Implicit1_0) .is_ok_and(|v| v.as_ref() == "preserve");