use serde_json::Value;
use zerodds_ast::Ast;
use zerodds_ast_layout::{Encoding, Endianness};
use zerodds_wire_codec::contract::Consumed;
use zerodds_wire_codec::value::ProbeRequest;
pub use zerodds_wire_codec::encode::ProbeError;
#[inline]
pub fn encode(ast: &Ast, request: &ProbeRequest, values: &Value) -> Result<Vec<u8>, ProbeError> {
zerodds_wire_codec::encode::encode(ast, request, values)
}
#[inline]
pub fn encode_recording(
ast: &Ast,
request: &ProbeRequest,
values: &Value,
seen: &mut Consumed,
) -> Result<Vec<u8>, ProbeError> {
zerodds_wire_codec::encode::encode_recording(ast, request, values, seen)
}
#[inline]
pub fn encode_with_padding(
ast: &Ast,
request: &ProbeRequest,
values: &Value,
) -> Result<(Vec<u8>, Vec<bool>), ProbeError> {
zerodds_wire_codec::encode::encode_with_padding(ast, request, values)
}
pub fn model_from_idl(source: &str, path: &str) -> Result<Ast, ProbeError> {
model_from_idl_in_dialect(source, path, zerodds_ast_layout::Dialect::default())
}
pub fn model_from_idl_in_dialect(
source: &str,
path: &str,
dialect: zerodds_ast_layout::Dialect,
) -> Result<Ast, ProbeError> {
let options = zerodds_idl_lower::Options {
hashed_id_advances_the_counter: if dialect.hashed_id_advances_the_counter() {
zerodds_idl_lower::HashedIdCounter::Advances
} else {
zerodds_idl_lower::HashedIdCounter::Skipped
},
hashed_name_spelling: if dialect.hashid_reads_the_escaped_spelling() {
zerodds_idl_lower::HashedNameSpelling::AsWritten
} else {
zerodds_idl_lower::HashedNameSpelling::Stripped
},
name_of_the_enclosing_scope: if dialect.admits_the_name_of_the_enclosing_scope() {
zerodds_idl_lower::NameOfTheEnclosingScope::Admitted
} else {
zerodds_idl_lower::NameOfTheEnclosingScope::Refused
},
..zerodds_idl_lower::Options::default()
};
model_from_idl_with(source, path, options)
}
pub fn model_from_idl_with(
source: &str,
path: &str,
options: zerodds_idl_lower::Options,
) -> Result<Ast, ProbeError> {
let composed = zerodds_idl_compose::compose_source_with(
path,
source,
&zerodds_idl_compose::PathLoader::bare(),
zerodds_idl_compose::ComposeOptions {
default_extensibility: options.default_extensibility,
default_nested: options.default_nested,
hashed_id_advances_the_counter: options.hashed_id_advances_the_counter,
hashed_name_spelling: options.hashed_name_spelling,
name_of_the_enclosing_scope: options.name_of_the_enclosing_scope,
defines: Vec::new(),
key_on_union_discriminator: zerodds_idl_compose::KeyOnUnionDiscriminator::Carried,
dialect: zerodds_idl_compose::ComposeOptions::default().dialect,
},
);
if !composed.diagnostics.is_empty() {
return Err(ProbeError::NoLayout {
detail: composed
.diagnostics
.iter()
.map(ToString::to_string)
.collect::<Vec<_>>()
.join("; "),
});
}
Ok(composed.ast)
}
#[must_use]
pub fn hex(bytes: &[u8]) -> String {
let mut out = String::with_capacity(bytes.len() * 2);
for byte in bytes {
out.push_str(&format!("{byte:02x}"));
}
out
}
pub fn probe(
idl: &str,
type_name: &str,
values: &Value,
encoding: Encoding,
endianness: Endianness,
) -> Result<Vec<u8>, ProbeError> {
let ast = model_from_idl(idl, "probe.idl")?;
encode(
&ast,
&ProbeRequest::new(type_name, encoding, endianness),
values,
)
}