use super::super::render;
use super::{
Landing, PART_LIMIT, PATH_SEGMENT_LIMIT, Part, Pattern, PublicationGround, PublicationRecord,
PublishedStamp, SITE_LIMIT, Seat, Seating, Site, SiteRoot, Stamp, StampError, StampName,
StampedPlan, Visibility,
};
use crate::bounded::{Bounded, NonEmpty, NonEmptyError, first_duplicate_position};
use crate::identity::{self, Identity};
use crate::plan::DigestContract;
use crate::token::{GeneratedTree, rendered_name};
impl Seat {
pub fn declared(name: &str, seating: Seating) -> Result<Self, StampError> {
if !rendered_name(name) {
return Err(StampError::NotAnIdentifier);
}
Ok(Self {
name: name.to_owned(),
seating,
})
}
#[must_use]
pub fn name(&self) -> &str {
self.name.as_str()
}
#[must_use]
pub const fn seating(&self) -> Seating {
self.seating
}
}
impl Pattern {
pub fn declared(note: &str, parts: Vec<Part>, body: GeneratedTree) -> Result<Self, StampError> {
seat_names_closed(&parts)?;
let admitted: NonEmpty<Part, PART_LIMIT> =
NonEmpty::new(parts).map_err(|refusal| match refusal {
NonEmptyError::Empty(_) => StampError::PatternEmpty,
NonEmptyError::Overflow(overflow) => StampError::PatternUnbounded { overflow },
})?;
Ok(Self {
note: note.to_owned(),
parts: admitted,
body,
})
}
#[must_use]
pub fn note(&self) -> &str {
self.note.as_str()
}
#[must_use]
pub fn parts(&self) -> &NonEmpty<Part, PART_LIMIT> {
&self.parts
}
#[must_use]
pub const fn body(&self) -> &GeneratedTree {
&self.body
}
pub fn seats(&self) -> impl Iterator<Item = &Seat> {
self.parts.iter().filter_map(|part| match part {
Part::Seat(seat) => Some(seat),
Part::Literal(_) | Part::Reach => None,
})
}
#[must_use]
pub fn seat_count(&self) -> usize {
self.seats().count()
}
#[must_use]
pub fn reaches(&self) -> bool {
self.parts.iter().any(|part| match part {
Part::Reach => true,
Part::Literal(_) | Part::Seat(_) => false,
})
}
}
impl StampName {
pub fn declared(spelling: &str) -> Result<Self, StampError> {
if !rendered_name(spelling) {
return Err(StampError::NotAnIdentifier);
}
Ok(Self {
spelling: spelling.to_owned(),
})
}
#[must_use]
pub fn spelling(&self) -> &str {
self.spelling.as_str()
}
}
impl SiteRoot {
pub fn spelled(segments: Vec<String>) -> Result<Self, StampError> {
let names = match segments.split_first() {
Some((root, rest)) if root.as_str() == "crate" || root.as_str() == "self" => rest,
Some(_) | None => {
let qualifiers = segments
.iter()
.take_while(|segment| segment.as_str() == "super")
.count();
segments.get(qualifiers..).unwrap_or(&[])
}
};
for segment in names {
if !rendered_name(segment.as_str()) {
return Err(StampError::NotAnIdentifier);
}
}
let admitted: NonEmpty<String, PATH_SEGMENT_LIMIT> =
NonEmpty::new(segments).map_err(|refusal| match refusal {
NonEmptyError::Empty(_) => StampError::PathEmpty,
NonEmptyError::Overflow(overflow) => StampError::PathUnbounded { overflow },
})?;
Ok(Self { segments: admitted })
}
#[must_use]
pub fn segments(&self) -> &NonEmpty<String, PATH_SEGMENT_LIMIT> {
&self.segments
}
#[must_use]
pub fn count(&self) -> usize {
self.segments.count()
}
}
impl Site {
pub fn declared(
name: &str,
root: SiteRoot,
reach: Visibility,
arguments: Vec<GeneratedTree>,
) -> Result<Self, StampError> {
let admitted: Bounded<GeneratedTree, PART_LIMIT> = Bounded::new(arguments)
.map_err(|overflow| StampError::ArgumentsUnbounded { overflow })?;
Ok(Self {
name: name.to_owned(),
root,
reach,
arguments: admitted,
})
}
#[must_use]
pub fn name(&self) -> &str {
self.name.as_str()
}
#[must_use]
pub const fn root(&self) -> &SiteRoot {
&self.root
}
#[must_use]
pub const fn reach(&self) -> Visibility {
self.reach
}
#[must_use]
pub fn arguments(&self) -> &[GeneratedTree] {
self.arguments.as_slice()
}
}
impl Stamp {
pub fn declared(
name: StampName,
pattern: Pattern,
sites: Vec<Site>,
) -> Result<Self, StampError> {
site_names_closed(&sites)?;
sites_seated(&pattern, &sites)?;
let admitted: NonEmpty<Site, SITE_LIMIT> =
NonEmpty::new(sites).map_err(|refusal| match refusal {
NonEmptyError::Empty(_) => StampError::SitesAbsent,
NonEmptyError::Overflow(overflow) => StampError::SitesUnbounded { overflow },
})?;
Ok(Self {
name,
pattern,
sites: admitted,
})
}
#[must_use]
pub const fn name(&self) -> &StampName {
&self.name
}
#[must_use]
pub const fn pattern(&self) -> &Pattern {
&self.pattern
}
#[must_use]
pub fn sites(&self) -> &NonEmpty<Site, SITE_LIMIT> {
&self.sites
}
#[must_use]
pub fn count(&self) -> usize {
self.sites.count()
}
}
impl Landing {
#[must_use]
pub fn site(&self) -> &str {
self.site.as_str()
}
#[must_use]
pub const fn invocation(&self) -> &GeneratedTree {
&self.invocation
}
}
impl PublicationRecord {
#[must_use]
pub const fn ground(&self) -> PublicationGround {
self.ground
}
#[must_use]
pub const fn unit(&self) -> Identity<identity::GeneratedUnit> {
self.unit
}
#[must_use]
pub const fn staged(&self) -> DigestContract {
self.staged
}
#[must_use]
pub const fn covered(&self) -> &Stamp {
&self.stamp
}
pub fn manifest(&self) -> impl Iterator<Item = &str> {
self.stamp.sites().iter().map(Site::name)
}
}
impl StampedPlan {
pub(in crate::stamp) const fn read(
unit: Identity<identity::GeneratedUnit>,
staged: DigestContract,
) -> Self {
Self { unit, staged }
}
#[must_use]
pub const fn unit(&self) -> Identity<identity::GeneratedUnit> {
self.unit
}
#[must_use]
pub const fn staged(&self) -> DigestContract {
self.staged
}
}
impl PublishedStamp {
pub fn rendered(
planned: &StampedPlan,
stamp: &Stamp,
ground: PublicationGround,
) -> Result<Self, StampError> {
let definition = GeneratedTree::assembled(render::definition(stamp)?)?;
let mut landings: Vec<Landing> = Vec::new();
for site in stamp.sites() {
let invocation = GeneratedTree::assembled(render::invocation(stamp, site)?)?;
landings.push(Landing {
site: site.name().to_owned(),
invocation,
});
}
Ok(Self {
definition,
landings,
record: PublicationRecord {
ground,
unit: planned.unit,
staged: planned.staged,
stamp: stamp.clone(),
},
})
}
#[must_use]
pub const fn name(&self) -> &StampName {
self.record.covered().name()
}
#[must_use]
pub const fn definition(&self) -> &GeneratedTree {
&self.definition
}
#[must_use]
pub fn landings(&self) -> &[Landing] {
self.landings.as_slice()
}
#[must_use]
pub fn count(&self) -> usize {
self.landings.len()
}
pub const fn record(&self) -> &PublicationRecord {
&self.record
}
}
fn seat_names_closed(parts: &[Part]) -> Result<(), StampError> {
let doubled = first_duplicate_position(parts, |left, right| match (left, right) {
(Part::Seat(left), Part::Seat(right)) => left.name() == right.name(),
(Part::Literal(_) | Part::Reach, _) | (_, Part::Literal(_) | Part::Reach) => false,
});
if let Some(position) = doubled {
return Err(StampError::SeatNameDoubled {
at: counted(position),
});
}
Ok(())
}
fn site_names_closed(sites: &[Site]) -> Result<(), StampError> {
if let Some(position) =
first_duplicate_position(sites, |left, right| left.name() == right.name())
{
return Err(StampError::SiteNameDoubled {
at: counted(position),
});
}
Ok(())
}
fn sites_seated(pattern: &Pattern, sites: &[Site]) -> Result<(), StampError> {
let seats = counted(pattern.seat_count());
let reaches = pattern.reaches();
for (position, site) in sites.iter().enumerate() {
let supplied = counted(site.arguments().len());
if supplied != seats {
return Err(StampError::ArgumentsUnmatched {
at: counted(position),
seats,
supplied,
});
}
if !reaches && site.reach() != Visibility::Private {
return Err(StampError::ReachUnseated {
at: counted(position),
});
}
}
Ok(())
}
fn counted(value: usize) -> u32 {
u32::try_from(value).unwrap_or(u32::MAX)
}