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}