Browse Source

dev: add Invoker

KernelDeimos 10 months ago
parent
commit
20b58ddcb5
2 changed files with 91 additions and 0 deletions
  1. 53 0
      src/puter-js-common/src/libs/invoker.js
  2. 38 0
      src/puter-js-common/test/test.js

+ 53 - 0
src/puter-js-common/src/libs/invoker.js

@@ -0,0 +1,53 @@
+const { AdvancedBase } = require("../..");
+
+class Invoker extends AdvancedBase {
+    static create ({
+        decorators,
+        delegate,
+    }) {
+        const invoker = new Invoker();
+        invoker.decorators = decorators;
+        invoker.delegate = delegate;
+        return invoker;
+    }
+    async run (args) {
+        let fn = this.delegate;
+        const decorators = this.decorators;
+        for ( let i = decorators.length-1 ; i >= 0 ; i-- ) {
+            const dec = decorators[i];
+            fn = this.add_dec_(dec, fn);
+        }
+        return await fn(args);
+    }
+    add_dec_ (dec, fn) {
+        return async (args) => {
+            try {
+                if ( dec.on_call ) {
+                    args = await dec.on_call(args);
+                }
+                let result = await fn(args);
+                if ( dec.on_return ) {
+                    result = await dec.on_return(result);
+                }
+                return result;
+            } catch (e) {
+                if ( ! dec.on_error ) throw e;
+
+                let cancel = false;
+                const a = {
+                    error () { return e },
+                    cancel_error () { cancel = true; },
+                };
+                const result = await dec.on_error(a);
+                if ( cancel ) {
+                    return result;
+                }
+                throw result ?? e;
+            }
+        }
+    }
+}
+
+module.exports = {
+    Invoker,
+};

+ 38 - 0
src/puter-js-common/test/test.js

@@ -19,6 +19,7 @@
 const { expect } = require('chai');
 const { BasicBase } = require('../src/bases/BasicBase');
 const { AdvancedBase } = require('../src/AdvancedBase');
+const { Invoker } = require('../src/libs/invoker');
 
 class ClassA extends BasicBase {
     static STATIC_OBJ = {
@@ -70,3 +71,40 @@ describe('AdvancedBase', () => {
     });
 });
 
+describe('lib:invoker', () => {
+    it('works', async () => {
+        const invoker = Invoker.create({
+            decorators: [
+                {
+                    name: 'uphill both ways',
+                    on_call: (args) => {
+                        return {
+                            ...args,
+                            n: args.n + 1,
+                        };
+                    },
+                    on_return: (result) => {
+                        return {
+                            n: result.n + 1,
+                        };
+                    },
+                },
+                {
+                    name: 'error number five',
+                    on_error: a => {
+                        a.cancel_error();
+                        return { n: 5 };
+                    },
+                }
+            ],
+            async delegate (args) {
+                const { n } = args;
+                if ( n === 3 ) {
+                    throw new Error('test error');
+                }
+                return { n: 'oops' };
+            }
+        });
+        expect(await invoker.run({ n: 2 })).to.deep.equal({ n: 6 });
+    });
+});