[
  {
    "path": ".gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, build with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n"
  },
  {
    "path": "LICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n"
  },
  {
    "path": "README.md",
    "content": "dag has two main concept:\n1. **Pipeline** executes the functions sequentially and in order.\n2. **Spawns** executes the functions concurrently, so there is no ordering guarantee.\n\n## Example 1\n![example1](images/1.png)  \n```Go\nd := dag.New()\nd.Pipeline(f1, f2, f3)\nd.Run()\n```\nIn the above example, f1 starts first, and after completion, f2 starts then f3.  \nFull example : [examples/ex1/ex1.go](examples/ex1/ex1.go)\n\n## Example 2\n![example2](images/2.png)  \n```Go\nd := dag.New()\nd.Spawns(f1, f2, f3)\nd.Run()\n```\nThe order of execution of f1, f2 and f3 is *nondeterministic*  \nFull example : [examples/ex2/ex2.go](examples/ex2/ex2.go)\n\n## Example 3\n![example3](images/3.png)  \nIn this example f4 must be executed after complition of f1, f2 and f3. You can use **Join** method:\n```Go\nd := dag.New()\nd.Spawns(f1, f2, f3).Join().Pipeline(f4)\nd.Run()\n```\nFull example : [examples/ex3/ex3.go](examples/ex3/ex3.go)\n\n## Example 4\n![example4](images/4.png)  \nAfter *pipeline* we can use **Then** method:\n```Go\nd := dag.New()\nd.Pipeline(f1, f2, f3).Then().Spawns(f4, f5, f6)\nd.Run()\n```\nFull example : [examples/ex4/ex4.go](examples/ex4/ex4.go)\n\n## Example 5\n![example5](images/5.png)  \n```Go\nd := dag.New()\nd.Spawns(f1, f2, f3).\n\tJoin().\n\tPipeline(f4, f5).\n\tThen().\n\tSpawns(f6, f7, f8)\nd.Run()\n```\nFull example : [examples/ex5/ex5.go](examples/ex5/ex5.go)\n\n## Example 6\n![example6](images/6.png)  \nWe want to execute two pipeline concrrently, we can use **pipeline.Of** inside the *Spawns* method:\n```Go\nd := dag.New()\nd.Spawns(pipeline.Of(f1, f3), pipeline.Of(f2, f4)).\n\tJoin().\n\tPipeline(f5)\nd.Run()\n```\nFull example : [examples/ex6/ex6.go](examples/ex6/ex6.go)\n\n## Example 7\nWe can use **OnComplete** method after *Pipeline* or *Spawns* to notify when functions has completed.\n```Go\nd := dag.New()\nd.Pipeline(f1, f2).OnComplete(f3).\n\t  Then().\n  Spawns(f1, f2).OnComplete(f4)\nd.Run()\n```\nFull example : [examples/ex7/ex7.go](examples/ex7/ex7.go)\n\n## Example 8\nBasically, Run() will block until all functions are done. If you don't want to be blocked, you can use RunAsync() method. It\n accepts a callback function, that will be called when all functions are done.\n ```Go\t\n d := dag.New()\n d.Pipeline(f1, f2).Then().Spawns(f3, f4)\n d.RunAsync(onComplete)\n ```\nFull example : [examples/ex8/ex8.go](examples/ex8/ex8.go)\n"
  },
  {
    "path": "async-runner.go",
    "content": "package dag\n\nimport \"sync\"\n\nfunc runAsync(job *Job) {\n\n\twg := &sync.WaitGroup{}\n\twg.Add(len(job.tasks))\n\n\tfor _, task := range job.tasks {\n\t\tgo func(task func()) {\n\t\t\ttask()\n\t\t\twg.Done()\n\t\t}(task)\n\t}\n\n\twg.Wait()\n\tif job.onComplete != nil {\n\t\tjob.onComplete()\n\t}\n}\n"
  },
  {
    "path": "dag.go",
    "content": "package dag\n\n// Dag represents directed acyclic graph\ntype Dag struct {\n\tjobs []*Job\n}\n\n// New creates new DAG\nfunc New() *Dag {\n\treturn &Dag{\n\t\tjobs: make([]*Job, 0),\n\t}\n}\n\nfunc (dag *Dag) lastJob() *Job {\n\tjobsCount := len(dag.jobs)\n\tif jobsCount == 0 {\n\t\treturn nil\n\t}\n\n\treturn dag.jobs[jobsCount-1]\n}\n\n// Run starts the tasks\n// It will block until all functions are done\nfunc (dag *Dag) Run() {\n\n\tfor _, job := range dag.jobs {\n\t\trun(job)\n\t}\n\n}\n\n// RunAsync executes Run on another goroutine\nfunc (dag *Dag) RunAsync(onComplete func()) {\n\tgo func() {\n\n\t\tdag.Run()\n\n\t\tif onComplete != nil {\n\t\t\tonComplete()\n\t\t}\n\n\t}()\n}\n\n// Pipeline executes tasks sequentially\nfunc (dag *Dag) Pipeline(tasks ...func()) *pipelineResult {\n\n\tjob := &Job{\n\t\ttasks:      make([]func(), len(tasks)),\n\t\tsequential: true,\n\t}\n\n\tfor i, task := range tasks {\n\t\tjob.tasks[i] = task\n\t}\n\n\tdag.jobs = append(dag.jobs, job)\n\n\treturn &pipelineResult{\n\t\tdag,\n\t}\n}\n\n// Spawns executes tasks concurrently\nfunc (dag *Dag) Spawns(tasks ...func()) *spawnsResult {\n\n\tjob := &Job{\n\t\ttasks:      make([]func(), len(tasks)),\n\t\tsequential: false,\n\t}\n\n\tfor i, task := range tasks {\n\t\tjob.tasks[i] = task\n\t}\n\n\tdag.jobs = append(dag.jobs, job)\n\n\treturn &spawnsResult{\n\t\tdag,\n\t}\n}\n"
  },
  {
    "path": "dsl.go",
    "content": "package dag\n\ntype pipelineResult struct {\n\tdag *Dag\n}\n\nfunc (result *pipelineResult) Then() *pipelineDSL {\n\treturn &pipelineDSL{\n\t\tresult.dag,\n\t}\n}\n\nfunc (result *pipelineResult) OnComplete(action func()) *pipelineResult {\n\tjob := result.dag.lastJob()\n\tif job != nil {\n\t\tjob.onComplete = action\n\t}\n\treturn result\n}\n\ntype pipelineDSL struct {\n\tdag *Dag\n}\n\nfunc (dsl *pipelineDSL) Spawns(tasks ...func()) *spawnsResult {\n\tdsl.dag.Spawns(tasks...)\n\treturn &spawnsResult{\n\t\tdsl.dag,\n\t}\n}\n\ntype spawnsResult struct {\n\tdag *Dag\n}\n\nfunc (result *spawnsResult) Join() *spawnsDSL {\n\treturn &spawnsDSL{\n\t\tresult.dag,\n\t}\n}\n\nfunc (result *spawnsResult) OnComplete(action func()) *spawnsResult {\n\tjob := result.dag.lastJob()\n\tif job != nil {\n\t\tjob.onComplete = action\n\t}\n\treturn result\n}\n\ntype spawnsDSL struct {\n\tdag *Dag\n}\n\nfunc (dsl *spawnsDSL) Pipeline(tasks ...func()) *pipelineResult {\n\tdsl.dag.Pipeline(tasks...)\n\treturn &pipelineResult{\n\t\tdsl.dag,\n\t}\n}\n"
  },
  {
    "path": "examples/ex1/ex1.go",
    "content": "package main\n\nimport \"github.com/mostafa-asg/dag\"\n\nfunc main() {\n\n\td := dag.New()\n\td.Pipeline(f1, f2, f3)\n\td.Run()\n}\n\nfunc f1() {\n\tprintln(\"f1\")\n}\nfunc f2() {\n\tprintln(\"f2\")\n}\nfunc f3() {\n\tprintln(\"f3\")\n}\n"
  },
  {
    "path": "examples/ex2/ex2.go",
    "content": "package main\n\nimport \"github.com/mostafa-asg/dag\"\n\nfunc main() {\n\n\td := dag.New()\n\td.Spawns(f1, f2, f3)\n\td.Run()\n}\n\nfunc f1() {\n\tprintln(\"f1\")\n}\nfunc f2() {\n\tprintln(\"f2\")\n}\nfunc f3() {\n\tprintln(\"f3\")\n}\n"
  },
  {
    "path": "examples/ex3/ex3.go",
    "content": "package main\n\nimport \"github.com/mostafa-asg/dag\"\n\nfunc main() {\n\n\td := dag.New()\n\td.Spawns(f1, f2, f3).Join().Pipeline(f4)\n\td.Run()\n}\n\nfunc f1() {\n\tprintln(\"f1\")\n}\nfunc f2() {\n\tprintln(\"f2\")\n}\nfunc f3() {\n\tprintln(\"f3\")\n}\nfunc f4() {\n\tprintln(\"f4\")\n}\n"
  },
  {
    "path": "examples/ex4/ex4.go",
    "content": "package main\n\nimport \"github.com/mostafa-asg/dag\"\n\nfunc main() {\n\n\td := dag.New()\n\td.Pipeline(f1, f2, f3).Then().Spawns(f4, f5, f6)\n\td.Run()\n}\n\nfunc f1() {\n\tprintln(\"f1\")\n}\nfunc f2() {\n\tprintln(\"f2\")\n}\nfunc f3() {\n\tprintln(\"f3\")\n}\nfunc f4() {\n\tprintln(\"f4\")\n}\nfunc f5() {\n\tprintln(\"f5\")\n}\nfunc f6() {\n\tprintln(\"f6\")\n}\n"
  },
  {
    "path": "examples/ex5/ex5.go",
    "content": "package main\n\nimport \"github.com/mostafa-asg/dag\"\n\nfunc main() {\n\n\td := dag.New()\n\td.Spawns(f1, f2, f3).\n\t\tJoin().\n\t\tPipeline(f4, f5).\n\t\tThen().\n\t\tSpawns(f6, f7, f8)\n\td.Run()\n}\n\nfunc f1() {\n\tprintln(\"f1\")\n}\nfunc f2() {\n\tprintln(\"f2\")\n}\nfunc f3() {\n\tprintln(\"f3\")\n}\nfunc f4() {\n\tprintln(\"f4\")\n}\nfunc f5() {\n\tprintln(\"f5\")\n}\nfunc f6() {\n\tprintln(\"f6\")\n}\nfunc f7() {\n\tprintln(\"f7\")\n}\nfunc f8() {\n\tprintln(\"f8\")\n}\n"
  },
  {
    "path": "examples/ex6/ex6.go",
    "content": "package main\n\nimport \"github.com/mostafa-asg/dag\"\nimport \"github.com/mostafa-asg/dag/pipeline\"\n\nfunc main() {\n\n\td := dag.New()\n\td.Spawns(pipeline.Of(f1, f3), pipeline.Of(f2, f4)).\n\t\tJoin().\n\t\tPipeline(f5)\n\td.Run()\n}\n\nfunc f1() {\n\tprintln(\"f1\")\n}\nfunc f2() {\n\tprintln(\"f2\")\n}\nfunc f3() {\n\tprintln(\"f3\")\n}\nfunc f4() {\n\tprintln(\"f4\")\n}\nfunc f5() {\n\tprintln(\"f5\")\n}\n"
  },
  {
    "path": "examples/ex7/ex7.go",
    "content": "package main\n\nimport (\n\t\"github.com/mostafa-asg/dag\"\n)\n\nfunc main() {\n\n\td := dag.New()\n\td.Pipeline(f1, f2).OnComplete(f3).\n\t\tThen().\n\t\tSpawns(f1, f2).OnComplete(f4)\n\td.Run()\n\n}\n\nfunc f1() {\n\tprintln(\"f1\")\n}\nfunc f2() {\n\tprintln(\"f2\")\n}\nfunc f3() {\n\tprintln(\"complete\")\n}\nfunc f4() {\n\tprintln(\"finish\")\n}\n"
  },
  {
    "path": "examples/ex8/ex8.go",
    "content": "package main\n\nimport (\n\t\"sync\"\n\n\t\"github.com/mostafa-asg/dag\"\n)\n\nvar wg = &sync.WaitGroup{}\n\nfunc main() {\n\n\twg.Add(1)\n\n\td := dag.New()\n\td.Pipeline(f1, f2).Then().Spawns(f3, f4)\n\td.RunAsync(onComplete)\n\n\twg.Wait()\n}\n\nfunc f1() {\n\tprintln(\"f1\")\n}\nfunc f2() {\n\tprintln(\"f2\")\n}\nfunc f3() {\n\tprintln(\"f3\")\n}\nfunc f4() {\n\tprintln(\"f4\")\n}\nfunc onComplete() {\n\tprintln(\"All functions have completed\")\n\twg.Done()\n}\n"
  },
  {
    "path": "job.go",
    "content": "package dag\n\n// Job - Each job consists of one or more tasks\n// Each Job can runs tasks in order(Sequential) or unordered\ntype Job struct {\n\ttasks      []func()\n\tsequential bool\n\tonComplete func()\n}\n"
  },
  {
    "path": "pipeline/pipeline.go",
    "content": "package pipeline\n\n// Of wraps tasks as a single function\nfunc Of(tasks ...func()) func() {\n\n\treturn func() {\n\n\t\tfor _, task := range tasks {\n\t\t\ttask()\n\t\t}\n\n\t}\n\n}\n"
  },
  {
    "path": "runner.go",
    "content": "package dag\n\nfunc run(job *Job) {\n\n\tif job.sequential {\n\t\trunSync(job)\n\t} else {\n\t\trunAsync(job)\n\t}\n\n}\n"
  },
  {
    "path": "sync-runner.go",
    "content": "package dag\n\nfunc runSync(job *Job) {\n\n\tfor _, task := range job.tasks {\n\t\ttask()\n\t}\n\tif job.onComplete != nil {\n\t\tjob.onComplete()\n\t}\n\n}\n"
  }
]