Jelajahi Sumber

Add debug functions

Billy Barrow 3 minggu lalu
induk
melakukan
fa3a276746

+ 141 - 0
src/lib/Debug.vala

@@ -0,0 +1,141 @@
+namespace Invercargill {
+
+    internal class DebugPrinter {
+
+        private const string COLOUR_RESET = "\x1b[0m";
+        private const string COLOUR_DEBUG = "\x1b[90m";
+        private const string COLOUR_PROGRAM = "\x1b[97m";
+        private const string COLOUR_CLASS = "\x1b[92m";
+        private const string COLOUR_CATEGORY = "\x1b[96m";
+
+        private static string format_colour(string colour, string message) {
+            return colour + message + COLOUR_RESET;
+        }
+
+        private static DebugOutputDelegate get_delegate(DebugOutputDelegate? override_func, bool formatting = true) {
+            DebugOutputDelegate default_output_func = m => printerr(m);
+            var output_func = override_func ?? default_output_func;
+            if(formatting){
+                return output_func;
+            }
+            return (o) => output_func(o
+                .replace(COLOUR_RESET, "")
+                .replace(COLOUR_DEBUG, "")
+                .replace(COLOUR_PROGRAM, "")
+                .replace(COLOUR_CLASS, "")
+                .replace(COLOUR_CATEGORY, ""));
+        }
+
+        public static void print_enumerable_dump<T>(Enumerable<T> enumerable, TransformDelegate<T, string> stringifier, string additional_message = "", DebugOutputDelegate? output_func = null, bool formatting = true) {
+            var output = get_delegate(output_func, formatting);
+
+            output("\n\n" + format_colour(COLOUR_DEBUG, "Invercargill enumerable debug dump: ") + format_colour(COLOUR_PROGRAM, additional_message) + "\n");
+            print_enumerable_info(enumerable.get_info(), 0, output_func, formatting);
+            
+            var count = 0;
+            output(format_colour(COLOUR_DEBUG, "\nBegin iterating enumerable:\n"));
+            foreach (var item in enumerable) {
+                output(format_colour(COLOUR_DEBUG, @" $count:\t") + format_colour(COLOUR_PROGRAM, stringifier(item)) + "\n");
+                count++;
+            }
+
+            output(format_colour(COLOUR_DEBUG, "End of enumerable iteration and dump.\n"));
+        }
+
+        public static void print_enumerable_information(EnumerableInfo info, string additional_message = "", DebugOutputDelegate? output_func = null, bool formatting = true) {
+            var output = get_delegate(output_func, formatting);
+
+            output("\n\n" + format_colour(COLOUR_DEBUG, "Invercargill enumerable debug type: ") + format_colour(COLOUR_PROGRAM, additional_message) + "\n");
+            print_enumerable_info(info, 0, output_func, formatting);
+        }
+
+        public static void print_trace_registration(EnumerableInfo info, string additional_message = "", DebugOutputDelegate? output_func = null, bool formatting = true) {
+            var output = get_delegate(output_func, formatting);
+
+            output("\n\n" + format_colour(COLOUR_DEBUG, "Invercargill enumerable debug trace: ") + format_colour(COLOUR_PROGRAM, additional_message) + "\n");
+            print_enumerable_info(info, 0, output_func, formatting);
+            output(format_colour(COLOUR_DEBUG, "Trace registered, output will be printed when the enumerable is iterated.\n"));
+        }
+
+        public static void print_trace(string message, string additional_message = "", DebugOutputDelegate? output_func = null, bool formatting = true) {
+            var output = get_delegate(output_func, formatting);
+            var seperator = (additional_message != null && additional_message.length > 0) ? ": " : "";
+            output(format_colour(COLOUR_DEBUG, "[TRACE] ") + format_colour(COLOUR_PROGRAM, additional_message) + format_colour(COLOUR_DEBUG, seperator) + format_colour(COLOUR_PROGRAM, message) + "\n");
+        }
+
+        public static void print_trace_debug_message(string message, string additional_message = "", DebugOutputDelegate? output_func = null, bool formatting = true) {
+            var output = get_delegate(output_func, formatting);
+            var seperator = (additional_message != null && additional_message.length > 0) ? ": " : "";
+            output(format_colour(COLOUR_DEBUG, "[TRACE] ") + format_colour(COLOUR_PROGRAM, additional_message) + format_colour(COLOUR_DEBUG, seperator) + format_colour(COLOUR_DEBUG, message) + "\n");
+        }
+
+        private static void print_enumerable_info(EnumerableInfo info, int indents, DebugOutputDelegate? output_func = null, bool formatting = true) {
+            var output = get_delegate(output_func, formatting);
+
+            var indent = "";
+            for(int i = 0; i < indents; i++) indent += "  ";
+            var has_sources = info.sources.any();
+            var known_count = info.count != null;
+            output(
+                format_colour(COLOUR_DEBUG, indent) +
+                format_colour(COLOUR_CATEGORY, info.category.to_string()) +
+                format_colour(COLOUR_DEBUG, " Enumerable ") +
+                format_colour(COLOUR_CLASS, info.enumerable_type.name()) +
+                format_colour(COLOUR_DEBUG, " with ") +
+                (known_count ? format_colour(COLOUR_PROGRAM, @"$(info.count) ") : "") +
+                format_colour(COLOUR_CLASS, info.element_type.name() ?? "UNKNOWN") +
+                format_colour(COLOUR_DEBUG, info.count == 1 ? " element" : " elements") +
+                (has_sources ? format_colour(COLOUR_DEBUG, ", sources from:") : ".") +
+                "\n"
+            );
+
+            if(has_sources)
+                info.sources.iterate(s => print_enumerable_info(s, indents + 1, output_func, formatting));
+
+        }
+
+    }
+
+    internal class TraceEnumerable<T> : Enumerable<T> {
+        private Enumerable<T> input;
+        private TransformDelegate<T, string> stringifier;
+        private string additional_message;
+        private DebugOutputDelegate? output_func;
+        private bool formatting;
+
+        public TraceEnumerable(owned Enumerable<T> input, EnumerableInfo info, owned TransformDelegate<T, string> stringifier, string additional_message = "", owned DebugOutputDelegate? output_func, bool formatting) {
+            this.input = (owned)input;
+            this.additional_message = additional_message;
+            this.stringifier = (owned)stringifier;
+            this.output_func = (owned)output_func;
+            this.formatting = formatting;
+            DebugPrinter.print_trace_registration(info, additional_message, output_func, formatting);
+        }
+
+        public override int? peek_count() {
+            var count = input.peek_count();
+            var str = count == null ? "NULL" : count.to_string();
+            DebugPrinter.print_trace_debug_message(@"peek_count() returned value $str", additional_message, output_func, formatting);
+            return count;
+        }
+
+        public override EnumerableInfo get_info() {
+            DebugPrinter.print_trace_debug_message(@"get_info() called", additional_message, output_func, formatting);
+            return new EnumerableInfo.infer_single(this, EnumerableCategory.COMPUTED, input);
+        }
+
+        public override Tracker<T> get_tracker() {
+            var tracker = input.get_tracker();
+            return new LambdaTracker<T>(
+                () => tracker.has_next(),
+                () => {
+                    var item = tracker.get_next();
+                    DebugPrinter.print_trace(stringifier(item), additional_message, output_func, formatting);
+                    return item;
+                }
+            );
+        }
+    }
+
+
+}

