1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use azure::core::errors::AzureError;
use azure::storage::blob::BlobBlockType;
use azure::storage::blob::BlobBlockWithSize;
use base64;
use serde_xml_rs::deserialize;
use std::borrow::Borrow;

#[derive(Debug, Deserialize)]
struct Name {
    #[serde(rename = "$value")]
    pub value: String,
}

#[derive(Debug, Deserialize)]
struct Size {
    #[serde(rename = "$value")]
    pub value: u64,
}

#[derive(Debug, Deserialize)]
struct InnerBlock {
    #[serde(rename = "Name")]
    pub name: Name,
    #[serde(rename = "Size")]
    pub size: Size,
}

#[derive(Debug, Deserialize)]
struct OuterBlock {
    #[serde(rename = "Block")]
    pub block: Option<Vec<InnerBlock>>,
}

#[derive(Debug, Deserialize)]
struct BlockList {
    #[serde(rename = "CommittedBlocks")]
    pub committed_blocks: OuterBlock,
    #[serde(rename = "UncommittedBlocks")]
    pub uncommitted_blocks: OuterBlock,
}

#[derive(Default, Debug, Clone, PartialEq)]
pub struct BlockWithSizeList<T>
where
    T: Borrow<[u8]>,
{
    pub blocks: Vec<BlobBlockWithSize<T>>,
}

impl BlockWithSizeList<Vec<u8>> {
    pub fn try_from(xml: &str) -> Result<BlockWithSizeList<Vec<u8>>, AzureError> {
        let bl: BlockList = deserialize(xml.as_bytes())?;
        debug!("bl == {:?}", bl);

        let mut lbs = BlockWithSizeList { blocks: Vec::new() };

        if let Some(b) = bl.committed_blocks.block {
            for b_val in b {
                lbs.blocks.push(BlobBlockWithSize {
                    block_list_type: BlobBlockType::Committed(base64::decode(&b_val.name.value)?.to_owned()),
                    size_in_bytes: b_val.size.value,
                });
            }
        }

        if let Some(b) = bl.uncommitted_blocks.block {
            for b_val in b {
                lbs.blocks.push(BlobBlockWithSize {
                    block_list_type: BlobBlockType::Uncommitted(base64::decode(&b_val.name.value)?.to_owned()),
                    size_in_bytes: b_val.size.value,
                });
            }
        }

        Ok(lbs)
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn try_parse() {
        let range = "<?xml version=\"1.0\" encoding=\"utf-8\"?>  
            <BlockList>  
              <CommittedBlocks>  
                   <Block>  
                       <Name>YmFzZTY0LWVuY29kZWQtYmxvY2staWQ=</Name>  
                       <Size>200</Size>  
                    </Block>  
               </CommittedBlocks>  
               <UncommittedBlocks>  
                    <Block>  
                        <Name>YmFzZTY0LWVuY29kZWQtYmxvY2staWQtbnVtYmVyMg==</Name>  
                        <Size>4096</Size>  
                    </Block>  
               </UncommittedBlocks>  
            </BlockList>  ";

        let bl = BlockWithSizeList::try_from(range).unwrap();
        assert!(bl.blocks.len() == 2);
        assert!(bl.blocks[0].size_in_bytes == 200);
        assert!(bl.blocks[1].size_in_bytes == 4096);

        assert!(bl.blocks[0].block_list_type == BlobBlockType::Committed(Vec::from(b"base64-encoded-block-id" as &[u8])));
        let b2 = BlobBlockType::Uncommitted(Vec::from(b"base64-encoded-block-id-number2" as &[u8]));
        assert!(
            bl.blocks[1].block_list_type == b2,
            "bl.blocks[1].block_list_type == {:?}, b2 == {:?}",
            bl.blocks[1].block_list_type,
            b2
        );
    }

    #[test]
    fn try_parse2() {
        let range = "<?xml version=\"1.0\" encoding=\"utf-8\"?><BlockList><CommittedBlocks /><UncommittedBlocks><Block><Name>YmxvY2sx</Name><Size>62</Size></Block><Block><Name>YmxvY2sy</Name><Size>62</Size></Block><Block><Name>YmxvY2sz</Name><Size>62</Size></Block></UncommittedBlocks></BlockList>";

        let bl = BlockWithSizeList::try_from(range).unwrap();
        assert!(bl.blocks.len() == 3);
        assert!(bl.blocks[0].size_in_bytes == 62);
        assert!(bl.blocks[1].size_in_bytes == 62);
        assert!(bl.blocks[2].size_in_bytes == 62);
    }
}