use std::path::PathBuf;
use std::time::Duration;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Spec {
pub name: String,
pub metadata: Metadata,
pub sections: Vec<Section>,
pub source_path: PathBuf,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Metadata {
pub context: Option<String>,
pub sources: Vec<String>,
pub schemas: Vec<String>,
pub requires: Vec<SpecRef>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SpecRef {
pub label: String,
pub path: PathBuf,
pub anchor: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Section {
pub title: String,
pub depth: u8,
pub prose: String,
pub clauses: Vec<Clause>,
pub subsections: Vec<Section>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Clause {
pub id: ClauseId,
pub keyword: Keyword,
pub severity: Severity,
pub text: String,
pub condition: Option<String>,
pub otherwise: Vec<Clause>,
pub temporal: Option<Temporal>,
pub hints: Vec<String>,
pub source_location: SourceLocation,
pub content_hash: String,
#[serde(default)]
pub pending: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct ClauseId(pub String);
impl std::fmt::Display for ClauseId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Keyword {
Must,
MustNot,
Should,
ShouldNot,
May,
Wont,
Given,
Otherwise,
MustAlways,
MustBy,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Severity {
Required,
Recommended,
Optional,
NegativeConfirmation,
}
impl Keyword {
pub fn severity(self) -> Severity {
match self {
Keyword::Must | Keyword::MustNot | Keyword::MustAlways | Keyword::MustBy => {
Severity::Required
}
Keyword::Should | Keyword::ShouldNot => Severity::Recommended,
Keyword::May => Severity::Optional,
Keyword::Wont => Severity::NegativeConfirmation,
Keyword::Given | Keyword::Otherwise => Severity::Required,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Temporal {
Invariant,
Deadline(Duration),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceLocation {
pub file: PathBuf,
pub line: usize,
}
#[derive(Debug, Clone, thiserror::Error)]
#[error("{file}:{line}: {message}")]
pub struct ParseError {
pub file: PathBuf,
pub line: usize,
pub message: String,
}