+ 2 - 0
src/lib/Delegates.vala

@@ -22,4 +22,6 @@ namespace Invercargill {
 
     public delegate Tout ErrorCatchingDelegate<Tin, Tout>(Tin item, Error error);
 
+    public delegate void DebugOutputDelegate(string output);
+
 }

+ 13 - 0
src/lib/Enumerable.vala

@@ -482,6 +482,19 @@ namespace Invercargill {
             return this;
         }
 
+        public virtual void debug_dump(TransformDelegate<T, string> stringifier, string additional_message = "", DebugOutputDelegate? output_func = null, bool formatting = true) {
+            DebugPrinter.print_enumerable_dump<T>(this, stringifier, additional_message, output_func, formatting);
+        }
+
+        public virtual Enumerable<T> debug_type(string additional_message = "", DebugOutputDelegate? output_func = null, bool formatting = true) {
+            DebugPrinter.print_enumerable_information(get_info(), additional_message, output_func, formatting);
+            return this;
+        }
+
+        public virtual Enumerable<T> debug_trace(owned TransformDelegate<T, string> stringifier, string additional_message = "", owned DebugOutputDelegate? output_func = null, bool formatting = true) {
+            return new TraceEnumerable<T>(this, get_info(), (owned)stringifier, additional_message, (owned)output_func, formatting);
+        }
+
         private PredicateDelegate<T> resolve_nullable_predicate(PredicateDelegate<T>? predicate) {
             if(predicate == null) {
                 return (p) => true;

+ 17 - 0
src/lib/EnumerableInfo.vala

@@ -57,6 +57,23 @@ namespace Invercargill {
         COMPUTED,
         CACHED,
         PROXY;
+
+        public string to_string() {
+            switch (this) {
+                case EnumerableCategory.IN_MEMORY:
+                    return "IN_MEMORY";
+                case EnumerableCategory.EXTERNAL:
+                    return "EXTERNAL";
+                case EnumerableCategory.COMPUTED:
+                    return "COMPUTED";
+                case EnumerableCategory.CACHED:
+                    return "CACHED";
+                case EnumerableCategory.PROXY:
+                    return "PROXY";
+                default:
+                    assert_not_reached();
+            }
+        }
     }
 
 }

+ 1 - 1
src/lib/Modifiers/Take.vala

@@ -13,7 +13,7 @@ namespace Invercargill.Modifiers {
             var c = input.peek_count();
             if(c == null)
                 return null;
-            return int.min(0, c - n_items);
+            return int.min(c, n_items);
         }
 
         public override EnumerableInfo get_info() {

+ 6 - 0
src/lib/Promotions/Equatables.vala

@@ -11,10 +11,16 @@ namespace Invercargill {
             return this;
         }
 
+        protected override Enumerable<T> new_wrap(Enumerable<T> enumerable) {
+            return new Equatables<T>().wrap(enumerable);
+        }
+
         public bool equals(Enumerable<T> other) {
             return this == other || this.matches(other, (a, b) => ((Equatable<T>)a).equals (b));
         }
 
+
+
     }
 
 }

