Skip to content

Support optional message metadata tagged with @ (non-RFC) #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions Sources/NIOIRC/IRCDispatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ public protocol IRCDispatcher {
message : String) throws
func doMessage (sender : IRCUserID?,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice to keep the old API as well for backwards compat, i.e. the one taking no metadata. The default implementation of the one w/ metadata could call into the older method (vs raising notImplemented).

recipients : [ IRCMessageRecipient ],
message : String) throws
message : String,
metadata : String?) throws

func doIsOnline (_ nicks : [ IRCNickName ]) throws
func doList (_ channels : [ IRCChannelName ]?,
Expand Down Expand Up @@ -103,7 +104,9 @@ public extension IRCDispatcher {
let sender = message.origin != nil
? IRCUserID(message.origin!) : nil
try doMessage(sender: sender,
recipients: recipients, message: payload)
recipients: recipients,
message: payload,
metadata: message.metadata)
case .NOTICE(let recipients, let message):
try doNotice(recipients: recipients, message: message)

Expand Down Expand Up @@ -205,15 +208,19 @@ public extension IRCDispatcher {
func doNotice(recipients: [ IRCMessageRecipient ], message: String) throws {
throw InternalDispatchError.notImplemented(function: #function)
}
func doMessage(sender: IRCUserID?, recipients: [ IRCMessageRecipient ],
message: String) throws

func doMessage(sender: IRCUserID?,
recipients: [ IRCMessageRecipient ],
message: String,
metadata: String) throws
{
throw InternalDispatchError.notImplemented(function: #function)
}

func doIsOnline(_ nicks: [ IRCNickName ]) throws {
throw InternalDispatchError.notImplemented(function: #function)
}

func doList(_ channels : [ IRCChannelName ]?, _ target: String?) throws {
throw InternalDispatchError.notImplemented(function: #function)
}
Expand Down
51 changes: 45 additions & 6 deletions Sources/NIOIRC/IRCMessageParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ public struct IRCMessageParser {
// [':' SOURCE]? ' ' COMMAND [' ' ARGS]? [' :' LAST-ARG]?
let cSpace : UInt8 = 32
let cColon : UInt8 = 58
let cAt : UInt8 = 64
let c0 : UInt8 = 48 + 0
let c9 : UInt8 = 48 + 9
guard !line.isEmpty else { throw Error.syntaxError }
Expand Down Expand Up @@ -135,13 +136,37 @@ public struct IRCMessageParser {
return String(data: Data(slice), encoding: .utf8) // Sigh, the pain.
}

/* parse metadata */

let metadataSource : Swift.Slice<UnsafeRawBufferPointer>?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistent indent w/ the rest would be nice :-) (I don't personally care too much about indentation styles, but they should be consistent within a project, which is 2-space right now in NIOIRC)


if cursor[cursor.startIndex] == cAt {
let startIndex = cursor.startIndex.advanced(by: 1)
let spaceIdx = line.firstIndex(of: cSpace)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it safe to assume that the metadata itself doesn't contain spaces? Is there some escaping maybe?


guard let endSourceIdx = spaceIdx, endSourceIdx > startIndex else {
throw Error.invalidPrefix(Data(line))
}

metadataSource = cursor[startIndex..<endSourceIdx]
assert(!cursor.isEmpty)

cursor = cursor[endSourceIdx..<cursor.endIndex]
skipSpaces()
}
else {
metadataSource = nil
}

/* parse source */

let source : Swift.Slice<UnsafeRawBufferPointer>?

if cursor[cursor.startIndex] == cColon {
// let spaceIdx = cursor.firstIndex(of: cSpace)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should probably remove such comments? :-)

let startIndex = cursor.startIndex.advanced(by: 1)
let spaceIdx = line.firstIndex(of: cSpace)
// let spaceIdx = line.firstIndex(of: cSpace)
let spaceIdx = cursor.firstIndex(of: cSpace)

guard let endSourceIdx = spaceIdx, endSourceIdx > startIndex else {
throw Error.invalidPrefix(Data(line))
Expand All @@ -159,7 +184,9 @@ public struct IRCMessageParser {

/* parse command name */

guard !cursor.isEmpty else { throw Error.invalidCommand(Data(line)) }
guard !cursor.isEmpty else {
throw Error.invalidCommand(Data(line))
}
guard isLetter(cursor[cursor.startIndex])
|| isDigit(cursor[cursor.startIndex]) else {
throw Error.invalidCommand(Data(line))
Expand Down Expand Up @@ -239,6 +266,16 @@ public struct IRCMessageParser {

/* construct */

let metadataString: String?
if let metadataSource = metadataSource {
guard let string = makeString(from: metadataSource) else {
throw Error.invalidPrefix(Data(line))
}
metadataString = string
} else {
metadataString = nil
}

let origin: String?
if let source = source {
guard let sourceString = makeString(from: source) else {
Expand All @@ -252,10 +289,12 @@ public struct IRCMessageParser {

switch commandKey {
case .string(let s):
return IRCMessage(origin: origin,
return IRCMessage(metadata: metadataString,
origin: origin,
command: try IRCCommand(s, arguments: args))
case .int(let i):
return IRCMessage(origin: origin,
return IRCMessage(metadata: metadataString,
origin: origin,
command: try IRCCommand(i, arguments: args))
}
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/NIOIRC/IRCMessageTarget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public extension IRCMessageTarget {
.map { $0.replacingOccurrences(of: "\r", with: "") }

let messages = lines.map {
IRCMessage(origin: origin, command: .PRIVMSG(recipients, $0))
IRCMessage(metadata: nil, origin: origin, command: .PRIVMSG(recipients, $0))
}
sendMessages(messages, promise: nil)
}
Expand All @@ -65,14 +65,14 @@ public extension IRCMessageTarget {
.map { $0.replacingOccurrences(of: "\r", with: "") }

let messages = lines.map {
IRCMessage(origin: origin, command: .NOTICE(recipients, $0))
IRCMessage(metadata: nil, origin: origin, command: .NOTICE(recipients, $0))
}
sendMessages(messages, promise: nil)
}

@inlinable
func sendRawReply(_ code: IRCCommandCode, _ args: String...) {
sendMessage(IRCMessage(origin: origin, command: .numeric(code, args)))
sendMessage(IRCMessage(metadata: nil, origin: origin, command: .numeric(code, args)))
}
}

22 changes: 17 additions & 5 deletions Sources/NIOIRC/Model/IRCMessage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,22 @@ import struct Foundation.Data
public struct IRCMessage : Codable, CustomStringConvertible {

public enum CodingKeys: String, CodingKey {
case origin, target, command, arguments
case metadata, origin, target, command, arguments
}

@inlinable
public init(origin: String? = nil, target: String? = nil,
public init(metadata: String? = nil, origin: String? = nil, target: String? = nil,
command: IRCCommand)
{
self._storage = _Storage(origin: origin, target: target, command: command)
self._storage = _Storage(metadata: metadata, origin: origin, target: target, command: command)
}

@inlinable
public var metadata : String? {
set { copyStorageIfNeeded(); _storage.metadata = newValue }
get { return _storage.metadata }
}

/**
* True origin of message. Do not set in clients.
*
Expand Down Expand Up @@ -67,6 +73,7 @@ public struct IRCMessage : Codable, CustomStringConvertible {
@inlinable
public var description: String {
var ms = "<IRCMsg:"
if let metadata = metadata { ms += " from=\(origin)" }
if let origin = origin { ms += " from=\(origin)" }
if let target = target { ms += " to=\(target)" }
ms += " "
Expand All @@ -87,18 +94,21 @@ public struct IRCMessage : Codable, CustomStringConvertible {

@usableFromInline
class _Storage {
@usableFromInline var metadata : String?
@usableFromInline var origin : String?
@usableFromInline var target : String?
@usableFromInline var command : IRCCommand

@usableFromInline
init(origin: String?, target: String?, command: IRCCommand) {
init(metadata: String?, origin: String?, target: String?, command: IRCCommand) {
self.metadata = metadata
self.origin = origin
self.target = target
self.command = command
}
@usableFromInline
init(_ other: _Storage) {
self.metadata = other.metadata
self.origin = other.origin
self.target = other.target
self.command = other.command
Expand All @@ -116,13 +126,15 @@ public struct IRCMessage : Codable, CustomStringConvertible {
let args = try c.decodeIfPresent([ String ].self, forKey: .arguments)
let command = try IRCCommand(cmd, arguments: args ?? [])

self.init(origin: try c.decodeIfPresent(String.self, forKey: .origin),
self.init(metadata: try c.decodeIfPresent(String.self, forKey: .metadata),
origin: try c.decodeIfPresent(String.self, forKey: .origin),
target: try c.decodeIfPresent(String.self, forKey: .target),
command: command)
}
@inlinable
public func encode(to encoder: Encoder) throws {
var c = encoder.container(keyedBy: CodingKeys.self)
try c.encodeIfPresent(metadata, forKey: .metadata)
try c.encodeIfPresent(origin, forKey: .origin)
try c.encodeIfPresent(target, forKey: .target)
try c.encode(command.commandAsString, forKey: .command)
Expand Down