Skip to content

Commit 428e8df

Browse files
Merge pull request #36 from Devolutions/stack/xml-fromxml-tag-macro
refactor(winrm): alias-based tags via tag! macro
2 parents 08f74b1 + f7e98d7 commit 428e8df

26 files changed

Lines changed: 423 additions & 512 deletions

crates/ironposh-client-core/src/runspace/win_rs.rs

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use base64::Engine;
22
use ironposh_winrm::{
3-
cores::{Attribute, DesiredStream, Receive, Shell, Tag, Text, Time, tag_name},
3+
cores::{Attribute, DesiredStreamTag, StreamTag, Tag, Text, Time},
44
rsp::{
55
commandline::CommandLineValue,
6-
receive::{CommandStateValue, ReceiveValue},
7-
shell_value::ShellValue,
6+
receive::{CommandStateTag, CommandStateValue, ReceiveTag, ReceiveValue},
7+
shell_value::{ShellTag, ShellValue},
88
},
99
soap::{SoapEnvelope, body::SoapBody},
1010
ws_management::{OptionSetValue, SelectorSetValue, WsAction, WsMan},
@@ -67,7 +67,7 @@ impl WinRunspace {
6767
option_set: Option<OptionSetValue>,
6868
open_content: &'a str,
6969
) -> impl Into<Element<'a>> {
70-
let shell = Tag::from_name(Shell)
70+
let shell = Tag::from_name(ShellTag)
7171
.with_attribute(ironposh_winrm::cores::Attribute::ShellId(
7272
self.id.to_string().into(),
7373
))
@@ -130,7 +130,7 @@ impl WinRunspace {
130130
// Join stream names with spaces as required by Windows Shell schema
131131
let combined_streams = stream_names.join(" ");
132132
let mut tag =
133-
Tag::from_name(DesiredStream).with_value(Text::from(combined_streams));
133+
Tag::from_name(DesiredStreamTag).with_value(Text::from(combined_streams));
134134

135135
if let Some(command_id) = command_id {
136136
tag = tag.with_attribute(Attribute::CommandId(command_id));
@@ -144,7 +144,7 @@ impl WinRunspace {
144144
.desired_streams(desired_stream_tags)
145145
.build();
146146

147-
let receive_tag = Tag::from_name(Receive)
147+
let receive_tag = Tag::from_name(ReceiveTag)
148148
.with_value(receive)
149149
.with_declaration(ironposh_winrm::cores::Namespace::WsmanShell);
150150

@@ -172,9 +172,12 @@ impl WinRunspace {
172172

173173
/// Build a Disconnect request targeting this shell (MS-WSMV 3.1.4.13).
174174
pub(crate) fn fire_disconnect<'a>(&'a self, ws_man: &'a WsMan) -> impl Into<Element<'a>> {
175-
use ironposh_winrm::{cores::Namespace, rsp::disconnect::DisconnectValue};
175+
use ironposh_winrm::{
176+
cores::Namespace,
177+
rsp::disconnect::{DisconnectTag, DisconnectValue},
178+
};
176179

177-
let disconnect_tag = Tag::from_name(tag_name::Disconnect)
180+
let disconnect_tag = Tag::from_name(DisconnectTag)
178181
.with_declaration(Namespace::WsmanShell)
179182
.with_value(DisconnectValue::builder().build());
180183

@@ -197,13 +200,16 @@ impl WinRunspace {
197200
option_set: Option<OptionSetValue>,
198201
connect_payload: &'a str,
199202
) -> impl Into<Element<'a>> {
200-
use ironposh_winrm::{cores::Namespace, rsp::connect::ConnectValue};
203+
use ironposh_winrm::{
204+
cores::Namespace,
205+
rsp::connect::{ConnectTag, ConnectValue},
206+
};
201207

202208
let connect_value = ConnectValue {
203209
connect_xml: Tag::new(connect_payload).with_declaration(Namespace::PowerShellRemoting),
204210
};
205211

206-
let connect_tag = Tag::from_name(tag_name::Connect)
212+
let connect_tag = Tag::from_name(ConnectTag)
207213
.with_declaration(Namespace::WsmanShell)
208214
.with_value(connect_value);
209215

@@ -218,9 +224,9 @@ impl WinRunspace {
218224

219225
/// Build a Reconnect request targeting this shell (MS-WSMV 3.1.4.14).
220226
pub(crate) fn fire_reconnect<'a>(&'a self, ws_man: &'a WsMan) -> impl Into<Element<'a>> {
221-
use ironposh_winrm::cores::{Empty, Namespace};
227+
use ironposh_winrm::cores::{Empty, Namespace, ReconnectTag};
222228

223-
let reconnect_tag = Tag::from_name(tag_name::Reconnect)
229+
let reconnect_tag = Tag::from_name(ReconnectTag)
224230
.with_declaration(Namespace::WsmanShell)
225231
.with_value(Empty);
226232

@@ -390,20 +396,17 @@ impl WinRunspace {
390396
data: &'a [String],
391397
) -> Result<impl Into<Element<'a>>, crate::PwshCoreError> {
392398
use ironposh_winrm::{
393-
cores::{
394-
Namespace, Tag,
395-
tag_name::{Send, Stream},
396-
},
397-
rsp::send::SendValue,
399+
cores::{Namespace, StreamTag, Tag},
400+
rsp::send::{SendTag, SendValue},
398401
soap::body::SoapBody,
399402
};
400403

401404
// Create a Stream tag for each fragment
402405
// Each fragment is a base64-encoded PSRP fragment that goes in its own <rsp:Stream> element
403-
let streams: Vec<Tag<Text, Stream>> = data
406+
let streams: Vec<Tag<Text, StreamTag>> = data
404407
.iter()
405408
.map(|fragment| {
406-
Tag::from_name(Stream)
409+
Tag::from_name(StreamTag)
407410
.with_value(Text::from(fragment.as_str()))
408411
.with_attribute(Attribute::Name("stdin".into()))
409412
})
@@ -413,12 +416,12 @@ impl WinRunspace {
413416

414417
// Add send tag with SendValue containing multiple streams
415418
let send_tag = if let Some(cmd_id) = command_id {
416-
Tag::from_name(Send)
419+
Tag::from_name(SendTag)
417420
.with_value(send_value)
418421
.with_attribute(Attribute::CommandId(cmd_id))
419422
.with_declaration(Namespace::WsmanShell)
420423
} else {
421-
Tag::from_name(Send)
424+
Tag::from_name(SendTag)
422425
.with_value(send_value)
423426
.with_declaration(Namespace::WsmanShell)
424427
};
@@ -460,18 +463,15 @@ impl WinRunspace {
460463
connection: &'a WsMan,
461464
id: Uuid,
462465
) -> Result<impl Into<Element<'a>>, crate::PwshCoreError> {
463-
use ironposh_winrm::cores::{
464-
Namespace,
465-
tag_name::{Signal, SignalCode},
466-
};
466+
use ironposh_winrm::cores::{Namespace, SignalCodeTag, SignalTag};
467467

468468
// Build <rsp:Code>http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/ctrl_c</rsp:Code>
469-
let code = Tag::from_name(SignalCode).with_value(Text::from(
469+
let code = Tag::from_name(SignalCodeTag).with_value(Text::from(
470470
"http://schemas.microsoft.com/wbem/wsman/1/windows/shell/signal/terminate",
471471
));
472472

473473
// Build <w:Signal CommandId="...">...</w:Signal>
474-
let signal = Tag::from_name(Signal)
474+
let signal = Tag::from_name(SignalTag)
475475
.with_attribute(Attribute::CommandId(id))
476476
.with_value(code)
477477
.with_declaration(Namespace::WsmanShell);
@@ -535,10 +535,10 @@ impl Stream {
535535
}
536536
}
537537

538-
impl<'a> TryFrom<&Tag<'a, Text<'a>, tag_name::Stream>> for Stream {
538+
impl<'a> TryFrom<&Tag<'a, Text<'a>, StreamTag>> for Stream {
539539
type Error = crate::PwshCoreError;
540540

541-
fn try_from(value: &Tag<'a, Text<'a>, tag_name::Stream>) -> Result<Self, Self::Error> {
541+
fn try_from(value: &Tag<'a, Text<'a>, StreamTag>) -> Result<Self, Self::Error> {
542542
let attributes = &value.attributes;
543543
let name = attributes
544544
.iter()
@@ -577,11 +577,11 @@ pub struct CommandState {
577577
pub exit_code: Option<i32>,
578578
}
579579

580-
impl<'a> TryFrom<&Tag<'a, CommandStateValue<'a>, tag_name::CommandState>> for CommandState {
580+
impl<'a> TryFrom<&Tag<'a, CommandStateValue<'a>, CommandStateTag>> for CommandState {
581581
type Error = crate::PwshCoreError;
582582

583583
fn try_from(
584-
value: &Tag<'a, CommandStateValue<'a>, tag_name::CommandState>,
584+
value: &Tag<'a, CommandStateValue<'a>, CommandStateTag>,
585585
) -> Result<Self, Self::Error> {
586586
let command_id = value
587587
.attributes

crates/ironposh-client-core/tests/test_send_roundtrip.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ use ironposh_psrp::{
77
fragmentation::{DefragmentResult, Defragmenter, Fragmenter},
88
};
99
use ironposh_winrm::{
10-
cores::{Attribute, ReceiveResponse, Send, Tag, Text, tag_name::Stream},
11-
rsp::{receive::ReceiveResponseValue, send::SendValue},
10+
cores::{Attribute, StreamTag, Tag, Text},
11+
rsp::{
12+
receive::{ReceiveResponseTag, ReceiveResponseValue},
13+
send::{SendTag, SendValue},
14+
},
1215
soap::{SoapEnvelope, body::SoapBody},
1316
};
1417
use ironposh_xml::builder::Element;
@@ -42,18 +45,18 @@ fn test_send_receive_roundtrip_with_fragmentation() {
4245
.collect();
4346

4447
// 4. Create SendValue with multiple Stream elements (NEW CODE PATH)
45-
let streams: Vec<Tag<Text, Stream>> = base64_fragments
48+
let streams: Vec<Tag<Text, StreamTag>> = base64_fragments
4649
.iter()
4750
.map(|fragment| {
48-
Tag::from_name(Stream)
51+
Tag::from_name(StreamTag)
4952
.with_value(Text::from(fragment.as_str()))
5053
.with_attribute(Attribute::Name("stdin".into()))
5154
})
5255
.collect();
5356

5457
let send_value = SendValue::builder().streams(streams).build();
5558

56-
let send_tag = Tag::from_name(Send)
59+
let send_tag = Tag::from_name(SendTag)
5760
.with_value(send_value)
5861
.with_attribute(Attribute::CommandId(command_id))
5962
.with_declaration(ironposh_winrm::cores::Namespace::WsmanShell);
@@ -80,10 +83,10 @@ fn test_send_receive_roundtrip_with_fragmentation() {
8083

8184
// 6. Simulate receiving the response - create ReceiveResponse with same streams
8285
// (In real flow, server would echo back or client would receive similar structure)
83-
let receive_streams: Vec<Tag<Text, Stream>> = base64_fragments
86+
let receive_streams: Vec<Tag<Text, StreamTag>> = base64_fragments
8487
.iter()
8588
.map(|fragment| {
86-
Tag::from_name(Stream)
89+
Tag::from_name(StreamTag)
8790
.with_value(Text::from(fragment.as_str()))
8891
.with_attribute(Attribute::Name("stdout".into()))
8992
.with_attribute(Attribute::CommandId(command_id))
@@ -95,7 +98,7 @@ fn test_send_receive_roundtrip_with_fragmentation() {
9598
.command_state(None)
9699
.build();
97100

98-
let receive_response_tag = Tag::from_name(ReceiveResponse)
101+
let receive_response_tag = Tag::from_name(ReceiveResponseTag)
99102
.with_value(receive_response_value)
100103
.with_declaration(ironposh_winrm::cores::Namespace::WsmanShell);
101104

@@ -183,7 +186,7 @@ fn create_large_session_capability() -> SessionCapability {
183186
fn test_send_with_no_fragments() {
184187
let send_value = SendValue::builder().streams(vec![]).build();
185188

186-
let send_tag = Tag::from_name(Send)
189+
let send_tag = Tag::from_name(SendTag)
187190
.with_value(send_value)
188191
.with_declaration(ironposh_winrm::cores::Namespace::WsmanShell);
189192

@@ -222,13 +225,13 @@ fn test_send_with_single_fragment() {
222225

223226
let base64_fragment = base64::engine::general_purpose::STANDARD.encode(&fragments[0]);
224227

225-
let stream = Tag::from_name(Stream)
228+
let stream = Tag::from_name(StreamTag)
226229
.with_value(Text::from(base64_fragment.as_str()))
227230
.with_attribute(Attribute::Name("stdin".into()));
228231

229232
let send_value = SendValue::builder().streams(vec![stream]).build();
230233

231-
let send_tag = Tag::from_name(Send)
234+
let send_tag = Tag::from_name(SendTag)
232235
.with_value(send_value)
233236
.with_attribute(Attribute::CommandId(command_id))
234237
.with_declaration(ironposh_winrm::cores::Namespace::WsmanShell);

crates/ironposh-test-support/src/fake_server.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,9 @@ use ironposh_psrp::{
1515
PowerShellRemotingMessage, Size,
1616
};
1717
use ironposh_winrm::{
18-
cores::{
19-
tag_name::{Envelope, Stream},
20-
Attribute, Namespace, ReceiveResponse, Tag, Text,
21-
},
22-
rsp::receive::ReceiveResponseValue,
23-
soap::{body::SoapBody, SoapEnvelope},
18+
cores::{Attribute, Namespace, StreamTag, Tag, Text},
19+
rsp::receive::{ReceiveResponseTag, ReceiveResponseValue},
20+
soap::{body::SoapBody, Envelope, SoapEnvelope},
2421
};
2522
use ironposh_xml::builder::Element;
2623
use uuid::Uuid;
@@ -156,10 +153,10 @@ pub fn receive_response_xml(rpid: Uuid, messages: &[&dyn PsObjectWithType]) -> S
156153
})
157154
.collect();
158155

159-
let streams: Vec<Tag<'_, Text<'_>, Stream>> = base64_fragments
156+
let streams: Vec<Tag<'_, Text<'_>, StreamTag>> = base64_fragments
160157
.iter()
161158
.map(|fragment| {
162-
Tag::from_name(Stream)
159+
Tag::from_name(StreamTag)
163160
.with_value(Text::from(fragment.as_str()))
164161
.with_attribute(Attribute::Name("stdout".into()))
165162
})
@@ -170,7 +167,7 @@ pub fn receive_response_xml(rpid: Uuid, messages: &[&dyn PsObjectWithType]) -> S
170167
.command_state(None)
171168
.build();
172169

173-
let receive_response_tag = Tag::from_name(ReceiveResponse)
170+
let receive_response_tag = Tag::from_name(ReceiveResponseTag)
174171
.with_value(receive_response_value)
175172
.with_declaration(Namespace::WsmanShell);
176173

@@ -180,7 +177,7 @@ pub fn receive_response_xml(rpid: Uuid, messages: &[&dyn PsObjectWithType]) -> S
180177

181178
let envelope = SoapEnvelope::builder().body(body).build();
182179

183-
let soap = Tag::<SoapEnvelope<'_>, Envelope>::new(envelope)
180+
let soap = Envelope::new(envelope)
184181
.with_declaration(Namespace::SoapEnvelope2003)
185182
.with_declaration(Namespace::WsAddressing2004)
186183
.with_declaration(Namespace::DmtfWsmanSchema)

crates/ironposh-winrm/src/cores/tag.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ impl_tag_from!(uuid::Uuid => Tag<'a, WsUuid, N>);
260260
#[cfg(test)]
261261
mod tests {
262262
use super::*;
263-
use crate::cores::{CommandId, CommandResponse};
263+
use crate::cores::CommandResponse;
264264
use ironposh_xml::parser::parse;
265265

266266
const RSP: &str = "http://schemas.microsoft.com/wbem/wsman/1/windows/shell";
@@ -275,9 +275,8 @@ mod tests {
275275
r#"<rsp:CommandResponse xmlns:rsp="{RSP}"><rsp:CommandId>{uuid}</rsp:CommandId></rsp:CommandResponse>"#
276276
);
277277
let doc = parse(&xml).unwrap();
278-
let tag: Tag<'_, Tag<'_, WsUuid, CommandId>, CommandResponse> =
279-
Tag::from_xml(doc.root_element())
280-
.expect("nested CommandResponse/CommandId should parse");
278+
let tag = CommandResponse::from_xml(doc.root_element())
279+
.expect("nested CommandResponse/CommandId should parse");
281280
assert_eq!(tag.value.value.0.to_string().to_uppercase(), uuid);
282281
}
283282
}

0 commit comments

Comments
 (0)