1# Copyright (c) 2012 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5class FileNotFoundError(Exception):
6  def __init__(self, filename):
7    Exception.__init__(self, filename)
8
9class StatInfo(object):
10  '''The result of calling Stat on a FileSystem.
11  '''
12  def __init__(self, version, child_versions=None):
13    self.version = version
14    self.child_versions = child_versions
15
16  def __eq__(self, other):
17    return (isinstance(other, StatInfo) and
18            self.version == other.version and
19            self.child_versions == other.child_versions)
20
21  def __ne__(self, other):
22    return not (self == other)
23
24  def __str__(self):
25    return '{version: %s, child_versions: %s}' % (self.version,
26                                                  self.child_versions)
27
28  def __repr__(self):
29    return str(self)
30
31def ToUnicode(data):
32  '''Returns the str |data| as a unicode object. It's expected to be utf8, but
33  there are also latin-1 encodings in there for some reason. Fall back to that.
34  '''
35  try:
36    return unicode(data, 'utf-8')
37  except:
38    return unicode(data, 'latin-1')
39
40class FileSystem(object):
41  '''A FileSystem interface that can read files and directories.
42  '''
43  def Read(self, paths, binary=False):
44    '''Reads each file in paths and returns a dictionary mapping the path to the
45    contents. If a path in paths ends with a '/', it is assumed to be a
46    directory, and a list of files in the directory is mapped to the path.
47
48    If binary=False, the contents of each file will be unicode parsed as utf-8,
49    and failing that as latin-1 (some extension docs use latin-1). If
50    binary=True then the contents will be a str.
51    '''
52    raise NotImplementedError(self.__class__)
53
54  def ReadSingle(self, path, binary=False):
55    '''Reads a single file from the FileSystem.
56    '''
57    return self.Read([path], binary=binary).Get()[path]
58
59  # TODO(cduvall): Allow Stat to take a list of paths like Read.
60  def Stat(self, path):
61    '''Returns a |StatInfo| object containing the version of |path|. If |path|
62    is a directory, |StatInfo| will have the versions of all the children of
63    the directory in |StatInfo.child_versions|.
64    '''
65    raise NotImplementedError(self.__class__)
66
67  def GetIdentity(self):
68    '''The identity of the file system, exposed for caching classes to
69    namespace their caches. this will usually depend on the configuration of
70    that file system - e.g. a LocalFileSystem with a base path of /var is
71    different to that of a SubversionFileSystem with a base path of /bar, is
72    different to a LocalFileSystem with a base path of /usr.
73    '''
74    raise NotImplementedError(self.__class__)
75
76  def Walk(self, root):
77    '''Recursively walk the directories in a file system, starting with root.
78    Emulates os.walk from the standard os module.
79    '''
80    basepath = root.rstrip('/') + '/'
81
82    def walk(root):
83      if not root.endswith('/'):
84        root += '/'
85
86      dirs, files = [], []
87
88      for f in self.ReadSingle(root):
89        if f.endswith('/'):
90          dirs.append(f)
91        else:
92          files.append(f)
93
94      yield root[len(basepath):].rstrip('/'), dirs, files
95
96      for d in dirs:
97        for walkinfo in walk(root + d):
98          yield walkinfo
99
100    for walkinfo in walk(root):
101      yield walkinfo
102