+ 36 - 0
src/lib/Promotions/Numbers/Implementations.vala

@@ -1,6 +1,9 @@
 namespace Invercargill {
 
     public class SignedNativeIntegers : Numbers<int, SignedNativeIntegers> {
+        protected override Enumerable<int> new_wrap(Enumerable<int> enumerable) {
+            return new SignedNativeIntegers().wrap(enumerable);
+        }
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(int));
         }
@@ -34,6 +37,9 @@ namespace Invercargill {
     }
 
     public class UnsignedNativeIntegers : Numbers<uint, UnsignedNativeIntegers> {
+        protected override Enumerable<uint> new_wrap(Enumerable<uint> enumerable) {
+            return new UnsignedNativeIntegers().wrap(enumerable);
+        }
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(uint));
         }
@@ -67,6 +73,9 @@ namespace Invercargill {
     }
 
     public class Signed8BitIntegers : Numbers<int8, Signed8BitIntegers> {
+        protected override Enumerable<int8> new_wrap(Enumerable<int8> enumerable) {
+            return new Signed8BitIntegers().wrap(enumerable);
+        }
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(int8));
         }
@@ -100,6 +109,9 @@ namespace Invercargill {
     }
 
     public class Unsigned8BitIntegers : Numbers<uint8, Unsigned8BitIntegers> {
+        protected override Enumerable<uint8> new_wrap(Enumerable<uint8> enumerable) {
+            return new Unsigned8BitIntegers().wrap(enumerable);
+        }
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(uint8));
         }
@@ -133,6 +145,9 @@ namespace Invercargill {
     }
 
     public class Signed16BitIntegers : Numbers<int16, Signed16BitIntegers> {
+        protected override Enumerable<int16> new_wrap(Enumerable<int16> enumerable) {
+            return new Signed16BitIntegers().wrap(enumerable);
+        }
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(int16));
         }
@@ -166,6 +181,9 @@ namespace Invercargill {
     }
 
     public class Unsigned16BitIntegers : Numbers<uint16, Unsigned16BitIntegers> {
+        protected override Enumerable<uint16> new_wrap(Enumerable<uint16> enumerable) {
+            return new Unsigned16BitIntegers().wrap(enumerable);
+        }
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(uint16));
         }
@@ -199,6 +217,9 @@ namespace Invercargill {
     }
 
     public class Signed32BitIntegers : Numbers<int32, Signed32BitIntegers> {
+        protected override Enumerable<int32> new_wrap(Enumerable<int32> enumerable) {
+            return new Signed32BitIntegers().wrap(enumerable);
+        }
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(int32));
         }
@@ -232,6 +253,9 @@ namespace Invercargill {
     }
 
     public class Unsigned32BitIntegers : Numbers<uint32, Unsigned32BitIntegers> {
+        protected override Enumerable<uint32> new_wrap(Enumerable<uint32> enumerable) {
+            return new Unsigned32BitIntegers().wrap(enumerable);
+        }
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(uint32));
         }
@@ -265,6 +289,9 @@ namespace Invercargill {
     }
 
     public class Signed64BitIntegers : Numbers<int64?, Signed64BitIntegers> {
+        protected override Enumerable<int64?> new_wrap(Enumerable<int64?> enumerable) {
+            return new Signed64BitIntegers().wrap(enumerable);
+        }
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(int64?));
         }
@@ -298,6 +325,9 @@ namespace Invercargill {
     }
 
     public class Unsigned64BitIntegers : Numbers<uint64?, Unsigned64BitIntegers> {
+        protected override Enumerable<uint64?> new_wrap(Enumerable<uint64?> enumerable) {
+            return new Unsigned64BitIntegers().wrap(enumerable);
+        }
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(uint64?));
         }
