fstree.py 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. from enum import Enum
  2. from collections import OrderedDict
  3. class FsNode:
  4. class NodeType(Enum):
  5. File = 0
  6. Directory = 1
  7. def __init__(self, name: str, nodetype: "FsNode.Type", **kwargs):
  8. self.name = name
  9. self.nodetype = nodetype
  10. self.data = kwargs
  11. self.parent = None
  12. self.children = OrderedDict()
  13. def addChild(self, node: "FsNode"):
  14. self.children[node.name] = node
  15. node.parent = self
  16. def addDirectory(self, path):
  17. fragments = path.split("/")
  18. name = fragments[-1]
  19. fragments = fragments[:-1]
  20. parent_node = self.traverse(fragments)
  21. if not parent_node:
  22. raise Exception(f"No parent node found for: {path}")
  23. parent_node.addChild(FsNode(name, FsNode.NodeType.Directory))
  24. def addFile(self, path, md5, size):
  25. fragments = path.split("/")
  26. name = fragments[-1]
  27. fragments = fragments[:-1]
  28. parent_node = self.traverse(fragments)
  29. if not parent_node:
  30. raise Exception(f"No parent node found for: {path}")
  31. parent_node.addChild(FsNode(name, FsNode.NodeType.File, md5=md5, size=size))
  32. def getChild(self, name):
  33. return self.children[name]
  34. def traverse(self, fragments):
  35. current = self
  36. for fragment in fragments:
  37. current = current.getChild(fragment)
  38. if not current:
  39. break
  40. return current
  41. def getPath(self):
  42. fragments = []
  43. current = self
  44. while current.parent:
  45. fragments.append(current.name)
  46. current = current.parent
  47. return "/".join(reversed(fragments))
  48. def dump(self):
  49. ret = {}
  50. ret["name"] = (self.name,)
  51. ret["type"] = (self.nodetype,)
  52. ret["path"] = (self.getPath(),)
  53. if len(self.children):
  54. ret["children"] = [node.dump() for node in self.children.values()]
  55. return ret
  56. def walk_nodes(node: FsNode):
  57. yield node
  58. for child in node.children.values():
  59. yield from walk_nodes(child)
  60. # Returns filenames: [only_in_left], [changed], [only_in_right]
  61. def compare_fs_trees(left: FsNode, right: FsNode):
  62. # import pprint
  63. # pprint.pprint(left.dump())
  64. # pprint.pprint(right.dump())
  65. left_dict = dict((node.getPath(), node) for node in walk_nodes(left))
  66. right_dict = dict((node.getPath(), node) for node in walk_nodes(right))
  67. left_names = set(left_dict.keys())
  68. right_names = set(right_dict.keys())
  69. common_names = left_names.intersection(right_names)
  70. return (
  71. list(left_names - right_names),
  72. list(
  73. name
  74. for name in common_names
  75. if left_dict[name].data != right_dict[name].data
  76. ),
  77. list(right_names - left_names),
  78. )