Skip to content

feat(xml): namespace-correct FromXml core + winrm migration#33

Merged
irvingouj@Devolutions (irvingoujAtDevolution) merged 3 commits into
masterfrom
stack/xml-fromxml-core-migrate
Jun 25, 2026
Merged

feat(xml): namespace-correct FromXml core + winrm migration#33
irvingouj@Devolutions (irvingoujAtDevolution) merged 3 commits into
masterfrom
stack/xml-fromxml-core-migrate

Conversation

@irvingoujAtDevolution

Copy link
Copy Markdown
Collaborator

Part 1/6. Base: master.

Introduces the generic, namespace-aware FromXml core (ironposh-xml/src/mapping.rs) and the #[derive(FromXml)] proc-macro, then migrates ironposh-winrm off the old XML visitor framework to direct recursive descent. Identity is the (namespace-URI, local-name) pair; prefixes are irrelevant. Includes the fmt/clippy cleanup for the migration (folded in because the migration commit isn't independently lint-clean).

Net: large deletion (the visitor framework). The visitor stays only in ironposh-xml for the PSRP CLIXML primitive layer.

FromXml stack (review/merge bottom-up):

  1. core + winrm migration
  2. nested-tag descend fix
  3. delete dead AnyTag/TagList + markers
  4. tag! macro + aliases
  5. harden / reject malformed XML
  6. tests

…sitor)

The deserialize side is where the visitor ceremony lived (accumulator
structs, finish(), NotSupposeToBeCalled). Replace it with a direct,
attribute-free derive:

- ironposh-xml `mapping`: `FromXml`/`ToXml` + `NodeExt` — element identity
  is the (namespace-URI, local-name) pair; the prefix is never compared.
- `#[derive(FromXml)]`: generates a flat `from_xml(node)` that matches each
  child by (URI, name) sourced from the field's `Tag<_, _, N>` type. No
  visitor struct, no finish(). Reuses the existing Tag/TagName types, so
  migrating a struct is a one-line derive add.

Proven on `DisconnectValue`: parses IdleTimeOut regardless of prefix, and
ignores a same-local-name child in the wrong namespace (the bug the old
visit_node had — it matched on local name only).

Additive: the existing XmlVisitor/SimpleXmlDeserialize path is untouched;
removing it across the winrm structs is the mechanical follow-up.
Deserialization across the WinRM/SOAP/WS-Management layer no longer uses the
accumulator-visitor pattern. Every type now implements a single direct
`ironposh_xml::mapping::FromXml::from_xml(node) -> Result<Self>`, and matching
is by `(namespace-URI, local-name)` via `NodeExt` — the prefix is never
compared, fixing the long-standing local-name-only matching bug.

Removed (no longer needed):
- `SimpleXmlDeserialize` derive + the `impl_xml_deserialize!` / `impl_tag_value!`
  macro_rules (the latter two were already dead).
- Every hand-written `XmlVisitor` impl: TagVisitor, TagList, AnyTag, the leaf
  value visitors (Text/Empty/WsUuid/Time/numerics/Unparsed), SoapEnvelope,
  Receive/ReceiveResponse, Send, SelectorSet/OptionSet, CommandLine, namespace
  visitors.
- The duplicate NodeDeserializer in winrm.

Converted:
- Structs whose fields are `Tag<_, _, N>` now `#[derive(FromXml)]` (one line);
  the value/leaf types and the irregular hand-written ones implement `FromXml`
  directly. Serialization (TagValue / Element builder) is unchanged.

Correctness fixes surfaced by namespace-aware matching:
- `creationXml` / `connectXml` / `connectResponseXml` carry the PowerShell
  namespace on the wire; their TagName definitions said `None`. Set to the real
  URI and taught the builder to emit a default-namespace element unprefixed
  (instead of erroring), keeping serialized output byte-identical.
- Unknown extension namespaces/elements are now ignored (SOAP must-ignore)
  rather than failing the whole envelope.

`ironposh-xml` keeps the legacy `XmlVisitor`/`XmlDeserialize` traits only
because ps_value's CLIXML primitive layer still rides on them; ps_value's
ComplexObject tree is untouched. Net: 310 insertions, 1804 deletions.
- rustfmt: import ordering/grouping on the touched files.
- clippy (-D warnings): drop the redundant `continue` from the FromXml derive
  output; merge the identical default-namespace match arms in the Element
  writer; use `Self` in the hand-written FromXml impls.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant