lib/serialization: prepare to collect errors and customize reactions to them
[nit.git] / lib / serialization / serialization.nit
index aa8da21..d120254 100644 (file)
@@ -16,7 +16,7 @@
 
 # Abstract services to serialize Nit objects to different formats
 #
 
 # Abstract services to serialize Nit objects to different formats
 #
-# This module declares the `auto_serializable` annotation to mark Nit classes as serializable.
+# This module declares the `serialize` annotation to mark Nit classes as serializable.
 # For an introduction to this service, refer to the documentation of the `serialization` group.
 # This documentation provides more technical information on interesting entitie of this module.
 #
 # For an introduction to this service, refer to the documentation of the `serialization` group.
 # This documentation provides more technical information on interesting entitie of this module.
 #
@@ -44,6 +44,8 @@
 #   `notify_of_creation` must be redefined.
 module serialization is
        new_annotation auto_serializable
 #   `notify_of_creation` must be redefined.
 module serialization is
        new_annotation auto_serializable
+       new_annotation serialize
+       new_annotation noserialize
 end
 
 # Abstract serialization service to be sub-classed by specialized services.
 end
 
 # Abstract serialization service to be sub-classed by specialized services.
@@ -86,7 +88,7 @@ end
 # Abstract deserialization service
 #
 # After initialization of one of its sub-classes, call `deserialize`
 # Abstract deserialization service
 #
 # After initialization of one of its sub-classes, call `deserialize`
-interface Deserializer
+abstract class Deserializer
        # Main method of this class, returns a Nit object
        fun deserialize: nullable Object is abstract
 
        # Main method of this class, returns a Nit object
        fun deserialize: nullable Object is abstract
 
@@ -108,6 +110,48 @@ interface Deserializer
                print "Error: doesn't know how to deserialize class \"{class_name}\""
                abort
        end
                print "Error: doesn't know how to deserialize class \"{class_name}\""
                abort
        end
+
+       # Should `self` keep trying to deserialize an object after an error?
+       #
+       # This behavior takes effect after each attribute deserialization with
+       # errors such as a missing attribute or the value is of the wrong type.
+       # If `keep_going`, the attribute will be skipped but the engine will
+       # deserialize the next attribute.
+       # If `not keep_going`, the engine stops deserializing right away.
+       #
+       # When at `true`, this may cause the accumulation of a lot of entries in `errors`.
+       #
+       # Default at `true`.
+       var keep_going: nullable Bool is writable
+
+       # Errors encountered in the last call to `deserialize`
+       var errors = new Array[Error]
+end
+
+# Deserialization got wrong attribute names
+class AttributeTypeError
+       super Error
+
+       # Parent object of the problematic attribute
+       var receiver: Object
+
+       # Name of the problematic attribute in `receiver`
+       var attribute_name: String
+
+       # Deserialized object that isn't of the `expected_type`
+       var attribute: nullable Object
+
+       # Name of the type expected for `attribute`
+       var expected_type: String
+
+       redef fun to_s
+       do
+               var attribute = attribute
+               var found_type = if attribute != null then attribute.class_name else "null"
+
+               return "Deserialization Error: {
+               }Wrong type on `{receiver.class_name}::{attribute_name}` expected `{expected_type}`, got `{found_type}`"
+       end
 end
 
 # Instances of this class can be passed to `Serializer::serialize`
 end
 
 # Instances of this class can be passed to `Serializer::serialize`
@@ -141,6 +185,19 @@ interface Serializable
        init from_deserializer(deserializer: Deserializer) do end
 end
 
        init from_deserializer(deserializer: Deserializer) do end
 end
 
+redef interface Object
+       # Is `self` the same as `other` in a serialization context?
+       #
+       # Used to determine if an object has already been serialized.
+       fun is_same_serialized(other: nullable Object): Bool do return is_same_instance(other)
+
+       # Hash value use for serialization
+       #
+       # Used in combination with `is_same_serialized`. If two objects are the same
+       # in a serialization context, they must have the same `serialization_hash`.
+       fun serialization_hash: Int do return object_id
+end
+
 # Instances of this class are not delayed and instead serialized immediately
 # This applies mainly to `universal` types
 interface DirectSerializable
 # Instances of this class are not delayed and instead serialized immediately
 # This applies mainly to `universal` types
 interface DirectSerializable
@@ -155,4 +212,39 @@ redef class Int super DirectSerializable end
 redef class Float super DirectSerializable end
 redef class NativeString super DirectSerializable end
 redef class String super DirectSerializable end
 redef class Float super DirectSerializable end
 redef class NativeString super DirectSerializable end
 redef class String super DirectSerializable end
-redef class Array[E] super Serializable end
+redef class SimpleCollection[E] super Serializable end
+redef class Map[K, V] super Serializable end
+
+redef class Couple[F, S]
+       super Serializable
+
+       redef init from_deserializer(v)
+       do
+               v.notify_of_creation self
+               var first = v.deserialize_attribute("first")
+               var second = v.deserialize_attribute("second")
+               init(first, second)
+       end
+
+       redef fun core_serialize_to(v)
+       do
+               v.serialize_attribute("first", first)
+               v.serialize_attribute("second", second)
+       end
+end
+
+redef class Container[E]
+       super Serializable
+
+       redef init from_deserializer(v)
+       do
+               v.notify_of_creation self
+               var item = v.deserialize_attribute("item")
+               init item
+       end
+
+       redef fun core_serialize_to(v)
+       do
+               v.serialize_attribute("item", first)
+       end
+end