# Path: therapist/collection.py
# class Collection(object):
#     """A generic iterable set of objects."""
# 
#     class Meta:
#         pass
# 
#     def __init__(self, objects=None):
#         self._objects = []
# 
#         if objects:
#             for obj in objects:
#                 self.append(obj)
# 
#     def __iter__(self):
#         return iter(self._objects)
# 
#     def __getitem__(self, item):
#         return self._objects[item]
# 
#     def __str__(self):  # pragma: no cover
#         return str(self._objects)
# 
#     def __repr__(self):  # pragma: no cover
#         return "<{}>".format(self.__class__.__name__)
# 
#     def __bool__(self):
#         return bool(self._objects)
# 
#     __nonzero__ = __bool__
# 
#     @property
#     def objects(self):
#         return self._objects
# 
#     def append(self, v):
#         object_class = getattr(self.Meta, "object_class", object)
# 
#         if not isinstance(v, object_class):
#             raise TypeError("Expected an instance of `{}`.".format(object_class.__name__))
# 
#         self._objects.append(v)
# 
# Path: therapist/exc.py
# class Error(Exception):
#     def __init__(self, message=None, *args, **kwargs):
#         self.message = message
#         super(Error, self).__init__(*args, **kwargs)
# 
# Path: therapist/process.py
# class Process(object):
#     def __init__(self, name, **kwargs):
#         self.name = name
#         self.description = kwargs.pop("description", None)
# 
#         self._include = None
#         self.include = kwargs.pop("include", None)
# 
#         self._exclude = None
#         self.exclude = kwargs.pop("exclude", None)
# 
#         self.config = kwargs
# 
#     def __call__(self, **kwargs):
#         return self.execute(**kwargs)
# 
#     def __str__(self):
#         return self.description if self.description else self.name
# 
#     def __repr__(self):
#         return "<{} {}>".format(self.__class__.__name__, self.name)
# 
#     @property
#     def include(self):
#         return self._include
# 
#     @include.setter
#     def include(self, value):
#         if value is None:
#             self._include = []
#         else:
#             self._include = value if isinstance(value, list) else [value]
# 
#     @property
#     def exclude(self):
#         return self._exclude
# 
#     @exclude.setter
#     def exclude(self, value):
#         if value is None:
#             self._exclude = []
#         else:
#             self._exclude = value if isinstance(value, list) else [value]
# 
#     def filter_files(self, files):
#         if self.include:
#             spec = PathSpec(map(GitWildMatchPattern, self.include))
#             files = list(spec.match_files(files))
# 
#         if self.exclude:
#             spec = PathSpec(map(GitWildMatchPattern, self.exclude))
#             exclude = list(spec.match_files(files))
#             files = list(filter(lambda f: f not in exclude, files))
# 
#         return files
# 
#     def execute(self, **kwargs):
#         raise NotImplementedError()  # pragma: no cover
# 
# Path: therapist/runner/result.py
# class Result(object):
#     SUCCESS = 0
#     FAILURE = 1
#     ERROR = 2
# 
#     def __init__(self, process, status=None):
#         self._process = None
#         self.process = process
#         self.status = status
#         self.output = None
#         self.error = None
#         self.start_time = time.time()
#         self.end_time = self.start_time
#         self.modified_files = []
# 
#     def __str__(self):
#         if self.is_success:
#             return "SUCCESS"
#         elif self.is_failure:
#             return "FAILURE"
#         elif self.is_error:
#             return "ERROR"
#         else:
#             return "SKIP"
# 
#     def __repr__(self):  # pragma: no cover
#         return "<Result {}>".format(self.process)
# 
#     @property
#     def process(self):
#         return self._process
# 
#     @process.setter
#     def process(self, value):
#         if not (isinstance(value, Process)):
#             raise TypeError("Expected a `Process` object.")
# 
#         self._process = value
# 
#     @property
#     def is_success(self):
#         return self.status == self.SUCCESS
# 
#     @property
#     def is_failure(self):
#         return self.status == self.FAILURE
# 
#     @property
#     def is_error(self):
#         return self.status == self.ERROR
# 
#     @property
#     def is_skip(self):
#         return not (self.is_success or self.is_failure or self.is_error)
# 
#     @property
#     def execution_time(self):
#         return self.end_time - self.start_time
# 
#     def mark_complete(self, status=None, output=None, error=None):
#         if status is not None:
#             self.status = status
#         self.output = output
#         self.error = error
#         self.end_time = time.time()
# 
#     @property
#     def has_modified_files(self):
#         return len(self.modified_files) > 0
# 
#     def add_modified_file(self, path):
#         self.modified_files.append(path)


import os
import subprocess
from therapist.collection import Collection
from therapist.exc import Error
from therapist.process import Process
from therapist.runner.result import Result



class Action(Process):
    def get_working_directory(self, cwd):
        return os.path.join(cwd, self.config.get("working_dir", ""))

    def filter_files(self, files, **kwargs):
        files = super().filter_files(files)

        cwd = kwargs.get("cwd")

        # Filter by files root
        files_root = os.path.abspath(os.path.join(cwd, self.config.get("files_root", "")))
        if files_root:

            def files_root_matches(f):
                return os.path.abspath(os.path.join(cwd, f)).startswith(files_root)

            files = [f for f in files if files_root_matches(f)]

        # Rewrite the file paths relative to the working directory

