train_track

Core framework crate for Railscale. Defines all protocol-agnostic traits and the generic Pipeline orchestrator.

Module Layout

src/
  lib.rs          — re-exports
  error.rs        — RailscaleError enum
  sampler.rs      — metrics-full request sampling
  atom/
    frame.rs      — Frame trait, ParsedData enum
    parser.rs     — FrameParser trait
  core/
    pipeline.rs   — FramePipeline trait
    service.rs    — Service trait, Pipeline struct, OtelMetrics
  io/
    source.rs     — StreamSource trait
    destination.rs — StreamDestination trait
    router.rs     — DestinationRouter trait
  destinations/
    file.rs       — FileDestination (BufWriter-based)
    multi.rs      — MultiDestination wrapper
    route.rs      — MatchStrategy, MatchingRouter

Core Traits

Frame

pub trait Frame: Send + Sized + Sync {
    fn as_bytes(&self) -> &[u8];
    fn into_bytes(self) -> Bytes;
    fn routing_key(&self) -> Option<&[u8]>;
}

FrameParser

pub trait FrameParser<R: AsyncRead + Unpin + Send>: Send {
    type Frame: Frame;
    type Error: Into<RailscaleError>;
    fn parse(self, reader: R) -> impl Stream<Item = Result<ParsedData<Self::Frame>, Self::Error>> + Send;
}

FramePipeline

pub trait FramePipeline: Send + Sync {
    type Frame: Frame;
    fn process(&self, frame: Self::Frame) -> Self::Frame;
}

StreamSource

pub trait StreamSource: Send {
    type ReadHalf: AsyncRead + Unpin + Send;
    type WriteHalf: AsyncWrite + Unpin + Send;
    fn accept(&self) -> impl Future<Output = Result<(Self::ReadHalf, Self::WriteHalf), impl Into<RailscaleError>>> + Send;
}

StreamDestination

pub trait StreamDestination: Send {
    type Error: Into<RailscaleError>;
    async fn write(&mut self, bytes: Bytes) -> Result<(), Self::Error>;
    async fn relay_response<W: AsyncWrite + Send + Unpin>(&mut self, client: &mut W) -> Result<u64, Self::Error>;
}

DestinationRouter

pub trait DestinationRouter: Send + Sync {
    type Destination: StreamDestination;
    async fn route(&self, routing_key: &[u8]) -> Result<Self::Destination, RailscaleError>;
}

Pipeline Orchestrator

Pipeline<Src, Par, Pip, Rtr> ties everything together:

  • source: Src — accepts connections
  • parser_factory: fn() -> Par — creates a parser per connection
  • pipeline: Arc<Pip> — shared frame processor
  • router: Arc<Rtr> — shared destination router

See Railscale Connection Lifecycle for the full do_connection flow.

Routing

MatchStrategy provides SIMD-accelerated domain matching:

  • Exact — full domain match
  • Suffix — e.g., .example.com
  • Prefix — e.g., api.
  • Contains — substring via memmem::Finder

MatchingRouter<D> chains strategies with factory functions, implementing DestinationRouter.