From e2b16c07c49521f2f644ad12a35245ab7942e375 Mon Sep 17 00:00:00 2001 From: Junyi Ou Date: Wed, 24 Jun 2026 15:58:41 -0400 Subject: [PATCH 1/3] refactor(winrm): delete dead AnyTag/TagList; derive reads identity via NamedTag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two steps toward de-uglifying the Tag layer, both behavior-preserving: 1. `NamedTag` trait: exposes a tag's (TAG_NAME, NAMESPACE) at the type level, forwarding from `N: TagName`. `#[derive(FromXml)]` now reads identity via `` instead of syntactically extracting the `N` param — so it will keep working once fields become type aliases for `Tag<'a, V, N>`. 2. Delete `AnyTag` + `TagList`: the dynamic heterogeneous-list escape hatch. `TagList::with_tag` was never called (never built with content), its ~45 AnyTag variants were dead (SOAP envelope/header/body are typed structs now; the PSRP CLIXML tags belong to ps_value), and the four `Tag` bodies on SoapBody (pull/release/get_status/command) were never populated and only checked for `.is_none()` in tests. Removed the fields, the two files, and the stale test assertions. Verified: workspace tests pass; real-server matrix 10/10. --- crates/ironposh-macros/src/lib.rs | 79 +++------- crates/ironposh-winrm/src/cores/anytag.rs | 147 ------------------ crates/ironposh-winrm/src/cores/mod.rs | 3 - crates/ironposh-winrm/src/cores/tag.rs | 19 +++ crates/ironposh-winrm/src/cores/tag_list.rs | 47 ------ crates/ironposh-winrm/src/soap/body.rs | 8 - .../tests/test_initial_build_request.rs | 5 +- .../tests/test_parse_error_response.rs | 4 - .../tests/test_parse_receive_response.rs | 1 - 9 files changed, 41 insertions(+), 272 deletions(-) delete mode 100644 crates/ironposh-winrm/src/cores/anytag.rs delete mode 100644 crates/ironposh-winrm/src/cores/tag_list.rs diff --git a/crates/ironposh-macros/src/lib.rs b/crates/ironposh-macros/src/lib.rs index d6b468b..232f71d 100644 --- a/crates/ironposh-macros/src/lib.rs +++ b/crates/ironposh-macros/src/lib.rs @@ -1291,10 +1291,10 @@ fn impl_from_xml(input: &DeriveInput) -> TokenStream2 { .map(|field| { let field_name = field.ident.as_ref().unwrap().clone(); let is_optional = is_option_type(&field.ty); - let tag_name_type = extract_tag_name_type(&field.ty); + let value_type = inner_value_type(&field.ty); SimpleFieldEntry { field_name, - tag_name_type, + value_type, is_optional, } }) @@ -1305,19 +1305,19 @@ fn impl_from_xml(input: &DeriveInput) -> TokenStream2 { quote! { let mut #f = None; } }); - // One namespace-correct match per field: identity is (URI, local-name). - let matchers = entries.iter().filter_map(|e| { + // One namespace-correct match per field: identity is (URI, local-name), + // read from the field's tag type via `NamedTag` so it works through aliases. + let matchers = entries.iter().map(|e| { let f = &e.field_name; - e.tag_name_type.as_ref().map(|n| { - quote! { - if child.is_element_named( - ::NAMESPACE, - ::TAG_NAME, - ) { - #f = Some(ironposh_xml::mapping::FromXml::from_xml(child)?); - } + let ty = &e.value_type; + quote! { + if child.is_element_named( + <#ty as crate::cores::NamedTag>::NAMESPACE, + <#ty as crate::cores::NamedTag>::TAG_NAME, + ) { + #f = Some(ironposh_xml::mapping::FromXml::from_xml(child)?); } - }) + } }); let construct = entries.iter().map(|e| { @@ -1419,7 +1419,7 @@ fn impl_simple_tag_value(input: &DeriveInput) -> TokenStream2 { struct SimpleFieldEntry { field_name: Ident, - tag_name_type: Option, + value_type: Type, is_optional: bool, } @@ -1437,55 +1437,18 @@ fn is_option_type(ty: &Type) -> bool { false } -fn extract_tag_name_type(ty: &Type) -> Option { - // Try to extract TagName from Tag<'a, ValueType, TagName> or Option> - if let Type::Path(TypePath { path, .. }) = ty { - for segment in &path.segments { - if segment.ident == "Tag" || segment.ident == "Option" { - if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { - // For Option>, we need to look at the inner type - for arg in &args.args { - if let syn::GenericArgument::Type(inner_type) = arg { - if let Some(tag_name) = extract_tag_name_from_tag_type(inner_type) { - return Some(tag_name); - } - } - } - - // For Tag<'a, ValueType, TagName>, the third argument is TagName - if segment.ident == "Tag" && args.args.len() >= 3 { - if let syn::GenericArgument::Type(Type::Path(TypePath { path, .. })) = - &args.args[2] - { - if let Some(segment) = path.segments.last() { - return Some(segment.ident.clone()); - } - } - } - } - } - } - } - None -} - -fn extract_tag_name_from_tag_type(ty: &Type) -> Option { +/// The value a field carries: `Option` -> `T`, otherwise the type itself. +fn inner_value_type(ty: &Type) -> Type { if let Type::Path(TypePath { path, .. }) = ty { - for segment in &path.segments { - if segment.ident == "Tag" { + if let Some(segment) = path.segments.last() { + if segment.ident == "Option" { if let syn::PathArguments::AngleBracketed(args) = &segment.arguments { - if args.args.len() >= 3 { - if let syn::GenericArgument::Type(Type::Path(TypePath { path, .. })) = - &args.args[2] - { - if let Some(segment) = path.segments.last() { - return Some(segment.ident.clone()); - } - } + if let Some(syn::GenericArgument::Type(inner)) = args.args.first() { + return inner.clone(); } } } } } - None + ty.clone() } diff --git a/crates/ironposh-winrm/src/cores/anytag.rs b/crates/ironposh-winrm/src/cores/anytag.rs deleted file mode 100644 index b331095..0000000 --- a/crates/ironposh-winrm/src/cores/anytag.rs +++ /dev/null @@ -1,147 +0,0 @@ -use crate::{ - cores::{Tag, TagList, TagName, tag_name::*, tag_value::Text}, - rsp::{receive::ReceiveValue, shell_value::ShellValue}, -}; - -#[macro_export] -macro_rules! define_any_tag { - ($enum_name:ident, $(($variant:ident, $tag_name:ty, $tag_type:ty)),* $(,)?) => { - #[expect(clippy::large_enum_variant)] - #[derive(Debug, Clone)] - pub enum $enum_name<'a> { - $($variant($tag_type),)* - } - - $( - impl<'a> std::convert::TryInto<$tag_type> for AnyTag<'a> { - type Error = ironposh_xml::XmlError; - - fn try_into(self) -> Result<$tag_type, Self::Error> { - match self { - $enum_name::$variant(tag) => Ok(tag), - _ => Err(ironposh_xml::XmlError::InvalidXml(format!( - "Cannot convert {:?} to any tag type", - self - ))), - } - } - } - - impl<'a> std::convert::From<$tag_type> for $enum_name<'a> { - fn from(tag: $tag_type) -> Self { - $enum_name::$variant(tag) - } - } - )* - - - impl<'a> $enum_name<'a> { - pub fn into_element(self) -> ironposh_xml::builder::Element<'a> { - match self { - $($enum_name::$variant(tag) => tag.into_element(),)* - } - } - } - - impl<'a> ironposh_xml::mapping::FromXml<'a> for $enum_name<'a> { - fn from_xml(node: ironposh_xml::parser::Node<'a, 'a>) -> Result { - use ironposh_xml::mapping::NodeExt; - // Dispatch by (namespace-URI, local-name) — prefix is irrelevant. - $( - if node.is_element_named(<$tag_name>::NAMESPACE, <$tag_name>::TAG_NAME) { - return Ok($enum_name::$variant( - <$tag_type as ironposh_xml::mapping::FromXml>::from_xml(node)?, - )); - } - )* - Err(ironposh_xml::XmlError::InvalidXml(format!( - "Unknown tag: {} (namespace: {:?})", - node.tag_name().name(), - node.tag_name().namespace() - ))) - } - } - }; -} - -// Define the `AnyTag` enum for the following purposes: -// Right now, since we are having all tag and tag name definations defined in compile time (i.e Sized, no dynamic trait objects), -// Hence, for the purpose of having a dynamic representation of any tag, we define `AnyTag`. -// This will allow us to have a single enum that can represent any tag type, which can be useful for generic processing of XML documents. -// while perserving the compile-time safety of tag definitions. -define_any_tag!( - AnyTag, - // SOAP elements - (Envelope, Envelope, Tag<'a, TagList<'a>, Envelope>), - (Header, Header, Tag<'a, TagList<'a>, Header>), - (Body, Body, Tag<'a, TagList<'a>, Body>), - // WS-Addressing headers - (Action, Action, Tag<'a, Text<'a>, Action>), - (To, To, Tag<'a, Text<'a>, To>), - (MessageID, MessageID, Tag<'a, Text<'a>, MessageID>), - (RelatesTo, RelatesTo, Tag<'a, Text<'a>, RelatesTo>), - (ReplyTo, ReplyTo, Tag<'a, TagList<'a>, ReplyTo>), - (FaultTo, FaultTo, Tag<'a, Text<'a>, FaultTo>), - (From, From, Tag<'a, Text<'a>, From>), - (Address, Address, Tag<'a, Text<'a>, Address>), - // PowerShell remoting shell elements - (ShellId, ShellId, Tag<'a, Text<'a>, ShellId>), - (Shell, Shell, Tag<'a, ShellValue<'a>, Shell>), - (Receive, Receive, Tag<'a, ReceiveValue<'a>, Receive>), - (Name, Name, Tag<'a, Text<'a>, Name>), - (ResourceUri, ResourceUri, Tag<'a, Text<'a>, ResourceUri>), - (Owner, Owner, Tag<'a, Text<'a>, Owner>), - (ClientIP, ClientIP, Tag<'a, Text<'a>, ClientIP>), - (ProcessId, ProcessId, Tag<'a, Text<'a>, ProcessId>), - (IdleTimeOut, IdleTimeOut, Tag<'a, Text<'a>, IdleTimeOut>), - (InputStreams, InputStreams, Tag<'a, Text<'a>, InputStreams>), - ( - OutputStreams, - OutputStreams, - Tag<'a, Text<'a>, OutputStreams> - ), - ( - MaxIdleTimeOut, - MaxIdleTimeOut, - Tag<'a, Text<'a>, MaxIdleTimeOut> - ), - (Locale, Locale, Tag<'a, Text<'a>, Locale>), - (DataLocale, DataLocale, Tag<'a, Text<'a>, DataLocale>), - ( - CompressionMode, - CompressionMode, - Tag<'a, Text<'a>, CompressionMode> - ), - ( - ProfileLoaded, - ProfileLoaded, - Tag<'a, Text<'a>, ProfileLoaded> - ), - (Encoding, Encoding, Tag<'a, Text<'a>, Encoding>), - (BufferMode, BufferMode, Tag<'a, Text<'a>, BufferMode>), - (State, State, Tag<'a, Text<'a>, State>), - (ShellRunTime, ShellRunTime, Tag<'a, Text<'a>, ShellRunTime>), - ( - ShellInactivity, - ShellInactivity, - Tag<'a, Text<'a>, ShellInactivity> - ), - (CreationXml, CreationXml, Tag<'a, TagList<'a>, CreationXml>), - (Version, Version, Tag<'a, Text<'a>, Version>), - (BA, BA, Tag<'a, Text<'a>, BA>), - // PowerShell Serialization Format - (I32TagName, I32TagName, Tag<'a, Text<'a>, I32TagName>), // 32-bit integer - (TN, TN, Tag<'a, TagList<'a>, TN>), - (T, T, Tag<'a, Text<'a>, T>), - (ToString, ToString, Tag<'a, Text<'a>, ToString>), - (DCT, DCT, Tag<'a, TagList<'a>, DCT>), - (En, En, Tag<'a, TagList<'a>, En>), - (Key, Key, Tag<'a, Text<'a>, Key>), - (Value, Value, Tag<'a, Text<'a>, Value>), - (Nil, Nil, Tag<'a, Text<'a>, Nil>), - (B, B, Tag<'a, Text<'a>, B>), - (S, S, Tag<'a, Text<'a>, S>), - // Complex objects - (Obj, Obj, Tag<'a, TagList<'a>, Obj>), - (MS, MS, Tag<'a, TagList<'a>, MS>), -); diff --git a/crates/ironposh-winrm/src/cores/mod.rs b/crates/ironposh-winrm/src/cores/mod.rs index fcf11fe..7d5274c 100644 --- a/crates/ironposh-winrm/src/cores/mod.rs +++ b/crates/ironposh-winrm/src/cores/mod.rs @@ -1,14 +1,11 @@ -pub mod anytag; pub mod attribute; pub mod namespace; pub mod tag; -pub mod tag_list; pub mod tag_name; pub mod tag_value; pub use attribute::*; pub use namespace::*; pub use tag::*; -pub use tag_list::*; pub use tag_name::*; pub use tag_value::*; diff --git a/crates/ironposh-winrm/src/cores/tag.rs b/crates/ironposh-winrm/src/cores/tag.rs index fd6a18a..b579f58 100644 --- a/crates/ironposh-winrm/src/cores/tag.rs +++ b/crates/ironposh-winrm/src/cores/tag.rs @@ -202,6 +202,25 @@ where } } +/// A tag type's XML identity (name + namespace) exposed at the type level. +/// +/// `Tag<'a, V, N>` forwards to its `N: TagName`. Reading identity through this +/// trait — rather than naming `N` syntactically — lets `#[derive(FromXml)]` +/// work through type aliases like `pub type Get<'a> = Tag<'a, Text<'a>, GetTag>`. +pub trait NamedTag { + const TAG_NAME: &'static str; + const NAMESPACE: Option<&'static str>; +} + +impl<'a, V, N> NamedTag for Tag<'a, V, N> +where + V: TagValue<'a>, + N: TagName, +{ + const TAG_NAME: &'static str = N::TAG_NAME; + const NAMESPACE: Option<&'static str> = N::NAMESPACE; +} + impl<'a, V, N> AsRef for Tag<'a, V, N> where V: TagValue<'a>, diff --git a/crates/ironposh-winrm/src/cores/tag_list.rs b/crates/ironposh-winrm/src/cores/tag_list.rs deleted file mode 100644 index f8d98bb..0000000 --- a/crates/ironposh-winrm/src/cores/tag_list.rs +++ /dev/null @@ -1,47 +0,0 @@ -use ironposh_xml::{builder::Element, mapping::FromXml}; - -use crate::cores::{TagValue, anytag::AnyTag}; - -// This is just a temporary struct to hold a list of tags. -// to replace the actual TagValue going to be implemented for tags -#[derive(Debug, Clone, Default)] -pub struct TagList<'a> { - items: Vec>, -} - -impl<'a> TagList<'a> { - pub fn new() -> Self { - Self::default() - } - - pub fn add_tag(&mut self, tag: AnyTag<'a>) { - self.items.push(tag); - } - - pub fn with_tag(mut self, tag: AnyTag<'a>) -> Self { - self.add_tag(tag); - self - } - - pub fn inner(self) -> Vec> { - self.items - } -} - -impl<'a> TagValue<'a> for TagList<'a> { - fn append_to_element(self, element: Element<'a>) -> Element<'a> { - element.add_children(self.items.into_iter().map(AnyTag::into_element).collect()) - } -} - -impl<'a> FromXml<'a> for TagList<'a> { - fn from_xml(node: ironposh_xml::parser::Node<'a, 'a>) -> Result { - let mut items = Vec::new(); - for child in node.children() { - if child.is_element() { - items.push(AnyTag::from_xml(child)?); - } - } - Ok(TagList { items }) - } -} diff --git a/crates/ironposh-winrm/src/soap/body.rs b/crates/ironposh-winrm/src/soap/body.rs index fd11b4d..c3da76e 100644 --- a/crates/ironposh-winrm/src/soap/body.rs +++ b/crates/ironposh-winrm/src/soap/body.rs @@ -29,12 +29,6 @@ pub struct SoapBody<'a> { pub delete: Option, Delete>>, #[builder(default, setter(into, strip_option))] pub enumerate: Option, Enumerate>>, - #[builder(default, setter(into, strip_option))] - pub pull: Option, Pull>>, - #[builder(default, setter(into, strip_option))] - pub release: Option, Release>>, - #[builder(default, setter(into, strip_option))] - pub get_status: Option, GetStatus>>, /// WS-Transfer operations #[builder(default, setter(into, strip_option))] @@ -44,8 +38,6 @@ pub struct SoapBody<'a> { #[builder(default, setter(into, strip_option))] pub shell: Option, Shell>>, #[builder(default, setter(into, strip_option))] - pub command: Option, Command>>, - #[builder(default, setter(into, strip_option))] pub command_line: Option>, #[builder(default, setter(into, strip_option))] pub receive: Option, Receive>>, diff --git a/crates/ironposh-winrm/tests/test_initial_build_request.rs b/crates/ironposh-winrm/tests/test_initial_build_request.rs index 4949d5a..68fe7e4 100644 --- a/crates/ironposh-winrm/tests/test_initial_build_request.rs +++ b/crates/ironposh-winrm/tests/test_initial_build_request.rs @@ -1,5 +1,5 @@ use ironposh_winrm::{ - cores::{Tag, TagList, tag_name::*, tag_value::Text}, + cores::{Tag, tag_name::*, tag_value::Text}, rsp::shell_value::ShellValue, soap::{SoapEnvelope, body::SoapBody, header::SoapHeaders}, ws_addressing::AddressValue, @@ -16,9 +16,6 @@ mod tests { #[test] fn test_build_soap_envelope_for_shell_creation() { - // Create an empty TagList for creation_xml (simplified for now) - let _creation_xml_content = TagList::new(); - // Build the Shell content for the body let shell = ShellValue::builder() .name("Runspace1") diff --git a/crates/ironposh-winrm/tests/test_parse_error_response.rs b/crates/ironposh-winrm/tests/test_parse_error_response.rs index 08bc1c3..bd99d71 100644 --- a/crates/ironposh-winrm/tests/test_parse_error_response.rs +++ b/crates/ironposh-winrm/tests/test_parse_error_response.rs @@ -110,10 +110,6 @@ mod tests { "Body should not have ResourceCreated in error" ); assert!(body.shell.is_none(), "Body should not have Shell in error"); - assert!( - body.command.is_none(), - "Body should not have Command in error" - ); assert!( body.receive.is_none(), "Body should not have Receive in error" diff --git a/crates/ironposh-winrm/tests/test_parse_receive_response.rs b/crates/ironposh-winrm/tests/test_parse_receive_response.rs index 5b05f19..497310d 100644 --- a/crates/ironposh-winrm/tests/test_parse_receive_response.rs +++ b/crates/ironposh-winrm/tests/test_parse_receive_response.rs @@ -140,7 +140,6 @@ mod tests { "Body should not have ResourceCreated" ); assert!(body.shell.is_none(), "Body should not have Shell"); - assert!(body.command.is_none(), "Body should not have Command"); assert!(body.receive.is_none(), "Body should not have Receive"); assert!( body.command_response.is_none(), From cdcf0395c2917e5b78fd01960df1685960f6ce99 Mon Sep 17 00:00:00 2001 From: Junyi Ou Date: Wed, 24 Jun 2026 16:02:10 -0400 Subject: [PATCH 2/3] refactor(winrm): drop dead tag-name markers left after AnyTag removal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With AnyTag gone, ~22 TagName markers had no remaining referent: the PSRP CLIXML set (Obj, MS, TN, T, S, B, Nil, DCT, En, Key, Value, ToString, I32TagName, BA, Version), the InitRunspacepool set (MinRunspaces, MaxRunspaces, PSThreadOptions, ApartmentState, HostInfo, ApplicationArguments — ps_value's job), and FaultTo / From / FragmentTransfer. Removed. Workspace compiles, so nothing referenced them. --- crates/ironposh-winrm/src/cores/tag_name.rs | 31 --------------------- 1 file changed, 31 deletions(-) diff --git a/crates/ironposh-winrm/src/cores/tag_name.rs b/crates/ironposh-winrm/src/cores/tag_name.rs index 4d56dad..0504c6e 100644 --- a/crates/ironposh-winrm/src/cores/tag_name.rs +++ b/crates/ironposh-winrm/src/cores/tag_name.rs @@ -83,8 +83,6 @@ define_tagname!(To, Some(Namespace::WsAddressing2004.uri())); define_tagname!(MessageID, Some(Namespace::WsAddressing2004.uri())); define_tagname!(RelatesTo, Some(Namespace::WsAddressing2004.uri())); define_tagname!(ReplyTo, Some(Namespace::WsAddressing2004.uri())); -define_tagname!(FaultTo, Some(Namespace::WsAddressing2004.uri())); -define_tagname!(From, Some(Namespace::WsAddressing2004.uri())); define_tagname!(Address, Some(Namespace::WsAddressing2004.uri())); define_tagname!(ReferenceParameters, Some(Namespace::WsAddressing2004.uri())); @@ -120,7 +118,6 @@ define_tagname!(GetStatus, Some(Namespace::DmtfWsmanSchema.uri())); define_tagname!(ResourceURI, Some(Namespace::DmtfWsmanSchema.uri())); define_tagname!(OperationTimeout, Some(Namespace::DmtfWsmanSchema.uri())); define_tagname!(MaxEnvelopeSize, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(FragmentTransfer, Some(Namespace::DmtfWsmanSchema.uri())); define_tagname!(SelectorSet, Some(Namespace::DmtfWsmanSchema.uri())); define_tagname!(OptionSet, Some(Namespace::DmtfWsmanSchema.uri())); define_tagname!(Locale, Some(Namespace::DmtfWsmanSchema.uri())); @@ -145,31 +142,3 @@ define_tagname!(SequenceId, Some(Namespace::MsWsmanSchema.uri())); define_tagname!(OperationID, Some(Namespace::MsWsmanSchema.uri())); define_tagname!(SessionId, Some(Namespace::MsWsmanSchema.uri())); define_tagname!(DataLocale, Some(Namespace::MsWsmanSchema.uri())); - -// PowerShell Remoting Protocol; -define_tagname!(Obj, None); -define_tagname!(MS, None); -define_tagname!(Version, None); -define_tagname!(BA, None); - -// PowerShell Serialization Format -// define_tagname!(I32, None); // 32-bit integer -define_custom_tagname!(I32TagName, "I32", None); // 32-bit integer -define_tagname!(TN, None); // Type Name -define_tagname!(T, None); // Type -define_custom_tagname!(ToString, "ToString", None); // ToString representation -define_tagname!(DCT, None); // Dictionary -define_tagname!(En, None); // Dictionary Entry -define_tagname!(Key, None); // Dictionary Key -define_tagname!(Value, None); // Dictionary Value -define_tagname!(Nil, None); // Null Value -define_tagname!(B, None); // Boolean -define_tagname!(S, None); // String - -// PowerShell InitRunspacepool Message Tags -define_tagname!(MinRunspaces, None); // Minimum number of runspaces -define_tagname!(MaxRunspaces, None); // Maximum number of runspaces -define_tagname!(PSThreadOptions, None); // PowerShell thread options -define_tagname!(ApartmentState, None); // Apartment state for runspace -define_tagname!(HostInfo, None); // Host information -define_tagname!(ApplicationArguments, None); // Application arguments From f7e98d730336eb39dbdd3ccd874200a7e0d928b7 Mon Sep 17 00:00:00 2001 From: Junyi Ou Date: Wed, 24 Jun 2026 17:53:36 -0400 Subject: [PATCH 3/3] refactor(winrm): alias-based tags via tag! macro; drop raw Tag from APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keeps the compile-time tag safety (Tag<'a, V, N> with an N: TagName marker is still the enforcer) but hides the verbosity behind a `tag!` macro that, per tag, generates the marker (`Tag`, internal) plus an ergonomic type alias `Alias<'a> = Tag<'a, V, AliasTag>`. Fields and construction now read `Get<'a>` / `Get::new(..)` instead of `Tag<'a, Text<'a>, Get>` / `Tag::from_name(Get)...`. - `#[derive(FromXml)]` reads identity via `NamedTag` on the field type, so it works through the aliases (added in the prior commit). - Leaf-valued tags (Text/WsUuid/Time/Empty/…) are a readable table in cores/tag_name.rs; composite-valued tags (Shell=ShellValue, Body=SoapBody, …) are defined with `tag!` next to their value struct — no cores→domain dep inversion. - Multi-value names get distinct aliases sharing a wire name (LocaleEmpty/LocaleText, DataLocaleEmpty/DataLocaleText). Only genuinely value-varying construction (CommandLine's ) keeps an explicit marker. Verified: fmt + clippy (-D warnings) clean, workspace tests pass, real-server transport×auth×sealing matrix 10/10. --- .../src/runspace/win_rs.rs | 62 +++--- .../tests/test_send_roundtrip.rs | 25 ++- .../ironposh-test-support/src/fake_server.rs | 17 +- crates/ironposh-winrm/src/cores/tag.rs | 7 +- crates/ironposh-winrm/src/cores/tag_name.rs | 196 +++++++----------- crates/ironposh-winrm/src/macros.rs | 56 +++-- crates/ironposh-winrm/src/rsp/commandline.rs | 20 +- crates/ironposh-winrm/src/rsp/connect.rs | 13 +- crates/ironposh-winrm/src/rsp/disconnect.rs | 7 +- crates/ironposh-winrm/src/rsp/receive.rs | 28 ++- crates/ironposh-winrm/src/rsp/send.rs | 14 +- crates/ironposh-winrm/src/rsp/shell_value.rs | 57 +++-- crates/ironposh-winrm/src/soap/body.rs | 68 +++--- crates/ironposh-winrm/src/soap/fault.rs | 22 +- crates/ironposh-winrm/src/soap/header.rs | 44 ++-- crates/ironposh-winrm/src/soap/mod.rs | 13 +- crates/ironposh-winrm/src/test_macro.rs | 142 ++++--------- .../ironposh-winrm/src/ws_addressing/mod.rs | 7 +- .../ironposh-winrm/src/ws_management/body.rs | 20 +- .../src/ws_management/header.rs | 15 +- .../ironposh-winrm/src/ws_management/mod.rs | 18 +- .../tests/test_connect_request.rs | 6 +- .../tests/test_disconnect_request.rs | 8 +- .../tests/test_initial_build_request.rs | 36 ++-- .../ironposh-winrm/tests/test_receive_fix.rs | 12 +- .../tests/test_send_multiple_fragments.rs | 22 +- 26 files changed, 423 insertions(+), 512 deletions(-) diff --git a/crates/ironposh-client-core/src/runspace/win_rs.rs b/crates/ironposh-client-core/src/runspace/win_rs.rs index b0ef38e..7a839f4 100644 --- a/crates/ironposh-client-core/src/runspace/win_rs.rs +++ b/crates/ironposh-client-core/src/runspace/win_rs.rs @@ -1,10 +1,10 @@ use base64::Engine; use ironposh_winrm::{ - cores::{Attribute, DesiredStream, Receive, Shell, Tag, Text, Time, tag_name}, + cores::{Attribute, DesiredStreamTag, StreamTag, Tag, Text, Time}, rsp::{ commandline::CommandLineValue, - receive::{CommandStateValue, ReceiveValue}, - shell_value::ShellValue, + receive::{CommandStateTag, CommandStateValue, ReceiveTag, ReceiveValue}, + shell_value::{ShellTag, ShellValue}, }, soap::{SoapEnvelope, body::SoapBody}, ws_management::{OptionSetValue, SelectorSetValue, WsAction, WsMan}, @@ -67,7 +67,7 @@ impl WinRunspace { option_set: Option, open_content: &'a str, ) -> impl Into> { - let shell = Tag::from_name(Shell) + let shell = Tag::from_name(ShellTag) .with_attribute(ironposh_winrm::cores::Attribute::ShellId( self.id.to_string().into(), )) @@ -130,7 +130,7 @@ impl WinRunspace { // Join stream names with spaces as required by Windows Shell schema let combined_streams = stream_names.join(" "); let mut tag = - Tag::from_name(DesiredStream).with_value(Text::from(combined_streams)); + Tag::from_name(DesiredStreamTag).with_value(Text::from(combined_streams)); if let Some(command_id) = command_id { tag = tag.with_attribute(Attribute::CommandId(command_id)); @@ -144,7 +144,7 @@ impl WinRunspace { .desired_streams(desired_stream_tags) .build(); - let receive_tag = Tag::from_name(Receive) + let receive_tag = Tag::from_name(ReceiveTag) .with_value(receive) .with_declaration(ironposh_winrm::cores::Namespace::WsmanShell); @@ -172,9 +172,12 @@ impl WinRunspace { /// Build a Disconnect request targeting this shell (MS-WSMV 3.1.4.13). pub(crate) fn fire_disconnect<'a>(&'a self, ws_man: &'a WsMan) -> impl Into> { - use ironposh_winrm::{cores::Namespace, rsp::disconnect::DisconnectValue}; + use ironposh_winrm::{ + cores::Namespace, + rsp::disconnect::{DisconnectTag, DisconnectValue}, + }; - let disconnect_tag = Tag::from_name(tag_name::Disconnect) + let disconnect_tag = Tag::from_name(DisconnectTag) .with_declaration(Namespace::WsmanShell) .with_value(DisconnectValue::builder().build()); @@ -197,13 +200,16 @@ impl WinRunspace { option_set: Option, connect_payload: &'a str, ) -> impl Into> { - use ironposh_winrm::{cores::Namespace, rsp::connect::ConnectValue}; + use ironposh_winrm::{ + cores::Namespace, + rsp::connect::{ConnectTag, ConnectValue}, + }; let connect_value = ConnectValue { connect_xml: Tag::new(connect_payload).with_declaration(Namespace::PowerShellRemoting), }; - let connect_tag = Tag::from_name(tag_name::Connect) + let connect_tag = Tag::from_name(ConnectTag) .with_declaration(Namespace::WsmanShell) .with_value(connect_value); @@ -218,9 +224,9 @@ impl WinRunspace { /// Build a Reconnect request targeting this shell (MS-WSMV 3.1.4.14). pub(crate) fn fire_reconnect<'a>(&'a self, ws_man: &'a WsMan) -> impl Into> { - use ironposh_winrm::cores::{Empty, Namespace}; + use ironposh_winrm::cores::{Empty, Namespace, ReconnectTag}; - let reconnect_tag = Tag::from_name(tag_name::Reconnect) + let reconnect_tag = Tag::from_name(ReconnectTag) .with_declaration(Namespace::WsmanShell) .with_value(Empty); @@ -390,20 +396,17 @@ impl WinRunspace { data: &'a [String], ) -> Result>, crate::PwshCoreError> { use ironposh_winrm::{ - cores::{ - Namespace, Tag, - tag_name::{Send, Stream}, - }, - rsp::send::SendValue, + cores::{Namespace, StreamTag, Tag}, + rsp::send::{SendTag, SendValue}, soap::body::SoapBody, }; // Create a Stream tag for each fragment // Each fragment is a base64-encoded PSRP fragment that goes in its own element - let streams: Vec> = data + let streams: Vec> = data .iter() .map(|fragment| { - Tag::from_name(Stream) + Tag::from_name(StreamTag) .with_value(Text::from(fragment.as_str())) .with_attribute(Attribute::Name("stdin".into())) }) @@ -413,12 +416,12 @@ impl WinRunspace { // Add send tag with SendValue containing multiple streams let send_tag = if let Some(cmd_id) = command_id { - Tag::from_name(Send) + Tag::from_name(SendTag) .with_value(send_value) .with_attribute(Attribute::CommandId(cmd_id)) .with_declaration(Namespace::WsmanShell) } else { - Tag::from_name(Send) + Tag::from_name(SendTag) .with_value(send_value) .with_declaration(Namespace::WsmanShell) }; @@ -460,18 +463,15 @@ impl WinRunspace { connection: &'a WsMan, id: Uuid, ) -> Result>, crate::PwshCoreError> { - use ironposh_winrm::cores::{ - Namespace, - tag_name::{Signal, SignalCode}, - }; + use ironposh_winrm::cores::{Namespace, SignalCodeTag, SignalTag}; // Build http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/ctrl_c - let code = Tag::from_name(SignalCode).with_value(Text::from( + let code = Tag::from_name(SignalCodeTag).with_value(Text::from( "http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate", )); // Build ... - let signal = Tag::from_name(Signal) + let signal = Tag::from_name(SignalTag) .with_attribute(Attribute::CommandId(id)) .with_value(code) .with_declaration(Namespace::WsmanShell); @@ -535,10 +535,10 @@ impl Stream { } } -impl<'a> TryFrom<&Tag<'a, Text<'a>, tag_name::Stream>> for Stream { +impl<'a> TryFrom<&Tag<'a, Text<'a>, StreamTag>> for Stream { type Error = crate::PwshCoreError; - fn try_from(value: &Tag<'a, Text<'a>, tag_name::Stream>) -> Result { + fn try_from(value: &Tag<'a, Text<'a>, StreamTag>) -> Result { let attributes = &value.attributes; let name = attributes .iter() @@ -577,11 +577,11 @@ pub struct CommandState { pub exit_code: Option, } -impl<'a> TryFrom<&Tag<'a, CommandStateValue<'a>, tag_name::CommandState>> for CommandState { +impl<'a> TryFrom<&Tag<'a, CommandStateValue<'a>, CommandStateTag>> for CommandState { type Error = crate::PwshCoreError; fn try_from( - value: &Tag<'a, CommandStateValue<'a>, tag_name::CommandState>, + value: &Tag<'a, CommandStateValue<'a>, CommandStateTag>, ) -> Result { let command_id = value .attributes diff --git a/crates/ironposh-client-core/tests/test_send_roundtrip.rs b/crates/ironposh-client-core/tests/test_send_roundtrip.rs index 33e3776..2fe718a 100644 --- a/crates/ironposh-client-core/tests/test_send_roundtrip.rs +++ b/crates/ironposh-client-core/tests/test_send_roundtrip.rs @@ -7,8 +7,11 @@ use ironposh_psrp::{ fragmentation::{DefragmentResult, Defragmenter, Fragmenter}, }; use ironposh_winrm::{ - cores::{Attribute, ReceiveResponse, Send, Tag, Text, tag_name::Stream}, - rsp::{receive::ReceiveResponseValue, send::SendValue}, + cores::{Attribute, StreamTag, Tag, Text}, + rsp::{ + receive::{ReceiveResponseTag, ReceiveResponseValue}, + send::{SendTag, SendValue}, + }, soap::{SoapEnvelope, body::SoapBody}, }; use ironposh_xml::builder::Element; @@ -42,10 +45,10 @@ fn test_send_receive_roundtrip_with_fragmentation() { .collect(); // 4. Create SendValue with multiple Stream elements (NEW CODE PATH) - let streams: Vec> = base64_fragments + let streams: Vec> = base64_fragments .iter() .map(|fragment| { - Tag::from_name(Stream) + Tag::from_name(StreamTag) .with_value(Text::from(fragment.as_str())) .with_attribute(Attribute::Name("stdin".into())) }) @@ -53,7 +56,7 @@ fn test_send_receive_roundtrip_with_fragmentation() { let send_value = SendValue::builder().streams(streams).build(); - let send_tag = Tag::from_name(Send) + let send_tag = Tag::from_name(SendTag) .with_value(send_value) .with_attribute(Attribute::CommandId(command_id)) .with_declaration(ironposh_winrm::cores::Namespace::WsmanShell); @@ -80,10 +83,10 @@ fn test_send_receive_roundtrip_with_fragmentation() { // 6. Simulate receiving the response - create ReceiveResponse with same streams // (In real flow, server would echo back or client would receive similar structure) - let receive_streams: Vec> = base64_fragments + let receive_streams: Vec> = base64_fragments .iter() .map(|fragment| { - Tag::from_name(Stream) + Tag::from_name(StreamTag) .with_value(Text::from(fragment.as_str())) .with_attribute(Attribute::Name("stdout".into())) .with_attribute(Attribute::CommandId(command_id)) @@ -95,7 +98,7 @@ fn test_send_receive_roundtrip_with_fragmentation() { .command_state(None) .build(); - let receive_response_tag = Tag::from_name(ReceiveResponse) + let receive_response_tag = Tag::from_name(ReceiveResponseTag) .with_value(receive_response_value) .with_declaration(ironposh_winrm::cores::Namespace::WsmanShell); @@ -183,7 +186,7 @@ fn create_large_session_capability() -> SessionCapability { fn test_send_with_no_fragments() { let send_value = SendValue::builder().streams(vec![]).build(); - let send_tag = Tag::from_name(Send) + let send_tag = Tag::from_name(SendTag) .with_value(send_value) .with_declaration(ironposh_winrm::cores::Namespace::WsmanShell); @@ -222,13 +225,13 @@ fn test_send_with_single_fragment() { let base64_fragment = base64::engine::general_purpose::STANDARD.encode(&fragments[0]); - let stream = Tag::from_name(Stream) + let stream = Tag::from_name(StreamTag) .with_value(Text::from(base64_fragment.as_str())) .with_attribute(Attribute::Name("stdin".into())); let send_value = SendValue::builder().streams(vec![stream]).build(); - let send_tag = Tag::from_name(Send) + let send_tag = Tag::from_name(SendTag) .with_value(send_value) .with_attribute(Attribute::CommandId(command_id)) .with_declaration(ironposh_winrm::cores::Namespace::WsmanShell); diff --git a/crates/ironposh-test-support/src/fake_server.rs b/crates/ironposh-test-support/src/fake_server.rs index f296232..955ece9 100644 --- a/crates/ironposh-test-support/src/fake_server.rs +++ b/crates/ironposh-test-support/src/fake_server.rs @@ -15,12 +15,9 @@ use ironposh_psrp::{ PowerShellRemotingMessage, Size, }; use ironposh_winrm::{ - cores::{ - tag_name::{Envelope, Stream}, - Attribute, Namespace, ReceiveResponse, Tag, Text, - }, - rsp::receive::ReceiveResponseValue, - soap::{body::SoapBody, SoapEnvelope}, + cores::{Attribute, Namespace, StreamTag, Tag, Text}, + rsp::receive::{ReceiveResponseTag, ReceiveResponseValue}, + soap::{body::SoapBody, Envelope, SoapEnvelope}, }; use ironposh_xml::builder::Element; use uuid::Uuid; @@ -156,10 +153,10 @@ pub fn receive_response_xml(rpid: Uuid, messages: &[&dyn PsObjectWithType]) -> S }) .collect(); - let streams: Vec, Stream>> = base64_fragments + let streams: Vec, StreamTag>> = base64_fragments .iter() .map(|fragment| { - Tag::from_name(Stream) + Tag::from_name(StreamTag) .with_value(Text::from(fragment.as_str())) .with_attribute(Attribute::Name("stdout".into())) }) @@ -170,7 +167,7 @@ pub fn receive_response_xml(rpid: Uuid, messages: &[&dyn PsObjectWithType]) -> S .command_state(None) .build(); - let receive_response_tag = Tag::from_name(ReceiveResponse) + let receive_response_tag = Tag::from_name(ReceiveResponseTag) .with_value(receive_response_value) .with_declaration(Namespace::WsmanShell); @@ -180,7 +177,7 @@ pub fn receive_response_xml(rpid: Uuid, messages: &[&dyn PsObjectWithType]) -> S let envelope = SoapEnvelope::builder().body(body).build(); - let soap = Tag::, Envelope>::new(envelope) + let soap = Envelope::new(envelope) .with_declaration(Namespace::SoapEnvelope2003) .with_declaration(Namespace::WsAddressing2004) .with_declaration(Namespace::DmtfWsmanSchema) diff --git a/crates/ironposh-winrm/src/cores/tag.rs b/crates/ironposh-winrm/src/cores/tag.rs index b579f58..9d23c44 100644 --- a/crates/ironposh-winrm/src/cores/tag.rs +++ b/crates/ironposh-winrm/src/cores/tag.rs @@ -260,7 +260,7 @@ impl_tag_from!(uuid::Uuid => Tag<'a, WsUuid, N>); #[cfg(test)] mod tests { use super::*; - use crate::cores::{CommandId, CommandResponse}; + use crate::cores::CommandResponse; use ironposh_xml::parser::parse; const RSP: &str = "http://schemas.microsoft.com/wbem/wsman/1/windows/shell"; @@ -275,9 +275,8 @@ mod tests { r#"{uuid}"# ); let doc = parse(&xml).unwrap(); - let tag: Tag<'_, Tag<'_, WsUuid, CommandId>, CommandResponse> = - Tag::from_xml(doc.root_element()) - .expect("nested CommandResponse/CommandId should parse"); + let tag = CommandResponse::from_xml(doc.root_element()) + .expect("nested CommandResponse/CommandId should parse"); assert_eq!(tag.value.value.0.to_string().to_uppercase(), uuid); } } diff --git a/crates/ironposh-winrm/src/cores/tag_name.rs b/crates/ironposh-winrm/src/cores/tag_name.rs index 0504c6e..69d9f73 100644 --- a/crates/ironposh-winrm/src/cores/tag_name.rs +++ b/crates/ironposh-winrm/src/cores/tag_name.rs @@ -1,5 +1,5 @@ -use crate::cores::namespace::*; -use crate::{define_custom_tagname, define_tagname}; +use crate::cores::tag_value::{Empty, I32, ReadOnlyUnParsed, Text, Time, U32, WsUuid}; +use crate::tag; pub trait TagName { const TAG_NAME: &'static str; @@ -14,131 +14,91 @@ pub trait TagName { } } -// ========================== -// PowerShell Remoting Shell (rsp namespace) -// ========================== -define_tagname!(ShellId, Some(Namespace::WsmanShell.uri())); -define_tagname!(Name, Some(Namespace::WsmanShell.uri())); -define_tagname!(ResourceUri, Some(Namespace::WsmanShell.uri())); -define_tagname!(Owner, Some(Namespace::WsmanShell.uri())); -define_tagname!(ClientIP, Some(Namespace::WsmanShell.uri())); -define_tagname!(ProcessId, Some(Namespace::WsmanShell.uri())); -define_tagname!(IdleTimeOut, Some(Namespace::WsmanShell.uri())); -define_tagname!(InputStreams, Some(Namespace::WsmanShell.uri())); -define_tagname!(OutputStreams, Some(Namespace::WsmanShell.uri())); -define_tagname!(MaxIdleTimeOut, Some(Namespace::WsmanShell.uri())); -define_tagname!(CompressionMode, Some(Namespace::WsmanShell.uri())); -define_tagname!(ProfileLoaded, Some(Namespace::WsmanShell.uri())); -define_tagname!(Encoding, Some(Namespace::WsmanShell.uri())); -define_tagname!(BufferMode, Some(Namespace::WsmanShell.uri())); -define_tagname!(State, Some(Namespace::WsmanShell.uri())); -define_tagname!(ShellRunTime, Some(Namespace::WsmanShell.uri())); -define_tagname!(ShellInactivity, Some(Namespace::WsmanShell.uri())); -define_tagname!(CompressionType, Some(Namespace::WsmanShell.uri())); -define_tagname!(DesiredStream, Some(Namespace::WsmanShell.uri())); +// Leaf-valued tags live here (their value types are all in `cores`). Tags whose +// value is a domain struct (Shell = ShellValue, Body = SoapBody, …) are defined +// with `tag!` next to that struct instead — see the rsp/soap/ws_* modules. -define_custom_tagname!( - CreationXml, - "creationXml", - Some(Namespace::PowerShellRemoting.uri()) -); +// ============================================================ +// PowerShell Remoting Shell (rsp namespace) +// ============================================================ +tag!(ShellId = Text<'a> => WsmanShell); +tag!(Name = Text<'a> => WsmanShell); +tag!(ResourceUri = Text<'a> => WsmanShell); +tag!(Owner = Text<'a> => WsmanShell); +tag!(ClientIP = Text<'a> => WsmanShell); +tag!(ProcessId = Text<'a> => WsmanShell); +tag!(IdleTimeOut = Time => WsmanShell); +tag!(InputStreams = Text<'a> => WsmanShell); +tag!(OutputStreams = Text<'a> => WsmanShell); +tag!(MaxIdleTimeOut = Text<'a> => WsmanShell); +tag!(CompressionMode = Text<'a> => WsmanShell); +tag!(ProfileLoaded = Text<'a> => WsmanShell); +tag!(Encoding = Text<'a> => WsmanShell); +tag!(BufferMode = Text<'a> => WsmanShell); +tag!(State = Text<'a> => WsmanShell); +tag!(ShellRunTime = Text<'a> => WsmanShell); +tag!(ShellInactivity = Text<'a> => WsmanShell); +tag!(CompressionType = Text<'a> => WsmanShell); +tag!(DesiredStream = Text<'a> => WsmanShell); +tag!(Stream = Text<'a> => WsmanShell); +tag!(ExitCode = I32 => WsmanShell); +tag!(CommandId = WsUuid => WsmanShell); +tag!(CommandResponse = CommandId<'a> => WsmanShell); // wraps a single CommandId child +tag!(Command = Text<'a> => WsmanShell); +tag!(Arguments = Text<'a> => WsmanShell); +tag!(DisconnectResponse = Empty => WsmanShell); +tag!(Reconnect = Empty => WsmanShell); +tag!(ReconnectResponse = Empty => WsmanShell); +tag!(SignalCode = "Code": Text<'a> => WsmanShell); +tag!(Signal = SignalCode<'a> => WsmanShell); // wraps a single Code child +tag!(SignalResponse = Empty => WsmanShell); -define_tagname!(CommandLine, Some(Namespace::WsmanShell.uri())); -define_tagname!(Shell, Some(Namespace::WsmanShell.uri())); -define_tagname!(Command, Some(Namespace::WsmanShell.uri())); -define_tagname!(Receive, Some(Namespace::WsmanShell.uri())); -define_tagname!(ReceiveResponse, Some(Namespace::WsmanShell.uri())); -define_tagname!(CommandResponse, Some(Namespace::WsmanShell.uri())); -define_tagname!(CommandId, Some(Namespace::WsmanShell.uri())); -define_tagname!(Stream, Some(Namespace::WsmanShell.uri())); -define_tagname!(CommandState, Some(Namespace::WsmanShell.uri())); -define_tagname!(ExitCode, Some(Namespace::WsmanShell.uri())); -define_tagname!(Send, Some(Namespace::WsmanShell.uri())); -define_tagname!(Disconnect, Some(Namespace::WsmanShell.uri())); -define_tagname!(DisconnectResponse, Some(Namespace::WsmanShell.uri())); -define_tagname!(Reconnect, Some(Namespace::WsmanShell.uri())); -define_tagname!(ReconnectResponse, Some(Namespace::WsmanShell.uri())); -define_tagname!(Connect, Some(Namespace::WsmanShell.uri())); -define_tagname!(ConnectResponse, Some(Namespace::WsmanShell.uri())); -define_custom_tagname!( - ConnectXml, - "connectXml", - Some(Namespace::PowerShellRemoting.uri()) -); -define_custom_tagname!( - ConnectResponseXml, - "connectResponseXml", - Some(Namespace::PowerShellRemoting.uri()) -); -define_tagname!(Signal, Some(Namespace::WsmanShell.uri())); -define_tagname!(SignalResponse, Some(Namespace::WsmanShell.uri())); -define_custom_tagname!(SignalCode, "Code", Some(Namespace::WsmanShell.uri())); -define_tagname!(Arguments, Some(Namespace::WsmanShell.uri())); +tag!(CreationXml = "creationXml": Text<'a> => PowerShellRemoting); +tag!(ConnectXml = "connectXml": Text<'a> => PowerShellRemoting); +tag!(ConnectResponseXml = "connectResponseXml": Text<'a> => PowerShellRemoting); -// ==================== +// ============================================================ // WS-Addressing (a namespace) -// ==================== -define_tagname!(Action, Some(Namespace::WsAddressing2004.uri())); -define_tagname!(To, Some(Namespace::WsAddressing2004.uri())); -define_tagname!(MessageID, Some(Namespace::WsAddressing2004.uri())); -define_tagname!(RelatesTo, Some(Namespace::WsAddressing2004.uri())); -define_tagname!(ReplyTo, Some(Namespace::WsAddressing2004.uri())); -define_tagname!(Address, Some(Namespace::WsAddressing2004.uri())); -define_tagname!(ReferenceParameters, Some(Namespace::WsAddressing2004.uri())); +// ============================================================ +tag!(Action = Text<'a> => WsAddressing2004); +tag!(To = Text<'a> => WsAddressing2004); +tag!(MessageID = WsUuid => WsAddressing2004); +tag!(RelatesTo = WsUuid => WsAddressing2004); +tag!(Address = Text<'a> => WsAddressing2004); -// ============= +// ============================================================ // SOAP (s namespace) -// ============= -define_tagname!(Envelope, Some(Namespace::SoapEnvelope2003.uri())); -define_tagname!(Header, Some(Namespace::SoapEnvelope2003.uri())); -define_tagname!(Body, Some(Namespace::SoapEnvelope2003.uri())); - -// SOAP Fault elements -define_tagname!(Fault, Some(Namespace::SoapEnvelope2003.uri())); -define_tagname!(Code, Some(Namespace::SoapEnvelope2003.uri())); -define_tagname!(Reason, Some(Namespace::SoapEnvelope2003.uri())); -define_tagname!(Detail, Some(Namespace::SoapEnvelope2003.uri())); -define_tagname!(Subcode, Some(Namespace::SoapEnvelope2003.uri())); -define_custom_tagname!(SoapValue, "Value", Some(Namespace::SoapEnvelope2003.uri())); -define_custom_tagname!(SoapText, "Text", Some(Namespace::SoapEnvelope2003.uri())); +// ============================================================ +tag!(Detail = ReadOnlyUnParsed<'a> => SoapEnvelope2003); +tag!(SoapValue = "Value": Text<'a> => SoapEnvelope2003); +tag!(SoapText = "Text": Text<'a> => SoapEnvelope2003); -// =============================== +// ============================================================ // WS-Management DMTF (w namespace) -// =============================== -define_tagname!(Identify, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(Get, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(Put, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(Delete, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(Enumerate, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(Pull, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(Release, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(GetStatus, Some(Namespace::DmtfWsmanSchema.uri())); +// ============================================================ +tag!(Identify = Empty => DmtfWsmanSchema); +tag!(Get = Text<'a> => DmtfWsmanSchema); +tag!(Put = Text<'a> => DmtfWsmanSchema); +tag!(Delete = Text<'a> => DmtfWsmanSchema); +tag!(Enumerate = ReadOnlyUnParsed<'a> => DmtfWsmanSchema); +tag!(ResourceURI = Text<'a> => DmtfWsmanSchema); +tag!(OperationTimeout = Time => DmtfWsmanSchema); +tag!(MaxEnvelopeSize = U32 => DmtfWsmanSchema); +tag!(Selector = Text<'a> => DmtfWsmanSchema); +tag!(OptionTagName = "Option": Empty => DmtfWsmanSchema); +tag!(LocaleEmpty = "Locale": Empty => DmtfWsmanSchema); +tag!(LocaleText = "Locale": Text<'a> => DmtfWsmanSchema); -// WS-Management DMTF Headers (w namespace) -define_tagname!(ResourceURI, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(OperationTimeout, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(MaxEnvelopeSize, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(SelectorSet, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(OptionSet, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(Locale, Some(Namespace::DmtfWsmanSchema.uri())); -define_tagname!(Selector, Some(Namespace::DmtfWsmanSchema.uri())); -define_custom_tagname!( - OptionTagName, - "Option", - Some(Namespace::DmtfWsmanSchema.uri()) -); - -// =================================== +// ============================================================ // WS-Transfer (x namespace) -// =================================== -define_tagname!(Create, Some(Namespace::WsTransfer2004.uri())); - -define_tagname!(ResourceCreated, Some(Namespace::WsTransfer2004.uri())); +// ============================================================ +tag!(Create = Text<'a> => WsTransfer2004); -// ==================================== +// ============================================================ // Microsoft WS-Management (p namespace) -// ==================================== -define_tagname!(SequenceId, Some(Namespace::MsWsmanSchema.uri())); -define_tagname!(OperationID, Some(Namespace::MsWsmanSchema.uri())); -define_tagname!(SessionId, Some(Namespace::MsWsmanSchema.uri())); -define_tagname!(DataLocale, Some(Namespace::MsWsmanSchema.uri())); +// ============================================================ +tag!(SequenceId = Text<'a> => MsWsmanSchema); +tag!(OperationID = WsUuid => MsWsmanSchema); +tag!(SessionId = WsUuid => MsWsmanSchema); +tag!(DataLocaleEmpty = "DataLocale": Empty => MsWsmanSchema); +tag!(DataLocaleText = "DataLocale": Text<'a> => MsWsmanSchema); diff --git a/crates/ironposh-winrm/src/macros.rs b/crates/ironposh-winrm/src/macros.rs index ff0ce87..5ea9fd8 100644 --- a/crates/ironposh-winrm/src/macros.rs +++ b/crates/ironposh-winrm/src/macros.rs @@ -1,40 +1,38 @@ +/// Defines a WinRM/SOAP tag. +/// +/// Generates a zero-sized `TagName` marker (`Tag`) that pins the element's +/// wire name + namespace at the type level, plus an ergonomic type alias +/// `Alias<'a> = Tag<'a, Value, AliasTag>` used in struct fields and construction. +/// The marker stays internal; everything else writes the alias. +/// +/// Forms: +/// - `tag!(Get = Text<'a> => DmtfWsmanSchema)` — wire name is the alias ident. +/// - `tag!(SoapValue = "Value": Text<'a> => SoapEnvelope2003)` — custom wire name +/// (and lets several aliases share one wire name, e.g. `LocaleEmpty`/`LocaleText`). #[macro_export] -macro_rules! define_custom_tagname { - ($name:ident, $tagName:expr, $namespace:expr) => { - #[derive(Debug, Clone, PartialEq, Eq)] - pub struct $name; - - impl $crate::cores::TagName for $name { - const TAG_NAME: &'static str = $tagName; - const NAMESPACE: Option<&'static str> = $namespace; - - fn tag_name(&self) -> &'static str { - Self::TAG_NAME - } +macro_rules! tag { + ($alias:ident = $value:ty => $ns:ident) => { + $crate::tag!(@build $alias, stringify!($alias), $value, $ns); + }; + ($alias:ident = $wire:literal : $value:ty => $ns:ident) => { + $crate::tag!(@build $alias, $wire, $value, $ns); + }; + (@build $alias:ident, $wire:expr, $value:ty, $ns:ident) => { + paste::paste! { + #[derive(Debug, Clone, PartialEq, Eq)] + pub struct [<$alias Tag>]; - fn namespace(&self) -> Option<&'static str> { - Self::NAMESPACE + impl $crate::cores::TagName for [<$alias Tag>] { + const TAG_NAME: &'static str = $wire; + const NAMESPACE: Option<&'static str> = + ::core::option::Option::Some($crate::cores::Namespace::$ns.uri()); } - } - impl<'a> $name { - pub fn new_tag(value: V) -> $crate::cores::tag::Tag<'a, V, Self> - where - V: $crate::cores::TagValue<'a>, - { - $crate::cores::tag::Tag::new(value) - } + pub type $alias<'a> = $crate::cores::Tag<'a, $value, [<$alias Tag>]>; } }; } -#[macro_export] -macro_rules! define_tagname { - ($name:ident, $namespace:expr) => { - $crate::define_custom_tagname!($name, stringify!($name), $namespace); - }; -} - #[macro_export] macro_rules! impl_tag_from { ($src:ty => $taggen:ty) => { diff --git a/crates/ironposh-winrm/src/rsp/commandline.rs b/crates/ironposh-winrm/src/rsp/commandline.rs index 9f556ed..2b1d486 100644 --- a/crates/ironposh-winrm/src/rsp/commandline.rs +++ b/crates/ironposh-winrm/src/rsp/commandline.rs @@ -1,9 +1,9 @@ use ironposh_xml::mapping::{FromXml, NodeExt}; -use crate::cores::{ - Tag, TagName, TagValue, Text, - tag_name::{Arguments, Command}, -}; +use crate::cores::{ArgumentsTag, CommandTag, Tag, TagName, TagValue, Text}; +use crate::tag; + +tag!(CommandLine = CommandLineValue => WsmanShell); #[derive(Debug, Clone)] pub struct CommandLineValue { @@ -16,10 +16,12 @@ impl TagValue<'_> for CommandLineValue { self, mut element: ironposh_xml::builder::Element, ) -> ironposh_xml::builder::Element { + // `Command` carries either nothing (``) or text, so it's built + // explicitly from the marker rather than a single-value alias. let command_element = self.command.map_or_else( - || Tag::from_name(Command).with_value(()).into_element(), + || Tag::from_name(CommandTag).with_value(()).into_element(), |cmd| { - Tag::from_name(Command) + Tag::from_name(CommandTag) .with_value(Text::from(cmd)) .into_element() }, @@ -28,7 +30,7 @@ impl TagValue<'_> for CommandLineValue { element = element.add_child(command_element); for arg in self.arguments { - let arg_element = Tag::from_name(Arguments) + let arg_element = Tag::from_name(ArgumentsTag) .with_value(Text::from(arg)) .into_element(); element = element.add_child(arg_element); @@ -43,9 +45,9 @@ impl<'a> FromXml<'a> for CommandLineValue { let mut command = None; let mut arguments = Vec::new(); for child in node.children() { - if child.is_element_named(Command::NAMESPACE, Command::TAG_NAME) { + if child.is_element_named(CommandTag::NAMESPACE, CommandTag::TAG_NAME) { command = child.text().map(ToString::to_string); - } else if child.is_element_named(Arguments::NAMESPACE, Arguments::TAG_NAME) + } else if child.is_element_named(ArgumentsTag::NAMESPACE, ArgumentsTag::TAG_NAME) && let Some(text) = child.text() { arguments.push(text.to_string()); diff --git a/crates/ironposh-winrm/src/rsp/connect.rs b/crates/ironposh-winrm/src/rsp/connect.rs index 8018c35..0c46d84 100644 --- a/crates/ironposh-winrm/src/rsp/connect.rs +++ b/crates/ironposh-winrm/src/rsp/connect.rs @@ -1,16 +1,17 @@ -use crate::cores::{ - Tag, Text, - tag_name::{ConnectResponseXml, ConnectXml}, -}; +use crate::cores::{ConnectResponseXml, ConnectXml}; +use crate::tag; use ironposh_macros::{FromXml, SimpleTagValue}; +tag!(Connect = ConnectValue<'a> => WsmanShell); +tag!(ConnectResponse = ConnectResponseValue<'a> => WsmanShell); + /// Value for the Connect element (MS-WSMV 3.1.4.15). /// /// Carries the base64 PSRP payload (SESSION_CAPABILITY + CONNECT_RUNSPACEPOOL) /// in a `connectXml` child element, analogous to `creationXml` on shell create. #[derive(Debug, Clone, SimpleTagValue, FromXml)] pub struct ConnectValue<'a> { - pub connect_xml: Tag<'a, Text<'a>, ConnectXml>, + pub connect_xml: ConnectXml<'a>, } /// Value for the ConnectResponse element (MS-WSMV 3.1.4.15). @@ -20,5 +21,5 @@ pub struct ConnectValue<'a> { /// can surface a descriptive error instead of failing the whole envelope parse. #[derive(Debug, Clone, SimpleTagValue, FromXml)] pub struct ConnectResponseValue<'a> { - pub connect_response_xml: Option, ConnectResponseXml>>, + pub connect_response_xml: Option>, } diff --git a/crates/ironposh-winrm/src/rsp/disconnect.rs b/crates/ironposh-winrm/src/rsp/disconnect.rs index 4082e44..43f2c9a 100644 --- a/crates/ironposh-winrm/src/rsp/disconnect.rs +++ b/crates/ironposh-winrm/src/rsp/disconnect.rs @@ -1,12 +1,15 @@ -use crate::cores::{Tag, Time, tag_name::IdleTimeOut}; +use crate::cores::IdleTimeOut; +use crate::tag; use ironposh_macros::{FromXml, SimpleTagValue}; +tag!(Disconnect = DisconnectValue<'a> => WsmanShell); + /// Value for the Disconnect element (MS-WSMV 3.1.4.13). /// Optionally carries an IdleTimeOut serialized as `PT{seconds}S`. #[derive(Debug, Clone, typed_builder::TypedBuilder, SimpleTagValue, FromXml)] pub struct DisconnectValue<'a> { #[builder(default, setter(strip_option, into))] - pub idle_time_out: Option>, + pub idle_time_out: Option>, } #[cfg(test)] diff --git a/crates/ironposh-winrm/src/rsp/receive.rs b/crates/ironposh-winrm/src/rsp/receive.rs index 02ee4f9..bb47f05 100644 --- a/crates/ironposh-winrm/src/rsp/receive.rs +++ b/crates/ironposh-winrm/src/rsp/receive.rs @@ -1,17 +1,22 @@ use ironposh_macros::{FromXml, SimpleTagValue}; use crate::cores::{ - CommandState, DesiredStream, ExitCode, Stream, Tag, TagName, TagValue, Text, tag_value, + DesiredStream, DesiredStreamTag, ExitCode, Stream, StreamTag, TagName, TagValue, }; +use crate::tag; use ironposh_xml::{ XmlError, builder::Element, mapping::{FromXml, NodeExt}, }; +tag!(Receive = ReceiveValue<'a> => WsmanShell); +tag!(ReceiveResponse = ReceiveResponseValue<'a> => WsmanShell); +tag!(CommandState = CommandStateValue<'a> => WsmanShell); + #[derive(Debug, Clone, typed_builder::TypedBuilder)] pub struct ReceiveValue<'a> { - pub desired_streams: Vec, DesiredStream>>, + pub desired_streams: Vec>, } impl<'a> TagValue<'a> for ReceiveValue<'a> { @@ -27,8 +32,8 @@ impl<'a> FromXml<'a> for ReceiveValue<'a> { fn from_xml(node: ironposh_xml::parser::Node<'a, 'a>) -> Result { let mut desired_streams = Vec::new(); for child in node.children() { - if child.is_element_named(DesiredStream::NAMESPACE, DesiredStream::TAG_NAME) { - desired_streams.push(Tag::from_xml(child)?); + if child.is_element_named(DesiredStreamTag::NAMESPACE, DesiredStreamTag::TAG_NAME) { + desired_streams.push(DesiredStream::from_xml(child)?); } } Ok(ReceiveValue { desired_streams }) @@ -81,14 +86,14 @@ impl TryFrom<&str> for CommandStateValueState { #[derive(Debug, Clone, SimpleTagValue, FromXml)] pub struct CommandStateValue<'a> { - pub exit_code: Option>, + pub exit_code: Option>, } // ReceiveResponse main structure #[derive(Debug, Clone, typed_builder::TypedBuilder)] pub struct ReceiveResponseValue<'a> { - pub streams: Vec, Stream>>, - pub command_state: Option, CommandState>>, + pub streams: Vec>, + pub command_state: Option>, } impl<'a> TagValue<'a> for ReceiveResponseValue<'a> { @@ -106,10 +111,11 @@ impl<'a> FromXml<'a> for ReceiveResponseValue<'a> { let mut streams = Vec::new(); let mut command_state = None; for child in node.children() { - if child.is_element_named(Stream::NAMESPACE, Stream::TAG_NAME) { - streams.push(Tag::from_xml(child)?); - } else if child.is_element_named(CommandState::NAMESPACE, CommandState::TAG_NAME) { - command_state = Some(Tag::from_xml(child)?); + if child.is_element_named(StreamTag::NAMESPACE, StreamTag::TAG_NAME) { + streams.push(Stream::from_xml(child)?); + } else if child.is_element_named(CommandStateTag::NAMESPACE, CommandStateTag::TAG_NAME) + { + command_state = Some(CommandState::from_xml(child)?); } } Ok(ReceiveResponseValue { diff --git a/crates/ironposh-winrm/src/rsp/send.rs b/crates/ironposh-winrm/src/rsp/send.rs index b607234..c4b921a 100644 --- a/crates/ironposh-winrm/src/rsp/send.rs +++ b/crates/ironposh-winrm/src/rsp/send.rs @@ -1,18 +1,18 @@ -use crate::cores::{ - Tag, TagValue, Text, - tag_name::{Stream, TagName}, -}; +use crate::cores::{Stream, StreamTag, TagName, TagValue}; +use crate::tag; use ironposh_xml::{ XmlError, builder::Element, mapping::{FromXml, NodeExt}, }; +tag!(Send = SendValue<'a> => WsmanShell); + /// Value for Send element containing multiple Stream elements /// Each Stream contains a base64-encoded PSRP fragment #[derive(Debug, Clone, typed_builder::TypedBuilder)] pub struct SendValue<'a> { - pub streams: Vec, Stream>>, + pub streams: Vec>, } impl<'a> TagValue<'a> for SendValue<'a> { @@ -29,8 +29,8 @@ impl<'a> FromXml<'a> for SendValue<'a> { fn from_xml(node: ironposh_xml::parser::Node<'a, 'a>) -> Result { let mut streams = Vec::new(); for child in node.children() { - if child.is_element_named(Stream::NAMESPACE, Stream::TAG_NAME) { - streams.push(Tag::from_xml(child)?); + if child.is_element_named(StreamTag::NAMESPACE, StreamTag::TAG_NAME) { + streams.push(Stream::from_xml(child)?); } } Ok(SendValue { streams }) diff --git a/crates/ironposh-winrm/src/rsp/shell_value.rs b/crates/ironposh-winrm/src/rsp/shell_value.rs index 77a5acd..8227451 100644 --- a/crates/ironposh-winrm/src/rsp/shell_value.rs +++ b/crates/ironposh-winrm/src/rsp/shell_value.rs @@ -1,58 +1,53 @@ use crate::cores::{ - Tag, Text, Time, - tag_name::{ - BufferMode, ClientIP, CompressionMode, CreationXml, DataLocale, Encoding, IdleTimeOut, - InputStreams, Locale, MaxIdleTimeOut, Name, OutputStreams, Owner, ProcessId, ProfileLoaded, - ResourceUri, ShellId, ShellInactivity, ShellRunTime, State, - }, + BufferMode, ClientIP, CompressionMode, CreationXml, DataLocaleText, Encoding, IdleTimeOut, + InputStreams, LocaleText, MaxIdleTimeOut, Name, OutputStreams, Owner, ProcessId, ProfileLoaded, + ResourceUri, ShellId, ShellInactivity, ShellRunTime, State, }; +use crate::tag; use ironposh_macros::{FromXml, SimpleTagValue}; -// The XmlTagContainer derive macro generates: -// - TagValue implementation -// - ShellValueVisitor struct -// - XmlVisitor implementation for ShellValueVisitor -// - XmlDeserialize implementation +tag!(Shell = ShellValue<'a> => WsmanShell); + #[derive(Debug, Clone, typed_builder::TypedBuilder, SimpleTagValue, FromXml)] pub struct ShellValue<'a> { #[builder(default, setter(strip_option, into))] - pub shell_id: Option, ShellId>>, + pub shell_id: Option>, #[builder(default, setter(strip_option, into))] - pub name: Option, Name>>, + pub name: Option>, #[builder(default, setter(strip_option, into))] - pub resource_uri: Option, ResourceUri>>, + pub resource_uri: Option>, #[builder(default, setter(strip_option, into))] - pub owner: Option, Owner>>, + pub owner: Option>, #[builder(default, setter(strip_option, into))] - pub client_ip: Option, ClientIP>>, + pub client_ip: Option>, #[builder(default, setter(strip_option, into))] - pub process_id: Option, ProcessId>>, + pub process_id: Option>, #[builder(default, setter(strip_option(fallback_suffix = "_opt"), into))] - pub idle_time_out: Option>, + pub idle_time_out: Option>, #[builder(default, setter(strip_option, into))] - pub input_streams: Option, InputStreams>>, + pub input_streams: Option>, #[builder(default, setter(strip_option, into))] - pub output_streams: Option, OutputStreams>>, + pub output_streams: Option>, #[builder(default, setter(strip_option, into))] - pub max_idle_time_out: Option, MaxIdleTimeOut>>, + pub max_idle_time_out: Option>, #[builder(default, setter(strip_option, into))] - pub locale: Option, Locale>>, + pub locale: Option>, #[builder(default, setter(strip_option, into))] - pub data_locale: Option, DataLocale>>, + pub data_locale: Option>, #[builder(default, setter(strip_option, into))] - pub compression_mode: Option, CompressionMode>>, + pub compression_mode: Option>, #[builder(default, setter(strip_option, into))] - pub profile_loaded: Option, ProfileLoaded>>, + pub profile_loaded: Option>, #[builder(default, setter(strip_option, into))] - pub encoding: Option, Encoding>>, + pub encoding: Option>, #[builder(default, setter(strip_option, into))] - pub buffer_mode: Option, BufferMode>>, + pub buffer_mode: Option>, #[builder(default, setter(strip_option, into))] - pub state: Option, State>>, + pub state: Option>, #[builder(default, setter(strip_option, into))] - pub shell_run_time: Option, ShellRunTime>>, + pub shell_run_time: Option>, #[builder(default, setter(strip_option, into))] - pub shell_inactivity: Option, ShellInactivity>>, + pub shell_inactivity: Option>, #[builder(default, setter(strip_option, into))] - pub creation_xml: Option, CreationXml>>, + pub creation_xml: Option>, } diff --git a/crates/ironposh-winrm/src/soap/body.rs b/crates/ironposh-winrm/src/soap/body.rs index c3da76e..5be75c6 100644 --- a/crates/ironposh-winrm/src/soap/body.rs +++ b/crates/ironposh-winrm/src/soap/body.rs @@ -1,70 +1,76 @@ use ironposh_macros::{FromXml, SimpleTagValue}; +use crate::tag; use crate::{ - cores::*, + cores::{ + CommandResponse, Create, Delete, DisconnectResponse, Enumerate, Get, Identify, Put, + Reconnect, ReconnectResponse, Signal, SignalResponse, + }, rsp::{ - commandline::CommandLineValue, - connect::{ConnectResponseValue, ConnectValue}, - disconnect::DisconnectValue, - receive::{ReceiveResponseValue, ReceiveValue}, - send::SendValue, - shell_value::ShellValue, + commandline::CommandLine, + connect::{Connect, ConnectResponse}, + disconnect::Disconnect, + receive::{Receive, ReceiveResponse}, + send::Send, + shell_value::Shell, }, - soap::fault::SoapFaultValue, - ws_management::body::ResourceCreatedValue, + soap::fault::Fault, + ws_management::body::ResourceCreated, }; +tag!(Body = SoapBody<'a> => SoapEnvelope2003); + #[derive(Debug, Clone, typed_builder::TypedBuilder, SimpleTagValue, FromXml)] pub struct SoapBody<'a> { /// WS-Management operations #[builder(default, setter(into, strip_option))] - pub identify: Option>, + pub identify: Option>, #[builder(default, setter(into, strip_option))] - pub get: Option, Get>>, + pub get: Option>, #[builder(default, setter(into, strip_option))] - pub put: Option, Put>>, + pub put: Option>, #[builder(default, setter(into, strip_option))] - pub create: Option, Create>>, + pub create: Option>, #[builder(default, setter(into, strip_option))] - pub delete: Option, Delete>>, + pub delete: Option>, #[builder(default, setter(into, strip_option))] - pub enumerate: Option, Enumerate>>, + pub enumerate: Option>, /// WS-Transfer operations #[builder(default, setter(into, strip_option))] - pub resource_created: Option, ResourceCreated>>, + pub resource_created: Option>, /// PowerShell Remoting operations #[builder(default, setter(into, strip_option))] - pub shell: Option, Shell>>, + pub shell: Option>, #[builder(default, setter(into, strip_option))] - pub command_line: Option>, + pub command_line: Option>, #[builder(default, setter(into, strip_option))] - pub receive: Option, Receive>>, + pub receive: Option>, #[builder(default, setter(into, strip_option))] - pub receive_response: Option, ReceiveResponse>>, + pub receive_response: Option>, #[builder(default, setter(into, strip_option))] - pub command_response: Option, CommandResponse>>, + pub command_response: Option>, #[builder(default, setter(into, strip_option))] - pub send: Option, Send>>, + pub send: Option>, #[builder(default, setter(into, strip_option))] - pub signal: Option, SignalCode>, Signal>>, + pub signal: Option>, #[builder(default, setter(into, strip_option))] - pub signal_response: Option>, + pub signal_response: Option>, #[builder(default, setter(into, strip_option))] - pub disconnect: Option, Disconnect>>, + pub disconnect: Option>, #[builder(default, setter(into, strip_option))] - pub disconnect_response: Option>, + pub disconnect_response: Option>, #[builder(default, setter(into, strip_option))] - pub reconnect: Option>, + pub reconnect: Option>, #[builder(default, setter(into, strip_option))] - pub reconnect_response: Option>, + pub reconnect_response: Option>, #[builder(default, setter(into, strip_option))] - pub connect: Option, Connect>>, + pub connect: Option>, #[builder(default, setter(into, strip_option))] - pub connect_response: Option, ConnectResponse>>, + pub connect_response: Option>, /// SOAP fault handling #[builder(default, setter(into, strip_option))] - pub fault: Option, Fault>>, + pub fault: Option>, } diff --git a/crates/ironposh-winrm/src/soap/fault.rs b/crates/ironposh-winrm/src/soap/fault.rs index e4dce81..f2527d8 100644 --- a/crates/ironposh-winrm/src/soap/fault.rs +++ b/crates/ironposh-winrm/src/soap/fault.rs @@ -1,36 +1,42 @@ -use crate::cores::*; +use crate::cores::{Detail, SoapText, SoapValue}; +use crate::tag; use ironposh_macros::{FromXml, SimpleTagValue}; // SOAP Fault structures for handling SOAP error responses +tag!(Fault = SoapFaultValue<'a> => SoapEnvelope2003); +tag!(Code = SoapFaultCodeValue<'a> => SoapEnvelope2003); +tag!(Subcode = SoapFaultSubcodeValue<'a> => SoapEnvelope2003); +tag!(Reason = SoapFaultReasonValue<'a> => SoapEnvelope2003); + #[derive(Debug, Clone, typed_builder::TypedBuilder, SimpleTagValue, FromXml)] pub struct SoapFaultValue<'a> { #[builder(default, setter(into, strip_option))] - pub code: Option, Code>>, + pub code: Option>, #[builder(default, setter(into, strip_option))] - pub reason: Option, Reason>>, + pub reason: Option>, #[builder(default, setter(into, strip_option))] - pub detail: Option, Detail>>, + pub detail: Option>, } #[derive(Debug, Clone, typed_builder::TypedBuilder, SimpleTagValue, FromXml)] pub struct SoapFaultCodeValue<'a> { #[builder(default, setter(into, strip_option))] - pub value: Option, SoapValue>>, + pub value: Option>, #[builder(default, setter(into, strip_option))] - pub subcode: Option, Subcode>>, + pub subcode: Option>, } #[derive(Debug, Clone, typed_builder::TypedBuilder, SimpleTagValue, FromXml)] pub struct SoapFaultSubcodeValue<'a> { #[builder(default, setter(into, strip_option))] - pub value: Option, SoapValue>>, + pub value: Option>, } #[derive(Debug, Clone, typed_builder::TypedBuilder, SimpleTagValue, FromXml)] pub struct SoapFaultReasonValue<'a> { #[builder(default, setter(into, strip_option))] - pub text: Option, SoapText>>, + pub text: Option>, } impl SoapFaultValue<'_> { diff --git a/crates/ironposh-winrm/src/soap/header.rs b/crates/ironposh-winrm/src/soap/header.rs index efec08d..3bf3de2 100644 --- a/crates/ironposh-winrm/src/soap/header.rs +++ b/crates/ironposh-winrm/src/soap/header.rs @@ -1,8 +1,12 @@ -use crate::{ - cores::*, - ws_addressing::AddressValue, - ws_management::{OptionSetValue, SelectorSetValue}, +use crate::cores::{ + Action, CompressionType, DataLocaleEmpty, LocaleEmpty, MaxEnvelopeSize, MessageID, OperationID, + OperationTimeout, RelatesTo, ResourceURI, SequenceId, SessionId, To, }; +use crate::tag; +use crate::ws_addressing::ReplyTo; +use crate::ws_management::{OptionSet, SelectorSet}; + +tag!(Header = SoapHeaders<'a> => SoapEnvelope2003); #[derive( Debug, @@ -14,37 +18,37 @@ use crate::{ pub struct SoapHeaders<'a> { /// WS-Addressing headers #[builder(default, setter(into, strip_option))] - pub to: Option, To>>, + pub to: Option>, #[builder(default, setter(into, strip_option))] - pub action: Option, Action>>, + pub action: Option>, #[builder(default, setter(into, strip_option))] - pub reply_to: Option, ReplyTo>>, + pub reply_to: Option>, #[builder(default, setter(into, strip_option))] - pub message_id: Option>, + pub message_id: Option>, #[builder(default, setter(into, strip_option))] - pub relates_to: Option>, + pub relates_to: Option>, /// WS-Management headers #[builder(default, setter(into, strip_option))] - pub resource_uri: Option, ResourceURI>>, + pub resource_uri: Option>, #[builder(default, setter(into, strip_option))] - pub max_envelope_size: Option>, + pub max_envelope_size: Option>, #[builder(default, setter(into, strip_option))] - pub locale: Option>, + pub locale: Option>, #[builder(default, setter(into, strip_option))] - pub data_locale: Option>, + pub data_locale: Option>, #[builder(default, setter(into, strip_option))] - pub session_id: Option>, + pub session_id: Option>, #[builder(default, setter(into, strip_option))] - pub operation_id: Option>, + pub operation_id: Option>, #[builder(default, setter(into, strip_option))] - pub sequence_id: Option, SequenceId>>, + pub sequence_id: Option>, #[builder(default, setter(into, strip_option(fallback_suffix = "_opt")))] - pub option_set: Option>, + pub option_set: Option>, #[builder(default, setter(into, strip_option(fallback_suffix = "_opt")))] - pub selector_set: Option>, + pub selector_set: Option>, #[builder(default, setter(into, strip_option))] - pub operation_timeout: Option>, + pub operation_timeout: Option>, #[builder(default, setter(into, strip_option))] - pub compression_type: Option, CompressionType>>, + pub compression_type: Option>, } diff --git a/crates/ironposh-winrm/src/soap/mod.rs b/crates/ironposh-winrm/src/soap/mod.rs index 1c50247..1d2c261 100644 --- a/crates/ironposh-winrm/src/soap/mod.rs +++ b/crates/ironposh-winrm/src/soap/mod.rs @@ -5,17 +5,18 @@ pub mod parsing; use ironposh_macros::FromXml; -use crate::{ - cores::{Tag, TagValue, tag_name::*}, - soap::{body::SoapBody, header::SoapHeaders}, -}; +use crate::cores::TagValue; +use crate::tag; +use crate::{soap::body::Body, soap::header::Header}; + +tag!(Envelope = SoapEnvelope<'a> => SoapEnvelope2003); #[derive(Debug, Clone, typed_builder::TypedBuilder, FromXml)] pub struct SoapEnvelope<'a> { #[builder(default, setter(into, strip_option))] - pub header: Option, Header>>, + pub header: Option>, #[builder(setter(into))] - pub body: Tag<'a, SoapBody<'a>, Body>, + pub body: Body<'a>, } impl<'a> TagValue<'a> for SoapEnvelope<'a> { diff --git a/crates/ironposh-winrm/src/test_macro.rs b/crates/ironposh-winrm/src/test_macro.rs index b0a0fd7..048ccb6 100644 --- a/crates/ironposh-winrm/src/test_macro.rs +++ b/crates/ironposh-winrm/src/test_macro.rs @@ -1,132 +1,60 @@ -use crate::cores::tag_name::*; -use crate::cores::{Tag, Text}; +use crate::cores::Text; +use crate::tag; use ironposh_macros::{FromXml, SimpleTagValue}; -// Example struct with mixed required and optional fields using new derive macros +// Self-contained tags for exercising the SimpleTagValue + FromXml derives. +tag!(ReqA = Text<'a> => WsAddressing2004); +tag!(ReqB = Text<'a> => WsAddressing2004); +tag!(OptA = Text<'a> => WsAddressing2004); +tag!(OptB = Text<'a> => WsAddressing2004); + +// Example struct with mixed required and optional fields using the derives. #[derive(Debug, Clone, SimpleTagValue, FromXml)] pub struct TestStruct<'a> { - pub action: Tag<'a, Text<'a>, Action>, // Required - pub message_id: Tag<'a, Text<'a>, MessageID>, // Required - pub to: Option, To>>, // Optional - pub relates_to: Option, RelatesTo>>, // Optional + pub req_a: ReqA<'a>, // Required + pub req_b: ReqB<'a>, // Required + pub opt_a: Option>, // Optional + pub opt_b: Option>, // Optional } #[cfg(test)] mod tests { use super::*; - use crate::cores::TagValue; + use crate::cores::{Tag, TagValue}; use ironposh_xml::mapping::FromXml; + const A: &str = "http://schemas.xmlsoap.org/ws/2004/08/addressing"; + #[test] fn test_serialization_and_deserialization_roundtrip() { - // Create a test struct with both required and optional fields let original = TestStruct { - action: Tag::new(Text::from("test-action")), - message_id: Tag::new(Text::from("msg-123")), - to: Some(Tag::new(Text::from("destination"))), - relates_to: None, // This optional field is not set + req_a: Tag::new(Text::from("a-value")), + req_b: Tag::new(Text::from("b-value")), + opt_a: Some(Tag::new(Text::from("opt-a-value"))), + opt_b: None, }; - // Test that the TagValue implementation works (serialize to XML) + // Serialize via the TagValue derive (must not panic). let element = ironposh_xml::builder::Element::new("test"); - let _serialized_element = original.append_to_element(element); - - // The TagValue implementation worked if we got here without panicking - - // Test deserialization. Action/MessageID/To live in the WS-Addressing - // namespace, so the document must declare it — matching is by URI. - let test_xml = r#" - test-action - msg-123 - destination - "#; - - // Parse the XML back - let doc = ironposh_xml::parser::parse(test_xml).expect("Failed to parse XML"); - let root = doc.root_element(); - - // Deserialize back to struct - let deserialized = TestStruct::from_xml(root).expect("Failed to deserialize"); - - println!("Deserialized struct: {deserialized:#?}"); - - // Verify deserialization matches original - assert_eq!(deserialized.action.value.as_ref(), "test-action"); - assert_eq!(deserialized.message_id.value.as_ref(), "msg-123"); - assert!(deserialized.to.is_some()); - assert_eq!(deserialized.to.unwrap().value.as_ref(), "destination"); - assert!(deserialized.relates_to.is_none()); - } - - #[test] - fn test_deserialize_with_all_fields() { - let xml = r#" - - test-action - msg-123 - destination - relation-123 - - "#; - - let doc = ironposh_xml::parser::parse(xml).expect("Failed to parse XML"); - let root = doc.root_element(); + let _serialized = original.append_to_element(element); - let result = TestStruct::from_xml(root).expect("Failed to deserialize"); - println!("Deserialized with all fields: {result:#?}"); - - // Verify required fields - assert_eq!(result.action.value.as_ref(), "test-action"); - assert_eq!(result.message_id.value.as_ref(), "msg-123"); - - // Verify optional fields - assert!(result.to.is_some()); - assert_eq!(result.to.unwrap().value.as_ref(), "destination"); - assert!(result.relates_to.is_some()); - assert_eq!(result.relates_to.unwrap().value.as_ref(), "relation-123"); - } - - #[test] - fn test_deserialize_with_only_required_fields() { - let xml = r#" - - test-action - msg-123 - - "#; - - let doc = ironposh_xml::parser::parse(xml).expect("Failed to parse XML"); - let root = doc.root_element(); - - let result = TestStruct::from_xml(root).expect("Failed to deserialize"); - println!("Deserialized with required fields only: {result:#?}"); - - // Verify required fields - assert_eq!(result.action.value.as_ref(), "test-action"); - assert_eq!(result.message_id.value.as_ref(), "msg-123"); + // Deserialize a namespaced document — matching is by URI. + let xml = format!( + r#"a-valueb-valueopt-a-value"# + ); + let doc = ironposh_xml::parser::parse(&xml).expect("parse"); + let parsed = TestStruct::from_xml(doc.root_element()).expect("deserialize"); - // Verify optional fields are None - assert!(result.to.is_none()); - assert!(result.relates_to.is_none()); + assert_eq!(parsed.req_a.value.as_ref(), "a-value"); + assert_eq!(parsed.req_b.value.as_ref(), "b-value"); + assert_eq!(parsed.opt_a.unwrap().value.as_ref(), "opt-a-value"); + assert!(parsed.opt_b.is_none()); } #[test] fn test_deserialize_missing_required_field() { - let xml = r#" - - test-action - - - "#; - - let doc = ironposh_xml::parser::parse(xml).expect("Failed to parse XML"); - let root = doc.root_element(); - - let result = TestStruct::from_xml(root); - assert!(result.is_err()); - println!( - "Expected error for missing required field: {:?}", - result.err() - ); + let xml = format!(r#"only-a"#); + let doc = ironposh_xml::parser::parse(&xml).expect("parse"); + assert!(TestStruct::from_xml(doc.root_element()).is_err()); } } diff --git a/crates/ironposh-winrm/src/ws_addressing/mod.rs b/crates/ironposh-winrm/src/ws_addressing/mod.rs index cc33e4a..4ab1396 100644 --- a/crates/ironposh-winrm/src/ws_addressing/mod.rs +++ b/crates/ironposh-winrm/src/ws_addressing/mod.rs @@ -1,10 +1,13 @@ use ironposh_macros::{FromXml, SimpleTagValue}; -use crate::cores::{Tag, tag_name::*, tag_value::Text}; +use crate::cores::Address; +use crate::tag; + +tag!(ReplyTo = AddressValue<'a> => WsAddressing2004); #[derive(Debug, Clone, SimpleTagValue, FromXml)] pub struct AddressValue<'a> { - pub url: Tag<'a, Text<'a>, Address>, + pub url: Address<'a>, } // impl<'a> TagValue<'a> for AddressValue<'a> { diff --git a/crates/ironposh-winrm/src/ws_management/body.rs b/crates/ironposh-winrm/src/ws_management/body.rs index 4bcddd3..bbfd51b 100644 --- a/crates/ironposh-winrm/src/ws_management/body.rs +++ b/crates/ironposh-winrm/src/ws_management/body.rs @@ -1,10 +1,12 @@ use ironposh_macros::{FromXml, SimpleTagValue}; use ironposh_xml::builder::Element; -use crate::{ - cores::{ResourceURI, SelectorSet, Tag, TagValue, tag_name::*, tag_value::Text}, - ws_management::SelectorSetValue, -}; +use crate::cores::{Address, ResourceURI, TagValue, tag_value::Text}; +use crate::tag; +use crate::ws_management::SelectorSet; + +tag!(ReferenceParameters = ReferenceParametersValue<'a> => WsAddressing2004); +tag!(ResourceCreated = ResourceCreatedValue<'a> => WsTransfer2004); // Enumeration operations #[derive(Debug, Clone, Default)] @@ -127,14 +129,14 @@ impl<'a> TagValue<'a> for GetStatusValue<'a> { #[derive(Debug, Clone, SimpleTagValue, FromXml)] pub struct ReferenceParametersValue<'a> { - pub resource_uri: Tag<'a, Text<'a>, ResourceURI>, - pub selector_set: Tag<'a, SelectorSetValue, SelectorSet>, + pub resource_uri: ResourceURI<'a>, + pub selector_set: SelectorSet<'a>, } #[derive(Debug, Clone, SimpleTagValue, FromXml)] pub struct ResourceCreatedValue<'a> { - pub address: Tag<'a, Text<'a>, Address>, - pub reference_parameters: Tag<'a, ReferenceParametersValue<'a>, ReferenceParameters>, + pub address: Address<'a>, + pub reference_parameters: ReferenceParameters<'a>, } #[cfg(test)] @@ -169,7 +171,7 @@ mod tests { let element = ironposh_xml::parser::parse(xml).unwrap(); let root = element.root_element(); - let tag: Tag<'_, ResourceCreatedValue, ResourceCreated> = Tag::from_xml(root).unwrap(); + let tag = ResourceCreated::from_xml(root).unwrap(); let value = tag.value; assert_eq!( diff --git a/crates/ironposh-winrm/src/ws_management/header.rs b/crates/ironposh-winrm/src/ws_management/header.rs index e31bc60..94dfe07 100644 --- a/crates/ironposh-winrm/src/ws_management/header.rs +++ b/crates/ironposh-winrm/src/ws_management/header.rs @@ -5,7 +5,11 @@ use ironposh_xml::{ mapping::{FromXml, NodeExt}, }; -use crate::cores::{self, OptionTagName, Selector, Tag, TagName, TagValue, Text}; +use crate::cores::{self, OptionTagNameTag, Selector, SelectorTag, TagName, TagValue, Text}; +use crate::tag; + +tag!(SelectorSet = SelectorSetValue => DmtfWsmanSchema); +tag!(OptionSet = OptionSetValue => DmtfWsmanSchema); #[derive(Debug, Clone, Default)] pub struct SelectorSetValue { @@ -39,8 +43,7 @@ impl SelectorSetValue { impl<'a> TagValue<'a> for SelectorSetValue { fn append_to_element(self, mut element: Element<'a>) -> Element<'a> { for (name, value) in self.selectors { - let selector = Tag::from_name(Selector) - .with_value(Text::from(value)) + let selector = Selector::new(Text::from(value)) .with_attribute(crate::cores::Attribute::Name(name.into())); let selector = selector.into_element(); @@ -56,7 +59,7 @@ impl<'a> FromXml<'a> for SelectorSetValue { fn from_xml(node: ironposh_xml::parser::Node<'a, 'a>) -> Result { let mut selectors = HashMap::new(); for child in node.children() { - if child.is_element_named(Selector::NAMESPACE, Selector::TAG_NAME) + if child.is_element_named(SelectorTag::NAMESPACE, SelectorTag::TAG_NAME) && let Some(name) = child .attributes() .find(|attr| attr.name() == "Name") @@ -99,7 +102,7 @@ impl<'a> TagValue<'a> for OptionSetValue { for (name, value) in self.options { let option_element = Element::new("Option") .set_namespace(ironposh_xml::builder::Namespace::from( - OptionTagName::NAMESPACE.expect("OptionTagName definately has a namespace"), + OptionTagNameTag::NAMESPACE.expect("OptionTagName definately has a namespace"), )) .set_text(value) .add_attribute(cores::Attribute::Name(name.into()).into()) @@ -115,7 +118,7 @@ impl<'a> FromXml<'a> for OptionSetValue { fn from_xml(node: ironposh_xml::parser::Node<'a, 'a>) -> Result { let mut options = HashMap::new(); for child in node.children() { - if child.is_element_named(OptionTagName::NAMESPACE, OptionTagName::TAG_NAME) + if child.is_element_named(OptionTagNameTag::NAMESPACE, OptionTagNameTag::TAG_NAME) && let Some(name) = child .attributes() .find(|attr| attr.name() == "Name") diff --git a/crates/ironposh-winrm/src/ws_management/mod.rs b/crates/ironposh-winrm/src/ws_management/mod.rs index c3f932b..15a7d16 100644 --- a/crates/ironposh-winrm/src/ws_management/mod.rs +++ b/crates/ironposh-winrm/src/ws_management/mod.rs @@ -3,13 +3,8 @@ pub mod header; pub use header::*; use crate::{ - cores::{ - Attribute, Tag, Time, WsUuid, - namespace::Namespace, - tag_name::{Action, Envelope}, - tag_value::Text, - }, - soap::{SoapEnvelope, body::SoapBody, header::SoapHeaders}, + cores::{Action, Attribute, Tag, Time, WsUuid, namespace::Namespace, tag_value::Text}, + soap::{Envelope, SoapEnvelope, body::SoapBody, header::SoapHeaders}, ws_addressing::AddressValue, }; @@ -107,7 +102,7 @@ impl WsMan { resource_body: SoapBody<'a>, option_set: Option, selector_set: Option, - ) -> Tag<'a, SoapEnvelope<'a>, Envelope> { + ) -> Envelope<'a> { self.invoke_with_operation_timeout( action, resource_uri, @@ -126,7 +121,7 @@ impl WsMan { option_set: Option, selector_set: Option, operation_timeout_secs: Option, - ) -> Tag<'a, SoapEnvelope<'a>, Envelope> { + ) -> Envelope<'a> { // Generate a unique message ID and operation ID for this request let message_id = uuid::Uuid::new_v4(); let operation_id = uuid::Uuid::new_v4(); @@ -142,8 +137,7 @@ impl WsMan { // Create the SOAP header with all required fields let header = SoapHeaders::builder() .action( - Tag::new(action.as_str().to_owned()) - .with_name(Action) + Action::new(action.as_str().to_owned()) .with_attribute(Attribute::MustUnderstand(true)), ) .data_locale( @@ -189,7 +183,7 @@ impl WsMan { // Convert to XML using Tag wrapper with proper namespaces - let mut soap = Tag::::new(envelope) + let mut soap = Envelope::new(envelope) .with_declaration(Namespace::SoapEnvelope2003) .with_declaration(Namespace::WsAddressing2004) .with_declaration(Namespace::DmtfWsmanSchema) diff --git a/crates/ironposh-winrm/tests/test_connect_request.rs b/crates/ironposh-winrm/tests/test_connect_request.rs index 9660117..d8da4cf 100644 --- a/crates/ironposh-winrm/tests/test_connect_request.rs +++ b/crates/ironposh-winrm/tests/test_connect_request.rs @@ -1,6 +1,6 @@ use ironposh_winrm::{ - cores::{Namespace, Tag, Text, tag_name::*}, - rsp::connect::ConnectValue, + cores::{Namespace, Tag, Text}, + rsp::connect::{ConnectTag, ConnectValue}, soap::{SoapEnvelope, body::SoapBody}, ws_management::{SelectorSetValue, WsAction, WsMan}, }; @@ -29,7 +29,7 @@ fn test_build_connect_envelope() { .with_declaration(Namespace::PowerShellRemoting), }; - let connect_tag = Tag::from_name(Connect) + let connect_tag = Tag::from_name(ConnectTag) .with_declaration(Namespace::WsmanShell) .with_value(connect_value); diff --git a/crates/ironposh-winrm/tests/test_disconnect_request.rs b/crates/ironposh-winrm/tests/test_disconnect_request.rs index b12c898..d349c5c 100644 --- a/crates/ironposh-winrm/tests/test_disconnect_request.rs +++ b/crates/ironposh-winrm/tests/test_disconnect_request.rs @@ -1,6 +1,6 @@ use ironposh_winrm::{ cores::{Empty, Namespace, Tag, Time, tag_name::*}, - rsp::disconnect::DisconnectValue, + rsp::disconnect::{DisconnectTag, DisconnectValue}, soap::{SoapEnvelope, body::SoapBody}, ws_management::{SelectorSetValue, WsAction, WsMan}, }; @@ -35,7 +35,7 @@ fn test_build_disconnect_envelope() { .idle_time_out(Tag::new(Time(180.0))) .build(); - let disconnect_tag = Tag::from_name(Disconnect) + let disconnect_tag = Tag::from_name(DisconnectTag) .with_declaration(Namespace::WsmanShell) .with_value(disconnect_value); @@ -78,7 +78,7 @@ fn test_build_disconnect_envelope_without_idle_timeout() { .to("http://10.10.0.3:5985/wsman".to_string()) .build(); - let disconnect_tag = Tag::from_name(Disconnect) + let disconnect_tag = Tag::from_name(DisconnectTag) .with_declaration(Namespace::WsmanShell) .with_value(DisconnectValue::builder().build()); @@ -107,7 +107,7 @@ fn test_build_reconnect_envelope() { .to("http://10.10.0.3:5985/wsman".to_string()) .build(); - let reconnect_tag = Tag::from_name(Reconnect) + let reconnect_tag = Tag::from_name(ReconnectTag) .with_declaration(Namespace::WsmanShell) .with_value(Empty); diff --git a/crates/ironposh-winrm/tests/test_initial_build_request.rs b/crates/ironposh-winrm/tests/test_initial_build_request.rs index 68fe7e4..0392704 100644 --- a/crates/ironposh-winrm/tests/test_initial_build_request.rs +++ b/crates/ironposh-winrm/tests/test_initial_build_request.rs @@ -1,9 +1,9 @@ use ironposh_winrm::{ cores::{Tag, tag_name::*, tag_value::Text}, - rsp::shell_value::ShellValue, - soap::{SoapEnvelope, body::SoapBody, header::SoapHeaders}, - ws_addressing::AddressValue, - ws_management::header::OptionSetValue, + rsp::shell_value::{ShellTag, ShellValue}, + soap::{Envelope, SoapEnvelope, body::SoapBody, header::SoapHeaders}, + ws_addressing::{AddressValue, ReplyToTag}, + ws_management::header::{OptionSetTag, OptionSetValue}, }; #[cfg(test)] @@ -26,7 +26,7 @@ mod tests { // Create shell tag with attributes let shell_tag = Tag::new(shell) - .with_name(Shell) + .with_name(ShellTag) .with_attribute(ironposh_winrm::cores::Attribute::ShellId( "2D6534D0-6B12-40E3-B773-CBA26459CFA8".into(), )) @@ -42,10 +42,10 @@ mod tests { url: Tag::new(Text::from( "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous", )) - .with_name(Address) + .with_name(AddressTag) .with_attribute(ironposh_winrm::cores::Attribute::MustUnderstand(true)), }) - .with_name(ReplyTo) + .with_name(ReplyToTag) .with_attribute(ironposh_winrm::cores::Attribute::MustUnderstand(true)); // Build the complete SOAP envelope @@ -58,7 +58,7 @@ mod tests { Tag::new(Text::from( "http://schemas.xmlsoap.org/ws/2004/09/transfer/Create", )) - .with_name(Action) + .with_name(ActionTag) .with_attribute(ironposh_winrm::cores::Attribute::MustUnderstand(true)), ) .reply_to(reply_to_address) @@ -71,21 +71,21 @@ mod tests { Tag::new(Text::from( "http://schemas.microsoft.com/powershell/Microsoft.PowerShell", )) - .with_name(ResourceURI) + .with_name(ResourceURITag) .with_attribute(ironposh_winrm::cores::Attribute::MustUnderstand(true)), ) .max_envelope_size( Tag::new(512000) - .with_name(MaxEnvelopeSize) + .with_name(MaxEnvelopeSizeTag) .with_attribute(ironposh_winrm::cores::Attribute::MustUnderstand(true)), ) .locale( - Tag::new(()).with_name(Locale).with_attribute( + Tag::new(()).with_name(LocaleEmptyTag).with_attribute( ironposh_winrm::cores::Attribute::MustUnderstand(false), ), ) .data_locale( - Tag::new(()).with_name(DataLocale).with_attribute( + Tag::new(()).with_name(DataLocaleEmptyTag).with_attribute( ironposh_winrm::cores::Attribute::MustUnderstand(false), ), ) @@ -94,7 +94,7 @@ mod tests { uuid::Uuid::from_str("9EC885D6-F5A4-4771-9D47-4BDF7DAAEA8C") .expect("Failed to parse UUID"), ) - .with_name(SessionId) + .with_name(SessionIdTag) .with_attribute(ironposh_winrm::cores::Attribute::MustUnderstand(false)), ) .operation_id( @@ -102,25 +102,25 @@ mod tests { uuid::Uuid::from_str("73C4BCA6-7FF0-4AFE-B8C3-335FB19BA649") .expect("Failed to parse UUID"), ) - .with_name(OperationID) + .with_name(OperationIDTag) .with_attribute(ironposh_winrm::cores::Attribute::MustUnderstand(false)), ) .sequence_id( Tag::new(Text::from("1")) - .with_name(SequenceId) + .with_name(SequenceIdTag) .with_attribute(ironposh_winrm::cores::Attribute::MustUnderstand( false, )), ) .option_set( Tag::new(option_set_tag) - .with_name(OptionSet) + .with_name(OptionSetTag) .with_attribute(ironposh_winrm::cores::Attribute::MustUnderstand(true)), ) .operation_timeout(Time::from(60000)) .compression_type( Tag::new(Text::from("xpress")) - .with_name(CompressionType) + .with_name(CompressionTypeTag) .with_attribute(ironposh_winrm::cores::Attribute::MustUnderstand(true)), ) .build(), @@ -129,7 +129,7 @@ mod tests { .build(); // Convert envelope to Tag and add namespace declarations - let envelope: Tag<'_, _, Envelope> = envelope.into(); + let envelope: Envelope<'_> = envelope.into(); let envelope = envelope .with_declaration(ironposh_winrm::cores::namespace::Namespace::SoapEnvelope2003) .with_declaration(ironposh_winrm::cores::namespace::Namespace::WsAddressing2004) diff --git a/crates/ironposh-winrm/tests/test_receive_fix.rs b/crates/ironposh-winrm/tests/test_receive_fix.rs index bb8ba2c..ba1648e 100644 --- a/crates/ironposh-winrm/tests/test_receive_fix.rs +++ b/crates/ironposh-winrm/tests/test_receive_fix.rs @@ -1,6 +1,6 @@ use ironposh_winrm::{ - cores::{DesiredStream, Receive, Tag, Text}, - rsp::receive::ReceiveValue, + cores::{DesiredStreamTag, Tag, Text}, + rsp::receive::{ReceiveTag, ReceiveValue}, soap::SoapEnvelope, }; use ironposh_xml::{builder::Element, mapping::FromXml}; @@ -14,13 +14,13 @@ fn test_receive_with_single_desired_stream() { // Create a ReceiveValue with single DesiredStream containing space-separated streams let receive = ReceiveValue::builder() .desired_streams(vec![ - Tag::from_name(DesiredStream) + Tag::from_name(DesiredStreamTag) .with_value(Text::from("stdout stderr")) .with_attribute(ironposh_winrm::cores::Attribute::CommandId(command_id)), ]) .build(); - let receive_tag = Tag::from_name(Receive) + let receive_tag = Tag::from_name(ReceiveTag) .with_value(receive) .with_declaration(ironposh_winrm::cores::Namespace::WsmanShell); @@ -50,11 +50,11 @@ fn test_receive_shell_level_without_command_id() { // Test case: Shell-level receive without CommandId let receive = ReceiveValue::builder() .desired_streams(vec![ - Tag::from_name(DesiredStream).with_value(Text::from("stdout stderr")), + Tag::from_name(DesiredStreamTag).with_value(Text::from("stdout stderr")), ]) .build(); - let receive_tag = Tag::from_name(Receive) + let receive_tag = Tag::from_name(ReceiveTag) .with_value(receive) .with_declaration(ironposh_winrm::cores::Namespace::WsmanShell); diff --git a/crates/ironposh-winrm/tests/test_send_multiple_fragments.rs b/crates/ironposh-winrm/tests/test_send_multiple_fragments.rs index 28ff58d..2293a7d 100644 --- a/crates/ironposh-winrm/tests/test_send_multiple_fragments.rs +++ b/crates/ironposh-winrm/tests/test_send_multiple_fragments.rs @@ -1,6 +1,6 @@ use ironposh_winrm::{ - cores::{Attribute, Send, Tag, Text, tag_name::Stream}, - rsp::send::SendValue, + cores::{Attribute, StreamTag, Tag, Text}, + rsp::send::{SendTag, SendValue}, }; use ironposh_xml::builder::Element; use uuid::Uuid; @@ -19,10 +19,10 @@ fn test_send_with_multiple_stream_fragments() { let command_id = Uuid::new_v4(); // Create Stream tags for each fragment - let streams: Vec> = fragments + let streams: Vec> = fragments .iter() .map(|fragment| { - Tag::from_name(Stream) + Tag::from_name(StreamTag) .with_value(Text::from(fragment.as_str())) .with_attribute(Attribute::Name("stdin".into())) }) @@ -32,7 +32,7 @@ fn test_send_with_multiple_stream_fragments() { let send_value = SendValue::builder().streams(streams).build(); // Create Send tag with CommandId - let send_tag = Tag::from_name(Send) + let send_tag = Tag::from_name(SendTag) .with_value(send_value) .with_attribute(Attribute::CommandId(command_id)) .with_declaration(ironposh_winrm::cores::Namespace::WsmanShell); @@ -86,10 +86,10 @@ fn test_send_with_multiple_stream_fragments() { fn test_send_without_command_id() { let fragments = ["FRAGMENT1==".to_string(), "FRAGMENT2==".to_string()]; - let streams: Vec> = fragments + let streams: Vec> = fragments .iter() .map(|fragment| { - Tag::from_name(Stream) + Tag::from_name(StreamTag) .with_value(Text::from(fragment.as_str())) .with_attribute(Attribute::Name("stdin".into())) }) @@ -97,7 +97,7 @@ fn test_send_without_command_id() { let send_value = SendValue::builder().streams(streams).build(); - let send_tag = Tag::from_name(Send) + let send_tag = Tag::from_name(SendTag) .with_value(send_value) .with_declaration(ironposh_winrm::cores::Namespace::WsmanShell); @@ -128,10 +128,10 @@ fn test_send_large_response_multiple_fragments() { let command_id = Uuid::new_v4(); - let streams: Vec> = fragments + let streams: Vec> = fragments .iter() .map(|fragment| { - Tag::from_name(Stream) + Tag::from_name(StreamTag) .with_value(Text::from(fragment.as_str())) .with_attribute(Attribute::Name("stdin".into())) }) @@ -139,7 +139,7 @@ fn test_send_large_response_multiple_fragments() { let send_value = SendValue::builder().streams(streams).build(); - let send_tag = Tag::from_name(Send) + let send_tag = Tag::from_name(SendTag) .with_value(send_value) .with_attribute(Attribute::CommandId(command_id)) .with_declaration(ironposh_winrm::cores::Namespace::WsmanShell);