Files
MyFSIO/rust/myfsio-engine/crates/myfsio-xml/src/request.rs

164 lines
5.4 KiB
Rust

use quick_xml::events::Event;
use quick_xml::Reader;
#[derive(Debug, Default)]
pub struct DeleteObjectsRequest {
pub objects: Vec<ObjectIdentifier>,
pub quiet: bool,
}
#[derive(Debug)]
pub struct ObjectIdentifier {
pub key: String,
pub version_id: Option<String>,
}
#[derive(Debug, Default)]
pub struct CompleteMultipartUpload {
pub parts: Vec<CompletedPart>,
}
#[derive(Debug)]
pub struct CompletedPart {
pub part_number: u32,
pub etag: String,
}
pub fn parse_complete_multipart_upload(xml: &str) -> Result<CompleteMultipartUpload, String> {
let mut reader = Reader::from_str(xml);
let mut result = CompleteMultipartUpload::default();
let mut buf = Vec::new();
let mut current_tag = String::new();
let mut part_number: Option<u32> = None;
let mut etag: Option<String> = None;
let mut in_part = false;
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) => {
let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
current_tag = name.clone();
if name == "Part" {
in_part = true;
part_number = None;
etag = None;
}
}
Ok(Event::Text(ref e)) => {
if in_part {
let text = e.unescape().map_err(|e| e.to_string())?.to_string();
match current_tag.as_str() {
"PartNumber" => {
part_number = Some(
text.trim()
.parse()
.map_err(|e: std::num::ParseIntError| e.to_string())?,
);
}
"ETag" => {
etag = Some(text.trim().trim_matches('"').to_string());
}
_ => {}
}
}
}
Ok(Event::End(ref e)) => {
let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
if name == "Part" && in_part {
if let (Some(pn), Some(et)) = (part_number.take(), etag.take()) {
result.parts.push(CompletedPart {
part_number: pn,
etag: et,
});
}
in_part = false;
}
}
Ok(Event::Eof) => break,
Err(e) => return Err(format!("XML parse error: {}", e)),
_ => {}
}
buf.clear();
}
result.parts.sort_by_key(|p| p.part_number);
Ok(result)
}
pub fn parse_delete_objects(xml: &str) -> Result<DeleteObjectsRequest, String> {
let mut reader = Reader::from_str(xml);
let mut result = DeleteObjectsRequest::default();
let mut buf = Vec::new();
let mut current_tag = String::new();
let mut current_key: Option<String> = None;
let mut current_version_id: Option<String> = None;
let mut in_object = false;
loop {
match reader.read_event_into(&mut buf) {
Ok(Event::Start(ref e)) => {
let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
current_tag = name.clone();
if name == "Object" {
in_object = true;
current_key = None;
current_version_id = None;
}
}
Ok(Event::Text(ref e)) => {
let text = e.unescape().map_err(|e| e.to_string())?.to_string();
match current_tag.as_str() {
"Key" if in_object => {
current_key = Some(text.trim().to_string());
}
"VersionId" if in_object => {
current_version_id = Some(text.trim().to_string());
}
"Quiet" => {
result.quiet = text.trim() == "true";
}
_ => {}
}
}
Ok(Event::End(ref e)) => {
let name = String::from_utf8_lossy(e.name().as_ref()).to_string();
if name == "Object" && in_object {
if let Some(key) = current_key.take() {
result.objects.push(ObjectIdentifier {
key,
version_id: current_version_id.take(),
});
}
in_object = false;
}
}
Ok(Event::Eof) => break,
Err(e) => return Err(format!("XML parse error: {}", e)),
_ => {}
}
buf.clear();
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_complete_multipart() {
let xml = r#"<CompleteMultipartUpload>
<Part><PartNumber>2</PartNumber><ETag>"etag2"</ETag></Part>
<Part><PartNumber>1</PartNumber><ETag>"etag1"</ETag></Part>
</CompleteMultipartUpload>"#;
let result = parse_complete_multipart_upload(xml).unwrap();
assert_eq!(result.parts.len(), 2);
assert_eq!(result.parts[0].part_number, 1);
assert_eq!(result.parts[0].etag, "etag1");
assert_eq!(result.parts[1].part_number, 2);
assert_eq!(result.parts[1].etag, "etag2");
}
}