aboutsummaryrefslogtreecommitdiffstats
path: root/dotfiles/repository.py
blob: 3444388721e6e7b0d435755ce6f46870d1361ace (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import py
from click import echo
from operator import attrgetter

from .dotfile import Dotfile
from .exceptions import DotfileException, TargetIgnored, IsDirectory, \
    InRepository


class Repository(object):
    """A repository is a directory that contains dotfiles.

    :param repodir: the location of the repository directory
    :param homedir: the location of the home directory (primarily for testing)
    :param ignore:  a list of targets to ignore
    """

    def __init__(self, repodir, homedir, ignore=[]):
        self.ignore = ignore
        self.homedir = homedir

        # create repository if needed
        self.repodir = repodir.ensure(dir=1)

    def __str__(self):
        """Return human-readable repository contents."""
        return ''.join('%s\n' % item for item in self.contents()).rstrip()

    def __repr__(self):
        return '<Repository %r>' % self.repodir

    def _target_to_name(self, target):
        """Return the expected symlink for the given repository target."""
        return self.homedir.join(self.repodir.bestrelpath(target))

    def _name_to_target(self, name):
        """Return the expected repository target for the given symlink."""
        return self.repodir.join(self.homedir.bestrelpath(name))

    def dotfile(self, name):
        """Return a valid dotfile for the given path."""

        # XXX: it must be below the home directory
        #      it cannot be contained in the repository
        #      it cannot be ignored
        #      it must be a file

        target = self._name_to_target(name)
        if target.basename in self.ignore:
            raise TargetIgnored(name)
        if name.check(dir=1):
            raise IsDirectory(name)

        for path in name.parts():
            try:
                if self.repodir.samefile(path):
                    raise InRepository(name)
            except py.error.ENOENT:
                # this occurs when the symlink does not yet exist
                continue

        # if not self.homedir.samefile(name.dirname):
        #     raise NotRootedInHome(name)
        # if name.dirname != self.homedir:
        #     raise IsNested(name)
        # if name.basename[0] != '.':
        #     raise NotADotfile(name)

        return Dotfile(name, target)

    def dotfiles(self, paths):
        """Return a list of dotfiles given a path."""
        dotfiles = []
        paths = map(py.path.local, paths)
        for path in paths:
            try:
                dotfiles.append(self.dotfile(path))
            except DotfileException as err:
                echo(err)
        return dotfiles

    def contents(self):
        """Return a list of all dotfiles in the repository path."""
        def filter(node):
            return node.check(dir=0) and node.basename not in self.ignore

        def recurse(node):
            return node.basename not in self.ignore

        def construct(target):
            return Dotfile(self._target_to_name(target), target)

        contents = self.repodir.visit(filter, recurse)
        return sorted(map(construct, contents), key=attrgetter('name'))