use byteordered::Endianness;
use serde::{Deserialize, Deserializer, Serialize};
use serde_yaml::Value;
use std::collections::BTreeMap;
use uuid::Uuid;
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum NativeByteOrder {
#[serde(alias = "little")]
#[serde(alias = "le")]
LittleEndian,
#[serde(alias = "big")]
#[serde(alias = "be")]
BigEndian,
}
impl From<NativeByteOrder> for Endianness {
fn from(value: NativeByteOrder) -> Self {
match value {
NativeByteOrder::LittleEndian => Endianness::Little,
NativeByteOrder::BigEndian => Endianness::Big,
}
}
}
#[derive(
Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize, Serialize,
)]
#[serde(rename_all = "kebab-case")]
pub enum PreferredDisplayBase {
#[serde(alias = "bin")]
Binary,
#[serde(alias = "oct")]
Octal,
#[default]
#[serde(alias = "dec")]
Decimal,
#[serde(alias = "hex")]
Hexadecimal,
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct IntegerFieldType {
pub size: usize,
#[serde(default = "default_alignment_bits")]
pub alignment: usize,
#[serde(default)]
pub preferred_display_base: PreferredDisplayBase,
}
impl FieldType for IntegerFieldType {
fn size(&self) -> usize {
self.size
}
fn alignment(&self) -> usize {
self.alignment
}
fn preferred_display_base(&self) -> Option<PreferredDisplayBase> {
Some(self.preferred_display_base)
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[serde(tag = "class", rename = "unsigned-integer")]
pub struct UnsignedIntegerFieldType {
#[serde(flatten)]
pub field_type: IntegerFieldType,
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[serde(tag = "class", rename = "signed-integer")]
pub struct SignedIntegerFieldType {
#[serde(flatten)]
pub field_type: IntegerFieldType,
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct RealFieldType {
pub size: usize,
#[serde(default = "default_alignment_bits")]
pub alignment: usize,
}
impl FieldType for RealFieldType {
fn size(&self) -> usize {
self.size
}
fn alignment(&self) -> usize {
self.alignment
}
fn preferred_display_base(&self) -> Option<PreferredDisplayBase> {
None
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[serde(untagged)]
pub enum EnumerationFieldTypeMappingSequence {
InclusiveRange(i64, i64),
Value(i64),
}
impl EnumerationFieldTypeMappingSequence {
pub fn contains(&self, value: i64) -> bool {
match self {
Self::InclusiveRange(min, max) => (value >= *min) && (value <= *max),
Self::Value(v) => *v == value,
}
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct EnumerationFieldType {
pub size: usize,
#[serde(default = "default_alignment_bits")]
pub alignment: usize,
#[serde(default)]
pub preferred_display_base: PreferredDisplayBase,
pub mappings: BTreeMap<String, Vec<EnumerationFieldTypeMappingSequence>>,
}
impl FieldType for EnumerationFieldType {
fn size(&self) -> usize {
self.size
}
fn alignment(&self) -> usize {
self.alignment
}
fn preferred_display_base(&self) -> Option<PreferredDisplayBase> {
Some(self.preferred_display_base)
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[serde(tag = "class")]
pub enum PrimitiveFieldType {
UnsignedInteger(IntegerFieldType),
SignedInteger(IntegerFieldType),
String,
Real(RealFieldType),
UnsignedEnumeration(EnumerationFieldType),
SignedEnumeration(EnumerationFieldType),
}
impl FieldType for PrimitiveFieldType {
fn size(&self) -> usize {
match self {
Self::UnsignedInteger(t) | Self::SignedInteger(t) => t.size,
Self::UnsignedEnumeration(t) | Self::SignedEnumeration(t) => t.size,
Self::Real(t) => t.size,
Self::String => 8,
}
}
fn alignment(&self) -> usize {
match self {
Self::UnsignedInteger(t) | Self::SignedInteger(t) => t.alignment,
Self::UnsignedEnumeration(t) | Self::SignedEnumeration(t) => t.alignment,
Self::Real(t) => t.alignment,
Self::String => 8,
}
}
fn preferred_display_base(&self) -> Option<PreferredDisplayBase> {
Some(match self {
Self::UnsignedInteger(t) | Self::SignedInteger(t) => t.preferred_display_base,
Self::UnsignedEnumeration(t) | Self::SignedEnumeration(t) => t.preferred_display_base,
Self::String | Self::Real(_) => return None,
})
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct StaticArrayFieldType {
pub length: usize,
pub element_field_type: PrimitiveFieldType,
}
impl FieldType for StaticArrayFieldType {
fn size(&self) -> usize {
self.element_field_type.size()
}
fn alignment(&self) -> usize {
self.element_field_type.alignment()
}
fn preferred_display_base(&self) -> Option<PreferredDisplayBase> {
self.element_field_type.preferred_display_base()
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct DynamicArrayFieldType {
pub element_field_type: PrimitiveFieldType,
}
impl FieldType for DynamicArrayFieldType {
fn size(&self) -> usize {
self.element_field_type.size()
}
fn alignment(&self) -> usize {
self.element_field_type.alignment()
}
fn preferred_display_base(&self) -> Option<PreferredDisplayBase> {
self.element_field_type.preferred_display_base()
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[serde(tag = "class")]
pub enum StructureMemberFieldType {
UnsignedInteger(IntegerFieldType),
SignedInteger(IntegerFieldType),
String,
Real(RealFieldType),
UnsignedEnumeration(EnumerationFieldType),
SignedEnumeration(EnumerationFieldType),
StaticArray(StaticArrayFieldType),
DynamicArray(DynamicArrayFieldType),
}
impl FieldType for StructureMemberFieldType {
fn size(&self) -> usize {
match self {
Self::UnsignedInteger(t) | Self::SignedInteger(t) => t.size,
Self::UnsignedEnumeration(t) | Self::SignedEnumeration(t) => t.size,
Self::Real(t) => t.size,
Self::String => 8, Self::StaticArray(t) => t.size(),
Self::DynamicArray(t) => t.size(),
}
}
fn alignment(&self) -> usize {
match self {
Self::UnsignedInteger(t) | Self::SignedInteger(t) => t.alignment,
Self::UnsignedEnumeration(t) | Self::SignedEnumeration(t) => t.alignment,
Self::Real(t) => t.alignment,
Self::String => 8,
Self::StaticArray(t) => t.alignment(),
Self::DynamicArray(t) => t.alignment(),
}
}
fn preferred_display_base(&self) -> Option<PreferredDisplayBase> {
Some(match self {
Self::UnsignedInteger(t) | Self::SignedInteger(t) => t.preferred_display_base,
Self::UnsignedEnumeration(t) | Self::SignedEnumeration(t) => t.preferred_display_base,
Self::StaticArray(t) => return t.preferred_display_base(),
Self::DynamicArray(t) => return t.preferred_display_base(),
Self::String | Self::Real(_) => return None,
})
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct StructureFieldTypeMember {
pub field_type: StructureMemberFieldType,
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[serde(tag = "class", rename = "structure")]
pub struct StructureFieldType {
#[serde(default = "default_struct_min_alignment")]
pub minimum_alignment: usize,
pub members: Vec<BTreeMap<String, StructureFieldTypeMember>>,
}
impl StructureFieldType {
pub(crate) fn alignment(&self) -> usize {
self.members
.iter()
.flat_map(|m| m.values())
.map(|ftm| ftm.field_type.alignment())
.max()
.unwrap_or_else(default_alignment_bits)
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[serde(untagged)]
pub enum FeaturesUnsignedIntegerFieldType {
#[serde(deserialize_with = "de_false")]
False(bool),
UnsignedInteger(UnsignedIntegerFieldType),
}
impl FeaturesUnsignedIntegerFieldType {
pub(crate) fn alignment(&self) -> usize {
match self {
Self::False(_) => 0,
Self::UnsignedInteger(ft) => ft.field_type.alignment,
}
}
pub(crate) fn as_ft(&self) -> Option<&UnsignedIntegerFieldType> {
match self {
FeaturesUnsignedIntegerFieldType::False(_) => None,
FeaturesUnsignedIntegerFieldType::UnsignedInteger(ft) => Some(ft),
}
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct ClockTypeOffset {
pub seconds: i64,
pub cycles: u64,
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct ClockType {
pub frequency: u64,
pub offset: Option<ClockTypeOffset>,
pub origin_is_unix_epoch: bool,
pub precision: u64,
pub uuid: Option<Uuid>,
pub description: Option<String>,
#[serde(alias = "$c-type")]
pub c_type: String,
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct EventRecordType {
pub log_level: Option<i32>,
pub specific_context_field_type: Option<StructureFieldType>,
pub payload_field_type: Option<StructureFieldType>,
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct DataStreamTypePacketFeatures {
pub total_size_field_type: UnsignedIntegerFieldType,
pub content_size_field_type: UnsignedIntegerFieldType,
pub beginning_timestamp_field_type: FeaturesUnsignedIntegerFieldType,
pub end_timestamp_field_type: FeaturesUnsignedIntegerFieldType,
pub discarded_event_records_counter_snapshot_field_type: FeaturesUnsignedIntegerFieldType,
pub sequence_number_field_type: FeaturesUnsignedIntegerFieldType,
}
impl DataStreamTypePacketFeatures {
pub(crate) fn alignment(&self) -> usize {
let aligns = [
self.total_size_field_type.field_type.alignment,
self.content_size_field_type.field_type.alignment,
self.beginning_timestamp_field_type.alignment(),
self.end_timestamp_field_type.alignment(),
self.discarded_event_records_counter_snapshot_field_type
.alignment(),
self.sequence_number_field_type.alignment(),
];
*aligns.iter().max().unwrap() }
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct DataStreamTypeEventRecordFeatures {
pub type_id_field_type: UnsignedIntegerFieldType,
pub timestamp_field_type: UnsignedIntegerFieldType,
}
impl DataStreamTypeEventRecordFeatures {
pub(crate) fn alignment(&self) -> usize {
let aligns = [
self.type_id_field_type.field_type.alignment,
self.timestamp_field_type.field_type.alignment,
];
*aligns.iter().max().unwrap() }
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct DataStreamTypeFeatures {
pub packet: DataStreamTypePacketFeatures,
pub event_record: DataStreamTypeEventRecordFeatures,
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct PacketContextExtraMembers(pub Vec<BTreeMap<String, StructureFieldTypeMember>>);
impl PacketContextExtraMembers {
pub(crate) fn alignment(&self) -> usize {
self.0
.iter()
.flat_map(|m| m.values())
.map(|ftm| ftm.field_type.alignment())
.max()
.unwrap_or_else(default_alignment_bits)
}
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct DataStreamType {
#[serde(alias = "$is-default")]
pub is_default: bool,
#[serde(alias = "$default-clock-type-name")]
pub default_clock_type_name: Option<String>,
#[serde(alias = "$features")]
pub features: DataStreamTypeFeatures,
#[serde(default)]
pub packet_context_field_type_extra_members: PacketContextExtraMembers,
pub event_record_common_context_field_type: Option<StructureFieldType>,
pub event_record_types: BTreeMap<String, EventRecordType>,
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct TraceTypeFeatures {
pub magic_field_type: FeaturesUnsignedIntegerFieldType,
pub uuid_field_type: bool,
pub data_stream_type_id_field_type: UnsignedIntegerFieldType,
}
impl TraceTypeFeatures {
pub(crate) fn alignment(&self) -> usize {
let aligns = [
self.magic_field_type.alignment(),
self.uuid_field_type
.then(default_alignment_bits)
.unwrap_or(0),
self.data_stream_type_id_field_type.field_type.alignment,
];
*aligns.iter().max().unwrap() }
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct TraceType {
pub native_byte_order: NativeByteOrder,
pub uuid: Option<Uuid>,
#[serde(alias = "$features")]
pub features: TraceTypeFeatures,
pub clock_types: BTreeMap<String, ClockType>,
pub data_stream_types: BTreeMap<String, DataStreamType>,
}
#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Trace {
pub environment: BTreeMap<String, Value>,
#[serde(alias = "type")]
pub typ: TraceType,
}
#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct CodeGenerationOptions {
pub file_name: String,
pub identifier: String,
}
impl Default for CodeGenerationOptions {
fn default() -> Self {
Self {
file_name: "barectf".to_owned(),
identifier: "barectf_".to_owned(),
}
}
}
#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug, Default, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct HeaderGenerationOptions {
pub identifier_prefix_definition: bool,
pub default_data_stream_type_name_definition: bool,
}
#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug, Default, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Options {
pub code_generation: CodeGenerationOptions,
pub header: HeaderGenerationOptions,
}
#[derive(Clone, Eq, PartialEq, PartialOrd, Hash, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Config {
#[serde(default)]
pub options: Options,
pub trace: Trace,
}
pub(crate) trait FieldType {
fn size(&self) -> usize;
fn alignment(&self) -> usize;
fn preferred_display_base(&self) -> Option<PreferredDisplayBase>;
}
const fn default_alignment_bits() -> usize {
8
}
const fn default_struct_min_alignment() -> usize {
1
}
fn de_false<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: Deserializer<'de>,
{
if bool::deserialize(deserializer)? {
Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Bool(true),
&"the `false` boolean",
))
} else {
Ok(false)
}
}