the-moby-effect
Version:
Moby/Docker API client built using effect-ts
314 lines • 10.5 kB
JavaScript
/**
* IPv4 or IPv6 cidr block schemas.
*
* @since 1.0.0
*/
import * as Array from "effect/Array";
import * as Effect from "effect/Effect";
import * as Function from "effect/Function";
import * as ParseResult from "effect/ParseResult";
import * as Schema from "effect/Schema";
import * as Stream from "effect/Stream";
import * as Address from "./address.js";
import * as CidrBlockMask from "./cidrBlockMask.js";
import * as IPv4 from "./ipv4.js";
import * as IPv6 from "./ipv6.js";
/** @internal */
export const tail = elements => elements.slice(1);
/** @internal */
export const splitLiteral = (str, delimiter) => str.split(delimiter);
/**
* @since 1.0.0
* @category Api interface
*/
export class CidrBlockBase extends /*#__PURE__*/Schema.Class("CidrBlockMixin")({
address: Address.Address,
mask: /*#__PURE__*/Schema.Union(CidrBlockMask.IPv4CidrMask, CidrBlockMask.IPv6CidrMask)
}) {
/** @since 1.0.0 */
family = this.address.family;
/** @internal */
onFamily({
onIPv4,
onIPv6
}) {
const isIPv4 = () => this.family === "ipv4";
const isIPv6 = () => this.family === "ipv6";
if (isIPv4()) {
return onIPv4(this);
} else if (isIPv6()) {
return onIPv6(this);
} else {
return Function.absurd(this.family);
}
}
/**
* The first address in the range given by this address' subnet, often
* referred to as the Network Address.
*
* @since 1.0.0
*/
get networkAddressAsBigint() {
return Effect.gen(this, function* () {
const bits = this.family === "ipv4" ? 32 : 128;
const bigIntegerAddress = yield* this.onFamily({
onIPv4: self => Schema.decode(IPv4.IPv4Bigint)(self.address.ip),
onIPv6: self => Schema.decode(IPv6.IPv6Bigint)(self.address.ip)
});
const intermediate = bigIntegerAddress.value.toString(2).padStart(bits, "0").slice(0, this.mask);
const networkAddressString = intermediate + "0".repeat(bits - this.mask);
const networkAddressBigInt = BigInt(`0b${networkAddressString}`);
return this.onFamily({
onIPv4: _self => IPv4.IPv4BigintBrand(networkAddressBigInt),
onIPv6: _self => IPv6.IPv6BigintBrand(networkAddressBigInt)
});
});
}
/**
* The first address in the range given by this address' subnet, often
* referred to as the Network Address.
*
* @since 1.0.0
*/
networkAddress() {
return this.onFamily({
onIPv4: self => Function.pipe(self.networkAddressAsBigint, Effect.flatMap(value => Schema.encode(IPv4.IPv4Bigint)({
value,
family: "ipv4"
})), Effect.flatMap(Schema.decode(IPv4.IPv4))),
onIPv6: self => Function.pipe(self.networkAddressAsBigint, Effect.flatMap(value => Schema.encode(IPv6.IPv6Bigint)({
value,
family: "ipv6"
})), Effect.flatMap(Schema.decode(IPv6.IPv6)))
});
}
/**
* The last address in the range given by this address' subnet, often
* referred to as the Broadcast Address.
*
* @since 1.0.0
*/
get broadcastAddressAsBigint() {
return Effect.gen(this, function* () {
const bits = this.family === "ipv4" ? 32 : 128;
const bigIntegerAddress = yield* this.onFamily({
onIPv4: self => Schema.decode(IPv4.IPv4Bigint)(self.address.ip),
onIPv6: self => Schema.decode(IPv6.IPv6Bigint)(self.address.ip)
});
const intermediate = bigIntegerAddress.value.toString(2).padStart(bits, "0").slice(0, this.mask);
const broadcastAddressString = intermediate + "1".repeat(bits - this.mask);
const broadcastAddressBigInt = BigInt(`0b${broadcastAddressString}`);
return this.onFamily({
onIPv4: _self => IPv4.IPv4BigintBrand(broadcastAddressBigInt),
onIPv6: _self => IPv6.IPv6BigintBrand(broadcastAddressBigInt)
});
});
}
/**
* The last address in the range given by this address' subnet, often
* referred to as the Broadcast Address.
*
* @since 1.0.0
*/
broadcastAddress() {
return this.onFamily({
onIPv4: self => Function.pipe(self.broadcastAddressAsBigint, Effect.flatMap(value => Schema.encode(IPv4.IPv4Bigint)({
value,
family: "ipv4"
})), Effect.flatMap(Schema.decode(IPv4.IPv4))),
onIPv6: self => Function.pipe(self.broadcastAddressAsBigint, Effect.flatMap(value => Schema.encode(IPv6.IPv6Bigint)({
value,
family: "ipv6"
})), Effect.flatMap(Schema.decode(IPv6.IPv6)))
});
}
/**
* A stream of all addresses in the range given by this address' subnet.
*
* @since 1.0.0
*/
get range() {
return this.onFamily({
onIPv4: self => Effect.gen(function* () {
const minValue = yield* self.networkAddressAsBigint;
const maxValue = yield* self.broadcastAddressAsBigint;
return Function.pipe(Stream.iterate(minValue, x => IPv4.IPv4BigintBrand(x + 1n)), Stream.takeWhile(n => n <= maxValue), Stream.flatMap(value => Schema.encode(IPv4.IPv4Bigint)({
value,
family: "ipv4"
})), Stream.mapEffect(Schema.decode(IPv4.IPv4)));
}).pipe(Stream.unwrap),
onIPv6: self => Effect.gen(function* () {
const minValue = yield* self.networkAddressAsBigint;
const maxValue = yield* self.broadcastAddressAsBigint;
return Function.pipe(Stream.iterate(minValue, x => IPv6.IPv6BigintBrand(x + 1n)), Stream.takeWhile(n => n <= maxValue), Stream.flatMap(value => Schema.encode(IPv6.IPv6Bigint)({
value,
family: "ipv6"
})), Stream.mapEffect(Schema.decode(IPv6.IPv6)));
}).pipe(Stream.unwrap)
});
}
/**
* The total number of addresses in the range given by this address' subnet.
*
* @since 1.0.0
*/
get total() {
return Effect.gen(this, function* () {
const minValue = yield* this.networkAddressAsBigint;
const maxValue = yield* this.broadcastAddressAsBigint;
return maxValue - minValue + 1n;
});
}
/**
* Finds the smallest CIDR block that contains all the given IP addresses.
*
* @since 1.0.0
*/
static cidrBlockForRange = inputs => Effect.gen(function* () {
const bigIntMinAndMax = args => {
return args.reduce(([min, max], e) => {
return [e < min ? e : min, e > max ? e : max];
}, [args[0], args[0]]);
};
const bigints = yield* Function.pipe(inputs, Array.map(address => address.family === "ipv4" ? Schema.decode(IPv4.IPv4Bigint)(address.ip) : Schema.decode(IPv6.IPv6Bigint)(address.ip)), Array.map(x => x), Array.map(Effect.map(({
value
}) => value)), Effect.all);
const bits = inputs[0].family === "ipv4" ? 32 : 128;
const [min, max] = bigIntMinAndMax(bigints);
const leadingZerosInMin = bits - min.toString(2).length;
const leadingZerosInMax = bits - max.toString(2).length;
const cidrMask = Math.min(leadingZerosInMin, leadingZerosInMax);
const cidrAddress = inputs[0].family === "ipv4" ? yield* Schema.encode(IPv4.IPv4Bigint)({
value: IPv4.IPv4BigintBrand(min),
family: "ipv4"
}) : yield* Schema.encode(IPv6.IPv6Bigint)({
value: IPv6.IPv6BigintBrand(min),
family: "ipv6"
});
return yield* Schema.decode(CidrBlockFromString)(`${cidrAddress}/${cidrMask}`);
});
}
/**
* @since 1.0.0
* @category Schemas
*/
export const IPv4CidrBlock = /*#__PURE__*/Schema.transformOrFail(Schema.Struct({
address: IPv4.IPv4,
mask: CidrBlockMask.IPv4CidrMask
}), CidrBlockBase, {
encode: data => Effect.gen(function* () {
const address = yield* Schema.decode(IPv4.IPv4)(data.address);
const mask = yield* Schema.decode(CidrBlockMask.IPv4CidrMask)(data.mask);
return {
address,
mask
};
}).pipe(Effect.mapError(({
issue
}) => issue)),
decode: data => ParseResult.succeed({
address: data.address.ip,
mask: data.mask
})
}).annotations({
identifier: "IPv4CidrBlock",
description: "An ipv4 cidr block"
});
/**
* A schema that transforms a `string` into a `CidrBlock`.
*
* @since 1.0.0
* @category Schemas
*/
export const IPv4CidrBlockFromString = /*#__PURE__*/Schema.transform(Schema.TemplateLiteral(Schema.String, Schema.Literal("/"), Schema.Number), IPv4CidrBlock, {
decode: str => {
const [address, mask] = splitLiteral(str, "/");
return {
address,
mask: Number.parseInt(mask, 10)
};
},
encode: ({
address,
mask
}) => `${address}/${mask}`
}).annotations({
identifier: "IPv4CidrBlockFromString",
description: "An ipv4 cidr block from string"
});
/**
* @since 1.0.0
* @category Schemas
*/
export const IPv6CidrBlock = /*#__PURE__*/Schema.transformOrFail(Schema.Struct({
address: IPv6.IPv6,
mask: CidrBlockMask.IPv6CidrMask
}), CidrBlockBase, {
encode: data => Effect.gen(function* () {
const address = yield* Schema.decode(IPv6.IPv6)(data.address);
const mask = yield* Schema.decode(CidrBlockMask.IPv6CidrMask)(data.mask);
return {
address,
mask
};
}).pipe(Effect.mapError(({
issue
}) => issue)),
decode: data => ParseResult.succeed({
address: data.address.ip,
mask: data.mask
})
}).annotations({
identifier: "IPv6CidrBlock",
description: "An ipv6 cidr block"
});
/**
* A schema that transforms a `string` into a `CidrBlock`.
*
* @since 1.0.0
* @category Schemas
*/
export const IPv6CidrBlockFromString = /*#__PURE__*/Schema.transform(Schema.TemplateLiteral(Schema.String, Schema.Literal("/"), Schema.Number), IPv6CidrBlock, {
decode: str => {
const [address, mask] = splitLiteral(str, "/");
return {
address,
mask: Number.parseInt(mask, 10)
};
},
encode: ({
address,
mask
}) => `${address}/${mask}`
}).annotations({
identifier: "IPv6CidrBlockFromString",
description: "An ipv6 cidr block from string"
});
/**
* @since 1.0.0
* @category Schemas
*/
export const CidrBlock = /*#__PURE__*/Schema.Union(IPv4CidrBlock, IPv6CidrBlock);
/**
* A schema that transforms a `string` into a `CidrBlock`.
*
* @since 1.0.0
* @category Schemas
*/
export const CidrBlockFromString = /*#__PURE__*/Schema.transform(Schema.TemplateLiteral(Schema.String, Schema.Literal("/"), Schema.Number), CidrBlock, {
decode: str => {
const [address, mask] = splitLiteral(str, "/");
return {
address,
mask: Number.parseInt(mask, 10)
};
},
encode: ({
address,
mask
}) => `${address}/${mask}`
}).annotations({
identifier: "CidrBlockFromString",
description: "A cidr block"
});
//# sourceMappingURL=cidrBlock.js.map