use thiserror::Error;
mod kafka;
mod manager;
pub mod protocol;
#[cfg(test)]
mod tests;
mod transport;
mod types;
mod webhook;
pub use kafka::{KafkaAdapter, KafkaConfig, KafkaMessage};
pub use manager::SubscriptionManager;
pub use transport::{BoxDynTransportAdapter, DeliveryResult, TransportAdapter, TransportManager};
pub use types::{
ActiveSubscription, ChangeSpineEnvelope, SubscriptionEvent, SubscriptionId,
SubscriptionOperation, SubscriptionPayload,
};
pub use webhook::{WebhookAdapter, WebhookPayload, WebhookTransportConfig};
pub type WebhookConfig = WebhookTransportConfig;
pub fn extract_rls_conditions(
clause: &crate::db::WhereClause,
) -> Result<Vec<(String, serde_json::Value)>, String> {
let mut conditions = Vec::new();
collect_eq_conditions(clause, &mut conditions)?;
Ok(conditions)
}
fn collect_eq_conditions(
clause: &crate::db::WhereClause,
out: &mut Vec<(String, serde_json::Value)>,
) -> Result<(), String> {
use crate::db::{WhereClause, WhereOperator};
match clause {
WhereClause::Field {
path,
operator: WhereOperator::Eq,
value,
} => {
if let Some(field) = path.last() {
out.push((field.clone(), value.clone()));
}
Ok(())
},
WhereClause::And(clauses) => {
for c in clauses {
collect_eq_conditions(c, out)?;
}
Ok(())
},
WhereClause::Field { path, operator, .. } => Err(format!(
"row-visibility condition on `{}` uses operator `{operator:?}`; only `Eq` can be \
enforced on the pushed event stream — refusing the subscription rather than \
delivering unfiltered rows (#596)",
path.last().map_or("<field>", String::as_str),
)),
_ => Err("row-visibility clause uses an `Or`/`Not`/native-column shape that cannot be \
enforced as event-stream equality — refusing the subscription rather than \
delivering unfiltered rows (#596)"
.to_string()),
}
}
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum SubscriptionError {
#[error("Subscription not found: {0}")]
SubscriptionNotFound(String),
#[error("Authentication required for subscription: {0}")]
AuthenticationRequired(String),
#[error("Not authorized for subscription: {0}")]
Forbidden(String),
#[error("Invalid subscription variables: {0}")]
InvalidVariables(String),
#[error("Subscription already exists: {0}")]
AlreadyExists(String),
#[error("Subscription not active: {0}")]
NotActive(String),
#[error("Subscription error: {0}")]
Internal(String),
#[error("Failed to send event: {0}")]
SendError(String),
#[error("Database connection error: {0}")]
DatabaseConnection(String),
#[error("Listener already running")]
ListenerAlreadyRunning,
#[error("Listener not running")]
ListenerNotRunning,
#[error("Failed to parse notification: {0}")]
InvalidNotification(String),
#[error("Failed to deliver to {transport}: {reason}")]
DeliveryFailed {
transport: String,
reason: String,
},
}