use bitflags::bitflags;
use serde::ser::{Serialize, SerializeMap, SerializeSeq};
use serde_derive::Serialize;
use super::profile::StringHandle;
use super::timestamp::Timestamp;
use crate::{CategoryHandle, Profile};
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct MarkerHandle(pub(crate) usize);
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct MarkerTypeHandle(pub(crate) usize);
#[derive(Debug, Clone)]
pub enum MarkerTiming {
Instant(Timestamp),
Interval(Timestamp, Timestamp),
IntervalStart(Timestamp),
IntervalEnd(Timestamp),
}
pub trait Marker {
fn marker_type(&self, profile: &mut Profile) -> MarkerTypeHandle;
fn name(&self, profile: &mut Profile) -> StringHandle;
fn category(&self, profile: &mut Profile) -> CategoryHandle;
fn string_field_value(&self, field_index: u32) -> StringHandle;
fn number_field_value(&self, field_index: u32) -> f64;
}
pub trait StaticSchemaMarker {
const UNIQUE_MARKER_TYPE_NAME: &'static str;
const DESCRIPTION: Option<&'static str> = None;
const LOCATIONS: MarkerLocations =
MarkerLocations::MARKER_CHART.union(MarkerLocations::MARKER_TABLE);
const CHART_LABEL: Option<&'static str> = None;
const TOOLTIP_LABEL: Option<&'static str> = None;
const TABLE_LABEL: Option<&'static str> = None;
const FIELDS: &'static [StaticSchemaMarkerField];
const GRAPHS: &'static [StaticSchemaMarkerGraph] = &[];
fn name(&self, profile: &mut Profile) -> StringHandle;
fn category(&self, profile: &mut Profile) -> CategoryHandle;
fn string_field_value(&self, field_index: u32) -> StringHandle;
fn number_field_value(&self, field_index: u32) -> f64;
}
impl<T: StaticSchemaMarker> Marker for T {
fn marker_type(&self, profile: &mut Profile) -> MarkerTypeHandle {
profile.static_schema_marker_type::<Self>()
}
fn name(&self, profile: &mut Profile) -> StringHandle {
<T as StaticSchemaMarker>::name(self, profile)
}
fn category(&self, profile: &mut Profile) -> CategoryHandle {
<T as StaticSchemaMarker>::category(self, profile)
}
fn string_field_value(&self, field_index: u32) -> StringHandle {
<T as StaticSchemaMarker>::string_field_value(self, field_index)
}
fn number_field_value(&self, field_index: u32) -> f64 {
<T as StaticSchemaMarker>::number_field_value(self, field_index)
}
}
#[derive(Debug, Clone)]
pub struct RuntimeSchemaMarkerSchema {
pub type_name: String,
pub description: Option<String>,
pub locations: MarkerLocations,
pub chart_label: Option<String>,
pub tooltip_label: Option<String>,
pub table_label: Option<String>,
pub fields: Vec<RuntimeSchemaMarkerField>,
pub graphs: Vec<RuntimeSchemaMarkerGraph>,
}
bitflags! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct MarkerLocations: u32 {
const MARKER_CHART = 1 << 0;
const MARKER_TABLE = 1 << 1;
const TIMELINE_OVERVIEW = 1 << 2;
const TIMELINE_MEMORY = 1 << 3;
const TIMELINE_IPC = 1 << 4;
const TIMELINE_FILEIO = 1 << 5;
}
}
pub struct StaticSchemaMarkerField {
pub key: &'static str,
pub label: &'static str,
pub format: MarkerFieldFormat,
pub flags: MarkerFieldFlags,
}
#[derive(Debug, Clone)]
pub struct RuntimeSchemaMarkerField {
pub key: String,
pub label: String,
pub format: MarkerFieldFormat,
pub flags: MarkerFieldFlags,
}
impl From<&StaticSchemaMarkerField> for RuntimeSchemaMarkerField {
fn from(schema: &StaticSchemaMarkerField) -> Self {
Self {
key: schema.key.into(),
label: schema.label.into(),
format: schema.format.clone(),
flags: schema.flags,
}
}
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum MarkerFieldFormat {
Url,
FilePath,
SanitizedString,
#[serde(rename = "unique-string")]
String,
Duration,
Time,
Seconds,
Milliseconds,
Microseconds,
Nanoseconds,
Bytes,
Percentage,
Integer,
Decimal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MarkerFieldFormatKind {
String,
Number,
}
impl MarkerFieldFormat {
pub fn kind(&self) -> MarkerFieldFormatKind {
match self {
Self::Url | Self::FilePath | Self::SanitizedString | Self::String => {
MarkerFieldFormatKind::String
}
Self::Duration
| Self::Time
| Self::Seconds
| Self::Milliseconds
| Self::Microseconds
| Self::Nanoseconds
| Self::Bytes
| Self::Percentage
| Self::Integer
| Self::Decimal => MarkerFieldFormatKind::Number,
}
}
}
bitflags! {
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
pub struct MarkerFieldFlags: u32 {
const SEARCHABLE = 0b00000001;
}
}
pub struct StaticSchemaMarkerGraph {
pub key: &'static str,
pub graph_type: MarkerGraphType,
pub color: Option<GraphColor>,
}
#[derive(Clone, Debug, Serialize)]
pub struct RuntimeSchemaMarkerGraph {
pub key: String,
#[serde(rename = "type")]
pub graph_type: MarkerGraphType,
#[serde(skip_serializing_if = "Option::is_none")]
pub color: Option<GraphColor>,
}
impl From<&StaticSchemaMarkerGraph> for RuntimeSchemaMarkerGraph {
fn from(schema: &StaticSchemaMarkerGraph) -> Self {
Self {
key: schema.key.into(),
graph_type: schema.graph_type,
color: schema.color,
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum MarkerGraphType {
Bar,
Line,
LineFilled,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum GraphColor {
Blue,
Green,
Grey,
Ink,
Magenta,
Orange,
Purple,
Red,
Teal,
Yellow,
}
#[derive(Debug, Clone)]
pub struct InternalMarkerSchema {
type_name: String,
locations: MarkerLocations,
chart_label: Option<String>,
tooltip_label: Option<String>,
table_label: Option<String>,
fields: Vec<RuntimeSchemaMarkerField>,
graphs: Vec<RuntimeSchemaMarkerGraph>,
string_field_count: usize,
number_field_count: usize,
description: Option<String>,
}
impl From<RuntimeSchemaMarkerSchema> for InternalMarkerSchema {
fn from(schema: RuntimeSchemaMarkerSchema) -> Self {
Self::from_runtime_schema(schema)
}
}
impl InternalMarkerSchema {
pub fn from_runtime_schema(schema: RuntimeSchemaMarkerSchema) -> Self {
let string_field_count = schema
.fields
.iter()
.filter(|f| f.format.kind() == MarkerFieldFormatKind::String)
.count();
let number_field_count = schema
.fields
.iter()
.filter(|f| f.format.kind() == MarkerFieldFormatKind::Number)
.count();
Self {
type_name: schema.type_name,
locations: schema.locations,
chart_label: schema.chart_label,
tooltip_label: schema.tooltip_label,
table_label: schema.table_label,
fields: schema.fields,
graphs: schema.graphs,
string_field_count,
number_field_count,
description: schema.description,
}
}
pub fn from_static_schema<T: StaticSchemaMarker>() -> Self {
let string_field_count = T::FIELDS
.iter()
.filter(|f| f.format.kind() == MarkerFieldFormatKind::String)
.count();
let number_field_count = T::FIELDS
.iter()
.filter(|f| f.format.kind() == MarkerFieldFormatKind::Number)
.count();
Self {
type_name: T::UNIQUE_MARKER_TYPE_NAME.into(),
locations: T::LOCATIONS,
chart_label: T::CHART_LABEL.map(Into::into),
tooltip_label: T::TOOLTIP_LABEL.map(Into::into),
table_label: T::TABLE_LABEL.map(Into::into),
fields: T::FIELDS.iter().map(Into::into).collect(),
string_field_count,
number_field_count,
description: T::DESCRIPTION.map(Into::into),
graphs: T::GRAPHS.iter().map(Into::into).collect(),
}
}
pub fn type_name(&self) -> &str {
&self.type_name
}
pub fn fields(&self) -> &[RuntimeSchemaMarkerField] {
&self.fields
}
pub fn string_field_count(&self) -> usize {
self.string_field_count
}
pub fn number_field_count(&self) -> usize {
self.number_field_count
}
fn serialize_self<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("name", &self.type_name)?;
map.serialize_entry("display", &SerializableSchemaDisplay(self.locations))?;
if let Some(label) = &self.chart_label {
map.serialize_entry("chartLabel", label)?;
}
if let Some(label) = &self.tooltip_label {
map.serialize_entry("tooltipLabel", label)?;
}
if let Some(label) = &self.table_label {
map.serialize_entry("tableLabel", label)?;
}
if let Some(description) = &self.description {
map.serialize_entry("description", description)?;
}
map.serialize_entry("fields", &SerializableSchemaFields(self))?;
if !self.graphs.is_empty() {
map.serialize_entry("graphs", &self.graphs)?;
}
map.end()
}
fn serialize_fields<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut seq = serializer.serialize_seq(None)?;
for field in &self.fields {
seq.serialize_element(&SerializableSchemaField(field))?;
}
seq.end()
}
}
impl Serialize for InternalMarkerSchema {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.serialize_self(serializer)
}
}
struct SerializableSchemaFields<'a>(&'a InternalMarkerSchema);
impl Serialize for SerializableSchemaFields<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.0.serialize_fields(serializer)
}
}
struct SerializableSchemaField<'a>(&'a RuntimeSchemaMarkerField);
impl Serialize for SerializableSchemaField<'_> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("key", &self.0.key)?;
if !self.0.label.is_empty() {
map.serialize_entry("label", &self.0.label)?;
}
map.serialize_entry("format", &self.0.format)?;
if self.0.flags.contains(MarkerFieldFlags::SEARCHABLE) {
map.serialize_entry("searchable", &true)?;
}
map.end()
}
}
struct SerializableSchemaDisplay(MarkerLocations);
impl Serialize for SerializableSchemaDisplay {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let mut seq = serializer.serialize_seq(None)?;
if self.0.contains(MarkerLocations::MARKER_CHART) {
seq.serialize_element("marker-chart")?;
}
if self.0.contains(MarkerLocations::MARKER_TABLE) {
seq.serialize_element("marker-table")?;
}
if self.0.contains(MarkerLocations::TIMELINE_OVERVIEW) {
seq.serialize_element("timeline-overview")?;
}
if self.0.contains(MarkerLocations::TIMELINE_MEMORY) {
seq.serialize_element("timeline-memory")?;
}
if self.0.contains(MarkerLocations::TIMELINE_IPC) {
seq.serialize_element("timeline-ipc")?;
}
if self.0.contains(MarkerLocations::TIMELINE_FILEIO) {
seq.serialize_element("timeline-fileio")?;
}
seq.end()
}
}