aboutsummaryrefslogtreecommitdiffstats
path: root/dotfiles/dotfile.py
diff options
context:
space:
mode:
Diffstat (limited to 'dotfiles/dotfile.py')
-rw-r--r--dotfiles/dotfile.py106
1 files changed, 63 insertions, 43 deletions
diff --git a/dotfiles/dotfile.py b/dotfiles/dotfile.py
index 3932d2a..d81e585 100644
--- a/dotfiles/dotfile.py
+++ b/dotfiles/dotfile.py
@@ -1,9 +1,14 @@
+import os
+
from click import echo
from hashlib import md5
from pathlib import Path
from .exceptions import \
- IsSymlink, NotASymlink, TargetExists, TargetMissing, Exists
+ IsSymlink, NotASymlink, Exists, NotFound, Dangling, \
+ TargetExists, TargetMissing
+
+UNUSED = False
class Dotfile(object):
@@ -12,8 +17,11 @@ class Dotfile(object):
:param name: name of the symlink in the home directory (~/.vimrc)
:param target: where the symlink should point to (~/Dotfiles/vimrc)
"""
+ RELATIVE_SYMLINKS = True
def __init__(self, name, target):
+ # if not name.is_file() and not name.is_symlink():
+ # raise NotFound(name)
self.name = Path(name)
self.target = Path(target)
@@ -39,13 +47,22 @@ class Dotfile(object):
ensure(self.name.parent, debug)
ensure(self.target.parent, debug)
- def _link(self, debug):
+ def _prune_dirs(self, debug):
+ # TODO
+ if debug:
+ echo('PRUNE <TODO>')
+
+ def _link(self, debug, home):
"""Create a symlink from name to target, no error checking."""
source = self.name
target = self.target
+
if self.name.is_symlink():
source = self.target
- target = self.name.realpath()
+ target = self.name.resolve()
+ elif self.RELATIVE_SYMLINKS:
+ target = os.path.relpath(target, source.parent)
+
if debug:
echo('LINK %s -> %s' % (source, target))
else:
@@ -58,44 +75,49 @@ class Dotfile(object):
else:
self.name.unlink()
- def short_name(self, homedir):
+ def short_name(self, home):
"""A shorter, more readable name given a home directory."""
- return self.name.relative_to(homedir)
- # return homedir.bestrelpath(self.name)
+ return self.name.relative_to(home)
- def is_present(self):
+ def _is_present(self):
"""Is this dotfile present in the repository?"""
- # return self.name.islink() and (self.name.realpath() == self.target)
return self.name.is_symlink() and (self.name.resolve() == self.target)
+ def _same_contents(self):
+ return (md5(self.name.read_bytes()).hexdigest() == \
+ md5(self.target.read_bytes()).hexdigest())
+
@property
def state(self):
"""The current state of this dotfile."""
- if not self.target.exists():
- # only for testing, cli should never reach this state
- return 'error'
- # elif self.name.check(exists=0):
- elif not self.name.exists():
+ if self.target.is_symlink():
+ return 'external'
+
+ if not self.name.exists():
# no $HOME file or symlink
return 'missing'
- # elif self.name.islink():
- elif self.name.is_symlink():
+
+ if self.name.is_symlink():
# name exists, is a link, but isn't a link to the target
if not self.name.samefile(self.target):
return 'conflict'
- else:
+ return 'link'
+
+ if not self._same_contents():
# name exists, is a file, but differs from the target
- # if self.name.computehash() != self.target.computehash():
- if md5(self.name.read_bytes()).hexdigest() != \
- md5(self.target.read_bytes()).hexdigest():
- return 'conflict'
- return 'ok'
+ return 'conflict'
+
+ return 'copy'
+
+ def add(self, copy=False, debug=False, home=Path.home()):
+ """Move a dotfile to its target and create a link.
- def add(self, debug=False):
- """Move a dotfile to it's target and create a symlink."""
- if self.is_present():
+ The link is either a symlink or a copy.
+ """
+ if copy:
+ raise NotImplementedError()
+ if self._is_present():
raise IsSymlink(self.name)
- # if self.target.check(exists=1):
if self.target.exists():
raise TargetExists(self.name)
self._ensure_dirs(debug)
@@ -103,16 +125,13 @@ class Dotfile(object):
if debug:
echo('MOVE %s -> %s' % (self.name, self.target))
else:
- # self.name.move(self.target)
self.name.replace(self.target)
- self._link(debug)
+ self._link(debug, home)
- def remove(self, debug=False):
- """Remove a symlink and move the target back to its name."""
- # if self.name.check(link=0):
+ def remove(self, copy=UNUSED, debug=False):
+ """Remove a dotfile and move target to its original location."""
if not self.name.is_symlink():
raise NotASymlink(self.name)
- # if self.target.check(exists=0):
if not self.target.is_file():
raise TargetMissing(self.name)
self._unlink(debug)
@@ -121,24 +140,25 @@ class Dotfile(object):
else:
self.target.replace(self.name)
- def link(self, debug=False):
- """Create a symlink from name to target."""
- # if self.name.check(exists=1):
+ def enable(self, copy=False, debug=False, home=Path.home()):
+ """Create a symlink or copy from name to target."""
+ if copy:
+ raise NotImplementedError()
if self.name.exists():
raise Exists(self.name)
- # if self.target.check(exists=0):
if not self.target.exists():
raise TargetMissing(self.name)
self._ensure_dirs(debug)
- self._link(debug)
+ self._link(debug, home)
- def unlink(self, debug=False):
- """Remove a symlink from name to target."""
- # if self.name.check(link=0):
+ def disable(self, copy=UNUSED, debug=False):
+ """Remove a dotfile from name to target."""
if not self.name.is_symlink():
raise NotASymlink(self.name)
- if not self.target.exists():
- raise TargetMissing(self.name)
- if not self.name.samefile(self.target):
- raise RuntimeError
+ if self.name.exists():
+ if not self.target.exists():
+ raise TargetMissing(self.name)
+ if not self.name.samefile(self.target):
+ raise RuntimeError
self._unlink(debug)
+ self._prune_dirs(debug)