15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
6b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)import sys
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
8b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)from docs_server_utils import StringIdentity
9a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)from file_system import FileSystem, FileNotFoundError, StatInfo
10effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochfrom future import Future
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from path_util import AssertIsDirectory, AssertIsValid
125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)from test_util import ChromiumPath
135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)def _ConvertToFilepath(path):
16b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  return path.replace('/', os.sep)
17b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
19a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)def _ConvertFromFilepath(path):
20a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  return path.replace(os.sep, '/')
21a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
23a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)def _ReadFile(filename):
24a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  try:
25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    with open(filename, 'rb') as f:
26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      return f.read()
27a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  except IOError as e:
28a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    raise FileNotFoundError('Read failed for %s: %s' % (filename, e))
29a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
31a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)def _ListDir(dir_name):
32a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  all_files = []
33a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  try:
34a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    files = os.listdir(dir_name)
35a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  except OSError as e:
36a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    raise FileNotFoundError('os.listdir failed for %s: %s' % (dir_name, e))
37a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  for os_path in files:
38a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    posix_path = _ConvertFromFilepath(os_path)
39a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    if os_path.startswith('.'):
40a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      continue
41a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    if os.path.isdir(os.path.join(dir_name, os_path)):
42a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      all_files.append(posix_path + '/')
43a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    else:
44a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      all_files.append(posix_path)
45a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  return all_files
46a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
48a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)def _CreateStatInfo(path):
49a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  try:
50a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    path_mtime = os.stat(path).st_mtime
51a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    if os.path.isdir(path):
52a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      child_versions = dict((_ConvertFromFilepath(filename),
53a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)                             os.stat(os.path.join(path, filename)).st_mtime)
54a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)          for filename in os.listdir(path))
55a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      # This file system stat mimics subversion, where the stat of directories
56a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      # is max(file stats). That means we need to recursively check the whole
57a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      # file system tree :\ so approximate that by just checking this dir.
58a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      version = max([path_mtime] + child_versions.values())
59a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    else:
60a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      child_versions = None
61a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)      version = path_mtime
62a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    return StatInfo(version, child_versions)
63a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)  except OSError as e:
64a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    raise FileNotFoundError('os.stat failed for %s: %s' % (path, e))
65a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
67c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)class LocalFileSystem(FileSystem):
68b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  '''FileSystem implementation which fetches resources from the local
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  filesystem.
70b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  '''
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, base_path):
72010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    # Enforce POSIX path, so path validity checks pass for Windows.
73010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)    base_path = base_path.replace(os.sep, '/')
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    AssertIsDirectory(base_path)
75b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    self._base_path = _ConvertToFilepath(base_path)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
77b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  @staticmethod
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def Create(*path):
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return LocalFileSystem(ChromiumPath(*path))
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
81e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch  def Read(self, paths, skip_not_found=False):
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    def resolve():
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      result = {}
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      for path in paths:
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        AssertIsValid(path)
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        full_path = os.path.join(self._base_path,
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                 _ConvertToFilepath(path).lstrip(os.sep))
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if path == '' or path.endswith('/'):
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          result[path] = _ListDir(full_path)
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        else:
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          result[path] = _ReadFile(full_path)
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return result
93effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return Future(callback=resolve)
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
95f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def Refresh(self):
96f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    return Future(value=())
97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Stat(self, path):
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    AssertIsValid(path)
100a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    full_path = os.path.join(self._base_path,
101a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)                             _ConvertToFilepath(path).lstrip(os.sep))
102a93a17c8d99d686bd4a1511e5504e5e6cc9fcadfTorne (Richard Coles)    return _CreateStatInfo(full_path)
103b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
104b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  def GetIdentity(self):
105b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    return '@'.join((self.__class__.__name__, StringIdentity(self._base_path)))
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  def __repr__(self):
1085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return 'LocalFileSystem(%s)' % self._base_path
109