1 /* 2 ------------------------------------------------------------------- 3 4 Copyright (C) 2014, Edwin van Leeuwen 5 6 This file is part of todod todo list manager. 7 8 Todod is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 Todod is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with Todod. If not, see <http://www.gnu.org/licenses/>. 20 21 ------------------------------------------------------------------- 22 */ 23 24 /// Manage dependencies between Todos (identified by their uuid) 25 module todod.dependency; 26 27 import std.algorithm; 28 import std.json; 29 import std.uuid; 30 31 import todod.storage; 32 33 version( unittest ) { 34 import std.stdio; 35 } 36 37 /// Contains one link between child which depends on parent 38 struct Link { 39 @disable this(); 40 41 UUID _parent; /// uuid of the parent 42 UUID _child; /// uuid of the child 43 44 /** 45 Set the link with @child depending on @parent 46 */ 47 this( UUID parent, UUID child ) { 48 _parent = parent; 49 _child = child; 50 } 51 52 unittest { 53 auto prnt = randomUUID; 54 auto chld = randomUUID; 55 auto lnk = Link( prnt, chld ); 56 assert( lnk._parent == prnt ); 57 assert( lnk._child == chld ); 58 } 59 } 60 61 JSONValue toJSON( Link link ) { 62 JSONValue[string] jsonLink; 63 jsonLink["parent"] = link._parent.toString; 64 jsonLink["child"] = link._child.toString; 65 return JSONValue( jsonLink ); 66 } 67 68 Link toLink( const JSONValue json ) { 69 Link link = Link( UUID(), UUID() ); 70 const JSONValue[string] jsonAA = json.object; 71 if ("parent" in jsonAA) 72 link._parent = UUID( jsonAA["parent"].str ); 73 if ("child" in jsonAA) 74 link._child = UUID( jsonAA["child"].str ); 75 return link; 76 } 77 78 unittest { 79 Link orig = Link( randomUUID, randomUUID ); 80 auto json = toJSON( orig ); 81 Link link = toLink( json ); 82 assert( link._parent == orig._parent ); 83 assert( link._child == orig._child ); 84 } 85 86 alias Dependencies = Link[]; 87 88 /// Is given uuid a child of anyone 89 bool isAChild( in Dependencies deps, in UUID child ) { 90 return canFind!( (a,b) => a._child == b )( deps, child ); 91 } 92 93 unittest { 94 Dependencies deps; 95 auto child = randomUUID; 96 deps ~= Link( randomUUID, child ); 97 deps ~= Link( randomUUID, randomUUID ); 98 deps ~= Link( randomUUID, randomUUID ); 99 assert( deps.isAChild( child ) ); 100 assert( !deps.isAChild( randomUUID ) ); 101 auto child2 = randomUUID; 102 deps ~= Link( randomUUID, child2 ); 103 deps ~= Link( randomUUID, randomUUID ); 104 assert( deps.isAChild( child ) ); 105 assert( deps.isAChild( child2 ) ); 106 assert( !deps.isAChild( randomUUID ) ); 107 } 108 109 /// Remove given uuid completely from the dependencies 110 Dependencies removeUUID( ref Dependencies deps, UUID theUUID ) { 111 Dependencies result; 112 foreach( lnk; deps ) // Ideally use remove!pred, but for some reason that did 113 // not work properly 114 if ( lnk._child != theUUID && lnk._parent != theUUID ) 115 result ~= lnk; 116 return result; 117 } 118 119 unittest { 120 Dependencies deps; 121 auto child = randomUUID; 122 auto parent = randomUUID; 123 deps ~= Link( randomUUID, child ); 124 deps ~= Link( parent, randomUUID ); 125 deps ~= Link( randomUUID, randomUUID ); 126 assert( deps.length == 3 ); 127 deps = removeUUID( deps, child ); 128 assert( deps.length == 2 ); 129 assert( removeUUID( deps, parent ).length == 1 ); 130 } 131 132 /// Group the parents in the dependencies by child. 133 UUID[][UUID] groupByChild( in Dependencies deps ) pure nothrow { 134 UUID[][UUID] groups; 135 foreach( link; deps ) 136 groups[link._child] ~= link._parent; 137 return groups; 138 } 139 140 unittest { 141 Dependencies deps; 142 auto child = randomUUID; 143 deps ~= Link( randomUUID, child ); 144 deps ~= Link( randomUUID, child ); 145 deps ~= Link( randomUUID, randomUUID ); 146 auto groups = groupByChild( deps ); 147 assert( child in groups ); 148 assert( groups[child].length == 2 ); 149 } 150 151 JSONValue toJSON(T)( in T[] range ) { 152 JSONValue[] json; 153 foreach (e; range) 154 json ~= toJSON( e ); 155 return JSONValue( json ); 156 } 157 158 Dependencies toDependencies( JSONValue json ) { 159 Dependencies deps; 160 foreach ( js; json.array ) 161 deps ~= toLink(js); 162 return deps; 163 } 164 165 unittest { 166 Dependencies deps; 167 auto child = randomUUID; 168 auto parent = randomUUID; 169 deps ~= Link( randomUUID, randomUUID ); 170 deps ~= Link( parent, child ); 171 deps ~= Link( randomUUID, randomUUID ); 172 auto json = toJSON( deps ); 173 auto dps = toDependencies( json ); 174 assert( dps.length == 3 ); 175 assert( dps[1]._parent == parent ); 176 assert( dps[1]._child == child ); 177 } 178 179 Dependencies loadDependencies( GitRepo gr ) { 180 Dependencies deps; 181 auto dependenciesFileName = "dependencies.json"; 182 auto content = readFile( gr.workPath, dependenciesFileName ); 183 if (content != "") 184 deps = toDependencies( parseJSON( content ) ); 185 return deps; 186 } 187 188 void writeDependencies( Dependencies deps, GitRepo gr ) { 189 auto dependenciesFileName = "dependencies.json"; 190 writeToFile( gr.workPath, dependenciesFileName, toJSON( deps ).toPrettyString ); 191 commitChanges( gr, dependenciesFileName, "Updating dependencies file" ); 192 }