blob: d9e72d111544248ce7c8d68be8ccd4b6564c2f65 [file] [log] [blame]
# Copyright (C) 2021 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import json
import six
from collections import defaultdict
from webkitcorepy import string_utils
class User(object):
class Encoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, dict):
return {key: self.default(value) for key, value in obj.items()}
if isinstance(obj, list):
return [self.default(value) for value in obj]
if not isinstance(obj, User):
return super(User.Encoder, self).default(obj)
result = {}
if obj._name:
result['name'] = obj._name
if obj.emails:
result['emails'] = obj.emails
if obj.username:
result['username'] = obj.username
return result
class Mapping(defaultdict):
def __init__(self):
super(User.Mapping, self).__init__(lambda: None)
def add(self, user):
return self.create(name=user._name, username=user.username, emails=user.emails)
def create(self, name=None, username=None, emails=None):
matched_key = None
user = None
for key in [name, username] + (emails or []):
if not key:
continue
candidate = self.get(key)
if not candidate:
continue
if user and hash(user) != hash(candidate):
raise RuntimeError("'{}' matches '{}', but '{}' already matched '{}'".format(
key, candidate, matched_key, user,
))
matched_key = key
user = candidate
if user:
if name:
user._name = name
if username:
user.username = username
for email in emails or []:
if email not in user.emails:
user.emails.append(email)
else:
user = User(name=name, username=username, emails=emails)
for key in [name, username] + (emails or []):
self[key] = user
return user
def __iter__(self):
yielded = set()
for user in self.values():
if hash(user) in yielded:
continue
yielded.add(hash(user))
yield user
def __init__(self, name=None, username=None, emails=None):
self._name = name
self.emails = list(filter(string_utils.decode, emails or []))
self.username = username
if not self.name:
raise TypeError('Not enough arguments to define user')
@property
def name(self):
if self._name:
return self._name
if self.username and isinstance(self.username, six.string_types):
return self.username
if self.email:
return self.email
if self.username:
return str(self.username)
return None
@property
def email(self):
if not self.emails:
return None
return self.emails[0]
def __hash__(self):
if self.username:
return hash(self.username)
if self._name:
return hash(self._name)
if self.email:
return hash(self.email)
return 0
def __repr__(self):
address = None
for candidate in [self.username, self.email]:
if isinstance(candidate, six.string_types) and candidate != self.name:
address = candidate
break
if not address:
return self.name
return u'{} <{}>'.format(self.name, address)
def __cmp__(self, other):
if isinstance(other, str):
ref_value = other
elif isinstance(other, User):
ref_value = other.username or self.name
else:
raise ValueError('Cannot compare {} with {}'.format(User, type(other)))
if (self.username or self.name) == ref_value:
return 0
return 1 if (self.username or self.name) > ref_value else -1
def __eq__(self, other):
return self.__cmp__(other) == 0
def __ne__(self, other):
return self.__cmp__(other) != 0
def __lt__(self, other):
return self.__cmp__(other) < 0
def __le__(self, other):
return self.__cmp__(other) <= 0
def __gt__(self, other):
return self.__cmp__(other) > 0
def __ge__(self, other):
return self.__cmp__(other) >= 0