1 /** 2 Copyright 2018 Mark Fisher 3 4 Permission is hereby granted, free of charge, to any person obtaining a copy of 5 this software and associated documentation files (the "Software"), to deal in 6 the Software without restriction, including without limitation the rights to 7 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 of the Software, and to permit persons to whom the Software is furnished to do 9 so, subject to the following conditions: 10 11 The above copyright notice and this permission notice shall be included in all 12 copies or substantial portions of the Software. 13 14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 SOFTWARE. 21 **/ 22 module dxx.app.workflow; 23 24 private import std.algorithm; 25 private import std.variant; 26 27 private import dxx.app.platform; 28 private import dxx.app.job; 29 30 interface WorkflowElement { 31 void setup(WorkflowJob job); 32 void process(WorkflowJob job); 33 nothrow void terminate(WorkflowJob job); 34 } 35 36 //abstract class WorkflowElementBase : WorkflowElement { 37 // override void process(WorkflowJob job) { 38 // processElement(job); 39 // } 40 // abstract void processElement(WorkflowJob job); 41 //} 42 43 interface Workflow { 44 @property nothrow 45 ref inout (WorkflowElement[]) workflowElements() inout; 46 47 @property nothrow 48 ref inout (string[]) args() inout; 49 50 @property nothrow 51 ref inout (Variant[string]) param() inout; 52 } 53 54 abstract class WorkflowBase : Workflow { 55 WorkflowElement[] _workflowElements; 56 string[] _args; 57 Variant[string] _param; 58 59 @property 60 ref inout (WorkflowElement[]) workflowElements() inout { 61 return _workflowElements; 62 } 63 @property 64 ref inout (string[]) args() inout { 65 return _args; 66 } 67 this(WorkflowElement[] elements,string[] args) { 68 _workflowElements = elements; 69 _args = args; 70 } 71 @property 72 ref inout (Variant[string]) param() inout { 73 return _param; 74 } 75 } 76 77 final class DefaultWorkflow : WorkflowBase { 78 this(WorkflowElement[] elements,string[] args) { 79 super(elements,args); 80 } 81 } 82 83 final class WorkflowJob : PlatformJobBase { 84 Workflow _workflow; 85 WorkflowRunner _runner; 86 87 this(Workflow wf,WorkflowRunner r) { 88 this._workflow = wf; 89 this._runner = r; 90 } 91 92 @property nothrow 93 inout(Workflow) workflow() inout { 94 return _workflow; 95 } 96 97 @property nothrow 98 inout(WorkflowRunner) workflowRunner() inout { 99 return _runner; 100 } 101 102 override void setup() { 103 super.setup; 104 workflow.workflowElements.each!(e=>e.setup(this)); 105 } 106 107 override void processPlatformJob() { 108 workflow.workflowElements.each!(e=>e.process(this)); 109 } 110 111 nothrow 112 override void terminate() { 113 super.terminate; 114 workflow.workflowElements.each!(e=>e.terminate(this)); 115 } 116 } 117 118 final class WorkflowRunner { 119 Job createJob(Workflow wf) { 120 auto job = new WorkflowJob(wf,this); 121 return job; 122 } 123 } 124 125 class WorkflowElementDelegate(alias D) : WorkflowElement { 126 override void setup(WorkflowJob job) {} 127 override void process(WorkflowJob job) { 128 D(job); 129 } 130 override void terminate(WorkflowJob job) {} 131 } 132 133 unittest { 134 import std.stdio; 135 136 class TestWorkflowElement : WorkflowElement { 137 bool _done = false; 138 override void setup(WorkflowJob job) { 139 //writeln("TestWorkflowElement.setup"); 140 } 141 override void process(WorkflowJob job) { 142 //writeln("TestWorkflowElement.process"); 143 _done = true; 144 } 145 override void terminate(WorkflowJob job) { 146 //writeln("TestWorkflowElement.terminate"); 147 } 148 } 149 string[] arg = [ "arg0","arg1","arg2" ]; 150 151 auto elem = new TestWorkflowElement; 152 WorkflowElement[] e = [ elem ]; 153 auto wf = new DefaultWorkflow(e,arg); 154 auto r = new WorkflowRunner; 155 auto j = r.createJob(wf); 156 j.execute(); 157 assert(j.terminated); 158 assert(j.status == Job.Status.TERMINATED); 159 assert(elem._done); 160 161 } 162 163 unittest { 164 import std.stdio; 165 class TestWorkflowElementException : WorkflowElement { 166 bool terminated = false; 167 override void setup(WorkflowJob job) { 168 //writeln("TestWorkflowElement.setup"); 169 } 170 override void process(WorkflowJob job) { 171 //writeln("TestWorkflowElementException.process"); 172 throw new Exception("workflow unittest"); 173 } 174 override void terminate(WorkflowJob job) { 175 //writeln("TestWorkflowElement.terminate"); 176 terminated = true; 177 } 178 } 179 string[] arg = [ "arg0","arg1","arg2" ]; 180 181 auto elem = new TestWorkflowElementException; 182 WorkflowElement[] e = [ elem ]; 183 auto wf = new DefaultWorkflow(e,arg); 184 auto r = new WorkflowRunner; 185 auto j = r.createJob(wf); 186 j.execute(); 187 assert(j.terminated); 188 writeln("Job status: ",j.status); 189 assert(j.status == Job.Status.THROWN_EXCEPTION); 190 assert(j.thrownException !is null); 191 assert(elem.terminated); 192 } 193 194