diff --git a/vdirsyncer/exceptions.py b/vdirsyncer/exceptions.py new file mode 100644 index 0000000..8ee9365 --- /dev/null +++ b/vdirsyncer/exceptions.py @@ -0,0 +1,8 @@ +class Error(Exception): + pass + +class AlreadyExistingError(Error): + pass + +class WrongEtagError(Error): + pass diff --git a/vdirsyncer/sync/base.py b/vdirsyncer/storage/base.py similarity index 56% rename from vdirsyncer/sync/base.py rename to vdirsyncer/storage/base.py index 228bbec..d41b016 100644 --- a/vdirsyncer/sync/base.py +++ b/vdirsyncer/storage/base.py @@ -1,4 +1,21 @@ -class Syncer(object): +class Item(object): + '''wrapper class for VCALENDAR and VCARD''' + def __init__(self, raw): + self.raw = raw + self._uid = None + + @property + def uid(self): + for line in raw.splitlines(): + if line.startswith(b'UID'): + return line.lstrip(b'UID:').strip() + + +class Storage(object): + def __init__(self, fileext, item_class=Item): + self.fileext = fileext + self.item_class = item_class + def list_items(self): ''' :returns: list of (href, etag) @@ -15,12 +32,14 @@ class Syncer(object): def item_exists(self, href): ''' check if item exists + :returns: True or False ''' raise NotImplementedError() def upload(self, obj): ''' - Upload a new object, raise error if it already exists. + Upload a new object, raise + :exc:`vdirsyncer.exceptions.AlreadyExistingError` if it already exists. :returns: (href, etag) ''' raise NotImplementedError() diff --git a/vdirsyncer/storage/filesystem.py b/vdirsyncer/storage/filesystem.py new file mode 100644 index 0000000..c1a625a --- /dev/null +++ b/vdirsyncer/storage/filesystem.py @@ -0,0 +1,50 @@ +import os +from vdirsyncer.storage.base import Storage, Item +from vdirsyncer.exceptions import AlreadyExistingError + +class FilesystemStorage(Storage): + def __init__(self, path, **kwargs): + self.path = path + super(FilesystemStorage, self).__init__(**kwargs) + + def _get_etag(self, href): + return os.path.getmtime(href) + + def _get_hrefs(self): + for fname in os.listdir(self.path): + href = os.path.join(self.path, fname) + if os.path.isfile(href): + yield href + + def list_items(self): + for href in self._get_hrefs(): + yield href, self._get_etag(href) + + def get_items(self, hrefs): + for href in hrefs: + with open(href, 'rb') as f: + yield Item(f.read()), href, self._get_etag(href) + + def item_exists(self, href): + return os.path.isfile(path) + + def _get_href(self, obj): + return os.path.join(self.path, obj.uid + b'b' + self.fileext) + + def upload(self, obj): + href = self._get_href(obj) + if os.path.exists(href): + raise AlreadyExistingError(href) + with open(href, 'wb+') as f: + f.write(obj.raw) + return href, self._get_etag(href) + + def update(self, obj, etag): + href = self._get_href(obj) + actual_etag = self._get_etag(href) + if etag != actual_etag: + raise WrongEtagError(etag, actual_etag) + + with open(href, 'wb') as f: + f.write(obj.raw) + return self._get_etag(href)