use super::{Response, Status};
use crate::timestamp::{
CheckContext, CheckError, CheckedTimestamp, ImprintOperand, ProfileCheckError, ProfileFailure,
ProfileInput, RequestFieldError, TokenExtension,
};
use crate::{Error, ErrorCode, provider::SignatureProvider, stream::Source};
use core::convert::Infallible;
use structured_asn1::{Reader, memory::Execution};
pub struct CheckedResponse<'a> {
status: Status,
timestamp: Option<CheckedTimestamp<'a>>,
}
impl<'a> CheckedResponse<'a> {
#[must_use]
pub fn status(&self) -> Status {
self.status
}
#[must_use]
pub fn timestamp(&self) -> Option<&CheckedTimestamp<'a>> {
self.timestamp.as_ref()
}
pub fn into_timestamp(self) -> Option<CheckedTimestamp<'a>> {
self.timestamp
}
}
impl<'doc> Response<'doc> {
#[expect(
clippy::result_large_err,
reason = "preserve the existing original token/source refusal without allocating an error box on resource exhaustion"
)]
pub fn check<'a, P, S: Source, PE, RE>(
&'a self,
provider: &'a P,
query: &Reader<'a>,
context: &CheckContext<'a, '_>,
covered: &mut S,
recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
) -> Result<CheckedResponse<'a>, CheckError<S::Error, PE, RE>>
where
'doc: 'a,
P: for<'scope> SignatureProvider<'scope, Error = PE>,
{
self.check_with_profile(provider, query, context, covered, recognizes, |_| {
Ok::<(), ProfileFailure<Infallible>>(())
})
.map_err(ProfileCheckError::into_common)
}
#[expect(
clippy::result_large_err,
reason = "retain the existing typed token/profile/source cause without another fallible allocation"
)]
pub fn check_with_profile<'a, P, S: Source, PE, RE, FE>(
&'a self,
provider: &'a P,
query: &Reader<'a>,
context: &CheckContext<'a, '_>,
covered: &mut S,
recognizes: impl FnMut(&TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
profile: impl for<'view, 'scope> FnOnce(
ProfileInput<'view, 'doc, 'a, 'scope>,
) -> Result<(), ProfileFailure<FE>>,
) -> Result<CheckedResponse<'a>, ProfileCheckError<S::Error, PE, RE, FE>>
where
'doc: 'a,
P: for<'scope> SignatureProvider<'scope, Error = PE>,
{
let Some(requested) = &context.requested else {
return Err(CheckError::Processing(Error::schema(
ErrorCode::InvalidLocation,
"timeStampResp.requestContext",
self.element().span().start,
))
.into());
};
requested
.request
.imprint()
.require_session(requested.parent)
.map_err(|source| {
CheckError::RequestField(RequestFieldError {
operand: ImprintOperand::Requested,
source,
})
})?;
let timestamp = match self.token_in(query).map_err(CheckError::Processing)? {
Some((token, reader)) => Some(
token
.check_with_profile(provider, &reader, context, covered, recognizes, profile)?,
),
None => None,
};
Ok(CheckedResponse {
status: self.status().status(),
timestamp,
})
}
}