use std::collections::BTreeMap;
use std::ops::Range;
pub type Span = Range<usize>;
#[derive(Debug, Clone)]
pub struct Spec {
pub setup: Setup,
pub endpoints: Vec<Endpoint>,
}
#[derive(Debug, Clone, Default)]
pub struct Setup {
pub version: Option<u32>,
pub base: Option<String>,
pub auth: Option<AuthSpec>,
pub jwt_verifier: Option<ValueSource>,
pub token_secret: Option<ValueSource>,
pub max_body_size: Option<u64>,
pub max_query_param_size: Option<u64>,
pub max_header_size: Option<u64>,
pub timeout_ms: Option<u64>,
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum AuthSpec {
BearerHeader { header: String, span: Span },
}
#[derive(Debug, Clone)]
pub enum ValueSource {
Env { name: String, span: Span },
Header { name: String, span: Span },
Literal { value: String, span: Span },
}
impl ValueSource {
pub fn span(&self) -> Span {
match self {
ValueSource::Env { span, .. }
| ValueSource::Header { span, .. }
| ValueSource::Literal { span, .. } => span.clone(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Method {
Get,
Post,
Put,
Delete,
Patch,
}
impl Method {
pub fn as_str(self) -> &'static str {
match self {
Method::Get => "GET",
Method::Post => "POST",
Method::Put => "PUT",
Method::Delete => "DELETE",
Method::Patch => "PATCH",
}
}
}
#[derive(Debug, Clone)]
pub struct Endpoint {
pub method: Method,
pub path: String,
pub path_segments: Vec<PathSegment>,
pub response_type: Option<String>,
pub query_params: Vec<NamedField>,
pub headers: Vec<NamedField>,
pub vars: Vec<VarDef>,
pub body: Option<BodySpec>,
pub exec: ExecSpec,
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum PathSegment {
Literal(String),
Param { name: String, ty: TypeExpr, span: Span },
}
#[derive(Debug, Clone)]
pub struct NamedField {
pub name: String,
pub optional: bool,
pub ty: TypeExpr,
pub span: Span,
}
#[derive(Debug, Clone)]
pub struct VarDef {
pub name: String,
pub source: ValueSource,
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum BodySpec {
Json { schema: Option<JsonSchema>, span: Span },
Form { fields: Vec<NamedField>, span: Span },
String { span: Span },
Binary { span: Span },
}
#[derive(Debug, Clone)]
pub struct JsonSchema {
pub fields: Vec<JsonField>,
}
#[derive(Debug, Clone)]
pub struct JsonField {
pub name: String,
pub optional: bool,
pub ty: JsonFieldType,
pub span: Span,
}
#[derive(Debug, Clone)]
pub enum JsonFieldType {
Scalar(TypeExpr),
Array(TypeExpr),
}
#[derive(Debug, Clone)]
pub enum TypeExpr {
Int,
Float,
Boolean,
Uuid,
String,
Json,
Binary,
IntRange { min: i64, max: i64, span: Span },
FloatRange { min: f64, max: f64, span: Span },
Union { variants: Vec<String>, span: Span },
Regex { pattern: String, span: Span },
}
impl TypeExpr {
pub fn name(&self) -> &'static str {
match self {
TypeExpr::Int => "int",
TypeExpr::Float => "float",
TypeExpr::Boolean => "boolean",
TypeExpr::Uuid => "uuid",
TypeExpr::String => "string",
TypeExpr::Json => "json",
TypeExpr::Binary => "binary",
TypeExpr::IntRange { .. } => "int(range)",
TypeExpr::FloatRange { .. } => "float(range)",
TypeExpr::Union { .. } => "union",
TypeExpr::Regex { .. } => "regex",
}
}
}
#[derive(Debug, Clone)]
pub struct ExecSpec {
pub raw: String,
pub span: Span,
pub pipeline: Vec<ExecStage>,
}
#[derive(Debug, Clone)]
pub enum ExecStage {
Source { reference: ValueRef, span: Span },
Command { tokens: Vec<ExecToken>, span: Span },
}
#[derive(Debug, Clone)]
pub enum ExecToken {
Text { parts: Vec<TextPart>, span: Span },
Group { pieces: Vec<GroupPiece>, span: Span },
}
#[derive(Debug, Clone)]
pub enum TextPart {
Literal(String),
Interp(ValueRef),
}
#[derive(Debug, Clone)]
pub struct GroupPiece {
pub parts: Vec<TextPart>,
}
#[derive(Debug, Clone)]
pub enum ValueRef {
Query(String),
Path(String),
Header(String),
Var(String),
Body { path: Vec<String> },
}
impl ValueRef {
pub fn describe(&self) -> String {
match self {
ValueRef::Query(n) => format!("query param `{}`", n),
ValueRef::Path(n) => format!("path param `{}`", n),
ValueRef::Header(n) => format!("header `{}`", n),
ValueRef::Var(n) => format!("var `{}`", n),
ValueRef::Body { path } if path.is_empty() => "body".to_string(),
ValueRef::Body { path } => format!("body field `{}`", path.join(".")),
}
}
}
pub type FieldMap = BTreeMap<String, NamedField>;