live-feed 0.1.0

Publisher SDK for advertising and serving live data feeds. Consumers use the live-stream crate.
Documentation
//! Builders for [`FeedDescriptor`] and [`FeedManifest`].

use live_data::{
    Capabilities, FeedDescriptor, FeedManifest, FormatPreference, TransportTag, WireSchema,
};

use crate::validate::{ValidationError, validate_descriptor};

/// Builder for a [`FeedDescriptor`].
#[derive(Debug, Clone)]
pub struct FeedDescriptorBuilder {
    name: String,
    schema: WireSchema,
    transports: Vec<TransportTag>,
    formats: Vec<FormatPreference>,
    capabilities: Capabilities,
    event_time_key: Option<String>,
    tags: Vec<String>,
    description: Option<String>,
}

impl FeedDescriptorBuilder {
    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,
        }
    }

    /// Add a single transport. Duplicate calls with the same tag are
    /// idempotent.
    pub fn transport(mut self, t: TransportTag) -> Self {
        if !self.transports.contains(&t) {
            self.transports.push(t);
        }
        self
    }

    pub fn transports<I: IntoIterator<Item = TransportTag>>(mut self, ts: I) -> Self {
        for t in ts {
            self = self.transport(t);
        }
        self
    }

    pub fn format(mut self, f: FormatPreference) -> Self {
        if !self.formats.contains(&f) {
            self.formats.push(f);
        }
        self
    }

    pub fn formats<I: IntoIterator<Item = FormatPreference>>(mut self, fs: I) -> Self {
        for f in fs {
            self = self.format(f);
        }
        self
    }

    pub fn capabilities(mut self, c: Capabilities) -> Self {
        self.capabilities = c;
        self
    }

    pub fn event_time_key(mut self, k: impl Into<String>) -> Self {
        self.event_time_key = Some(k.into());
        self
    }

    pub fn tag(mut self, t: impl Into<String>) -> Self {
        let t = t.into();
        if !self.tags.contains(&t) {
            self.tags.push(t);
        }
        self
    }

    pub fn description(mut self, d: impl Into<String>) -> Self {
        self.description = Some(d.into());
        self
    }

    /// Validate and produce the descriptor.
    pub fn build(self) -> Result<FeedDescriptor, ValidationError> {
        let descriptor = FeedDescriptor {
            name: self.name,
            schema: self.schema,
            transports: self.transports,
            formats: self.formats,
            capabilities: self.capabilities,
            event_time_key: self.event_time_key,
            tags: self.tags,
            description: self.description,
        };
        validate_descriptor(&descriptor)?;
        Ok(descriptor)
    }
}

/// Builder for a [`FeedManifest`].
///
/// Manifests track insertion order so the consumer sees feeds in the
/// order the publisher declared them.
#[derive(Debug, Clone)]
pub struct FeedManifestBuilder {
    server_version: String,
    feeds: Vec<FeedDescriptor>,
}

impl FeedManifestBuilder {
    pub fn new(server_version: impl Into<String>) -> Self {
        Self { server_version: server_version.into(), feeds: Vec::new() }
    }

    pub fn feed(mut self, d: FeedDescriptor) -> Self {
        self.feeds.push(d);
        self
    }

    pub fn feeds<I: IntoIterator<Item = FeedDescriptor>>(mut self, ds: I) -> Self {
        for d in ds {
            self.feeds.push(d);
        }
        self
    }

    pub fn build(self) -> FeedManifest {
        FeedManifest::new(self.server_version, self.feeds)
    }
}