# Path: therapist/collection.py
# class Collection(object):
#     """A generic iterable set of objects."""
# 
#     class Meta:
#         pass
# 
#     def __init__(self, objects=None):
#         self._objects = []
# 
#         if objects:
#             for obj in objects:
#                 self.append(obj)
# 
#     def __iter__(self):
#         return iter(self._objects)
# 
#     def __getitem__(self, item):
#         return self._objects[item]
# 
#     def __str__(self):  # pragma: no cover
#         return str(self._objects)
# 
#     def __repr__(self):  # pragma: no cover
#         return "<{}>".format(self.__class__.__name__)
# 
#     def __bool__(self):
#         return bool(self._objects)
# 
#     __nonzero__ = __bool__
# 
#     @property
#     def objects(self):
#         return self._objects
# 
#     def append(self, v):
#         object_class = getattr(self.Meta, "object_class", object)
# 
#         if not isinstance(v, object_class):
#             raise TypeError("Expected an instance of `{}`.".format(object_class.__name__))
# 
#         self._objects.append(v)
# 
# Path: therapist/exc.py
# class Error(Exception):
#     def __init__(self, message=None, *args, **kwargs):
#         self.message = message
#         super(Error, self).__init__(*args, **kwargs)
# 
# Path: therapist/process.py
# class Process(object):
#     def __init__(self, name, **kwargs):
#         self.name = name
#         self.description = kwargs.pop("description", None)
# 
#         self._include = None
#         self.include = kwargs.pop("include", None)
# 
#         self._exclude = None
#         self.exclude = kwargs.pop("exclude", None)
# 
#         self.config = kwargs
# 
#     def __call__(self, **kwargs):
#         return self.execute(**kwargs)
# 
#     def __str__(self):
#         return self.description if self.description else self.name
# 
#     def __repr__(self):
#         return "<{} {}>".format(self.__class__.__name__, self.name)
# 
#     @property
#     def include(self):
#         return self._include
# 
#     @include.setter
#     def include(self, value):
#         if value is None:
#             self._include = []
#         else:
#             self._include = value if isinstance(value, list) else [value]
# 
#     @property
#     def exclude(self):
#         return self._exclude
# 
#     @exclude.setter
#     def exclude(self, value):
#         if value is None:
#             self._exclude = []
#         else:
#             self._exclude = value if isinstance(value, list) else [value]
# 
#     def filter_files(self, files):
#         if self.include:
#             spec = PathSpec(map(GitWildMatchPattern, self.include))
#             files = list(spec.match_files(files))
# 
#         if self.exclude:
#             spec = PathSpec(map(GitWildMatchPattern, self.exclude))
#             exclude = list(spec.match_files(files))
#             files = list(filter(lambda f: f not in exclude, files))
# 
#         return files
# 
#     def execute(self, **kwargs):
#         raise NotImplementedError()  # pragma: no cover
# 
# Path: therapist/runner/result.py
# class Result(object):
#     SUCCESS = 0
#     FAILURE = 1
#     ERROR = 2
# 
#     def __init__(self, process, status=None):
#         self._process = None
#         self.process = process
#         self.status = status
#         self.output = None
#         self.error = None
#         self.start_time = time.time()
#         self.end_time = self.start_time
#         self.modified_files = []
# 
#     def __str__(self):
#         if self.is_success:
#             return "SUCCESS"
#         elif self.is_failure:
#             return "FAILURE"
#         elif self.is_error:
#             return "ERROR"
#         else:
#             return "SKIP"
# 
#     def __repr__(self):  # pragma: no cover
#         return "<Result {}>".format(self.process)
# 
#     @property
#     def process(self):
#         return self._process
# 
#     @process.setter
#     def process(self, value):
#         if not (isinstance(value, Process)):
#             raise TypeError("Expected a `Process` object.")
# 
#         self._process = value
# 
#     @property
#     def is_success(self):
#         return self.status == self.SUCCESS
# 
#     @property
#     def is_failure(self):
#         return self.status == self.FAILURE
# 
#     @property
#     def is_error(self):
#         return self.status == self.ERROR
# 
#     @property
#     def is_skip(self):
#         return not (self.is_success or self.is_failure or self.is_error)
# 
#     @property
#     def execution_time(self):
#         return self.end_time - self.start_time
# 
#     def mark_complete(self, status=None, output=None, error=None):
#         if status is not None:
#             self.status = status
#         self.output = output
#         self.error = error
#         self.end_time = time.time()
# 
#     @property
#     def has_modified_files(self):
#         return len(self.modified_files) > 0
# 
#     def add_modified_file(self, path):
#         self.modified_files.append(path)


# Path: therapist/runner/action.py
import os
import subprocess
from therapist.collection import Collection
from therapist.exc import Error
from therapist.process import Process
from therapist.runner.result import Result




class Action(Process):
    def get_working_directory(self, cwd):
        return os.path.join(cwd, self.config.get("working_dir", ""))

    def filter_files(self, files, **kwargs):
        files = super().filter_files(files)

        cwd = kwargs.get("cwd")

        # Filter by files root
        files_root = os.path.abspath(os.path.join(cwd, self.config.get("files_root", "")))
        if files_root:

            def files_root_matches(f):
                return os.path.abspath(os.path.join(cwd, f)).startswith(files_root)

            files = [f for f in files if files_root_matches(f)]

        # Rewrite the file paths relative to the working directory

        working_dir = self.get_working_directory(cwd)