enigma-bbs/core/ftn_address.js

209 lines
5.3 KiB
JavaScript

/* jslint node: true */
'use strict';
const _ = require('lodash');
const FTN_ADDRESS_REGEXP = /^([0-9]+:)?([0-9]+)(\/[0-9]+)?(\.[0-9]+)?(@[a-z0-9\-.]+)?$/i;
const FTN_PATTERN_REGEXP =
/^([0-9*]+:)?([0-9*]+)(\/[0-9*]+)?(\.[0-9*]+)?(@[a-z0-9\-.*]+)?$/i;
module.exports = class Address {
constructor(addr) {
if (addr) {
if (_.isObject(addr)) {
Object.assign(this, addr);
} else if (_.isString(addr)) {
const temp = Address.fromString(addr);
if (temp) {
Object.assign(this, temp);
}
}
}
}
static isValidAddress(addr) {
return addr && addr.isValid();
}
isValid() {
// FTN address is valid if we have at least a net/node
return _.isNumber(this.net) && _.isNumber(this.node);
}
isEqual(other) {
if (_.isString(other)) {
other = Address.fromString(other);
}
return (
this.net === other.net &&
this.node === other.node &&
this.zone === other.zone &&
this.point === other.point &&
this.domain === other.domain
);
}
getMatchAddr(pattern) {
const m = FTN_PATTERN_REGEXP.exec(pattern);
if (m) {
let addr = {};
if (m[1]) {
addr.zone = m[1].slice(0, -1);
if ('*' !== addr.zone) {
addr.zone = parseInt(addr.zone);
}
} else {
addr.zone = '*';
}
if (m[2]) {
addr.net = m[2];
if ('*' !== addr.net) {
addr.net = parseInt(addr.net);
}
} else {
addr.net = '*';
}
if (m[3]) {
addr.node = m[3].substr(1);
if ('*' !== addr.node) {
addr.node = parseInt(addr.node);
}
} else {
addr.node = '*';
}
if (m[4]) {
addr.point = m[4].substr(1);
if ('*' !== addr.point) {
addr.point = parseInt(addr.point);
}
} else {
addr.point = '*';
}
if (m[5]) {
addr.domain = m[5].substr(1);
} else {
addr.domain = '*';
}
return addr;
}
}
/*
getMatchScore(pattern) {
let score = 0;
const addr = this.getMatchAddr(pattern);
if(addr) {
const PARTS = [ 'net', 'node', 'zone', 'point', 'domain' ];
for(let i = 0; i < PARTS.length; ++i) {
const member = PARTS[i];
if(this[member] === addr[member]) {
score += 2;
} else if('*' === addr[member]) {
score += 1;
} else {
break;
}
}
}
return score;
}
*/
isPatternMatch(pattern) {
const addr = this.getMatchAddr(pattern);
if (addr) {
return (
('*' === addr.net || this.net === addr.net) &&
('*' === addr.node || this.node === addr.node) &&
('*' === addr.zone || this.zone === addr.zone) &&
('*' === addr.point || this.point === addr.point) &&
('*' === addr.domain || this.domain === addr.domain)
);
}
return false;
}
static fromString(addrStr) {
const m = FTN_ADDRESS_REGEXP.exec(addrStr);
if (m) {
// start with a 2D
let addr = {
net: parseInt(m[2]),
node: parseInt(m[3].substr(1)),
};
// 3D: Addition of zone if present
if (m[1]) {
addr.zone = parseInt(m[1].slice(0, -1));
}
// 4D if optional point is present
if (m[4]) {
addr.point = parseInt(m[4].substr(1));
}
// 5D with @domain
if (m[5]) {
addr.domain = m[5].substr(1);
}
return new Address(addr);
}
}
toString(dimensions) {
dimensions = dimensions || '5D';
let addrStr = `${this.zone}:${this.net}`;
// allow for e.g. '4D' or 5
const dim = parseInt(dimensions.toString()[0]);
if (dim >= 3) {
addrStr += `/${this.node}`;
}
// missing & .0 are equiv for point
if (dim >= 4 && this.point) {
addrStr += `.${this.point}`;
}
if (5 === dim && this.domain) {
addrStr += `@${this.domain.toLowerCase()}`;
}
return addrStr;
}
static getComparator() {
return function (left, right) {
let c = (left.zone || 0) - (right.zone || 0);
if (0 !== c) {
return c;
}
c = (left.net || 0) - (right.net || 0);
if (0 !== c) {
return c;
}
c = (left.node || 0) - (right.node || 0);
if (0 !== c) {
return c;
}
return (left.domain || '').localeCompare(right.domain || '');
};
}
};