import { IpAddress , IpAddressWithSubnetMask , SubnetCommand , VpcCommand } from './models' ; export type ParsedIpObject = IpAddress | IpAddressWithSubnetMask ; export type ParsingResult = ParsedIpObject [ ] | SubnetCommand | ParsingError | VpcCommand | null ; const CMD_SUBNET = 'subnet' ; const CMD_VPC = 'vpc' ; const ipAddressParser = { parse : function ( input ) : ParsingResult { const result = this . parseCommand ( input ) ; const matches = this . getMaches ( result . nextInput ) ; const correctInputs = matches . filter ( m => m . matches != null ) ; const incorrectInputs = matches . filter ( m => m . matches == null ) ; if ( correctInputs . length == 0 ) return null ; if ( incorrectInputs . length > 0 ) { return new ParsingError ( `Value(s) ${ incorrectInputs . map ( v => v . input ) . join ( ',' ) } was not recognized as valid ip address or ip address with a subnet mask` ) ; } const parsedObjects = matches . map ( m => this . parseSingle ( m . matches ! , m . input ) ) ; const parsingErrors = parsedObjects . filter ( p => p instanceof ParsingError ) ; if ( parsingErrors . length > 0 ) { return parsingErrors [ 0 ] as ParsingError ; } if ( result . command != null ) { const cmd = result . command == CMD_SUBNET ? this . createSubnetDefinition ( parsedObjects as ParsedIpObject [ ] ) : this . createVpcDefinition ( parsedObjects as ParsedIpObject [ ] ) ; return cmd ; } return parsedObjects as ParsedIpObject [ ] ; } , parseCommand ( input ) { if ( input . startsWith ( CMD_SUBNET ) ) return { command : CMD_SUBNET , nextInput : input . substring ( CMD_SUBNET . length ) } ; if ( input . startsWith ( CMD_VPC ) ) { return { command : CMD_VPC , nextInput : input . substring ( CMD_VPC . length ) } ; } return { command : null , nextInput : input } ; } , getMaches ( input ) { return input . replace ( /[\t\s]+/g , ' ' ) . split ( ' ' ) . filter ( s => s . length > 0 ) . map ( s => { const ipV4Regex = /^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\/\d+)?$/ ; const matches = ipV4Regex . exec ( s ) ; if ( matches == null || matches . length === 0 ) return { matches : null , input : s } ; return { matches , input : s } ; } ) ; } , parseSingle ( matches , input ) { const invalid = ( n ) => n < 0 || n > 255 ; const first = parseInt ( matches [ 1 ] ) ; const second = parseInt ( matches [ 2 ] ) ; const third = parseInt ( matches [ 3 ] ) ; const fourth = parseInt ( matches [ 4 ] ) ; if ( invalid ( first ) || invalid ( second ) || invalid ( third ) || invalid ( fourth ) ) return new ParsingError ( `${ input } doesn't represent a valid IP address space` ) ; const ipAddress = new IpAddress ( first , second , third , fourth ) ; if ( matches [ 5 ] ) { const maskPart = matches [ 5 ] . substr ( 1 ) ; const maskBits = parseInt ( maskPart ) ; if ( maskBits > 32 ) { return new ParsingError ( `Subnet mask value in ${ input } is out of range` ) ; } return new IpAddressWithSubnetMask ( ipAddress , maskBits ) ; } return ipAddress ; } , createSubnetDefinition ( items ) { if ( items . length != 1 ) return new ParsingError ( "Incorrect network definition" ) ; const first = items [ 0 ] ; if ( first instanceof IpAddressWithSubnetMask ) { return new SubnetCommand ( first ) ; } return new ParsingError ( "Network definition requires subnet mask" ) ; } , createVpcDefinition ( items ) { if ( items . length != 1 ) return new ParsingError ( "Incorrect VPC definition" ) ; const first = items [ 0 ] ; if ( first instanceof IpAddressWithSubnetMask ) { return new VpcCommand ( first ) ; } return new ParsingError ( "VPC definition requires subnet mask" ) ; } } ; export class ParsingError { errorMessage : string ; constructor ( message ) { this . errorMessage = message ; } } export default ipAddressParser ;