diff options
-rw-r--r-- | dotfiles/dotfile.py | 70 |
1 files changed, 32 insertions, 38 deletions
diff --git a/dotfiles/dotfile.py b/dotfiles/dotfile.py index 1a86f63..ddb2bb2 100644 --- a/dotfiles/dotfile.py +++ b/dotfiles/dotfile.py @@ -1,47 +1,63 @@ import errno +def unique_suffix(path_a, path_b): + discard = len(str(path_a.common(path_b))) + 1 + return (str(path_a)[discard:], str(path_b)[discard:]) + + class Dotfile(object): """ This class implements the 'dotfile' abstraction. A dotfile has two primary attributes: - name -- name of dotfile in the home directory (~/.vimrc) - target -- target the dotfile should point to (~/Dotfiles/vimrc) + name -- name of symlink in the home directory (~/.vimrc) + target -- where the symlink should point to (~/Dotfiles/vimrc) The above attributes are both py.path.local objects. The goal is for there to be no special logic or stored global state. Only the implementation of three operations made available to the caller: - add -- moves a dotfile into the repository and replaces it with a symlink + add -- move a dotfile into the repository and replace it with a symlink remove -- the opposite of add - sync -- ensure that each repository file has a corresponding symlink + sync -- ensure that each repository file has a corresponding symlink + unsync -- remove the symlink leaving only the repository file This is where most filesystem operations (link, delete, etc) should be called, and not in the layers above. """ - states = { - 'error': {'text': '(error)', 'color': 'red'}, - 'missing': {'text': '(missing)', 'color': 'yellow'}, - 'conflict': {'text': '(conflict)', 'color': 'yellow'}, - 'ok': {'text': '(ok)', 'color': 'green'}, - } - def __init__(self, name, target): self.name = name self.target = target - self._set_state() def __str__(self): - short_name, _ = self._truncate_paths() - return '%-18s %-s' % (short_name, self.state['text']) + short_name, _ = unique_suffix(self.name, self.target) + return '%s' % short_name def __repr__(self): return '<Dotfile %r>' % self.name + @property + def state(self): + + # lets be optimistic + state = 'ok' + + if self.target.check(exists=0): + # only for testing, cli should never reach this state + state = 'error' + elif self.name.check(exists=0): + # no $HOME symlink + state = 'missing' + elif self.name.check(link=0) or not self.name.samefile(self.target): + # if name exists but isn't a link to the target + state = 'conflict' + + return state + def add(self): if self.target.check(exists=1): raise OSError(errno.EEXIST, self.target) @@ -57,27 +73,5 @@ class Dotfile(object): def sync(self): self.name.mksymlinkto(self.target) - def is_ok(self): - return self.state == self.states['ok'] - - def _set_state(self): - - # only for testing, cli should never reach this state - if self.target.check(exists=0): - self.state = self.states['error'] - - # no $HOME symlink - elif self.name.check(exists=0): - self.state = self.states['missing'] - - # if name exists but isn't a link to the target - elif self.name.check(link=0) or not self.name.samefile(self.target): - self.state = self.states['conflict'] - - # all good - else: - self.state = self.states['ok'] - - def _truncate_paths(self): - discard = len(str(self.name.common(self.target))) + 1 - return (str(self.name)[discard:], str(self.target)[discard:]) + def unsync(self): + self.name.remove() |