├── .gitignore ├── LICENSE ├── README.md ├── deps-lock.txt ├── deps.dot ├── deps.png ├── deps.svg ├── deps.txt ├── dub.json ├── dub.selections.json ├── screenshot ├── README.md ├── rx-deps-nostd.png ├── rx-deps-nostd.svg ├── rx-deps-nosubject.png ├── rx-deps-nosubject.svg ├── rx-deps.png └── rx-deps.svg └── source └── app.d /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | docs/ 5 | /graphviz 6 | graphviz.so 7 | graphviz.dylib 8 | graphviz.dll 9 | graphviz.a 10 | graphviz.lib 11 | graphviz-test-* 12 | *.exe 13 | *.o 14 | *.obj 15 | *.lst 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DDeps 2 | 3 | Source review support tool 4 | 5 | A tool to create a module dependency graph for the D language. 6 | The feature is that you can record snapshots in two versions and compare them to visualize the differences. 7 | 8 | # Screenshot (Example) 9 | 10 | **basic** 11 | 12 | ![rx](https://raw.githubusercontent.com/lempiji/ddeps/master/screenshot/rx-deps.png) 13 | 14 | **no core, no std** 15 | 16 | ![rx-nostd](https://raw.githubusercontent.com/lempiji/ddeps/master/screenshot/rx-deps-nostd.png) 17 | 18 | **exclude rx.subject** 19 | 20 | ![rx-nosubject](https://raw.githubusercontent.com/lempiji/ddeps/master/screenshot/rx-deps-nosubject.png) 21 | 22 | # Requirements 23 | 1. dub 24 | 2. Graphviz (for visualize) 25 | 26 | # Settings 27 | 28 | ## For library (example) 29 | ```json 30 | "configurations": [ 31 | { 32 | "name": "default" 33 | }, 34 | { 35 | "name": "diff", 36 | "postGenerateCommands": [ 37 | "dub build -c makedeps", 38 | "dub fetch ddeps", 39 | "dub run ddeps -- --focus=rx -o deps.dot", 40 | "dot -Tsvg -odeps.svg deps.dot" 41 | ] 42 | }, 43 | { 44 | "name": "diff-update", 45 | "postGenerateCommands": [ 46 | "dub fetch ddeps", 47 | "dub run ddeps -- --update" 48 | ] 49 | }, 50 | { 51 | "name": "makedeps", 52 | "dflags": ["-deps=deps.txt"] 53 | } 54 | ] 55 | ``` 56 | 57 | ## For executable 58 | ```json 59 | "configurations": [ 60 | { 61 | "name": "default" 62 | }, 63 | { 64 | "name": "diff", 65 | "postGenerateCommands": [ 66 | "dub build -c makedeps", 67 | "dub fetch ddeps", 68 | "dub run ddeps -- -o deps.dot", 69 | "dot -Tsvg -odeps.svg deps.dot" 70 | ] 71 | }, 72 | { 73 | "name": "diff-update", 74 | "postGenerateCommands": [ 75 | "dub fetch ddeps", 76 | "dub run ddeps -- --update" 77 | ] 78 | }, 79 | { 80 | "name": "makedeps", 81 | "dflags": ["-deps=deps.txt"] 82 | } 83 | ] 84 | ``` 85 | 86 | # Usage 87 | 88 | ## At first 89 | create lock file 90 | 91 | ```bash 92 | dub build -c makedeps 93 | dub build -c diff-update 94 | ``` 95 | 96 | ## Basic 97 | 1. Modify source 98 | 2. Update diff 99 | - `dub build -c diff` 100 | 3. Do review with the dependency graph diff 101 | - Open the `deps.svg` in browser 102 | 103 | ## Compare 2 versions 104 | 105 | 1. checkout a target version 106 | - `git reset --hard XXX` or `git checkout XXXXX` 107 | 2. reset to source version 108 | - `git reset --hard HEAD~10` (e.g. 10 versions ago) 109 | 3. create `deps-lock.txt` 110 | - `dub build -c makedeps` 111 | - `dub build -c diff-update` 112 | - if `dub.json` / `dub.sdl` has not configure then add these. 113 | 4. reset to target version 114 | - `git reset --hard ORIG_HEAD` 115 | 5. make diff 116 | - `dub build -c diff` 117 | 6. open `deps.svg` 118 | 119 | 120 | # Arguments 121 | 122 | | name | Usage | description | default | 123 | |:-----|:------------|:--|:--| 124 | | input | `-i XXX` or `--input=XXX` | deps file name | `deps.txt` | 125 | | output | `-o XXX` or `--output=XXX` | destination file name | write to stdout | 126 | | update | `-u` or `--update` | update lock file | false | 127 | | lock | `-l XXX` or `--lock=XXX` | lock file name | `deps-lock.txt` | 128 | | focus | `-f XXX` or `--focus=XXX` | filtering target by name | `app` | 129 | | depth | `-d N` or `--depth=N` | search depth | 1 | 130 | | exclude | `-e XXX [-e YYY]` or `--exclude=XXX [--exclude=YYY]` | exclude module names | `object` | 131 | | help | `--help` | show help | | 132 | -------------------------------------------------------------------------------- /deps.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | { 3 | "app" 4 | "core.bitop" 5 | "core.checkedint" 6 | "core.exception" 7 | "core.internal.traits" 8 | "core.memory" 9 | "core.stdc.string" 10 | "std.algorithm" 11 | "std.algorithm.comparison" 12 | "std.algorithm.iteration" 13 | "std.algorithm.mutation" 14 | "std.algorithm.searching" 15 | "std.array" 16 | "std.ascii" 17 | "std.container.rbtree" 18 | "std.conv" 19 | "std.encoding" 20 | "std.exception" 21 | "std.file" 22 | "std.format" 23 | "std.functional" 24 | "std.getopt" 25 | "std.internal.memory" 26 | "std.internal.unicode_tables" 27 | "std.meta" 28 | "std.range" 29 | "std.range.primitives" 30 | "std.stdio" 31 | "std.system" 32 | "std.traits" 33 | "std.typecons" 34 | "std.uni" 35 | "std.utf" 36 | } 37 | "app" -> "core.bitop"; 38 | "app" -> "core.checkedint"; 39 | "app" -> "core.exception"; 40 | "app" -> "core.internal.traits"; 41 | "app" -> "core.memory"; 42 | "app" -> "core.stdc.string"; 43 | "app" -> "std.algorithm"; 44 | "app" -> "std.algorithm.comparison"; 45 | "app" -> "std.algorithm.iteration"; 46 | "app" -> "std.algorithm.mutation"; 47 | "app" -> "std.algorithm.searching"; 48 | "app" -> "std.array"; 49 | "app" -> "std.ascii"; 50 | "app" -> "std.container.rbtree"; 51 | "app" -> "std.conv"; 52 | "app" -> "std.encoding"; 53 | "app" -> "std.exception"; 54 | "app" -> "std.file"; 55 | "app" -> "std.format"; 56 | "app" -> "std.functional"; 57 | "app" -> "std.getopt"; 58 | "app" -> "std.internal.memory"; 59 | "app" -> "std.internal.unicode_tables"; 60 | "app" -> "std.meta"; 61 | "app" -> "std.range"; 62 | "app" -> "std.range.primitives"; 63 | "app" -> "std.stdio"; 64 | "app" -> "std.system"; 65 | "app" -> "std.traits"; 66 | "app" -> "std.typecons"; 67 | "app" -> "std.uni"; 68 | "app" -> "std.utf"; 69 | } 70 | -------------------------------------------------------------------------------- /deps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lempiji/ddeps/abe8c4a5d9c56889e923dff7abd1c0a0064beb6a/deps.png -------------------------------------------------------------------------------- /deps.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | app 14 | 15 | app 16 | 17 | 18 | core.bitop 19 | 20 | core.bitop 21 | 22 | 23 | app->core.bitop 24 | 25 | 26 | 27 | 28 | core.checkedint 29 | 30 | core.checkedint 31 | 32 | 33 | app->core.checkedint 34 | 35 | 36 | 37 | 38 | core.exception 39 | 40 | core.exception 41 | 42 | 43 | app->core.exception 44 | 45 | 46 | 47 | 48 | core.internal.traits 49 | 50 | core.internal.traits 51 | 52 | 53 | app->core.internal.traits 54 | 55 | 56 | 57 | 58 | core.memory 59 | 60 | core.memory 61 | 62 | 63 | app->core.memory 64 | 65 | 66 | 67 | 68 | core.stdc.string 69 | 70 | core.stdc.string 71 | 72 | 73 | app->core.stdc.string 74 | 75 | 76 | 77 | 78 | std.algorithm 79 | 80 | std.algorithm 81 | 82 | 83 | app->std.algorithm 84 | 85 | 86 | 87 | 88 | std.algorithm.comparison 89 | 90 | std.algorithm.comparison 91 | 92 | 93 | app->std.algorithm.comparison 94 | 95 | 96 | 97 | 98 | std.algorithm.iteration 99 | 100 | std.algorithm.iteration 101 | 102 | 103 | app->std.algorithm.iteration 104 | 105 | 106 | 107 | 108 | std.algorithm.mutation 109 | 110 | std.algorithm.mutation 111 | 112 | 113 | app->std.algorithm.mutation 114 | 115 | 116 | 117 | 118 | std.algorithm.searching 119 | 120 | std.algorithm.searching 121 | 122 | 123 | app->std.algorithm.searching 124 | 125 | 126 | 127 | 128 | std.array 129 | 130 | std.array 131 | 132 | 133 | app->std.array 134 | 135 | 136 | 137 | 138 | std.ascii 139 | 140 | std.ascii 141 | 142 | 143 | app->std.ascii 144 | 145 | 146 | 147 | 148 | std.container.rbtree 149 | 150 | std.container.rbtree 151 | 152 | 153 | app->std.container.rbtree 154 | 155 | 156 | 157 | 158 | std.conv 159 | 160 | std.conv 161 | 162 | 163 | app->std.conv 164 | 165 | 166 | 167 | 168 | std.encoding 169 | 170 | std.encoding 171 | 172 | 173 | app->std.encoding 174 | 175 | 176 | 177 | 178 | std.exception 179 | 180 | std.exception 181 | 182 | 183 | app->std.exception 184 | 185 | 186 | 187 | 188 | std.file 189 | 190 | std.file 191 | 192 | 193 | app->std.file 194 | 195 | 196 | 197 | 198 | std.format 199 | 200 | std.format 201 | 202 | 203 | app->std.format 204 | 205 | 206 | 207 | 208 | std.functional 209 | 210 | std.functional 211 | 212 | 213 | app->std.functional 214 | 215 | 216 | 217 | 218 | std.getopt 219 | 220 | std.getopt 221 | 222 | 223 | app->std.getopt 224 | 225 | 226 | 227 | 228 | std.internal.memory 229 | 230 | std.internal.memory 231 | 232 | 233 | app->std.internal.memory 234 | 235 | 236 | 237 | 238 | std.internal.unicode_tables 239 | 240 | std.internal.unicode_tables 241 | 242 | 243 | app->std.internal.unicode_tables 244 | 245 | 246 | 247 | 248 | std.meta 249 | 250 | std.meta 251 | 252 | 253 | app->std.meta 254 | 255 | 256 | 257 | 258 | std.range 259 | 260 | std.range 261 | 262 | 263 | app->std.range 264 | 265 | 266 | 267 | 268 | std.range.primitives 269 | 270 | std.range.primitives 271 | 272 | 273 | app->std.range.primitives 274 | 275 | 276 | 277 | 278 | std.stdio 279 | 280 | std.stdio 281 | 282 | 283 | app->std.stdio 284 | 285 | 286 | 287 | 288 | std.system 289 | 290 | std.system 291 | 292 | 293 | app->std.system 294 | 295 | 296 | 297 | 298 | std.traits 299 | 300 | std.traits 301 | 302 | 303 | app->std.traits 304 | 305 | 306 | 307 | 308 | std.typecons 309 | 310 | std.typecons 311 | 312 | 313 | app->std.typecons 314 | 315 | 316 | 317 | 318 | std.uni 319 | 320 | std.uni 321 | 322 | 323 | app->std.uni 324 | 325 | 326 | 327 | 328 | std.utf 329 | 330 | std.utf 331 | 332 | 333 | app->std.utf 334 | 335 | 336 | 337 | 338 | 339 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | "lempiji" 4 | ], 5 | "description": "Source review support tool.", 6 | "license": "MIT", 7 | "name": "ddeps", 8 | "configurations": [ 9 | { 10 | "name": "default", 11 | "targetType": "executable" 12 | }, 13 | { 14 | "name": "diff", 15 | "postGenerateCommands": [ 16 | "dub build -c makedeps", 17 | "dub fetch ddeps", 18 | "dub run ddeps -- --output=deps.dot", 19 | "dot -Tsvg -odeps.svg deps.dot", 20 | "dot -Tpng -odeps.png deps.dot" 21 | ] 22 | }, 23 | { 24 | "name": "diff-update", 25 | "postGenerateCommands": [ 26 | "dub fetch ddeps", 27 | "dub run ddeps -- --update" 28 | ] 29 | }, 30 | { 31 | "name": "makedeps", 32 | "dflags": ["-deps=deps.txt"] 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /dub.selections.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileVersion": 1, 3 | "versions": { 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /screenshot/README.md: -------------------------------------------------------------------------------- 1 | # Settings 2 | 3 | | name | command | 4 | |:--------------------|:------| 5 | | `rx-deps` | `dub run ddeps -- -f rx -o deps.dot` | 6 | | `rx-deps-nostd` | `dub run ddeps -- -f rx -o deps.dot -e std -e core` | 7 | | `rx-deps-nosubject` | `dub run ddeps -- -f rx -o deps.dot -e std -e core -e rx.subject` | -------------------------------------------------------------------------------- /screenshot/rx-deps-nostd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lempiji/ddeps/abe8c4a5d9c56889e923dff7abd1c0a0064beb6a/screenshot/rx-deps-nostd.png -------------------------------------------------------------------------------- /screenshot/rx-deps-nostd.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | rx 14 | 15 | rx 16 | 17 | 18 | rx.algorithm 19 | 20 | rx.algorithm 21 | 22 | 23 | rx->rx.algorithm 24 | 25 | 26 | 27 | 28 | rx.disposable 29 | 30 | rx.disposable 31 | 32 | 33 | rx->rx.disposable 34 | 35 | 36 | 37 | 38 | rx.observable 39 | 40 | rx.observable 41 | 42 | 43 | rx->rx.observable 44 | 45 | 46 | 47 | 48 | rx.observer 49 | 50 | rx.observer 51 | 52 | 53 | rx->rx.observer 54 | 55 | 56 | 57 | 58 | rx.range 59 | 60 | rx.range 61 | 62 | 63 | rx->rx.range 64 | 65 | 66 | 67 | 68 | rx.scheduler 69 | 70 | rx.scheduler 71 | 72 | 73 | rx->rx.scheduler 74 | 75 | 76 | 77 | 78 | rx.subject 79 | 80 | rx.subject 81 | 82 | 83 | rx->rx.subject 84 | 85 | 86 | 87 | 88 | rx.util 89 | 90 | rx.util 91 | 92 | 93 | rx->rx.util 94 | 95 | 96 | 97 | 98 | rx.algorithm.all 99 | 100 | rx.algorithm.all 101 | 102 | 103 | rx.algorithm->rx.algorithm.all 104 | 105 | 106 | 107 | 108 | rx.algorithm.any 109 | 110 | rx.algorithm.any 111 | 112 | 113 | rx.algorithm->rx.algorithm.any 114 | 115 | 116 | 117 | 118 | rx.algorithm.buffer 119 | 120 | rx.algorithm.buffer 121 | 122 | 123 | rx.algorithm->rx.algorithm.buffer 124 | 125 | 126 | 127 | 128 | rx.algorithm.debounce 129 | 130 | rx.algorithm.debounce 131 | 132 | 133 | rx.algorithm->rx.algorithm.debounce 134 | 135 | 136 | 137 | 138 | rx.algorithm.filter 139 | 140 | rx.algorithm.filter 141 | 142 | 143 | rx.algorithm->rx.algorithm.filter 144 | 145 | 146 | 147 | 148 | rx.algorithm.fold 149 | 150 | rx.algorithm.fold 151 | 152 | 153 | rx.algorithm->rx.algorithm.fold 154 | 155 | 156 | 157 | 158 | rx.algorithm.groupby 159 | 160 | rx.algorithm.groupby 161 | 162 | 163 | rx.algorithm->rx.algorithm.groupby 164 | 165 | 166 | 167 | 168 | rx.algorithm.map 169 | 170 | rx.algorithm.map 171 | 172 | 173 | rx.algorithm->rx.algorithm.map 174 | 175 | 176 | 177 | 178 | rx.algorithm.merge 179 | 180 | rx.algorithm.merge 181 | 182 | 183 | rx.algorithm->rx.algorithm.merge 184 | 185 | 186 | 187 | 188 | rx.algorithm.scan 189 | 190 | rx.algorithm.scan 191 | 192 | 193 | rx.algorithm->rx.algorithm.scan 194 | 195 | 196 | 197 | 198 | rx.algorithm.tee 199 | 200 | rx.algorithm.tee 201 | 202 | 203 | rx.algorithm->rx.algorithm.tee 204 | 205 | 206 | 207 | 208 | rx.algorithm.uniq 209 | 210 | rx.algorithm.uniq 211 | 212 | 213 | rx.algorithm->rx.algorithm.uniq 214 | 215 | 216 | 217 | 218 | rx.algorithm.combineLatest 219 | 220 | rx.algorithm.combineLatest 221 | 222 | 223 | rx.algorithm->rx.algorithm.combineLatest 224 | 225 | 226 | 227 | 228 | rx.algorithm.all->rx.disposable 229 | 230 | 231 | 232 | 233 | rx.algorithm.all->rx.observable 234 | 235 | 236 | 237 | 238 | rx.algorithm.all->rx.observer 239 | 240 | 241 | 242 | 243 | rx.algorithm.all->rx.util 244 | 245 | 246 | 247 | 248 | rx.algorithm.any->rx.disposable 249 | 250 | 251 | 252 | 253 | rx.algorithm.any->rx.observable 254 | 255 | 256 | 257 | 258 | rx.algorithm.any->rx.observer 259 | 260 | 261 | 262 | 263 | rx.algorithm.any->rx.util 264 | 265 | 266 | 267 | 268 | rx.algorithm.buffer->rx.disposable 269 | 270 | 271 | 272 | 273 | rx.algorithm.buffer->rx.observable 274 | 275 | 276 | 277 | 278 | rx.algorithm.buffer->rx.observer 279 | 280 | 281 | 282 | 283 | rx.algorithm.debounce->rx.disposable 284 | 285 | 286 | 287 | 288 | rx.algorithm.debounce->rx.observable 289 | 290 | 291 | 292 | 293 | rx.algorithm.debounce->rx.observer 294 | 295 | 296 | 297 | 298 | rx.algorithm.debounce->rx.scheduler 299 | 300 | 301 | 302 | 303 | rx.algorithm.filter->rx.disposable 304 | 305 | 306 | 307 | 308 | rx.algorithm.filter->rx.observable 309 | 310 | 311 | 312 | 313 | rx.algorithm.filter->rx.observer 314 | 315 | 316 | 317 | 318 | rx.algorithm.filter->rx.util 319 | 320 | 321 | 322 | 323 | rx.algorithm.fold->rx.disposable 324 | 325 | 326 | 327 | 328 | rx.algorithm.fold->rx.observable 329 | 330 | 331 | 332 | 333 | rx.algorithm.fold->rx.observer 334 | 335 | 336 | 337 | 338 | rx.algorithm.groupby->rx.disposable 339 | 340 | 341 | 342 | 343 | rx.algorithm.groupby->rx.observable 344 | 345 | 346 | 347 | 348 | rx.algorithm.groupby->rx.observer 349 | 350 | 351 | 352 | 353 | rx.algorithm.groupby->rx.subject 354 | 355 | 356 | 357 | 358 | rx.algorithm.groupby->rx.util 359 | 360 | 361 | 362 | 363 | rx.algorithm.map->rx.disposable 364 | 365 | 366 | 367 | 368 | rx.algorithm.map->rx.observable 369 | 370 | 371 | 372 | 373 | rx.algorithm.map->rx.observer 374 | 375 | 376 | 377 | 378 | rx.algorithm.map->rx.util 379 | 380 | 381 | 382 | 383 | rx.algorithm.merge->rx.disposable 384 | 385 | 386 | 387 | 388 | rx.algorithm.merge->rx.observable 389 | 390 | 391 | 392 | 393 | rx.algorithm.merge->rx.observer 394 | 395 | 396 | 397 | 398 | rx.algorithm.merge->rx.util 399 | 400 | 401 | 402 | 403 | rx.algorithm.scan->rx.disposable 404 | 405 | 406 | 407 | 408 | rx.algorithm.scan->rx.observable 409 | 410 | 411 | 412 | 413 | rx.algorithm.scan->rx.observer 414 | 415 | 416 | 417 | 418 | rx.algorithm.scan->rx.util 419 | 420 | 421 | 422 | 423 | rx.algorithm.tee->rx.disposable 424 | 425 | 426 | 427 | 428 | rx.algorithm.tee->rx.observable 429 | 430 | 431 | 432 | 433 | rx.algorithm.tee->rx.observer 434 | 435 | 436 | 437 | 438 | rx.algorithm.tee->rx.util 439 | 440 | 441 | 442 | 443 | rx.algorithm.uniq->rx.disposable 444 | 445 | 446 | 447 | 448 | rx.algorithm.uniq->rx.observable 449 | 450 | 451 | 452 | 453 | rx.algorithm.uniq->rx.observer 454 | 455 | 456 | 457 | 458 | rx.algorithm.uniq->rx.util 459 | 460 | 461 | 462 | 463 | rx.disposable->rx.util 464 | 465 | 466 | 467 | 468 | rx.observable->rx.disposable 469 | 470 | 471 | 472 | 473 | rx.observable->rx.observer 474 | 475 | 476 | 477 | 478 | rx.observable->rx.util 479 | 480 | 481 | 482 | 483 | rx.range->rx.disposable 484 | 485 | 486 | 487 | 488 | rx.range->rx.observable 489 | 490 | 491 | 492 | 493 | rx.range->rx.observer 494 | 495 | 496 | 497 | 498 | rx.range->rx.util 499 | 500 | 501 | 502 | 503 | rx.range.drop 504 | 505 | rx.range.drop 506 | 507 | 508 | rx.range->rx.range.drop 509 | 510 | 511 | 512 | 513 | rx.range.take 514 | 515 | rx.range.take 516 | 517 | 518 | rx.range->rx.range.take 519 | 520 | 521 | 522 | 523 | rx.range.takeLast 524 | 525 | rx.range.takeLast 526 | 527 | 528 | rx.range->rx.range.takeLast 529 | 530 | 531 | 532 | 533 | rx.scheduler->rx.disposable 534 | 535 | 536 | 537 | 538 | rx.scheduler->rx.observable 539 | 540 | 541 | 542 | 543 | rx.scheduler->rx.observer 544 | 545 | 546 | 547 | 548 | rx.subject->rx.disposable 549 | 550 | 551 | 552 | 553 | rx.subject->rx.observable 554 | 555 | 556 | 557 | 558 | rx.subject->rx.observer 559 | 560 | 561 | 562 | 563 | rx.subject->rx.util 564 | 565 | 566 | 567 | 568 | rx.algorithm.combineLatest->rx.disposable 569 | 570 | 571 | 572 | 573 | rx.algorithm.combineLatest->rx.observable 574 | 575 | 576 | 577 | 578 | rx.algorithm.combineLatest->rx.observer 579 | 580 | 581 | 582 | 583 | rx.algorithm.combineLatest->rx.util 584 | 585 | 586 | 587 | 588 | rx.range.drop->rx.disposable 589 | 590 | 591 | 592 | 593 | rx.range.drop->rx.observable 594 | 595 | 596 | 597 | 598 | rx.range.drop->rx.observer 599 | 600 | 601 | 602 | 603 | rx.range.drop->rx.util 604 | 605 | 606 | 607 | 608 | rx.range.take->rx.disposable 609 | 610 | 611 | 612 | 613 | rx.range.take->rx.observable 614 | 615 | 616 | 617 | 618 | rx.range.take->rx.observer 619 | 620 | 621 | 622 | 623 | rx.range.take->rx.util 624 | 625 | 626 | 627 | 628 | rx.range.takeLast->rx.disposable 629 | 630 | 631 | 632 | 633 | rx.range.takeLast->rx.observable 634 | 635 | 636 | 637 | 638 | rx.range.takeLast->rx.observer 639 | 640 | 641 | 642 | 643 | rx.range.takeLast->rx.util 644 | 645 | 646 | 647 | 648 | 649 | -------------------------------------------------------------------------------- /screenshot/rx-deps-nosubject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lempiji/ddeps/abe8c4a5d9c56889e923dff7abd1c0a0064beb6a/screenshot/rx-deps-nosubject.png -------------------------------------------------------------------------------- /screenshot/rx-deps-nosubject.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | rx 14 | 15 | rx 16 | 17 | 18 | rx.algorithm 19 | 20 | rx.algorithm 21 | 22 | 23 | rx->rx.algorithm 24 | 25 | 26 | 27 | 28 | rx.disposable 29 | 30 | rx.disposable 31 | 32 | 33 | rx->rx.disposable 34 | 35 | 36 | 37 | 38 | rx.observable 39 | 40 | rx.observable 41 | 42 | 43 | rx->rx.observable 44 | 45 | 46 | 47 | 48 | rx.observer 49 | 50 | rx.observer 51 | 52 | 53 | rx->rx.observer 54 | 55 | 56 | 57 | 58 | rx.range 59 | 60 | rx.range 61 | 62 | 63 | rx->rx.range 64 | 65 | 66 | 67 | 68 | rx.scheduler 69 | 70 | rx.scheduler 71 | 72 | 73 | rx->rx.scheduler 74 | 75 | 76 | 77 | 78 | rx.util 79 | 80 | rx.util 81 | 82 | 83 | rx->rx.util 84 | 85 | 86 | 87 | 88 | rx.algorithm.all 89 | 90 | rx.algorithm.all 91 | 92 | 93 | rx.algorithm->rx.algorithm.all 94 | 95 | 96 | 97 | 98 | rx.algorithm.any 99 | 100 | rx.algorithm.any 101 | 102 | 103 | rx.algorithm->rx.algorithm.any 104 | 105 | 106 | 107 | 108 | rx.algorithm.buffer 109 | 110 | rx.algorithm.buffer 111 | 112 | 113 | rx.algorithm->rx.algorithm.buffer 114 | 115 | 116 | 117 | 118 | rx.algorithm.debounce 119 | 120 | rx.algorithm.debounce 121 | 122 | 123 | rx.algorithm->rx.algorithm.debounce 124 | 125 | 126 | 127 | 128 | rx.algorithm.filter 129 | 130 | rx.algorithm.filter 131 | 132 | 133 | rx.algorithm->rx.algorithm.filter 134 | 135 | 136 | 137 | 138 | rx.algorithm.fold 139 | 140 | rx.algorithm.fold 141 | 142 | 143 | rx.algorithm->rx.algorithm.fold 144 | 145 | 146 | 147 | 148 | rx.algorithm.groupby 149 | 150 | rx.algorithm.groupby 151 | 152 | 153 | rx.algorithm->rx.algorithm.groupby 154 | 155 | 156 | 157 | 158 | rx.algorithm.map 159 | 160 | rx.algorithm.map 161 | 162 | 163 | rx.algorithm->rx.algorithm.map 164 | 165 | 166 | 167 | 168 | rx.algorithm.merge 169 | 170 | rx.algorithm.merge 171 | 172 | 173 | rx.algorithm->rx.algorithm.merge 174 | 175 | 176 | 177 | 178 | rx.algorithm.scan 179 | 180 | rx.algorithm.scan 181 | 182 | 183 | rx.algorithm->rx.algorithm.scan 184 | 185 | 186 | 187 | 188 | rx.algorithm.tee 189 | 190 | rx.algorithm.tee 191 | 192 | 193 | rx.algorithm->rx.algorithm.tee 194 | 195 | 196 | 197 | 198 | rx.algorithm.uniq 199 | 200 | rx.algorithm.uniq 201 | 202 | 203 | rx.algorithm->rx.algorithm.uniq 204 | 205 | 206 | 207 | 208 | rx.algorithm.combineLatest 209 | 210 | rx.algorithm.combineLatest 211 | 212 | 213 | rx.algorithm->rx.algorithm.combineLatest 214 | 215 | 216 | 217 | 218 | rx.algorithm.all->rx.disposable 219 | 220 | 221 | 222 | 223 | rx.algorithm.all->rx.observable 224 | 225 | 226 | 227 | 228 | rx.algorithm.all->rx.observer 229 | 230 | 231 | 232 | 233 | rx.algorithm.all->rx.util 234 | 235 | 236 | 237 | 238 | rx.algorithm.any->rx.disposable 239 | 240 | 241 | 242 | 243 | rx.algorithm.any->rx.observable 244 | 245 | 246 | 247 | 248 | rx.algorithm.any->rx.observer 249 | 250 | 251 | 252 | 253 | rx.algorithm.any->rx.util 254 | 255 | 256 | 257 | 258 | rx.algorithm.buffer->rx.disposable 259 | 260 | 261 | 262 | 263 | rx.algorithm.buffer->rx.observable 264 | 265 | 266 | 267 | 268 | rx.algorithm.buffer->rx.observer 269 | 270 | 271 | 272 | 273 | rx.algorithm.debounce->rx.disposable 274 | 275 | 276 | 277 | 278 | rx.algorithm.debounce->rx.observable 279 | 280 | 281 | 282 | 283 | rx.algorithm.debounce->rx.observer 284 | 285 | 286 | 287 | 288 | rx.algorithm.debounce->rx.scheduler 289 | 290 | 291 | 292 | 293 | rx.algorithm.filter->rx.disposable 294 | 295 | 296 | 297 | 298 | rx.algorithm.filter->rx.observable 299 | 300 | 301 | 302 | 303 | rx.algorithm.filter->rx.observer 304 | 305 | 306 | 307 | 308 | rx.algorithm.filter->rx.util 309 | 310 | 311 | 312 | 313 | rx.algorithm.fold->rx.disposable 314 | 315 | 316 | 317 | 318 | rx.algorithm.fold->rx.observable 319 | 320 | 321 | 322 | 323 | rx.algorithm.fold->rx.observer 324 | 325 | 326 | 327 | 328 | rx.algorithm.groupby->rx.disposable 329 | 330 | 331 | 332 | 333 | rx.algorithm.groupby->rx.observable 334 | 335 | 336 | 337 | 338 | rx.algorithm.groupby->rx.observer 339 | 340 | 341 | 342 | 343 | rx.algorithm.groupby->rx.util 344 | 345 | 346 | 347 | 348 | rx.algorithm.map->rx.disposable 349 | 350 | 351 | 352 | 353 | rx.algorithm.map->rx.observable 354 | 355 | 356 | 357 | 358 | rx.algorithm.map->rx.observer 359 | 360 | 361 | 362 | 363 | rx.algorithm.map->rx.util 364 | 365 | 366 | 367 | 368 | rx.algorithm.merge->rx.disposable 369 | 370 | 371 | 372 | 373 | rx.algorithm.merge->rx.observable 374 | 375 | 376 | 377 | 378 | rx.algorithm.merge->rx.observer 379 | 380 | 381 | 382 | 383 | rx.algorithm.merge->rx.util 384 | 385 | 386 | 387 | 388 | rx.algorithm.scan->rx.disposable 389 | 390 | 391 | 392 | 393 | rx.algorithm.scan->rx.observable 394 | 395 | 396 | 397 | 398 | rx.algorithm.scan->rx.observer 399 | 400 | 401 | 402 | 403 | rx.algorithm.scan->rx.util 404 | 405 | 406 | 407 | 408 | rx.algorithm.tee->rx.disposable 409 | 410 | 411 | 412 | 413 | rx.algorithm.tee->rx.observable 414 | 415 | 416 | 417 | 418 | rx.algorithm.tee->rx.observer 419 | 420 | 421 | 422 | 423 | rx.algorithm.tee->rx.util 424 | 425 | 426 | 427 | 428 | rx.algorithm.uniq->rx.disposable 429 | 430 | 431 | 432 | 433 | rx.algorithm.uniq->rx.observable 434 | 435 | 436 | 437 | 438 | rx.algorithm.uniq->rx.observer 439 | 440 | 441 | 442 | 443 | rx.algorithm.uniq->rx.util 444 | 445 | 446 | 447 | 448 | rx.disposable->rx.util 449 | 450 | 451 | 452 | 453 | rx.observable->rx.disposable 454 | 455 | 456 | 457 | 458 | rx.observable->rx.observer 459 | 460 | 461 | 462 | 463 | rx.observable->rx.util 464 | 465 | 466 | 467 | 468 | rx.range->rx.disposable 469 | 470 | 471 | 472 | 473 | rx.range->rx.observable 474 | 475 | 476 | 477 | 478 | rx.range->rx.observer 479 | 480 | 481 | 482 | 483 | rx.range->rx.util 484 | 485 | 486 | 487 | 488 | rx.range.drop 489 | 490 | rx.range.drop 491 | 492 | 493 | rx.range->rx.range.drop 494 | 495 | 496 | 497 | 498 | rx.range.take 499 | 500 | rx.range.take 501 | 502 | 503 | rx.range->rx.range.take 504 | 505 | 506 | 507 | 508 | rx.range.takeLast 509 | 510 | rx.range.takeLast 511 | 512 | 513 | rx.range->rx.range.takeLast 514 | 515 | 516 | 517 | 518 | rx.scheduler->rx.disposable 519 | 520 | 521 | 522 | 523 | rx.scheduler->rx.observable 524 | 525 | 526 | 527 | 528 | rx.scheduler->rx.observer 529 | 530 | 531 | 532 | 533 | rx.algorithm.combineLatest->rx.disposable 534 | 535 | 536 | 537 | 538 | rx.algorithm.combineLatest->rx.observable 539 | 540 | 541 | 542 | 543 | rx.algorithm.combineLatest->rx.observer 544 | 545 | 546 | 547 | 548 | rx.algorithm.combineLatest->rx.util 549 | 550 | 551 | 552 | 553 | rx.range.drop->rx.disposable 554 | 555 | 556 | 557 | 558 | rx.range.drop->rx.observable 559 | 560 | 561 | 562 | 563 | rx.range.drop->rx.observer 564 | 565 | 566 | 567 | 568 | rx.range.drop->rx.util 569 | 570 | 571 | 572 | 573 | rx.range.take->rx.disposable 574 | 575 | 576 | 577 | 578 | rx.range.take->rx.observable 579 | 580 | 581 | 582 | 583 | rx.range.take->rx.observer 584 | 585 | 586 | 587 | 588 | rx.range.take->rx.util 589 | 590 | 591 | 592 | 593 | rx.range.takeLast->rx.disposable 594 | 595 | 596 | 597 | 598 | rx.range.takeLast->rx.observable 599 | 600 | 601 | 602 | 603 | rx.range.takeLast->rx.observer 604 | 605 | 606 | 607 | 608 | rx.range.takeLast->rx.util 609 | 610 | 611 | 612 | 613 | 614 | -------------------------------------------------------------------------------- /screenshot/rx-deps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lempiji/ddeps/abe8c4a5d9c56889e923dff7abd1c0a0064beb6a/screenshot/rx-deps.png -------------------------------------------------------------------------------- /screenshot/rx-deps.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | %3 11 | 12 | 13 | core.atomic 14 | 15 | core.atomic 16 | 17 | 18 | core.internal.traits 19 | 20 | core.internal.traits 21 | 22 | 23 | core.stdc.string 24 | 25 | core.stdc.string 26 | 27 | 28 | core.sync.condition 29 | 30 | core.sync.condition 31 | 32 | 33 | core.sync.mutex 34 | 35 | core.sync.mutex 36 | 37 | 38 | core.thread 39 | 40 | core.thread 41 | 42 | 43 | core.time 44 | 45 | core.time 46 | 47 | 48 | rx 49 | 50 | rx 51 | 52 | 53 | rx.algorithm 54 | 55 | rx.algorithm 56 | 57 | 58 | rx->rx.algorithm 59 | 60 | 61 | 62 | 63 | rx.disposable 64 | 65 | rx.disposable 66 | 67 | 68 | rx->rx.disposable 69 | 70 | 71 | 72 | 73 | rx.observable 74 | 75 | rx.observable 76 | 77 | 78 | rx->rx.observable 79 | 80 | 81 | 82 | 83 | rx.observer 84 | 85 | rx.observer 86 | 87 | 88 | rx->rx.observer 89 | 90 | 91 | 92 | 93 | rx.range 94 | 95 | rx.range 96 | 97 | 98 | rx->rx.range 99 | 100 | 101 | 102 | 103 | rx.scheduler 104 | 105 | rx.scheduler 106 | 107 | 108 | rx->rx.scheduler 109 | 110 | 111 | 112 | 113 | rx.subject 114 | 115 | rx.subject 116 | 117 | 118 | rx->rx.subject 119 | 120 | 121 | 122 | 123 | rx.util 124 | 125 | rx.util 126 | 127 | 128 | rx->rx.util 129 | 130 | 131 | 132 | 133 | rx.algorithm.all 134 | 135 | rx.algorithm.all 136 | 137 | 138 | rx.algorithm->rx.algorithm.all 139 | 140 | 141 | 142 | 143 | rx.algorithm.any 144 | 145 | rx.algorithm.any 146 | 147 | 148 | rx.algorithm->rx.algorithm.any 149 | 150 | 151 | 152 | 153 | rx.algorithm.buffer 154 | 155 | rx.algorithm.buffer 156 | 157 | 158 | rx.algorithm->rx.algorithm.buffer 159 | 160 | 161 | 162 | 163 | rx.algorithm.debounce 164 | 165 | rx.algorithm.debounce 166 | 167 | 168 | rx.algorithm->rx.algorithm.debounce 169 | 170 | 171 | 172 | 173 | rx.algorithm.filter 174 | 175 | rx.algorithm.filter 176 | 177 | 178 | rx.algorithm->rx.algorithm.filter 179 | 180 | 181 | 182 | 183 | rx.algorithm.fold 184 | 185 | rx.algorithm.fold 186 | 187 | 188 | rx.algorithm->rx.algorithm.fold 189 | 190 | 191 | 192 | 193 | rx.algorithm.groupby 194 | 195 | rx.algorithm.groupby 196 | 197 | 198 | rx.algorithm->rx.algorithm.groupby 199 | 200 | 201 | 202 | 203 | rx.algorithm.map 204 | 205 | rx.algorithm.map 206 | 207 | 208 | rx.algorithm->rx.algorithm.map 209 | 210 | 211 | 212 | 213 | rx.algorithm.merge 214 | 215 | rx.algorithm.merge 216 | 217 | 218 | rx.algorithm->rx.algorithm.merge 219 | 220 | 221 | 222 | 223 | rx.algorithm.scan 224 | 225 | rx.algorithm.scan 226 | 227 | 228 | rx.algorithm->rx.algorithm.scan 229 | 230 | 231 | 232 | 233 | rx.algorithm.tee 234 | 235 | rx.algorithm.tee 236 | 237 | 238 | rx.algorithm->rx.algorithm.tee 239 | 240 | 241 | 242 | 243 | rx.algorithm.uniq 244 | 245 | rx.algorithm.uniq 246 | 247 | 248 | rx.algorithm->rx.algorithm.uniq 249 | 250 | 251 | 252 | 253 | rx.algorithm.combineLatest 254 | 255 | rx.algorithm.combineLatest 256 | 257 | 258 | rx.algorithm->rx.algorithm.combineLatest 259 | 260 | 261 | 262 | 263 | rx.algorithm.all->rx.disposable 264 | 265 | 266 | 267 | 268 | rx.algorithm.all->rx.observable 269 | 270 | 271 | 272 | 273 | rx.algorithm.all->rx.observer 274 | 275 | 276 | 277 | 278 | rx.algorithm.all->rx.util 279 | 280 | 281 | 282 | 283 | std.functional 284 | 285 | std.functional 286 | 287 | 288 | rx.algorithm.all->std.functional 289 | 290 | 291 | 292 | 293 | std.range 294 | 295 | std.range 296 | 297 | 298 | rx.algorithm.all->std.range 299 | 300 | 301 | 302 | 303 | rx.algorithm.any->rx.disposable 304 | 305 | 306 | 307 | 308 | rx.algorithm.any->rx.observable 309 | 310 | 311 | 312 | 313 | rx.algorithm.any->rx.observer 314 | 315 | 316 | 317 | 318 | rx.algorithm.any->rx.util 319 | 320 | 321 | 322 | 323 | rx.algorithm.any->std.functional 324 | 325 | 326 | 327 | 328 | rx.algorithm.any->std.range 329 | 330 | 331 | 332 | 333 | rx.algorithm.buffer->rx.disposable 334 | 335 | 336 | 337 | 338 | rx.algorithm.buffer->rx.observable 339 | 340 | 341 | 342 | 343 | rx.algorithm.buffer->rx.observer 344 | 345 | 346 | 347 | 348 | rx.algorithm.buffer->std.range 349 | 350 | 351 | 352 | 353 | rx.algorithm.debounce->core.thread 354 | 355 | 356 | 357 | 358 | rx.algorithm.debounce->core.time 359 | 360 | 361 | 362 | 363 | rx.algorithm.debounce->rx.disposable 364 | 365 | 366 | 367 | 368 | rx.algorithm.debounce->rx.observable 369 | 370 | 371 | 372 | 373 | rx.algorithm.debounce->rx.observer 374 | 375 | 376 | 377 | 378 | rx.algorithm.debounce->rx.scheduler 379 | 380 | 381 | 382 | 383 | rx.algorithm.debounce->std.range 384 | 385 | 386 | 387 | 388 | rx.algorithm.filter->rx.disposable 389 | 390 | 391 | 392 | 393 | rx.algorithm.filter->rx.observable 394 | 395 | 396 | 397 | 398 | rx.algorithm.filter->rx.observer 399 | 400 | 401 | 402 | 403 | rx.algorithm.filter->rx.util 404 | 405 | 406 | 407 | 408 | rx.algorithm.filter->std.functional 409 | 410 | 411 | 412 | 413 | rx.algorithm.filter->std.range 414 | 415 | 416 | 417 | 418 | rx.algorithm.fold->rx.disposable 419 | 420 | 421 | 422 | 423 | rx.algorithm.fold->rx.observable 424 | 425 | 426 | 427 | 428 | rx.algorithm.fold->rx.observer 429 | 430 | 431 | 432 | 433 | rx.algorithm.groupby->rx.disposable 434 | 435 | 436 | 437 | 438 | rx.algorithm.groupby->rx.observable 439 | 440 | 441 | 442 | 443 | rx.algorithm.groupby->rx.observer 444 | 445 | 446 | 447 | 448 | rx.algorithm.groupby->rx.subject 449 | 450 | 451 | 452 | 453 | rx.algorithm.groupby->rx.util 454 | 455 | 456 | 457 | 458 | rx.algorithm.groupby->std.functional 459 | 460 | 461 | 462 | 463 | rx.algorithm.groupby->std.range 464 | 465 | 466 | 467 | 468 | rx.algorithm.map->rx.disposable 469 | 470 | 471 | 472 | 473 | rx.algorithm.map->rx.observable 474 | 475 | 476 | 477 | 478 | rx.algorithm.map->rx.observer 479 | 480 | 481 | 482 | 483 | rx.algorithm.map->rx.util 484 | 485 | 486 | 487 | 488 | rx.algorithm.map->std.functional 489 | 490 | 491 | 492 | 493 | rx.algorithm.map->std.range 494 | 495 | 496 | 497 | 498 | rx.algorithm.merge->rx.disposable 499 | 500 | 501 | 502 | 503 | rx.algorithm.merge->rx.observable 504 | 505 | 506 | 507 | 508 | rx.algorithm.merge->rx.observer 509 | 510 | 511 | 512 | 513 | rx.algorithm.merge->rx.util 514 | 515 | 516 | 517 | 518 | rx.algorithm.merge->std.range 519 | 520 | 521 | 522 | 523 | rx.algorithm.scan->rx.disposable 524 | 525 | 526 | 527 | 528 | rx.algorithm.scan->rx.observable 529 | 530 | 531 | 532 | 533 | rx.algorithm.scan->rx.observer 534 | 535 | 536 | 537 | 538 | rx.algorithm.scan->rx.util 539 | 540 | 541 | 542 | 543 | rx.algorithm.scan->std.functional 544 | 545 | 546 | 547 | 548 | rx.algorithm.scan->std.range 549 | 550 | 551 | 552 | 553 | std.typecons 554 | 555 | std.typecons 556 | 557 | 558 | rx.algorithm.scan->std.typecons 559 | 560 | 561 | 562 | 563 | rx.algorithm.tee->rx.disposable 564 | 565 | 566 | 567 | 568 | rx.algorithm.tee->rx.observable 569 | 570 | 571 | 572 | 573 | rx.algorithm.tee->rx.observer 574 | 575 | 576 | 577 | 578 | rx.algorithm.tee->rx.util 579 | 580 | 581 | 582 | 583 | rx.algorithm.tee->std.functional 584 | 585 | 586 | 587 | 588 | rx.algorithm.tee->std.range 589 | 590 | 591 | 592 | 593 | rx.algorithm.uniq->rx.disposable 594 | 595 | 596 | 597 | 598 | rx.algorithm.uniq->rx.observable 599 | 600 | 601 | 602 | 603 | rx.algorithm.uniq->rx.observer 604 | 605 | 606 | 607 | 608 | rx.algorithm.uniq->rx.util 609 | 610 | 611 | 612 | 613 | rx.algorithm.uniq->std.functional 614 | 615 | 616 | 617 | 618 | rx.algorithm.uniq->std.range 619 | 620 | 621 | 622 | 623 | rx.disposable->core.atomic 624 | 625 | 626 | 627 | 628 | rx.disposable->core.internal.traits 629 | 630 | 631 | 632 | 633 | rx.disposable->core.stdc.string 634 | 635 | 636 | 637 | 638 | rx.disposable->core.sync.mutex 639 | 640 | 641 | 642 | 643 | rx.disposable->rx.util 644 | 645 | 646 | 647 | 648 | std.concurrency 649 | 650 | std.concurrency 651 | 652 | 653 | rx.disposable->std.concurrency 654 | 655 | 656 | 657 | 658 | rx.observable->rx.disposable 659 | 660 | 661 | 662 | 663 | rx.observable->rx.observer 664 | 665 | 666 | 667 | 668 | rx.observable->rx.util 669 | 670 | 671 | 672 | 673 | rx.observable->std.functional 674 | 675 | 676 | 677 | 678 | rx.observable->std.range 679 | 680 | 681 | 682 | 683 | rx.observer->std.range 684 | 685 | 686 | 687 | 688 | std.typetuple 689 | 690 | std.typetuple 691 | 692 | 693 | rx.observer->std.typetuple 694 | 695 | 696 | 697 | 698 | rx.range->core.atomic 699 | 700 | 701 | 702 | 703 | rx.range->rx.disposable 704 | 705 | 706 | 707 | 708 | rx.range->rx.observable 709 | 710 | 711 | 712 | 713 | rx.range->rx.observer 714 | 715 | 716 | 717 | 718 | rx.range->rx.util 719 | 720 | 721 | 722 | 723 | rx.range->std.range 724 | 725 | 726 | 727 | 728 | rx.range.drop 729 | 730 | rx.range.drop 731 | 732 | 733 | rx.range->rx.range.drop 734 | 735 | 736 | 737 | 738 | rx.range.take 739 | 740 | rx.range.take 741 | 742 | 743 | rx.range->rx.range.take 744 | 745 | 746 | 747 | 748 | rx.range.takeLast 749 | 750 | rx.range.takeLast 751 | 752 | 753 | rx.range->rx.range.takeLast 754 | 755 | 756 | 757 | 758 | rx.scheduler->core.internal.traits 759 | 760 | 761 | 762 | 763 | rx.scheduler->core.thread 764 | 765 | 766 | 767 | 768 | rx.scheduler->core.time 769 | 770 | 771 | 772 | 773 | rx.scheduler->rx.disposable 774 | 775 | 776 | 777 | 778 | rx.scheduler->rx.observable 779 | 780 | 781 | 782 | 783 | rx.scheduler->rx.observer 784 | 785 | 786 | 787 | 788 | std.algorithm.internal 789 | 790 | std.algorithm.internal 791 | 792 | 793 | rx.scheduler->std.algorithm.internal 794 | 795 | 796 | 797 | 798 | std.exception 799 | 800 | std.exception 801 | 802 | 803 | rx.scheduler->std.exception 804 | 805 | 806 | 807 | 808 | std.parallelism 809 | 810 | std.parallelism 811 | 812 | 813 | rx.scheduler->std.parallelism 814 | 815 | 816 | 817 | 818 | rx.scheduler->std.range 819 | 820 | 821 | 822 | 823 | rx.subject->core.atomic 824 | 825 | 826 | 827 | 828 | rx.subject->rx.disposable 829 | 830 | 831 | 832 | 833 | rx.subject->rx.observable 834 | 835 | 836 | 837 | 838 | rx.subject->rx.observer 839 | 840 | 841 | 842 | 843 | rx.subject->rx.util 844 | 845 | 846 | 847 | 848 | rx.subject->std.range 849 | 850 | 851 | 852 | 853 | rx.util->core.atomic 854 | 855 | 856 | 857 | 858 | rx.util->core.sync.condition 859 | 860 | 861 | 862 | 863 | rx.util->core.sync.mutex 864 | 865 | 866 | 867 | 868 | rx.algorithm.combineLatest->rx.disposable 869 | 870 | 871 | 872 | 873 | rx.algorithm.combineLatest->rx.observable 874 | 875 | 876 | 877 | 878 | rx.algorithm.combineLatest->rx.observer 879 | 880 | 881 | 882 | 883 | rx.algorithm.combineLatest->rx.util 884 | 885 | 886 | 887 | 888 | rx.algorithm.combineLatest->std.range 889 | 890 | 891 | 892 | 893 | rx.algorithm.combineLatest->std.typecons 894 | 895 | 896 | 897 | 898 | std.meta 899 | 900 | std.meta 901 | 902 | 903 | rx.algorithm.combineLatest->std.meta 904 | 905 | 906 | 907 | 908 | rx.range.drop->rx.disposable 909 | 910 | 911 | 912 | 913 | rx.range.drop->rx.observable 914 | 915 | 916 | 917 | 918 | rx.range.drop->rx.observer 919 | 920 | 921 | 922 | 923 | rx.range.drop->rx.util 924 | 925 | 926 | 927 | 928 | rx.range.drop->std.range 929 | 930 | 931 | 932 | 933 | rx.range.take->rx.disposable 934 | 935 | 936 | 937 | 938 | rx.range.take->rx.observable 939 | 940 | 941 | 942 | 943 | rx.range.take->rx.observer 944 | 945 | 946 | 947 | 948 | rx.range.take->rx.util 949 | 950 | 951 | 952 | 953 | rx.range.take->std.range 954 | 955 | 956 | 957 | 958 | rx.range.takeLast->rx.disposable 959 | 960 | 961 | 962 | 963 | rx.range.takeLast->rx.observable 964 | 965 | 966 | 967 | 968 | rx.range.takeLast->rx.observer 969 | 970 | 971 | 972 | 973 | rx.range.takeLast->rx.util 974 | 975 | 976 | 977 | 978 | rx.range.takeLast->std.range 979 | 980 | 981 | 982 | 983 | 984 | -------------------------------------------------------------------------------- /source/app.d: -------------------------------------------------------------------------------- 1 | import std.stdio; 2 | import std.algorithm; 3 | import std.conv; 4 | import std.container.rbtree; 5 | import std.array; 6 | import std.file; 7 | import std.getopt; 8 | import std.range; 9 | 10 | version (unittest) 11 | { 12 | } 13 | else 14 | { 15 | int main(string[] args) 16 | { 17 | string outfile; 18 | string depsfile = "deps.txt"; 19 | string lockfile = "deps-lock.txt"; 20 | string focusName = "app"; 21 | int filterCost = 1; 22 | bool forceUpdate; 23 | string[] excludeNames = ["object"]; // default exclude 24 | 25 | // dfmt off 26 | auto helpInformation = getopt(args, 27 | "i|input", "deps file name", &depsfile, 28 | "o|output", "graph file name.\n\tIf not specified, it is standard output.", &outfile, 29 | "u|update", "update lock file", &forceUpdate, 30 | "l|lock", "lock file name", &lockfile, 31 | "f|focus", "filtering target name", &focusName, 32 | "d|depth", "depth for dependency search", &filterCost, 33 | "e|exclude", "exclude module names", &excludeNames 34 | ); 35 | // dfmt on 36 | 37 | if (helpInformation.helpWanted) 38 | { 39 | defaultGetoptPrinter("", helpInformation.options); 40 | return 0; 41 | } 42 | 43 | if (forceUpdate) 44 | { 45 | writefln!"copy lockfile (%s)"(lockfile); 46 | copy(depsfile, lockfile); 47 | return 0; 48 | } 49 | 50 | auto beforeGraph = readText(lockfile).toGraph(); 51 | auto afterGraph = readText(depsfile).toGraph(); 52 | 53 | auto diff = makeDiff(beforeGraph, afterGraph, DiffSettings(focusName, 54 | filterCost, excludeNames)); 55 | 56 | auto f = outfile ? File(outfile, "w") : stdout; 57 | scope (exit) 58 | f.close(); 59 | 60 | f.writeln("digraph {"); 61 | if (diff.keptNodes.length > 0) 62 | { 63 | f.writeln(" {"); 64 | foreach (m; diff.keptNodes) 65 | { 66 | f.writefln!" \"%s\""(m.name); 67 | } 68 | f.writeln(" }"); 69 | } 70 | if (diff.removedNodes.length > 0) 71 | { 72 | f.writeln(" {"); 73 | f.writeln(` node [style=filled color="#fdaeb7" fillcolor="#ffeef0"];`); 74 | foreach (m; diff.removedNodes) 75 | { 76 | f.writefln!" \"%s\""(m.name); 77 | } 78 | f.writeln(" }"); 79 | } 80 | if (diff.addedNodes.length > 0) 81 | { 82 | f.writeln(" {"); 83 | f.writeln(` node [style=filled color="#bef5cb" fillcolor="#e6ffed"];`); 84 | foreach (m; diff.addedNodes) 85 | { 86 | f.writefln!" \"%s\""(m.name); 87 | } 88 | f.writeln(" }"); 89 | } 90 | 91 | //////////////////////////////////////////////////////////////////////////// 92 | enum ModuleEditType 93 | { 94 | Keep, 95 | Remove, 96 | Add, 97 | } 98 | 99 | ModuleEditType[string] moduleSet; 100 | foreach (m; diff.keptNodes) 101 | moduleSet[m.name] = ModuleEditType.Keep; 102 | foreach (m; diff.removedNodes) 103 | moduleSet[m.name] = ModuleEditType.Remove; 104 | foreach (m; diff.addedNodes) 105 | moduleSet[m.name] = ModuleEditType.Add; 106 | 107 | if (diff.keptEdges.length > 0) 108 | { 109 | foreach (m; diff.keptEdges) 110 | { 111 | switch (moduleSet[m.import_.name]) with (ModuleEditType) 112 | { 113 | case Keep: 114 | f.writefln!` "%s" -> "%s";`(m.module_.name, m.import_.name); 115 | break; 116 | case Remove: 117 | f.writefln!` "%s" -> "%s" [color="#cb2431"];`(m.module_.name, 118 | m.import_.name); 119 | break; 120 | case Add: 121 | f.writefln!` "%s" -> "%s" [color="#2cbe4e"];`(m.module_.name, 122 | m.import_.name); 123 | break; 124 | default: 125 | writeln("// unknown edge: ", m); 126 | break; 127 | } 128 | } 129 | } 130 | if (diff.removedEdges.length > 0) 131 | { 132 | foreach (m; diff.removedEdges) 133 | f.writefln!` "%s" -> "%s" [color="#cb2431"];`(m.module_.name, m.import_.name); 134 | } 135 | if (diff.addedEdges.length > 0) 136 | { 137 | foreach (m; diff.addedEdges) 138 | f.writefln!` "%s" -> "%s" [color="#2cbe4e"];`(m.module_.name, m.import_.name); 139 | } 140 | f.writeln("}"); 141 | 142 | return 0; 143 | } 144 | } 145 | 146 | DependenciesGraph toGraph(string value) 147 | { 148 | auto nodes = new RedBlackTree!Node(); 149 | auto edges = new RedBlackTree!Edge(); 150 | 151 | foreach (line; value.splitter("\n")) 152 | { 153 | if (line.length == 0) 154 | continue; 155 | 156 | auto tokens = splitter(line, " : "); 157 | auto sourceText = tokens.pop(); 158 | auto importType = tokens.pop(); 159 | auto targetText = tokens.pop(); 160 | 161 | auto sourceName = until(sourceText, " ").to!string(); 162 | auto sourceModule = Node(sourceName); 163 | nodes.insert(sourceModule); 164 | auto targetName = targetText.until(" ").to!string(); 165 | auto targetModule = Node(targetName); 166 | nodes.insert(targetModule); 167 | 168 | edges.insert(Edge("module", sourceModule, targetModule, importType)); 169 | } 170 | 171 | // TODO 省略できる気がする 172 | auto tempNodes = nodes[].array(); 173 | tempNodes.sort(); 174 | auto tempEdges = edges[].array(); 175 | tempEdges.sort(); 176 | return new DependenciesGraph(tempNodes, tempEdges); 177 | } 178 | 179 | unittest 180 | { 181 | auto graph = toGraph(` 182 | app (/app.d) : private : object (/object.d) 183 | app (/app.d) : private : std.stdio (/std/stdio.d) 184 | `); 185 | 186 | assert(graph.modules.length == 3); 187 | assert(graph.modules[0].name == "app"); 188 | assert(graph.modules[1].name == "object"); 189 | assert(graph.modules[2].name == "std.stdio"); 190 | 191 | assert(graph.dependencies.length == 2); 192 | assert(graph.dependencies[0].module_.name == "app"); 193 | assert(graph.dependencies[0].import_.name == "object"); 194 | assert(graph.dependencies[1].module_.name == "app"); 195 | assert(graph.dependencies[1].import_.name == "std.stdio"); 196 | } 197 | 198 | struct GraphDiff 199 | { 200 | Node[] keptNodes; 201 | Node[] removedNodes; 202 | Node[] addedNodes; 203 | 204 | Edge[] keptEdges; 205 | Edge[] removedEdges; 206 | Edge[] addedEdges; 207 | } 208 | 209 | struct DiffSettings 210 | { 211 | this(string filterName, int filterCost) 212 | { 213 | this(filterName, filterCost, null); 214 | } 215 | 216 | this(string filterName, int filterCost, string[] excludeNames) 217 | { 218 | this.filterName = filterName; 219 | this.filterCost = filterCost; 220 | this.excludeNames = excludeNames; 221 | } 222 | 223 | string filterName; 224 | int filterCost; 225 | string[] excludeNames; 226 | } 227 | 228 | GraphDiff makeDiff(DependenciesGraph before, DependenciesGraph after, DiffSettings settings) 229 | { 230 | GraphDiff result; 231 | 232 | auto beforeTempCost = makeNodeCostDic(before.dependencies, 233 | settings.filterName, settings.filterCost); 234 | auto afterTempCost = makeNodeCostDic(after.dependencies, 235 | settings.filterName, settings.filterCost); 236 | 237 | auto beforeEdges = before.dependencies.filter!(e => isOutputTarget(e, 238 | beforeTempCost, settings.filterCost, settings.excludeNames)).array(); 239 | auto afterEdges = after.dependencies.filter!(e => isOutputTarget(e, 240 | afterTempCost, settings.filterCost, settings.excludeNames)).array(); 241 | 242 | result.keptEdges = setIntersection(beforeEdges, afterEdges).array(); 243 | result.removedEdges = setDifference(beforeEdges, afterEdges).array(); 244 | result.addedEdges = setDifference(afterEdges, beforeEdges).array(); 245 | 246 | auto beforeCost = makeNodeCostDic(beforeEdges, settings.filterName, settings.filterCost); 247 | auto afterCost = makeNodeCostDic(afterEdges, settings.filterName, settings.filterCost); 248 | 249 | auto beforeNodes = before.modules.filter!(m => isOutputTarget(m, 250 | beforeCost, settings.filterCost)).array(); 251 | auto afterNodes = after.modules.filter!(m => isOutputTarget(m, afterCost, 252 | settings.filterCost)).array(); 253 | 254 | result.keptNodes = setIntersection(beforeNodes, afterNodes).array(); 255 | result.removedNodes = setDifference(beforeNodes, afterNodes).array(); 256 | result.addedNodes = setDifference(afterNodes, beforeNodes).array(); 257 | 258 | return result; 259 | } 260 | 261 | unittest 262 | { 263 | // Add node and edge 264 | auto before = toGraph(` 265 | app (/app.d) : private : object (/object.d) 266 | app (/app.d) : private : std.stdio (/std/stdio.d) 267 | `); 268 | auto after = toGraph(` 269 | app (/app.d) : private : object (/object.d) 270 | app (/app.d) : private : std.stdio (/std/stdio.d) 271 | app (/app.d) : private : std.algorithm (/std/algorithm.d) 272 | `); 273 | 274 | auto diff = makeDiff(before, after, DiffSettings("app", 1)); 275 | 276 | assert(diff.keptNodes.length == 3); 277 | assert(diff.keptNodes[0].name == "app"); 278 | assert(diff.keptNodes[1].name == "object"); 279 | assert(diff.keptNodes[2].name == "std.stdio"); 280 | assert(diff.removedNodes.length == 0); 281 | assert(diff.addedNodes.length == 1); 282 | assert(diff.addedNodes[0].name == "std.algorithm"); 283 | 284 | assert(diff.keptEdges.length == 2); 285 | assert(diff.keptEdges[0].module_.name == "app"); 286 | assert(diff.keptEdges[0].import_.name == "object"); 287 | assert(diff.keptEdges[1].module_.name == "app"); 288 | assert(diff.keptEdges[1].import_.name == "std.stdio"); 289 | assert(diff.removedEdges.length == 0); 290 | assert(diff.addedEdges.length == 1); 291 | assert(diff.addedEdges[0].module_.name == "app"); 292 | assert(diff.addedEdges[0].import_.name == "std.algorithm"); 293 | } 294 | 295 | unittest 296 | { 297 | // Remove node and edge 298 | auto before = toGraph(` 299 | app (/app.d) : private : object (/object.d) 300 | app (/app.d) : private : std.stdio (/std/stdio.d) 301 | app (/app.d) : private : std.algorithm (/std/algorithm.d) 302 | std.stdio (/std/stdio.d) : private : object (/object.d) 303 | `); 304 | auto after = toGraph(` 305 | app (/app.d) : private : object (/object.d) 306 | app (/app.d) : private : std.stdio (/std/stdio.d) 307 | std.stdio (/std/stdio.d) : private : object (/object.d) 308 | `); 309 | 310 | auto diff = makeDiff(before, after, DiffSettings("app", 1)); 311 | 312 | assert(diff.keptNodes.length == 3); 313 | assert(diff.keptNodes[0].name == "app"); 314 | assert(diff.keptNodes[1].name == "object"); 315 | assert(diff.keptNodes[2].name == "std.stdio"); 316 | assert(diff.removedNodes.length == 1); 317 | assert(diff.removedNodes[0].name == "std.algorithm"); 318 | assert(diff.addedNodes.length == 0); 319 | 320 | assert(diff.keptEdges.length == 2); 321 | assert(diff.keptEdges[0].module_.name == "app"); 322 | assert(diff.keptEdges[0].import_.name == "object"); 323 | assert(diff.keptEdges[1].module_.name == "app"); 324 | assert(diff.keptEdges[1].import_.name == "std.stdio"); 325 | assert(diff.removedEdges.length == 1); 326 | assert(diff.removedEdges[0].module_.name == "app"); 327 | assert(diff.removedEdges[0].import_.name == "std.algorithm"); 328 | assert(diff.addedEdges.length == 0); 329 | } 330 | 331 | unittest 332 | { 333 | // Add node and edge with excludeNames, 334 | // then omit about the std.conv 335 | auto before = toGraph(` 336 | app (/app.d) : private : object (/object.d) 337 | app (/app.d) : private : std.stdio (/std/stdio.d) 338 | std.stdio (/std/stdio.d) : private : object (/object.d) 339 | `); 340 | auto after = toGraph(` 341 | app (/app.d) : private : object (/object.d) 342 | app (/app.d) : private : std.conv (/std/conv.d) 343 | app (/app.d) : private : std.stdio (/std/stdio.d) 344 | std.stdio (/std/stdio.d) : private : object (/object.d) 345 | `); 346 | 347 | auto diff = makeDiff(before, after, DiffSettings("app", 2, ["std.conv"])); 348 | 349 | assert(diff.keptNodes.length == 3); 350 | assert(diff.keptNodes[0].name == "app"); 351 | assert(diff.keptNodes[1].name == "object"); 352 | assert(diff.keptNodes[2].name == "std.stdio"); 353 | assert(diff.removedNodes.length == 0); 354 | assert(diff.addedNodes.length == 0); 355 | 356 | assert(diff.keptEdges.length == 3); 357 | assert(diff.keptEdges[0].module_.name == "app"); 358 | assert(diff.keptEdges[0].import_.name == "object"); 359 | assert(diff.keptEdges[1].module_.name == "app"); 360 | assert(diff.keptEdges[1].import_.name == "std.stdio"); 361 | assert(diff.keptEdges[2].module_.name == "std.stdio"); 362 | assert(diff.keptEdges[2].import_.name == "object"); 363 | assert(diff.removedEdges.length == 0); 364 | assert(diff.addedEdges.length == 0); 365 | } 366 | 367 | unittest 368 | { 369 | // Remove node and edge with excludeNames, 370 | // then omit about the std.conv 371 | auto before = toGraph(` 372 | app (/app.d) : private : object (/object.d) 373 | app (/app.d) : private : std.conv (/std/conv.d) 374 | app (/app.d) : private : std.stdio (/std/stdio.d) 375 | std.stdio (/std/stdio.d) : private : object (/object.d) 376 | `); 377 | auto after = toGraph(` 378 | app (/app.d) : private : object (/object.d) 379 | app (/app.d) : private : std.stdio (/std/stdio.d) 380 | std.stdio (/std/stdio.d) : private : object (/object.d) 381 | `); 382 | 383 | auto diff = makeDiff(before, after, DiffSettings("app", 2, ["std.conv"])); 384 | 385 | assert(diff.keptNodes.length == 3); 386 | assert(diff.keptNodes[0].name == "app"); 387 | assert(diff.keptNodes[1].name == "object"); 388 | assert(diff.keptNodes[2].name == "std.stdio"); 389 | 390 | assert(diff.keptEdges.length == 3); 391 | assert(diff.keptEdges[0].module_.name == "app"); 392 | assert(diff.keptEdges[0].import_.name == "object"); 393 | assert(diff.keptEdges[1].module_.name == "app"); 394 | assert(diff.keptEdges[1].import_.name == "std.stdio"); 395 | assert(diff.keptEdges[2].module_.name == "std.stdio"); 396 | assert(diff.keptEdges[2].import_.name == "object"); 397 | assert(diff.addedEdges.length == 0); 398 | assert(diff.removedEdges.length == 0); 399 | } 400 | 401 | struct Node 402 | { 403 | string name; 404 | 405 | this(string name) 406 | { 407 | this.name = name; 408 | } 409 | 410 | int opCmp(ref const Node rhs) 411 | { 412 | return cmp(name, rhs.name); 413 | } 414 | 415 | int opCmp(ref const Node rhs) const 416 | { 417 | return cmp(name, rhs.name); 418 | } 419 | } 420 | 421 | struct Edge 422 | { 423 | string kind; 424 | Node module_; 425 | Node import_; 426 | string importType; 427 | 428 | this(string kind, Node module_, Node import_, string importType = "private") 429 | { 430 | this.kind = kind; 431 | this.module_ = module_; 432 | this.import_ = import_; 433 | this.importType = importType; 434 | } 435 | 436 | int opCmp(ref const Edge rhs) const 437 | { 438 | auto kind = cmp(this.kind, rhs.kind); 439 | if (kind != 0) 440 | return kind; 441 | auto module_ = cmp(this.module_.name, rhs.module_.name); 442 | if (module_ != 0) 443 | return module_; 444 | auto import_ = cmp(this.import_.name, rhs.import_.name); 445 | return import_; 446 | } 447 | } 448 | 449 | class DependenciesGraph 450 | { 451 | Node[] modules; 452 | Edge[] dependencies; 453 | 454 | this(Node[] modules, Edge[] dependencies) 455 | { 456 | this.modules = modules; 457 | this.dependencies = dependencies; 458 | } 459 | } 460 | 461 | auto pop(R)(auto ref R range) 462 | { 463 | scope (success) 464 | range.popFront(); 465 | 466 | return range.front; 467 | } 468 | 469 | int[string] makeNodeCostDic(Edge[] edges, string filterName, int maxCost = int.max) 470 | { 471 | typeof(return) result; 472 | 473 | auto nodeSet = new RedBlackTree!string; 474 | foreach (edge; edges) 475 | { 476 | nodeSet.insert(edge.module_.name); 477 | nodeSet.insert(edge.import_.name); 478 | } 479 | foreach (name; nodeSet[]) 480 | { 481 | result[name] = isFocusTarget(name, filterName) ? 0 : int.max; 482 | } 483 | 484 | bool changed; 485 | int currentCost = -1; 486 | 487 | do 488 | { 489 | changed = false; 490 | currentCost++; 491 | 492 | foreach (edge; edges) 493 | { 494 | if (result[edge.module_.name] == currentCost) 495 | { 496 | result[edge.import_.name] = min(result[edge.import_.name], currentCost + 1); 497 | changed = true; 498 | } 499 | } 500 | } 501 | while (changed && currentCost < maxCost); 502 | 503 | return result; 504 | } 505 | 506 | unittest 507 | { 508 | auto cost = makeNodeCostDic([Edge(null, Node("app"), Node("std.stdio"), null)], "app"); 509 | assert(cost["app"] == 0); 510 | assert(cost["std.stdio"] == 1); 511 | } 512 | 513 | bool isFocusTarget(const scope string nodeName, const scope string name) 514 | { 515 | if (nodeName.length < name.length) 516 | return false; 517 | if (nodeName[0 .. name.length] != name) 518 | return false; 519 | 520 | if (nodeName.length == name.length) 521 | return true; 522 | if (nodeName.length >= name.length + 1 && nodeName[name.length] == '.') 523 | return true; 524 | 525 | return false; 526 | } 527 | 528 | unittest 529 | { 530 | assert(isFocusTarget("app", "app")); 531 | assert(isFocusTarget("app.common", "app")); 532 | assert(isFocusTarget("app.testing.utils", "app")); 533 | assert(!isFocusTarget("std.algorithm", "app")); 534 | assert(!isFocusTarget("core.atomic", "app")); 535 | assert(!isFocusTarget("util", "app")); 536 | 537 | assert(isFocusTarget("core.atomic", "core")); 538 | 539 | assert(!isFocusTarget("app", "util")); 540 | } 541 | 542 | int getCost(Node node, int[string] cost) 543 | { 544 | if (!(node.name in cost)) 545 | return int.max; 546 | return cost[node.name]; 547 | } 548 | 549 | bool isOutputTarget(Node node, int[string] cost, int filter, string[] excludeNames = null) 550 | { 551 | foreach (excludeName; excludeNames) 552 | { 553 | if (node.name == excludeName || node.name.startsWith(chain(excludeName, "."))) 554 | return false; 555 | } 556 | 557 | return node.getCost(cost) <= filter; 558 | } 559 | 560 | unittest 561 | { 562 | int[string] cost = ["app" : 0, "std.stdio" : 1, "core.atomic" : 2, "object" : 3]; 563 | assert(isOutputTarget(Node("app"), cost, 1)); 564 | assert(isOutputTarget(Node("std.stdio"), cost, 1)); 565 | assert(!isOutputTarget(Node("core.atomic"), cost, 1)); 566 | assert(!isOutputTarget(Node("object"), cost, 1)); 567 | 568 | assert(isOutputTarget(Node("app"), cost, 2)); 569 | assert(isOutputTarget(Node("std.stdio"), cost, 2)); 570 | assert(isOutputTarget(Node("core.atomic"), cost, 2)); 571 | assert(!isOutputTarget(Node("object"), cost, 2)); 572 | 573 | assert(isOutputTarget(Node("app"), cost, 1, ["std.stdio"])); 574 | assert(!isOutputTarget(Node("std.stdio"), cost, 1, ["std.stdio"])); 575 | } 576 | 577 | bool isOutputTarget(Edge edge, int[string] cost, int filter, string[] excludeNames = null) 578 | { 579 | foreach (excludeName; excludeNames) 580 | { 581 | if (edge.module_.name == excludeName 582 | || edge.module_.name.startsWith(chain(excludeName, "."))) 583 | return false; 584 | if (edge.import_.name == excludeName 585 | || edge.import_.name.startsWith(chain(excludeName, "."))) 586 | return false; 587 | } 588 | 589 | if (edge.import_.getCost(cost) > filter) 590 | return false; 591 | if (edge.module_.getCost(cost) >= filter) 592 | return false; 593 | 594 | return true; 595 | } 596 | 597 | unittest 598 | { 599 | int[string] cost = ["app" : 0, "std.stdio" : 1, "core.atomic" : 2, "object" : 3]; 600 | assert(isOutputTarget(Edge("module", Node("app"), Node("std.stdio")), cost, 1)); 601 | assert(!isOutputTarget(Edge("module", Node("std.stdio"), Node("core.atomic")), cost, 1)); 602 | assert(isOutputTarget(Edge("module", Node("std.stdio"), Node("core.atomic")), cost, 2)); 603 | 604 | assert(isOutputTarget(Edge("module", Node("app"), Node("std.stdio")), cost, 1, ["std.conv"])); 605 | assert(!isOutputTarget(Edge("module", Node("app"), Node("std.stdio")), cost, 1, ["std.stdio"])); 606 | assert(!isOutputTarget(Edge("module", Node("std.stdio"), 607 | Node("core.atomic")), cost, 1, ["std.stdio"])); 608 | assert(!isOutputTarget(Edge("module", Node("std.stdio"), 609 | Node("core.atomic")), cost, 2, ["std.stdio"])); 610 | } 611 | --------------------------------------------------------------------------------