live-data 0.1.0

Shared descriptor, manifest, and subscription types for the live-feed publisher SDK and the live-stream consumer SDK.
Documentation
//! Per-feed descriptor and capability advertisement.

use serde::{Deserialize, Serialize};

use crate::schema::WireSchema;
use crate::transport::{FormatPreference, TransportTag};

/// Single named live feed, as advertised by a publisher.
///
/// A descriptor is information-only: it describes what the feed is and how it
/// can be consumed. The matching producer-side handle
/// lives in `live-stream`.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FeedDescriptor {
    /// Stable identifier for the feed, unique within a single publisher.
    pub name: String,
    /// Schema every emitted batch on the feed conforms to.
    pub schema: WireSchema,
    /// Transports the publisher is currently accepting subscriptions over.
    pub transports: Vec<TransportTag>,
    /// Formats the publisher can serialise batches into.
    pub formats: Vec<FormatPreference>,
    /// What the publisher is willing to do server-side at subscribe time.
    pub capabilities: Capabilities,
    /// Name of the column carrying the canonical event timestamp on
    /// this feed. Optional in the manifest grammar so future
    /// non-temporal feeds remain expressible, always populated for
    /// streaming market data feeds.
    #[serde(default)]
    pub event_time_key: Option<String>,
    /// Free-form classification tags, e.g. `["finance", "ticks"]`.
    #[serde(default)]
    pub tags: Vec<String>,
    /// Optional human-readable description.
    #[serde(default)]
    pub description: Option<String>,
}

impl FeedDescriptor {
    pub fn new(name: impl Into<String>, schema: WireSchema) -> Self {
        Self {
            name: name.into(),
            schema,
            transports: Vec::new(),
            formats: Vec::new(),
            capabilities: Capabilities::minimal(),
            event_time_key: None,
            tags: Vec::new(),
            description: None,
        }
    }
}

/// What the publisher is willing to do server-side when accepting a subscription.
///
/// Only `can_project` defaults to true. The other two fields exist so
/// the wire format is stable when filter and sampling support land, and so
/// consumers can already gate features on what the server advertises.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Capabilities {
    /// Server honours [`SubscriptionDescriptor::columns`] for projection.
    pub can_project: bool,
    /// Server honours [`SubscriptionDescriptor::filter`] for row-level filtering.
    pub can_filter: bool,
    /// Server honours [`SubscriptionDescriptor::sampling`].
    pub can_sample: bool,
}

impl Capabilities {
    /// Minimum guaranteed capability set
    pub const fn minimal() -> Self {
        Self { can_project: true, can_filter: false, can_sample: false }
    }

    /// All capabilities enabled.
    pub const fn all() -> Self {
        Self { can_project: true, can_filter: true, can_sample: true }
    }
}

impl Default for Capabilities {
    fn default() -> Self {
        Self::minimal()
    }
}