extern crate alloc;
use alloc::vec::Vec;
use uor_foundation::enforcement::ShapeViolation;
use uor_foundation::pipeline::{ConstrainedTypeShape, ConstraintRef, IntoBindingValue};
use uor_foundation::{DefaultHostTypes, ViolationKind};
use uor_foundation_sdk::{output_shape, prism_model};
use crate::resolvers::AddressResolverTuple;
use crate::shapes::bounds::AddrBounds;
use crate::shapes::hasher::Sha256Hasher;
#[allow(unused_imports)]
use crate::verbs::{address_inference, VERB_TERMS_ADDRESS_INFERENCE};
pub const ADDRESS_LABEL_BYTES: usize = 71;
pub const JSON_INPUT_MAX_BYTES: usize = 3968;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct JsonInput {
pub bytes: Vec<u8>,
}
impl JsonInput {
const LENGTH_VIOLATION: ShapeViolation = ShapeViolation {
shape_iri: "https://uor.foundation/addr/JsonInput",
constraint_iri: "https://uor.foundation/addr/JsonInput/maxBytes",
property_iri: "https://uor.foundation/addr/JsonInput/byteCount",
expected_range: "http://www.w3.org/2001/XMLSchema#nonNegativeInteger",
min_count: 0,
max_count: JSON_INPUT_MAX_BYTES as u32,
kind: ViolationKind::ValueCheck,
};
pub fn new(bytes: Vec<u8>) -> Result<Self, ShapeViolation> {
if bytes.len() > JSON_INPUT_MAX_BYTES {
return Err(Self::LENGTH_VIOLATION);
}
Ok(Self { bytes })
}
}
impl ConstrainedTypeShape for JsonInput {
const IRI: &'static str = "https://uor.foundation/addr/JsonInput";
const SITE_COUNT: usize = JSON_INPUT_MAX_BYTES;
const CONSTRAINTS: &'static [ConstraintRef] = &[];
const CYCLE_SIZE: u64 = u64::MAX;
}
impl uor_foundation::pipeline::__sdk_seal::Sealed for JsonInput {}
impl IntoBindingValue for JsonInput {
const MAX_BYTES: usize = JSON_INPUT_MAX_BYTES;
fn into_binding_bytes(&self, out: &mut [u8]) -> Result<usize, ShapeViolation> {
if self.bytes.len() > out.len() {
return Err(Self::LENGTH_VIOLATION);
}
out[..self.bytes.len()].copy_from_slice(&self.bytes);
Ok(self.bytes.len())
}
}
output_shape! {
pub struct AddressLabel;
impl ConstrainedTypeShape for AddressLabel {
const IRI: &'static str = "https://uor.foundation/addr/AddressLabel";
const SITE_COUNT: usize = 71;
const CONSTRAINTS: &'static [ConstraintRef] = &[
ConstraintRef::Site { position: 0 }, ConstraintRef::Site { position: 1 },
ConstraintRef::Site { position: 2 }, ConstraintRef::Site { position: 3 },
ConstraintRef::Site { position: 4 }, ConstraintRef::Site { position: 5 },
ConstraintRef::Site { position: 6 }, ConstraintRef::Site { position: 7 },
ConstraintRef::Site { position: 8 }, ConstraintRef::Site { position: 9 },
ConstraintRef::Site { position: 10 }, ConstraintRef::Site { position: 11 },
ConstraintRef::Site { position: 12 }, ConstraintRef::Site { position: 13 },
ConstraintRef::Site { position: 14 }, ConstraintRef::Site { position: 15 },
ConstraintRef::Site { position: 16 }, ConstraintRef::Site { position: 17 },
ConstraintRef::Site { position: 18 }, ConstraintRef::Site { position: 19 },
ConstraintRef::Site { position: 20 }, ConstraintRef::Site { position: 21 },
ConstraintRef::Site { position: 22 }, ConstraintRef::Site { position: 23 },
ConstraintRef::Site { position: 24 }, ConstraintRef::Site { position: 25 },
ConstraintRef::Site { position: 26 }, ConstraintRef::Site { position: 27 },
ConstraintRef::Site { position: 28 }, ConstraintRef::Site { position: 29 },
ConstraintRef::Site { position: 30 }, ConstraintRef::Site { position: 31 },
ConstraintRef::Site { position: 32 }, ConstraintRef::Site { position: 33 },
ConstraintRef::Site { position: 34 }, ConstraintRef::Site { position: 35 },
ConstraintRef::Site { position: 36 }, ConstraintRef::Site { position: 37 },
ConstraintRef::Site { position: 38 }, ConstraintRef::Site { position: 39 },
ConstraintRef::Site { position: 40 }, ConstraintRef::Site { position: 41 },
ConstraintRef::Site { position: 42 }, ConstraintRef::Site { position: 43 },
ConstraintRef::Site { position: 44 }, ConstraintRef::Site { position: 45 },
ConstraintRef::Site { position: 46 }, ConstraintRef::Site { position: 47 },
ConstraintRef::Site { position: 48 }, ConstraintRef::Site { position: 49 },
ConstraintRef::Site { position: 50 }, ConstraintRef::Site { position: 51 },
ConstraintRef::Site { position: 52 }, ConstraintRef::Site { position: 53 },
ConstraintRef::Site { position: 54 }, ConstraintRef::Site { position: 55 },
ConstraintRef::Site { position: 56 }, ConstraintRef::Site { position: 57 },
ConstraintRef::Site { position: 58 }, ConstraintRef::Site { position: 59 },
ConstraintRef::Site { position: 60 }, ConstraintRef::Site { position: 61 },
ConstraintRef::Site { position: 62 }, ConstraintRef::Site { position: 63 },
ConstraintRef::Site { position: 64 }, ConstraintRef::Site { position: 65 },
ConstraintRef::Site { position: 66 }, ConstraintRef::Site { position: 67 },
ConstraintRef::Site { position: 68 }, ConstraintRef::Site { position: 69 },
ConstraintRef::Site { position: 70 },
];
}
}
prism_model! {
pub struct AddressModel;
pub struct AddressRoute;
impl PrismModel<
DefaultHostTypes,
AddrBounds,
Sha256Hasher,
AddressResolverTuple<Sha256Hasher>
> for AddressModel {
type Input = JsonInput;
type Output = AddressLabel;
type Route = AddressRoute;
fn route(input: Self::Input) -> Self::Output {
address_inference(input)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn json_input_round_trips_canonical_bytes() {
let canonical = br#"{"foo":"bar"}"#.to_vec();
let input = JsonInput::new(canonical.clone()).expect("within bounds");
let mut out = [0u8; 4096];
let n = input.into_binding_bytes(&mut out).expect("buffer fits");
assert_eq!(n, canonical.len());
assert_eq!(&out[..n], canonical.as_slice());
}
#[test]
fn json_input_rejects_oversize() {
let oversize = vec![0u8; JSON_INPUT_MAX_BYTES + 1];
let err = JsonInput::new(oversize).expect_err("must reject oversize");
assert_eq!(err.shape_iri, JsonInput::LENGTH_VIOLATION.shape_iri);
}
#[test]
fn address_label_site_count_matches_wire_format_width() {
assert_eq!(
<AddressLabel as ConstrainedTypeShape>::SITE_COUNT,
ADDRESS_LABEL_BYTES
);
}
#[test]
fn address_label_carries_seventy_one_disjoint_site_constraints() {
let cs = <AddressLabel as ConstrainedTypeShape>::CONSTRAINTS;
assert_eq!(cs.len(), 71, "71 Site constraints (algebraic-closure)");
for c in cs {
assert!(matches!(c, ConstraintRef::Site { .. }));
}
}
#[test]
fn address_label_constraints_pin_every_wire_format_site() {
let cs = <AddressLabel as ConstrainedTypeShape>::CONSTRAINTS;
let positions: Vec<u32> = cs
.iter()
.filter_map(|c| match c {
ConstraintRef::Site { position } => Some(*position),
_ => None,
})
.collect();
assert_eq!(positions.len(), 71);
for (i, &p) in positions.iter().enumerate() {
assert_eq!(p, i as u32, "Site_{i} pins position {i}");
}
}
}