@@ -331,6 +361,9 @@ namespace Invercargill {
     }
 
     public class Doubles : Numbers<double?, Doubles> {
+        protected override Enumerable<double?> new_wrap(Enumerable<double?> enumerable) {
+            return new Doubles().wrap(enumerable);
+        }
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(double?));
         }
@@ -364,6 +397,9 @@ namespace Invercargill {
     }
 
     public class Floats : Numbers<float?, Floats> {
+        protected override Enumerable<float?> new_wrap(Enumerable<float?> enumerable) {
+            return new Floats().wrap(enumerable);
+        }
         public override bool can_wrap(GLib.Type element_type) {
             return element_type.is_a(typeof(float?));
         }

+ 4 - 0
src/lib/Promotions/PropertyGroups.vala

@@ -9,6 +9,10 @@ namespace Invercargill {
             return this;
         }
 
+        protected override Enumerable<Properties> new_wrap(Enumerable<Properties> enumerable) {
+            return new PropertyGroups().wrap(enumerable);
+        }
+
         public override bool can_wrap (GLib.Type element_type) {
             return element_type.is_a (typeof(Properties));
         }

+ 22 - 8
src/lib/StickyPromotion.vala

@@ -3,38 +3,52 @@ namespace Invercargill {
     public abstract class StickyPromotion<T, TPromotion> : EnumerableProxy<T>, Promotion<T> {
 
         public abstract Enumerable<T> wrap(Enumerable<T> enumerable);
+        protected abstract Enumerable<T> new_wrap(Enumerable<T> enumerable);
         public abstract bool can_wrap(GLib.Type element_type);
 
         public new TPromotion where(owned PredicateDelegate<T> predicate) {
-            return (TPromotion)wrap(inner.where((owned)predicate));
+            return (TPromotion)new_wrap(inner.where((owned)predicate));
         }
     
         public new TPromotion sort(owned CompareDelegate<T> compare) {
-            return (TPromotion)wrap(inner.sort((owned)compare));
+            return (TPromotion)new_wrap(inner.sort((owned)compare));
         }
     
         public new TPromotion concat(Enumerable<T> other) {
-            return (TPromotion)wrap(inner.concat(other));
+            return (TPromotion)new_wrap(inner.concat(other));
         }
     
         public new TPromotion take(int count) {
-            return (TPromotion)wrap(inner.take(count));
+            return (TPromotion)new_wrap(inner.take(count));
         }
     
         public new TPromotion skip(int count) {
-            return (TPromotion)wrap(inner.skip(count));
+            return (TPromotion)new_wrap(inner.skip(count));
         }
     
         public new TPromotion with(T item, uint times = 1) {
-            return (TPromotion)wrap(inner.with(item, times));
+            return (TPromotion)new_wrap(inner.with(item, times));
         }
     
         public new TPromotion seal() {
-            return (TPromotion)wrap(inner.seal());
+            return (TPromotion)new_wrap(inner.seal());
         }
 
         public new TPromotion cache() {
-            return (TPromotion)wrap(inner.cache());
+            return (TPromotion)new_wrap(inner.cache());
+        }
+
+        public new TPromotion act(ItemDelegate<T> handler) {
+            return (TPromotion)new_wrap(inner.act(handler));
+        }
+
+        public new TPromotion debug_trace(owned TransformDelegate<T, string> stringifier, string additional_message = "", owned DebugOutputDelegate? output_func = null, bool formatting = true) {
+            return (TPromotion)new_wrap(new TraceEnumerable<T>(this, get_info(), (owned)stringifier, additional_message, (owned)output_func, formatting));
+        }
+
+        public new TPromotion debug_type(string additional_message = "", DebugOutputDelegate? output_func = null, bool formatting = true) {
+            DebugPrinter.print_enumerable_information(get_info(), additional_message, output_func, formatting);
+            return (TPromotion)wrap(inner);
         }
     }
 

+ 1 - 0
src/lib/meson.build

@@ -26,6 +26,7 @@ sources += files('EnumerableProxy.vala')
 sources += files('EnumerableInfo.vala')
 sources += files('StickyPromotion.vala')
 sources += files('Element.vala')
+sources += files('Debug.vala')
 
 sources += files('Modifiers/Transform.vala')
 sources += files('Modifiers/Filter.vala')

+ 5 - 2
src/tests/Integration/Cache.vala

@@ -6,18 +6,21 @@ void cache_tests() {
 
     Test.add_func("/invercargill/operator/cache", () => {
         var runs = 0;
-        var enumerable = range(0, 64).act(() => runs++).cache().assert_promotion<SignedNativeIntegers>();
+        var enumerable = range(0, 64)
+            .assert_promotion<SignedNativeIntegers>()
+            .act(() => runs++)
+            .cache();
 
         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);
     });