use synta::traits::Encode;
use crate::ace88_types::AuthenticationContext;
use crate::ext_builder::encode_sequence;
#[derive(Debug, Default)]
pub struct AuthenticationContextsBuilder {
encoded: Vec<u8>,
error: Option<String>,
}
impl AuthenticationContextsBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn add(mut self, context_type: &str, context_info: Option<&str>) -> Self {
if self.error.is_some() {
return self;
}
let ct = synta::Utf8StringRef::new(context_type);
let ci: Option<synta::Utf8StringRef<'_>> = context_info.map(synta::Utf8StringRef::new);
let ctx = AuthenticationContext {
context_type: ct,
context_info: ci,
};
let mut enc = synta::Encoder::new(synta::Encoding::Der);
match ctx.encode(&mut enc).and_then(|()| enc.finish()) {
Ok(bytes) => self.encoded.extend_from_slice(&bytes),
Err(e) => {
self.error = Some(format!("AuthenticationContext DER encoding failed: {e}"));
}
}
self
}
pub fn build(self) -> Result<Vec<u8>, String> {
if let Some(e) = self.error {
return Err(e);
}
if self.encoded.is_empty() {
return Err("at least one AuthenticationContext entry is required".to_string());
}
Ok(encode_sequence(self.encoded))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_single_context_no_info() {
let der = AuthenticationContextsBuilder::new()
.add("urn:id:skatteverket:2:1.0", None)
.build()
.expect("build should succeed");
let decoded: Vec<crate::ace88_types::AuthenticationContext<'_>> =
synta::Decoder::new(&der, synta::Encoding::Der)
.decode()
.expect("round-trip decode failed");
assert_eq!(decoded.len(), 1);
assert!(decoded[0].context_info.is_none());
}
#[test]
fn build_two_contexts_with_info() {
let der = AuthenticationContextsBuilder::new()
.add("urn:id:ctx:1", Some("https://example.com/ctx1"))
.add("urn:id:ctx:2", Some("https://example.com/ctx2"))
.build()
.expect("build should succeed");
let decoded: Vec<crate::ace88_types::AuthenticationContext<'_>> =
synta::Decoder::new(&der, synta::Encoding::Der)
.decode()
.expect("round-trip decode failed");
assert_eq!(decoded.len(), 2);
assert!(decoded[0].context_info.is_some());
assert!(decoded[1].context_info.is_some());
}
#[test]
fn build_empty_returns_error() {
let err = AuthenticationContextsBuilder::new().build();
assert!(err.is_err());
}
}