Ver Fonte

Json improvements and bugfixes

Billy Barrow há 10 meses atrás
pai
commit
50a94ca545

+ 68 - 15
src/json/Json.vala

@@ -10,14 +10,51 @@ namespace InvercargillJson {
             node = Json.from_string (json);
         }
 
+        public JsonElement.from_file(string path) throws GLib.Error {
+            var parser = new Json.Parser();
+            parser.load_from_file(path);
+            node = parser.get_root();
+        }
+
+        public JsonElement.from_stream(InputStream stream) throws GLib.Error {
+            var parser = new Json.Parser();
+            parser.load_from_stream(stream);
+            node = parser.get_root();
+        }
+
+        public async JsonElement.from_stream_async(InputStream stream, GLib.Cancellable cancellable) throws GLib.Error {
+            var parser = new Json.Parser();
+            yield parser.load_from_stream_async (stream, cancellable);
+            node = parser.get_root();
+        }
+
         internal JsonElement.from_node(Json.Node node) {
             this.node = node;
         }
 
+        public JsonElement.from_properties(Invercargill.Properties properties) throws Invercargill.ElementError {
+            node = new Json.Node (Json.NodeType.OBJECT);
+            var object = new JsonObject();
+            foreach (var item in properties) {
+                object.set(item.key, new JsonElement.from_element(item.value));
+            }
+            node.set_object (object.json_glib_object);
+        }
+
+        public JsonElement.from_elements(Invercargill.Elements elements) throws Invercargill.ElementError {
+            node = new Json.Node (Json.NodeType.ARRAY);
+            var array = new JsonArray();
+            foreach (var item in elements) {
+                array.add(new JsonElement.from_element (item));
+            }
+            node.set_array (array.json_glib_object);
+        }
+
         public JsonElement.from_element(Invercargill.Element element) throws Invercargill.ElementError {
             // Null node is top priority
             if(element.is_null ()) {
                 node = new Json.Node(Json.NodeType.NULL);
+                node.init_null ();
                 return;
             }
 
@@ -49,24 +86,24 @@ namespace InvercargillJson {
                 node.set_string (element.as<DateTime>().format_iso8601());
                 return;
             }
+            if(type == typeof(Invercargill.BinaryData)) {
+                node.set_string (element.as<Invercargill.BinaryData>().to_base64());
+                return;
+            }
             if(type == typeof(string)) {
                 node.set_string (element.as<string>());
                 return;
             }
             if(type == typeof(bool)) {
-                node.set_string (element.as<string>());
+                node.set_boolean (element.as<bool>());
                 return;
             }
             if(type == typeof(double)) {
-                node.set_string (element.as<string>());
+                node.set_double (element.as<double?>());
                 return;
             }
             if(type == typeof(int64)) {
-                node.set_string (element.as<string>());
-                return;
-            }
-            if(type == typeof(bool)) {
-                node.set_string (element.as<string>());
+                node.set_int (element.as<int64?>());
                 return;
             }
             if(type == typeof(uint8)) {
@@ -125,6 +162,9 @@ namespace InvercargillJson {
         }
 
         public bool assignable_to_type (GLib.Type type) {
+            if(node.is_null() || type == typeof(Json.Node)) {
+                return true;
+            }
             if(node.get_node_type() == Json.NodeType.ARRAY) {
                 return
                     type == typeof(JsonArray) || 
@@ -138,9 +178,6 @@ namespace InvercargillJson {
                     type == typeof(Invercargill.Properties) ||
                     type == typeof(Json.Object);
             }
-            if(node.is_null() || type == typeof(Json.Node)) {
-                return true;
-            }
             if(node.get_value_type().is_a(typeof(int64)) || node.get_value_type ().is_a(typeof(double))) {
                 return
                     type == typeof(uint8) ||
@@ -158,6 +195,7 @@ namespace InvercargillJson {
             if(node.get_value_type().is_a(typeof(string))) {
                 return
                     type == typeof(string) ||
+                    type == typeof(Invercargill.BinaryData) ||
                     (type == typeof(DateTime) && new DateTime.from_iso8601(node.get_string(), null) != null);
             }
             if(node.get_value_type().is_a(typeof(bool))) {
@@ -173,15 +211,14 @@ namespace InvercargillJson {
                 result = null;
                 return false;
             }
-            if(node.is_null ()) {
-                result = null;
-                return true;
-            }
-
             if(typeof(T) == typeof(Json.Node)) {
                 result = node;
                 return true;
             }
+            if(node.is_null ()) {
+                result = null;
+                return true;
+            }
             if(typeof(T) == typeof(JsonArray) || typeof(T) == typeof(Invercargill.Elements)) {
                 result = new JsonArray.from_existing (node.get_array ());
                 return true;
@@ -256,6 +293,10 @@ namespace InvercargillJson {
                 result = new DateTime.from_iso8601(node.get_string (), null);
                 return true;
             }
+            if(typeof(T).is_a(typeof(Invercargill.BinaryData))) {
+                result = new Invercargill.BinaryData.from_base64(node.get_string ());
+                return true;
+            }
             if(typeof(T).is_a(typeof(bool))) {
                 result = node.get_boolean();
                 return true;
@@ -274,6 +315,18 @@ namespace InvercargillJson {
             return Json.to_string (node, pretty);
         }
 
+        public void write_to_stream(OutputStream stream) throws Error {
+            var gen = new Json.Generator();
+            gen.set_root (node);
+            gen.to_stream (stream);
+        }
+
+        public void write_to_file(string path) throws Error {
+            var gen = new Json.Generator();
+            gen.set_root (node);
+            gen.to_file (path);
+        }
+
     }
 
 }

+ 1 - 1
src/json/Object.vala

@@ -12,7 +12,7 @@ namespace InvercargillJson {
             object = new Json.Object();
         }
 
-        public JsonObject.from_existing(Json.Object object) {
+        internal JsonObject.from_existing(Json.Object object) {
             this.object = object;
         }
         public override void clear (string key) {

+ 45 - 0
src/lib/Attempt.vala

@@ -0,0 +1,45 @@
+namespace Invercargill {
+
+    public delegate T AttemptDelegate<T>() throws Error;
+    public delegate Tout AttemptTransformDelegate<Tin, Tout>(Tin input) throws Error;
+
+    [Compact]
+    [Immutable]
+    [CCode (copy_function = "invercargill_attempt_copy")]
+    public class Attempt<T> {
+
+        public T? result;
+        public Error? error;
+        public bool success;
+
+        public Attempt.unsuccessful(Error error) {
+            this.error = error;
+            success = false;
+        }
+
+        public Attempt.successful(T result) {
+            this.result = result;
+            success = true;
+        }
+
+        public Attempt(AttemptDelegate<T> func) {
+            try {
+                result = func();
+                success = true;
+            }
+            catch(Error e) {
+                error = e;
+                success = false;
+            }
+        }
+
+        public Attempt<T> copy() {
+            if(success) {
+                return new Attempt<T>.successful(result);
+            }
+            return new Attempt<T>.unsuccessful(error);
+        }
+
+    }
+
+}

+ 24 - 0
src/lib/Concrete/AttemptEnumerable.vala

@@ -0,0 +1,24 @@
+namespace Invercargill {
+
+    public class Attempts<T> : ProxyEnumerable<Attempt<T>>, Promotion<Attempt<T>> {
+
+        public Enumerable<Attempt<T>> wrap (Enumerable<Attempt<T>> enumerable) {
+            inner = enumerable;
+            results = where(a => a.success).select<T>(a => a.result);
+            errors = where(a => !a.success).select<Error>(a => a.error);
+            return this;
+        }
+        public bool can_wrap (GLib.Type element_type) {
+            return element_type.is_a (typeof(Attempt));
+        }
+
+        public Enumerable<T> results { get; private set; }
+        public Enumerable<Error> errors { get; private set; }
+
+        public bool fully_successful() {
+            return all(a => a.success);
+        }
+
+    }
+
+}

+ 39 - 0
src/lib/Concrete/CacheEnumerable.vala

@@ -0,0 +1,39 @@
+namespace Invercargill {
+
+    private class CacheEnumerable<T> : Enumerable<T> {
+
+        private Vector<T> vector;
+        private Enumerable<T> inner;
+        private Tracker<T> inner_tracker;
+
+        public CacheEnumerable(Enumerable<T> inner) {
+            this.inner = inner;
+            inner_tracker = this.inner.get_tracker();
+            vector = new Vector<T>();
+        }
+
+        public override Tracker<T> get_tracker () {
+            var i = 0;
+            return new LambdaTracker<T>(() => has_nth(i), () => vector.get_or_default(i++));
+        }
+
+        private bool has_nth(int n) {
+            lock(vector) {
+                if(vector.count() > n) {
+                    return true;
+                }
+                while(vector.count() <= n) {
+                    if(inner_tracker.has_next()) {
+                        vector.add(inner_tracker.get_next());
+                    }
+                    else {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        }
+
+    }
+
+}

+ 1 - 1
src/lib/Concrete/EquatableEnumerable.vala

@@ -1,6 +1,6 @@
 namespace Invercargill {
 
-    public class EquatableEnumerable<T> : ProxyEnumerable<T>, Promotion<T>, Equatable<Enumerable<T>> {
+    public class Equatables<T> : ProxyEnumerable<T>, Promotion<T>, Equatable<Enumerable<T>> {
 
         public bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(Equatable));

+ 1 - 1
src/lib/Concrete/Numbers/Implementations.vala

@@ -33,7 +33,7 @@ namespace Invercargill {
         }        
     }
 
-    public class UsignedNativeIntegers : NumberEnumerable<uint> {
+    public class UnsignedNativeIntegers : NumberEnumerable<uint> {
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(uint));
         }

+ 12 - 0
src/lib/Concrete/ProxyEnumerable.vala

@@ -209,6 +209,18 @@ namespace Invercargill {
             return inner.to_elements();
         }
 
+        public override Enumerable<Tout> select_where<Tout>(owned FilterTransformDelegate<T, Tout> transform) {
+            return inner.select_where<Tout>((owned)transform);
+        }
+
+        public override Attempts<Tout> try_select<Tout>(owned AttemptTransformDelegate<T, Tout> transform) {
+            return inner.try_select<Tout>((owned)transform);
+        }
+
+        public override Enumerable<T> cache() {
+            return inner.cache();
+        }
+
     }
 
 }

+ 1 - 1
src/lib/Delegates.vala

@@ -2,7 +2,7 @@ namespace Invercargill {
 
     public delegate void ItemDelegate<T>(T item);
 
-    public delegate bool TryTransformDelegate<Tin, Tout>(Tin item, ref Tout? res);
+    public delegate bool FilterTransformDelegate<Tin, Tout>(Tin item, ref Tout? res);
 
     public delegate Tout TransformDelegate<Tin, Tout>(Tin item);
 

+ 1 - 1
src/lib/Element.vala

@@ -21,7 +21,7 @@ namespace Invercargill {
     public interface Elements : Enumerable<Element> {
 
         public virtual Enumerable<T> elements_as<T>() {
-            return try_select<T>((e, ref r) => e.try_get_as<T>(out r));
+            return select_where<T>((e, ref r) => e.try_get_as<T>(out r));
         }
 
         public virtual Enumerable<T> assert_elements_as<T>() {

+ 19 - 3
src/lib/Enumerable.vala

@@ -87,14 +87,18 @@ namespace Invercargill {
             return new FilterQuery<T>(this, (owned)predicate);
         }
 
-        public virtual Enumerable<Tout> try_select<Tout>(owned TryTransformDelegate<T, Tout> transform) {
-            return new TryTransformQuery<T, Tout>(this, (owned)transform);
+        public virtual Enumerable<Tout> select_where<Tout>(owned FilterTransformDelegate<T, Tout> transform) {
+            return new FilterTransformQuery<T, Tout>(this, (owned)transform);
         }
 
         public virtual Enumerable<Tout> select<Tout>(owned TransformDelegate<T, Tout> transform) {
             return new TransformQuery<T, Tout>(this, (owned)transform);
         }
 
+        public virtual Attempts<Tout> try_select<Tout>(owned AttemptTransformDelegate<T, Tout> transform) {
+            return select<Attempt>(i => new Attempt<Tout>(() => transform(i))).cache().promote_to<Attempts>();
+        }
+
         public virtual Enumerable<Pair<TFirst, TSecond>> select_pairs<TFirst, TSecond>(owned TransformDelegate<T, TFirst> transform1, owned TransformDelegate<T, TSecond> transform2) {
             return select<Pair<TFirst, TSecond>>(i => new Pair<TFirst, TSecond>(transform1(i), true, transform2(i), true));
         }
@@ -391,6 +395,13 @@ namespace Invercargill {
             return new SealedEnumerable<T>(this);
         }
 
+        public virtual Enumerable<T> cache() {
+            if(this.get_type().is_a(typeof(CacheEnumerable))) {
+                return this;
+            }
+            return new CacheEnumerable<T>(this);
+        }
+
         public virtual Dictionary<TKey, T> to_dictionary<TKey>(TransformDelegate<T, TKey> key_selecter, HashFunc<TKey>? key_hash_func = null, EqualFunc<TKey>? key_equal_func = null) {
             var dict = new Dictionary<TKey, T>(key_hash_func, key_equal_func);       
             dict.set_all(select<KeyValuePair<TKey, T>>(i => new KeyValuePair<TKey, T>(key_selecter(i), i)));
@@ -415,7 +426,12 @@ namespace Invercargill {
 
         public virtual Elements to_elements() {
             var series = new ElementSeries();
-            series.add_all(select<Element>(i => new NativeElement<T>(i)));
+            if(typeof(T).is_a(typeof(Element))) {
+                series.add_all((Enumerable<Element>)this);
+            }
+            else {
+                series.add_all(select<Element>(i => new NativeElement<T>(i)));
+            }
             return series;
         }
 

+ 1 - 1
src/lib/Invercargill.vala

@@ -37,7 +37,7 @@ namespace Invercargill {
         };
     }
 
-    public static Enumerable<string> directory(string path, uint flags) throws FileError {
+    public static Enumerable<string> directory(string path, uint flags = 0) throws FileError {
         return new DirEnumerable(Dir.open(path, flags));
     }
 

+ 28 - 0
src/lib/PropertyMapper.vala

@@ -78,6 +78,34 @@ namespace Invercargill {
             return this;
         }
 
+        public PropertyMapperBuilder<T> map_collection<TCollection, TObj>(string name, owned PropertyGetter<T, Collection<TObj>> getter, owned PropertySetter<T, TCollection<TObj>> setter) requires (typeof(TCollection).is_a(typeof(Collection))) {
+            add_mapping(new PropertyMapping<T>() {
+                name = name,
+                getter = (o) => new NativeElement<Elements>(getter(o).to_elements()),
+                setter = (o,d) => {
+                    var collection = (Collection)Object.new(typeof(TCollection));
+                    collection.add_all(d.as<Elements>().elements_as<TObj>());
+                    setter(o, collection);
+                }
+            });
+            return this;
+        }
+
+        public PropertyMapperBuilder<T> map_collection_with<TCollection, TObj>(string name, owned PropertyGetter<T, Collection<TObj>> getter, owned PropertySetter<T, TCollection<TObj>> setter, PropertyMapper<TObj> mapper) requires (typeof(TCollection).is_a(typeof(Collection))) {
+            add_mapping(new PropertyMapping<T>() {
+                name = name,
+                getter = (o) => new NativeElement<Elements>(getter(o).select<Properties>(i => mapper.map_from(i)).to_elements()),
+                setter = (o,d) => {
+                    var collection = (Collection<TObj>)Object.new(typeof(TCollection));
+                    foreach (var properties in d.as<Elements>().elements_as<Properties>()) {
+                        collection.add(mapper.materialise(properties));
+                    }
+                    setter(o, collection);
+                }
+            });
+            return this;
+        }
+
         public PropertyMapperBuilder<T> set_constructor(ObjectConstructor<T> constructor) {
             this.constructor = constructor;
             return this;

+ 3 - 3
src/lib/Queries/TryTransform.vala → src/lib/Queries/FilterTransform.vala

@@ -1,9 +1,9 @@
 namespace Invercargill {
 
-    private class TryTransformQuery<Tin, Tout> : BaseQuery<Tin, Tout> {
-        private TryTransformDelegate<Tin, Tout> transform_func;
+    private class FilterTransformQuery<Tin, Tout> : BaseQuery<Tin, Tout> {
+        private FilterTransformDelegate<Tin, Tout> transform_func;
 
-        public TryTransformQuery(Enumerable<Tin> input, owned TryTransformDelegate<Tin, Tout> func) {
+        public FilterTransformQuery(Enumerable<Tin> input, owned FilterTransformDelegate<Tin, Tout> func) {
             this.input = input;
             transform_func = (owned)func;
         }

+ 4 - 1
src/lib/meson.build

@@ -23,6 +23,7 @@ sources += files('Element.vala')
 sources += files('Converter.vala')
 sources += files('PropertyMapper.vala')
 sources += files('KeyValuePair.vala')
+sources += files('Attempt.vala')
 
 sources += files('Queries/Query.vala')
 sources += files('Queries/Transform.vala')
@@ -34,7 +35,7 @@ sources += files('Queries/Take.vala')
 sources += files('Queries/Parallel.vala')
 sources += files('Queries/Unique.vala')
 sources += files('Queries/Position.vala')
-sources += files('Queries/TryTransform.vala')
+sources += files('Queries/FilterTransform.vala')
 
 sources += files('Concrete/ArrayEnumerable.vala')
 sources += files('Concrete/GeeEnumerable.vala')
@@ -52,6 +53,8 @@ sources += files('Concrete/Numbers/Implementations.vala')
 sources += files('Concrete/ProxyEnumerable.vala')
 sources += files('Concrete/SealedEnumerable.vala')
 sources += files('Concrete/EquatableEnumerable.vala')
+sources += files('Concrete/AttemptEnumerable.vala')
+sources += files('Concrete/CacheEnumerable.vala')
 
 sources += files('Collections/Collection.vala')
 sources += files('Collections/Series.vala')

+ 24 - 0
src/tests/Integration/Cache.vala

@@ -0,0 +1,24 @@
+using Invercargill;
+using Invercargill.Convert;
+
+void cache_tests() {
+
+
+    Test.add_func("/invercargill/operator/cache", () => {
+        var runs = 0;
+        var enumerable = range(0, 64).act(() => runs++).cache().promote_to<SignedNativeIntegers>();
+
+        var count = enumerable.count();
+        var min = enumerable.min();
+        var max = enumerable.max();
+        var average = enumerable.average();
+
+        assert_cmpint(64, CompareOperator.EQ, count);
+        assert_cmpint(0, CompareOperator.EQ, min);
+        assert_cmpint(63, CompareOperator.EQ, max);
+        assert_cmpint(31, CompareOperator.EQ, average);
+
+        assert_cmpint(64, CompareOperator.EQ, runs);
+    });
+
+}

+ 10 - 0
src/tests/Integration/Vector.vala

@@ -24,6 +24,16 @@ void vector_tests() {
         assert(vector.count() == 1);
     });
 
+    Test.add_func("/invercargill/structure/vector/from_attempts", () => {
+
+        var vector = range(0,64).select<Attempt<int>>(i => new Attempt<int>(() => i+2)).to_vector();
+
+        assert(vector.first_or_default().success);
+        assert(vector.get_or_default(1).success);
+        assert(vector.get_or_default(2).result == 4);
+        assert(vector.count() == 64);
+    });
+
     Test.add_func("/invercargill/structure/vector/add_objects", () => {
 
         var vector = new Vector<TestObject>();

+ 1 - 0
src/tests/TestRunner.vala

@@ -20,6 +20,7 @@ public static int main(string[] args) {
     dictionary_tests();
     property_mapper_tests();
     json_tests();
+    cache_tests();
     
     Test.run();
     

+ 1 - 0
src/tests/meson.build

@@ -19,6 +19,7 @@ sources += files('Integration/Numbers.vala')
 sources += files('Integration/Dictionary.vala')
 sources += files('Integration/PropertyMapper.vala')
 sources += files('Integration/Json.vala')
+sources += files('Integration/Cache.vala')
 
 sources += files('Speed/SpeedTest.vala')
 sources += files('Speed/Series.vala')