<file_sep>'use strict';


exports.isNullOrUndefined = function (value) {
    return value === null || value === undefined;
};

//  +7 (555) 666-77-88
function formatPhone(s) {
    let code = s.slice(0, 3);
    let g1 = s.slice(3, 6);
    let g2 = s.slice(6, 8);
    let g3 = s.slice(8, 10);

    return `+7 (${code}) ${g1}-${g2}-${g3}`;
}


exports.formatPhoneEntry = function (x) {
    let nameAndPhone = `${x.name}, ${formatPhone(x.phone)}`;

    return x.email
        ? nameAndPhone + `, ${x.email}`
        : nameAndPhone;
};
<file_sep>'use strict';
const { Storage } = require('./storage');
const validationRules = require('./validation-rules');
const { PhonebookEntry } = require('./phonebook-entry');
const { formatPhoneEntry } = require('./utils');
const { handlersList } = require('./search-handlers');

/**
 * Сделано задание на звездочку
 * Реализован метод importFromCsv
 */
exports.isStar = true;

/**
 * Телефонная книга
 */
let storage = new Storage();

/**
 * Добавление записи в телефонную книгу
 * @param {String} phone
 * @param {String} name
 * @param {String} email
 * @returns {Boolean} operation success
 */
exports.add = function (phone, name, email) {
    let isValid = [
        ...validationRules.name,
        ...validationRules.phone,
        ...validationRules.email,
        ...validationRules.insertDataIntegrity]
        .every(rule => rule(phone, name, email, storage));
    if (isValid) {
        storage.add(new PhonebookEntry(phone, name, email));
    }

    return isValid;
};

/**
 * Обновление записи в телефонной книге
 * @param {String} phone
 * @param {String} name
 * @param {String} email
 * @returns {Boolean} operation success
 */
exports.update = function (phone, name, email) {
    let isValid = [
        ...validationRules.name,
        ...validationRules.phone,
        ...validationRules.email,
        ...validationRules.updateDataIntegrity]
        .every(rule => rule(phone, name, email, storage));
    if (isValid) {
        let old = storage.find(x => x.phone === phone);
        storage.remove(old);
        storage.add(new PhonebookEntry(phone, name, email));
    }

    return isValid;
};

function find(query) {
    return handlersList
        .find(x => x.canHandle(query))
        .handle(query, storage);
}

/**
 * Удаление записей по запросу из телефонной книги
 * @param {String} query
 * @returns {Number} amount of remove entries
 */
exports.findAndRemove = function (query) {
    let entries = find(query);
    entries.forEach(x => storage.remove(x));

    return entries.length;
};

/**
 * Поиск записей по запросу в телефонной книге
 * @param {String} query
 * @returns {Array} list of formatted strings
 */
exports.find = function (query) {
    let entries = find(query)
        .sort((x, y) => x.name.localeCompare(y.name))
        .map(formatPhoneEntry);

    return Array.from(entries);
};

/**
 * Импорт записей из csv-формата
 * @star
 * @param {String} csv
 * @returns {Number} – количество добавленных и обновленных записей
 */
exports.importFromCsv = function (csv) {
    // Парсим csv
    // Добавляем в телефонную книгу
    // Либо обновляем, если запись с таким телефоном уже существует

    return csv.split('\n')
        .map(string => [...string.split(';')])
        .map(([name, phone, email]) => [phone, name, email === '' ? undefined : email])
        .map(fields => exports.add(...fields) || exports.update(...fields))
        .filter(result => result === true)
        .length;
};
<file_sep>'use strict';


const data = Symbol('data');
class Storage {
    constructor() {
        this[data] = new Set();
    }

    add(entry) {
        this[data].add(entry);
    }

    findAll(condition) {
        return Array.from(Array.from(this[data])
            .filter(condition));
    }

    find(condition) {
        return Array.from(this[data])
            .find(condition);
    }

    contains(condition) {
        return this.findAll(condition).length > 0;
    }

    remove(entry) {
        this[data].delete(entry);
    }
}

exports.Storage = Storage;

<file_sep>'use strict';
const { isNullOrUndefined } = require('./utils');

exports.phone = [
    phone => typeof phone === 'string',
    phone => /\d+/.test(phone),
    phone => phone.length === 10
];

exports.name = [
    (phone, name) => typeof name === 'string',
    (phone, name) => name.length > 0
];

exports.email = [
    (phone, name, email) => isNullOrUndefined(email) || typeof email === 'string',
    (phone, name, email) => isNullOrUndefined(email) || email.length > 3,
    (phone, name, email) => isNullOrUndefined(email) || /^\S+@\S+$/.test(email)
];

exports.insertDataIntegrity = [
    (phone, name, email, storage) => {
        return !storage.contains(x => Object.is(x.phone, phone));
    }
];

exports.updateDataIntegrity = [
    (phone, name, email, storage) => {
        return storage.contains(x => Object.is(x.phone, phone));
    },

    (phone, name, email, storage) => !storage.contains(
        existingEntry => existingEntry.phone === phone &&
            !isNullOrUndefined(existingEntry.name) &&
            isNullOrUndefined(name))

];

