use super::{
Diagnostic, DiagnosticName, DiagnosticNameRefusal, DiagnosticProjection, DiagnosticSeats,
Family, IntrinsicRefused, Observed, Phase, Placement, Refused, RelatedIdentity, RelatedSet,
Repair, Route, Site, SiteCoordinate,
};
use crate::bounded::{Bounded, Capping};
use crate::diagnostic::project::{diagnostic, intrinsic_site, placement_site};
use crate::identity::{
Contract, Identity, RelatedBody, RelatedIssue, Role, ServiceEntry, Transcript, encode_bytes,
};
use crate::request::Door;
use crate::token::{SourceCoordinate, SpanHandle};
impl Family {
#[must_use]
pub const fn declared(name: &'static str) -> Self {
assert!(
interior_separator(name.as_bytes()),
"a family name is namespace/stem, with material on both sides"
);
Self(name)
}
#[must_use]
pub const fn name(self) -> &'static str {
self.0
}
}
impl DiagnosticName {
pub const fn declared(name: &'static str) -> Result<Self, DiagnosticNameRefusal> {
if name.is_empty() {
return Err(DiagnosticNameRefusal::Empty);
}
if !crate::identity::name_is_grammatical(name) {
return Err(DiagnosticNameRefusal::NotKebabCase);
}
Ok(Self(name))
}
#[must_use]
pub const fn spelling(self) -> &'static str {
self.0
}
}
const fn interior_separator(name: &[u8]) -> bool {
let mut rest = name;
let mut ahead = 0_usize;
while let Some((byte, remaining)) = rest.split_first() {
if *byte == b'/' {
return ahead != 0 && !remaining.is_empty();
}
ahead = ahead.saturating_add(1);
rest = remaining;
}
false
}
fn related_content(family: Family, material: &[u8]) -> Vec<u8> {
let mut content = Vec::new();
encode_bytes(family.name().as_bytes(), &mut content);
encode_bytes(material, &mut content);
content
}
fn issue_identity(family: Family, material: &[u8]) -> Identity<RelatedIssue> {
Identity::derived(Transcript::rooted(
Role::DiagnosticRelation,
&related_content(family, material),
0,
))
}
fn body_identity(family: Family, material: &[u8]) -> Identity<RelatedBody> {
Identity::derived(Transcript::rooted(
Role::DiagnosticRelation,
&related_content(family, material),
0,
))
}
impl RelatedSet {
#[must_use]
pub fn derived_over(family: Family, issues: &[Vec<u8>]) -> Self {
if issues.is_empty() {
return Self::nothing_enumerated();
}
let mut body_material = Vec::new();
let mut per_issue = Vec::with_capacity(issues.len());
for issue in issues {
per_issue.push(RelatedIdentity::Issue(issue_identity(family, issue)));
encode_bytes(issue, &mut body_material);
}
let body = RelatedIdentity::Body(body_identity(family, &body_material));
let mut all = Vec::with_capacity(per_issue.len().saturating_add(1));
all.push(body);
all.append(&mut per_issue);
match Bounded::new(all) {
Ok(carried) => Self {
carried,
capping: Capping::Complete,
},
Err(_) => Self {
carried: Bounded::from_array([body]),
capping: Capping::Truncated {
omitted: issues.len(),
},
},
}
}
#[must_use]
pub const fn nothing_enumerated() -> Self {
Self {
carried: Bounded::empty(),
capping: Capping::Complete,
}
}
#[must_use]
pub fn carried(&self) -> &[RelatedIdentity] {
self.carried.as_slice()
}
#[must_use]
pub const fn capping(&self) -> Capping {
self.capping
}
}
impl Site {
pub const fn at_token(token: SpanHandle, coordinate: SiteCoordinate) -> Self {
Self::AtToken { token, coordinate }
}
pub const fn before_capture(coordinate: SourceCoordinate) -> Self {
Self::BeforeCapture { coordinate }
}
pub const fn whole_declaration() -> Self {
Self::WholeDeclaration
}
#[must_use]
pub const fn token(self) -> Option<SpanHandle> {
match self {
Self::AtToken { token, .. } => Some(token),
Self::BeforeCapture { .. } | Self::WholeDeclaration => None,
}
}
#[must_use]
pub const fn coordinate(self) -> Option<SiteCoordinate> {
match self {
Self::AtToken { coordinate, .. } => Some(coordinate),
Self::BeforeCapture { coordinate } => Some(SiteCoordinate::Resolved(coordinate)),
Self::WholeDeclaration => None,
}
}
}
impl Route {
pub(crate) const fn through(entry: Identity<ServiceEntry>) -> Self {
Self { entry }
}
#[must_use]
pub const fn entry(self) -> Identity<ServiceEntry> {
self.entry
}
}
impl Diagnostic {
pub fn refused<E: Refused>(refusal: &E, door: &Door, placement: &Placement<'_>) -> Self {
assemble_diagnostic(diagnostic(refusal, door, placement_site(placement)))
}
#[must_use]
pub const fn phase(&self) -> Phase {
self.phase
}
pub const fn site(&self) -> Site {
self.site
}
#[must_use]
pub fn summary(&self) -> &str {
&self.carried.summary
}
#[must_use]
pub fn expected(&self) -> Identity<Contract> {
self.carried.expected
}
#[must_use]
pub const fn observed(&self) -> Observed {
self.observed
}
#[must_use]
pub fn related(&self) -> &RelatedSet {
&self.carried.related
}
#[must_use]
pub fn repairs(&self) -> &[Repair] {
self.carried.repairs.as_slice()
}
#[must_use]
pub fn route(&self) -> Route {
self.carried.route
}
}
pub(crate) fn intrinsic_diagnostic<E: IntrinsicRefused>(refusal: &E, door: &Door) -> Diagnostic {
assemble_diagnostic(diagnostic(refusal, door, intrinsic_site(refusal)))
}
fn assemble_diagnostic(projection: DiagnosticProjection) -> Diagnostic {
Diagnostic {
phase: projection.phase,
site: projection.site,
observed: projection.observed,
carried: Box::new(DiagnosticSeats {
summary: projection.summary,
expected: projection.expected,
related: projection.related,
repairs: projection.repairs,
route: projection.route,
}),
}
}