1#![deny(missing_docs)]
81
82use std::collections::BTreeMap;
83use std::error::Error;
84use std::fmt::{self, Display, Formatter};
85
86pub use wip_interface_macros::{WipType, interface};
87pub use wip_protocol;
88pub use wip_protocol::{
89 Documentation, EnumCase, FieldDeclaration, InterfaceDescriptor, MAX_SAFE_INTEGER,
90 MIN_SAFE_INTEGER, OperationDeclaration, ParameterDeclaration, ReturnDeclaration,
91 TypeDeclaration, TypeExpr, UnionCase, ValidationError, Value,
92};
93
94pub trait WipType: Sized {
101 fn type_expr() -> TypeExpr;
103
104 fn collect_declarations(declarations: &mut Vec<TypeDeclaration>);
108
109 fn encode(self) -> Result<Value, CodecError>;
111
112 fn decode(value: Value) -> Result<Self, CodecError>;
114}
115
116pub trait InterfaceDefinition {
119 fn descriptor() -> InterfaceDescriptor;
121}
122
123pub trait InterfaceImplementation<T>: InterfaceDefinition {
129 fn dispatch(
132 implementation: &T,
133 operation: &str,
134 arguments: BTreeMap<String, Value>,
135 ) -> Result<Value, DispatchError>;
136}
137
138pub type OperationResult<T> = Result<T, HostError>;
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub enum EffectGuarantee {
152 NotCommitted,
154 Unknown,
156}
157
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
160pub enum HostErrorKind {
161 Internal,
163 ResourceLimitExceeded,
165}
166
167#[derive(Debug, Clone, PartialEq, Eq)]
174pub struct HostError {
175 kind: HostErrorKind,
176 effect_guarantee: EffectGuarantee,
177 message: String,
178}
179
180impl HostError {
181 #[must_use]
183 pub fn internal(message: impl Into<String>, effect_guarantee: EffectGuarantee) -> Self {
184 Self::new(HostErrorKind::Internal, effect_guarantee, message)
185 }
186
187 #[must_use]
189 pub fn resource_limit(message: impl Into<String>, effect_guarantee: EffectGuarantee) -> Self {
190 Self::new(
191 HostErrorKind::ResourceLimitExceeded,
192 effect_guarantee,
193 message,
194 )
195 }
196
197 fn new(
198 kind: HostErrorKind,
199 effect_guarantee: EffectGuarantee,
200 message: impl Into<String>,
201 ) -> Self {
202 Self {
203 kind,
204 effect_guarantee,
205 message: bounded_diagnostic(message.into()),
206 }
207 }
208
209 #[must_use]
211 pub fn kind(&self) -> HostErrorKind {
212 self.kind
213 }
214
215 #[must_use]
217 pub fn effect_guarantee(&self) -> EffectGuarantee {
218 self.effect_guarantee
219 }
220
221 #[must_use]
226 pub fn message(&self) -> &str {
227 &self.message
228 }
229}
230
231const MAX_DIAGNOSTIC_BYTES: usize = 512;
232
233fn bounded_diagnostic(mut message: String) -> String {
234 if message.len() <= MAX_DIAGNOSTIC_BYTES {
235 return message;
236 }
237 let mut end = MAX_DIAGNOSTIC_BYTES;
238 while !message.is_char_boundary(end) {
239 end -= 1;
240 }
241 message.truncate(end);
242 message
243}
244
245impl Display for HostError {
246 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
247 formatter.write_str(&self.message)
248 }
249}
250
251impl Error for HostError {}
252
253#[derive(Debug, Clone, PartialEq, Eq)]
255pub struct CodecError {
256 path: Vec<String>,
257 message: String,
258}
259
260impl CodecError {
261 #[must_use]
263 pub fn new(message: impl Into<String>) -> Self {
264 Self {
265 path: Vec::new(),
266 message: message.into(),
267 }
268 }
269
270 #[must_use]
272 pub fn expected(expected: &str, actual: &Value) -> Self {
273 Self::new(format!("expected {expected}, found {:?}", actual.kind()))
274 }
275
276 #[must_use]
278 pub fn at(mut self, segment: impl Into<String>) -> Self {
279 self.path.insert(0, segment.into());
280 self
281 }
282
283 #[must_use]
285 pub fn path(&self) -> &[String] {
286 &self.path
287 }
288
289 #[must_use]
291 pub fn message(&self) -> &str {
292 &self.message
293 }
294}
295
296impl Display for CodecError {
297 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
298 if self.path.is_empty() {
299 write!(formatter, "WIP codec error: {}", self.message)
300 } else {
301 write!(
302 formatter,
303 "WIP codec error at {}: {}",
304 self.path.join("."),
305 self.message
306 )
307 }
308 }
309}
310
311impl Error for CodecError {}
312
313#[derive(Debug)]
315pub enum DispatchError {
316 UnknownOperation(String),
318 InvalidArguments(wip_protocol::ValidationError),
320 Decode(CodecError),
322 Host(HostError),
324 Panic,
329 Encode(CodecError),
331 InvalidResult(wip_protocol::ValidationError),
333}
334
335#[derive(Debug, Clone, Copy, PartialEq, Eq)]
338pub enum DispatchPhase {
339 BeforeInvocation,
341 AfterInvocation,
344}
345
346impl DispatchError {
347 #[must_use]
349 pub fn phase(&self) -> DispatchPhase {
350 match self {
351 Self::UnknownOperation(_) | Self::InvalidArguments(_) | Self::Decode(_) => {
352 DispatchPhase::BeforeInvocation
353 }
354 Self::Host(_) | Self::Panic | Self::Encode(_) | Self::InvalidResult(_) => {
355 DispatchPhase::AfterInvocation
356 }
357 }
358 }
359
360 #[must_use]
366 pub fn effect_guarantee(&self) -> Option<EffectGuarantee> {
367 match self {
368 Self::UnknownOperation(_) | Self::InvalidArguments(_) | Self::Decode(_) => None,
369 Self::Host(error) => Some(error.effect_guarantee()),
370 Self::Panic | Self::Encode(_) | Self::InvalidResult(_) => {
371 Some(EffectGuarantee::Unknown)
372 }
373 }
374 }
375}
376
377impl Display for DispatchError {
378 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
379 match self {
380 Self::UnknownOperation(operation) => {
381 write!(formatter, "unknown operation `{operation}`")
382 }
383 Self::InvalidArguments(error) => write!(formatter, "invalid arguments: {error}"),
384 Self::Decode(error) => write!(formatter, "argument decoding failed: {error}"),
385 Self::Host(error) => write!(formatter, "host operation failed: {error}"),
386 Self::Panic => formatter.write_str("operation implementation panicked"),
387 Self::Encode(error) => write!(formatter, "result encoding failed: {error}"),
388 Self::InvalidResult(error) => write!(formatter, "invalid operation result: {error}"),
389 }
390 }
391}
392
393impl Error for DispatchError {
394 fn source(&self) -> Option<&(dyn Error + 'static)> {
395 match self {
396 Self::UnknownOperation(_) | Self::Panic => None,
397 Self::InvalidArguments(error) | Self::InvalidResult(error) => Some(error),
398 Self::Decode(error) | Self::Encode(error) => Some(error),
399 Self::Host(error) => Some(error),
400 }
401 }
402}
403
404#[derive(Debug, Clone, PartialEq, Eq, Default)]
409pub struct Bytes(pub Vec<u8>);
410
411impl From<Vec<u8>> for Bytes {
412 fn from(value: Vec<u8>) -> Self {
413 Self(value)
414 }
415}
416
417impl From<Bytes> for Vec<u8> {
418 fn from(value: Bytes) -> Self {
419 value.0
420 }
421}
422
423#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
429pub struct Entry(String);
430
431impl Entry {
432 pub fn new(path: impl Into<String>) -> Result<Self, CodecError> {
434 let path = path.into();
435 validate_entry_path(&path)?;
436 Ok(Self(path))
437 }
438
439 #[must_use]
441 pub fn as_str(&self) -> &str {
442 &self.0
443 }
444
445 #[must_use]
447 pub fn into_string(self) -> String {
448 self.0
449 }
450}
451
452impl TryFrom<String> for Entry {
453 type Error = CodecError;
454
455 fn try_from(path: String) -> Result<Self, Self::Error> {
456 Self::new(path)
457 }
458}
459
460impl std::str::FromStr for Entry {
461 type Err = CodecError;
462
463 fn from_str(path: &str) -> Result<Self, Self::Err> {
464 Self::new(path)
465 }
466}
467
468#[derive(Debug, Clone, PartialEq)]
470pub struct JsonMember {
471 pub name: String,
473 pub value: JsonValue,
475}
476
477#[derive(Debug, Clone, PartialEq)]
484pub enum JsonValue {
485 Null,
487 Boolean(bool),
489 Integer(i64),
491 Number(f64),
493 String(String),
495 Array(Vec<JsonValue>),
497 Object(Vec<JsonMember>),
499}
500
501impl WipType for () {
502 fn type_expr() -> TypeExpr {
503 TypeExpr::Unit
504 }
505
506 fn collect_declarations(_: &mut Vec<TypeDeclaration>) {}
507
508 fn encode(self) -> Result<Value, CodecError> {
509 Ok(Value::Unit)
510 }
511
512 fn decode(value: Value) -> Result<Self, CodecError> {
513 match value {
514 Value::Unit => Ok(()),
515 other => Err(CodecError::expected("unit", &other)),
516 }
517 }
518}
519
520impl WipType for bool {
521 fn type_expr() -> TypeExpr {
522 TypeExpr::Boolean
523 }
524
525 fn collect_declarations(_: &mut Vec<TypeDeclaration>) {}
526
527 fn encode(self) -> Result<Value, CodecError> {
528 Ok(Value::Boolean(self))
529 }
530
531 fn decode(value: Value) -> Result<Self, CodecError> {
532 match value {
533 Value::Boolean(value) => Ok(value),
534 other => Err(CodecError::expected("boolean", &other)),
535 }
536 }
537}
538
539macro_rules! integer_codec {
540 ($($ty:ty),+ $(,)?) => {
541 $(
542 impl WipType for $ty {
543 fn type_expr() -> TypeExpr {
544 TypeExpr::Integer
545 }
546
547 fn collect_declarations(_: &mut Vec<TypeDeclaration>) {}
548
549 fn encode(self) -> Result<Value, CodecError> {
550 let value = i64::try_from(self).map_err(|_| {
551 CodecError::new("integer is outside the protocol i64 range")
552 })?;
553 ensure_safe_integer(value)?;
554 Ok(Value::Integer(value))
555 }
556
557 fn decode(value: Value) -> Result<Self, CodecError> {
558 let Value::Integer(value) = value else {
559 return Err(CodecError::expected("integer", &value));
560 };
561 ensure_safe_integer(value)?;
562 Self::try_from(value).map_err(|_| {
563 CodecError::new(concat!("integer does not fit Rust `", stringify!($ty), "`"))
564 })
565 }
566 }
567 )+
568 };
569}
570
571integer_codec!(
572 i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
573);
574
575fn ensure_safe_integer(value: i64) -> Result<(), CodecError> {
576 if (MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&value) {
577 Ok(())
578 } else {
579 Err(CodecError::new(format!(
580 "integer {value} is outside the JavaScript-lossless protocol range"
581 )))
582 }
583}
584
585impl WipType for f64 {
586 fn type_expr() -> TypeExpr {
587 TypeExpr::Number
588 }
589
590 fn collect_declarations(_: &mut Vec<TypeDeclaration>) {}
591
592 fn encode(self) -> Result<Value, CodecError> {
593 ensure_finite(self)?;
594 Ok(Value::Number(self))
595 }
596
597 fn decode(value: Value) -> Result<Self, CodecError> {
598 match value {
599 Value::Number(value) => {
600 ensure_finite(value)?;
601 Ok(value)
602 }
603 other => Err(CodecError::expected("number", &other)),
604 }
605 }
606}
607
608impl WipType for f32 {
609 fn type_expr() -> TypeExpr {
610 TypeExpr::Number
611 }
612
613 fn collect_declarations(_: &mut Vec<TypeDeclaration>) {}
614
615 fn encode(self) -> Result<Value, CodecError> {
616 let value = f64::from(self);
617 ensure_finite(value)?;
618 Ok(Value::Number(value))
619 }
620
621 fn decode(value: Value) -> Result<Self, CodecError> {
622 let value = f64::decode(value)?;
623 let narrowed = value as Self;
624 if narrowed.is_finite() {
625 Ok(narrowed)
626 } else {
627 Err(CodecError::new("number does not fit finite Rust `f32`"))
628 }
629 }
630}
631
632fn ensure_finite(value: f64) -> Result<(), CodecError> {
633 if value.is_finite() {
634 Ok(())
635 } else {
636 Err(CodecError::new("protocol numbers must be finite"))
637 }
638}
639
640impl WipType for String {
641 fn type_expr() -> TypeExpr {
642 TypeExpr::String
643 }
644
645 fn collect_declarations(_: &mut Vec<TypeDeclaration>) {}
646
647 fn encode(self) -> Result<Value, CodecError> {
648 Ok(Value::String(self))
649 }
650
651 fn decode(value: Value) -> Result<Self, CodecError> {
652 match value {
653 Value::String(value) => Ok(value),
654 other => Err(CodecError::expected("string", &other)),
655 }
656 }
657}
658
659impl WipType for Bytes {
660 fn type_expr() -> TypeExpr {
661 TypeExpr::Bytes
662 }
663
664 fn collect_declarations(_: &mut Vec<TypeDeclaration>) {}
665
666 fn encode(self) -> Result<Value, CodecError> {
667 Ok(Value::Bytes(self.0))
668 }
669
670 fn decode(value: Value) -> Result<Self, CodecError> {
671 match value {
672 Value::Bytes(value) => Ok(Self(value)),
673 other => Err(CodecError::expected("bytes", &other)),
674 }
675 }
676}
677
678impl WipType for JsonValue {
679 fn type_expr() -> TypeExpr {
680 TypeExpr::Json
681 }
682
683 fn collect_declarations(_: &mut Vec<TypeDeclaration>) {}
684
685 fn encode(self) -> Result<Value, CodecError> {
686 encode_json(self)
687 }
688
689 fn decode(value: Value) -> Result<Self, CodecError> {
690 decode_json(value)
691 }
692}
693
694fn encode_json(value: JsonValue) -> Result<Value, CodecError> {
695 match value {
696 JsonValue::Null => Ok(Value::Unit),
697 JsonValue::Boolean(value) => Ok(Value::Boolean(value)),
698 JsonValue::Integer(value) => {
699 ensure_safe_integer(value)?;
700 Ok(Value::Integer(value))
701 }
702 JsonValue::Number(value) => {
703 ensure_finite(value)?;
704 Ok(Value::Number(value))
705 }
706 JsonValue::String(value) => Ok(Value::String(value)),
707 JsonValue::Array(values) => values
708 .into_iter()
709 .enumerate()
710 .map(|(index, value)| encode_json(value).map_err(|error| error.at(index.to_string())))
711 .collect::<Result<Vec<_>, _>>()
712 .map(Value::List),
713 JsonValue::Object(members) => {
714 let mut fields = BTreeMap::new();
715 for member in members {
716 let name = member.name;
717 let value = encode_json(member.value).map_err(|error| error.at(name.clone()))?;
718 if fields.insert(name.clone(), value).is_some() {
719 return Err(CodecError::new(format!("duplicate JSON member `{name}`")).at(name));
720 }
721 }
722 Ok(Value::Record(fields))
723 }
724 }
725}
726
727fn decode_json(value: Value) -> Result<JsonValue, CodecError> {
728 match value {
729 Value::Unit => Ok(JsonValue::Null),
730 Value::Boolean(value) => Ok(JsonValue::Boolean(value)),
731 Value::Integer(value) => {
732 ensure_safe_integer(value)?;
733 Ok(JsonValue::Integer(value))
734 }
735 Value::Number(value) => {
736 ensure_finite(value)?;
737 Ok(JsonValue::Number(value))
738 }
739 Value::String(value) => Ok(JsonValue::String(value)),
740 Value::Bytes(_) => Err(CodecError::new("bytes are not valid inside JSON")),
741 Value::List(values) => values
742 .into_iter()
743 .enumerate()
744 .map(|(index, value)| decode_json(value).map_err(|error| error.at(index.to_string())))
745 .collect::<Result<Vec<_>, _>>()
746 .map(JsonValue::Array),
747 Value::Record(fields) => fields
748 .into_iter()
749 .map(|(name, value)| {
750 decode_json(value)
751 .map(|value| JsonMember {
752 name: name.clone(),
753 value,
754 })
755 .map_err(|error| error.at(name))
756 })
757 .collect::<Result<Vec<_>, _>>()
758 .map(JsonValue::Object),
759 }
760}
761
762impl<T: WipType> WipType for Vec<T> {
763 fn type_expr() -> TypeExpr {
764 TypeExpr::List {
765 items: Box::new(T::type_expr()),
766 }
767 }
768
769 fn collect_declarations(declarations: &mut Vec<TypeDeclaration>) {
770 T::collect_declarations(declarations);
771 }
772
773 fn encode(self) -> Result<Value, CodecError> {
774 self.into_iter()
775 .enumerate()
776 .map(|(index, value)| value.encode().map_err(|error| error.at(index.to_string())))
777 .collect::<Result<Vec<_>, _>>()
778 .map(Value::List)
779 }
780
781 fn decode(value: Value) -> Result<Self, CodecError> {
782 let Value::List(values) = value else {
783 return Err(CodecError::expected("list", &value));
784 };
785 values
786 .into_iter()
787 .enumerate()
788 .map(|(index, value)| T::decode(value).map_err(|error| error.at(index.to_string())))
789 .collect()
790 }
791}
792
793impl WipType for Entry {
794 fn type_expr() -> TypeExpr {
795 TypeExpr::Entry
796 }
797
798 fn collect_declarations(_: &mut Vec<TypeDeclaration>) {}
799
800 fn encode(self) -> Result<Value, CodecError> {
801 validate_entry_path(&self.0)?;
802 Ok(Value::String(self.0))
803 }
804
805 fn decode(value: Value) -> Result<Self, CodecError> {
806 let Value::String(path) = value else {
807 return Err(CodecError::expected("entry path string", &value));
808 };
809 Self::new(path)
810 }
811}
812
813fn validate_entry_path(path: &str) -> Result<(), CodecError> {
814 wip_protocol::validate_path(path)
815 .map_err(|error| CodecError::new(format!("invalid canonical Worldspace path: {error}")))
816}
817
818#[doc(hidden)]
824pub mod __private {
825 use std::collections::BTreeMap;
826
827 use wip_protocol::{InterfaceDescriptor, TypeDeclaration, ValidationError, Value};
828
829 use crate::{CodecError, Documentation, WipType};
830
831 pub fn documentation(summary: &str, details: Option<&str>) -> Option<Documentation> {
833 if summary.is_empty() && details.is_none() {
834 None
835 } else {
836 Some(Documentation {
837 summary: summary.into(),
838 details: details.map(str::to_owned),
839 })
840 }
841 }
842
843 pub fn register_declaration(
847 declarations: &mut Vec<TypeDeclaration>,
848 declaration: TypeDeclaration,
849 ) -> bool {
850 if declarations.iter().any(|existing| existing == &declaration) {
851 false
852 } else {
853 declarations.push(declaration);
854 true
855 }
856 }
857
858 pub fn validate_arguments(
860 descriptor: &InterfaceDescriptor,
861 operation: &str,
862 arguments: &BTreeMap<String, Value>,
863 ) -> Result<(), ValidationError> {
864 descriptor.validate_arguments(operation, arguments)
865 }
866
867 pub fn decode_type_mismatch(expected: &str, actual: Value) -> CodecError {
869 CodecError::expected(expected, &actual)
870 }
871
872 pub fn decode_unknown_variant(type_name: &str, variant: String) -> CodecError {
874 CodecError::new(format!(
875 "unknown variant `{variant}` for WIP type `{type_name}`"
876 ))
877 .at(variant)
878 }
879
880 pub struct RecordDecoder {
882 fields: BTreeMap<String, Value>,
883 }
884
885 impl RecordDecoder {
886 pub fn new(fields: BTreeMap<String, Value>) -> Self {
888 Self { fields }
889 }
890
891 pub fn required<T: WipType>(&mut self, name: &str) -> Result<T, CodecError> {
893 let value = self.take(name).ok_or_else(|| {
894 CodecError::new(format!("missing required field `{name}`")).at(name)
895 })?;
896 T::decode(value).map_err(|error| error.at(name))
897 }
898
899 pub fn optional<T: WipType>(&mut self, name: &str) -> Result<Option<T>, CodecError> {
901 self.take(name)
902 .map(T::decode)
903 .transpose()
904 .map_err(|error| error.at(name))
905 }
906
907 pub fn finish(self) -> Result<(), CodecError> {
909 if let Some(name) = self.fields.keys().next() {
910 Err(CodecError::new(format!("unknown field `{name}`")).at(name.clone()))
911 } else {
912 Ok(())
913 }
914 }
915
916 fn take(&mut self, name: &str) -> Option<Value> {
917 self.fields.remove(name)
918 }
919 }
920}