raxb_xmlschema/
reader.rs

1use byteorder::ReadBytesExt;
2use lz4_flex::{block::DecompressError, decompress_size_prepended};
3use std::{collections::HashMap, ops::Range, string::FromUtf8Error};
4use thiserror::Error;
5
6use crate::cnst::{Order, MAGIC_BYTE};
7pub type SchemaBundleIndex = HashMap<String, Range<usize>>;
8
9#[derive(Error, Debug)]
10pub enum ReaderError {
11    #[error(transparent)]
12    Io(#[from] std::io::Error),
13    #[error(transparent)]
14    Utf8Error(#[from] FromUtf8Error),
15    #[error(transparent)]
16    UrlParse(#[from] url::ParseError),
17    #[cfg(feature = "writer")]
18    #[error(transparent)]
19    Reqwest(#[from] reqwest::Error),
20    #[error(transparent)]
21    DecompressError(#[from] DecompressError),
22    #[error("no entrypoint")]
23    NoEntrypoint,
24    #[error("invalid file format")]
25    InvalidFormat,
26    #[error("invalid file header")]
27    InvalidHead,
28}
29
30pub type ReaderResult<T> = Result<T, ReaderError>;
31
32fn read_string(rdr: &mut std::io::Cursor<Vec<u8>>) -> ReaderResult<String> {
33    let s_length = rdr.read_u32::<Order>()? as usize;
34    let mut buf = Vec::with_capacity(s_length);
35    for _ in 0..s_length {
36        buf.push(rdr.read_u8()?);
37    }
38    Ok(String::from_utf8(buf)?)
39}
40
41pub trait XmlSchemaResolver {
42    fn entrypoint(&self) -> &[u8];
43    fn resolve(&self, s: &str) -> Option<&[u8]>;
44}
45
46#[derive(Default, Debug)]
47pub struct SchemaBundleHeader {
48    entrypoint: Range<usize>,
49    name: String,
50    target_ns: String,
51    index: SchemaBundleIndex,
52}
53
54#[derive(Default)]
55pub struct SchemaBundle {
56    pub header: SchemaBundleHeader,
57    buffer: Vec<u8>,
58}
59
60impl SchemaBundle {
61    pub fn from_slice(b: &[u8]) -> ReaderResult<Self> {
62        let mut index = SchemaBundleIndex::default();
63        let mut rdr = std::io::Cursor::new(decompress_size_prepended(b)?);
64        let magic_byte = rdr.read_u32::<Order>()?;
65        if magic_byte != MAGIC_BYTE {
66            return Err(ReaderError::InvalidFormat);
67        }
68        let head_size = rdr.read_u64::<Order>()? as usize;
69        let name = read_string(&mut rdr)?;
70        let target_ns = read_string(&mut rdr)?;
71        let mut pos = 4 + 8 + 4 + name.len() + 4 + target_ns.len();
72        let mut entrypoint = 0..0usize;
73        loop {
74            match pos {
75                p if p == head_size => {
76                    break;
77                }
78                p if p > head_size => {
79                    return Err(ReaderError::InvalidHead);
80                }
81                _ => {
82                    let is_entrypoint = rdr.read_u8()? == 1;
83                    let start = rdr.read_u64::<Order>()? as usize;
84                    let end = rdr.read_u64::<Order>()? as usize;
85                    if is_entrypoint {
86                        entrypoint = start..end;
87                    }
88                    let name = read_string(&mut rdr)?;
89                    pos += 1 + 8 + 8 + 4 + name.len();
90                    index.insert(name, start..end);
91                }
92            }
93        }
94        let buffer = rdr.into_inner().split_off(head_size);
95        Ok(SchemaBundle {
96            header: SchemaBundleHeader {
97                entrypoint,
98                name,
99                target_ns,
100                index,
101            },
102            buffer,
103        })
104    }
105
106    pub fn name(&self) -> &str {
107        &self.header.name
108    }
109
110    pub fn target_ns(&self) -> &str {
111        &self.header.target_ns
112    }
113}
114
115impl XmlSchemaResolver for SchemaBundle {
116    fn entrypoint(&self) -> &[u8] {
117        &self.buffer[self.header.entrypoint.start..self.header.entrypoint.end]
118    }
119    fn resolve(&self, name: &str) -> Option<&[u8]> {
120        let result = self
121            .header
122            .index
123            .get(name)
124            .map(|e| &self.buffer[e.start..e.end]);
125        if result.is_none() {
126            eprintln!(
127                "'{name}' not found, available schemas are -> {:#?}",
128                self.header.index
129            );
130        }
131        result
132    }
133}