about summary refs log tree commit diff
path: root/IrcTokens/Line.cs
diff options
context:
space:
mode:
Diffstat (limited to 'IrcTokens/Line.cs')
-rw-r--r--IrcTokens/Line.cs139
1 files changed, 139 insertions, 0 deletions
diff --git a/IrcTokens/Line.cs b/IrcTokens/Line.cs
new file mode 100644
index 0000000..7592376
--- /dev/null
+++ b/IrcTokens/Line.cs
@@ -0,0 +1,139 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace IrcTokens
+{
+    /// <summary>
+    /// Tools to represent, parse, and format IRC lines
+    /// </summary>
+    public class Line
+    {
+        public Dictionary<string, string> Tags { get; set; }
+        public string Source { get; set; }
+        public string Command { get; set; }
+        public List<string> Params { get; set; }
+
+        private Hostmask _hostmask;
+        private string _rawLine;
+
+        public override string ToString() => 
+            $"Line(tags={string.Join(";", Tags.Select(kvp => $"{kvp.Key}={kvp.Value}"))}, params={string.Join(",", Params)})";
+
+        public Hostmask Hostmask =>
+            _hostmask ??= new Hostmask(Source);
+
+        public Line() { }
+
+        /// <summary>
+        /// Build new <see cref="Line"/> object parsed from <param name="line">a string</param>. Analogous to irctokens.tokenise()
+        /// </summary>
+        /// <param name="line">irc line to parse</param>
+        public Line(string line)
+        {
+            _rawLine = line;
+            string[] split;
+
+            if (line.StartsWith('@'))
+            {
+                Tags = new Dictionary<string, string>();
+
+                split = line.Split(" ");
+                var messageTags = split[0];
+                line = string.Join(" ", split.Skip(1));
+
+                foreach (var part in messageTags.Substring(1).Split(';'))
+                {
+                    if (part.Contains('='))
+                    {
+                        split = part.Split('=');
+                        Tags[split[0]] = Protocol.UnescapeTag(split[1]);
+                    }
+                    else
+                    {
+                        Tags[part] = string.Empty;
+                    }
+                }
+            }
+
+            string trailing;
+            if (line.Contains(" :"))
+            {
+                split = line.Split(" :");
+                line = split[0];
+                trailing = string.Join(" :", split.Skip(1));
+            }
+            else
+            {
+                trailing = null;
+            }
+
+            Params = line.Contains(' ')
+                ? line.Split(' ').Where(p => !string.IsNullOrWhiteSpace(p)).ToList()
+                : new List<string> {line};
+
+            if (Params[0].StartsWith(':'))
+            {
+                Source = Params[0].Substring(1);
+                Params.RemoveAt(0);
+            }
+
+            if (Params.Count > 0)
+            {
+                Command = Params[0].ToUpper();
+                Params.RemoveAt(0);
+            }
+
+            if (trailing != null)
+            {
+                Params.Add(trailing);
+            }
+        }
+
+        /// <summary>
+        /// Format a <see cref="Line"/> as a standards-compliant IRC line
+        /// </summary>
+        /// <returns>formatted irc line</returns>
+        public string Format()
+        {
+            var outs = new List<string>();
+
+            if (Tags != null && Tags.Any())
+            {
+                var tags = Tags.Keys
+                    .Select(key => string.IsNullOrWhiteSpace(Tags[key]) ? key : $"{key}={Protocol.EscapeTag(Tags[key])}")
+                    .ToList();
+
+                outs.Add($"@{string.Join(";", tags)}");
+            }
+
+            if (Source != null)
+            {
+                outs.Add($":{Source}");
+            }
+
+            outs.Add(Command);
+
+            if (Params != null && Params.Any())
+            {
+                var last = Params[^1];
+                Params.RemoveAt(Params.Count - 1);
+
+                foreach (var p in Params)
+                {
+                    if (p.Contains(' '))
+                        throw new ArgumentException("non-last parameters cannot have spaces");
+                    if (p.StartsWith(':'))
+                        throw new ArgumentException("non-last parameters cannot start with colon");
+                }
+                outs.AddRange(Params);
+
+                if (last == null || string.IsNullOrWhiteSpace(last) || last.Contains(' ') || last.StartsWith(':'))
+                    last = $":{last}";
+                outs.Add(last);
+            }
+
+            return string.Join(" ", outs);
+        }
+    }
+}