77 lines
2.5 KiB
C#
77 lines
2.5 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
|
||
|
namespace PluralKit.Bot.CommandSystem
|
||
|
{
|
||
|
public class Parameters
|
||
|
{
|
||
|
private static readonly Dictionary<char, char> _quotePairs = new Dictionary<char, char>()
|
||
|
{
|
||
|
{'\'', '\''}, {'"', '"'}
|
||
|
};
|
||
|
|
||
|
private readonly string _cmd;
|
||
|
private int _ptr;
|
||
|
|
||
|
public Parameters(string cmd)
|
||
|
{
|
||
|
_cmd = cmd;
|
||
|
_ptr = 0;
|
||
|
}
|
||
|
|
||
|
public string Pop()
|
||
|
{
|
||
|
var positions = NextWordPosition();
|
||
|
if (positions == null) return "";
|
||
|
|
||
|
var (start, end, advance) = positions.Value;
|
||
|
_ptr = end + advance;
|
||
|
return _cmd.Substring(start, end - start).Trim();
|
||
|
}
|
||
|
|
||
|
public string Peek()
|
||
|
{
|
||
|
var positions = NextWordPosition();
|
||
|
if (positions == null) return "";
|
||
|
|
||
|
var (start, end, _) = positions.Value;
|
||
|
return _cmd.Substring(start, end - start).Trim();
|
||
|
}
|
||
|
|
||
|
public string Remainder() => _cmd.Substring(_ptr).Trim();
|
||
|
public string FullCommand => _cmd;
|
||
|
|
||
|
// Returns tuple of (startpos, endpos, advanceafter)
|
||
|
private ValueTuple<int, int, int>? NextWordPosition()
|
||
|
{
|
||
|
// Is this the end of the string?
|
||
|
if (_cmd.Length <= _ptr) return null;
|
||
|
|
||
|
// Is this a quoted word?
|
||
|
if (_quotePairs.ContainsKey(_cmd[_ptr]))
|
||
|
{
|
||
|
// This is a quoted word, find corresponding end quote and return span
|
||
|
var endQuote = _quotePairs[_cmd[_ptr]];
|
||
|
var endQuotePosition = _cmd.IndexOf(endQuote, _ptr + 1);
|
||
|
|
||
|
// Position after the end quote should be a space (or EOL)
|
||
|
// Otherwise treat it as a standard word that's not quoted
|
||
|
if (_cmd.Length == endQuotePosition + 1 || _cmd[endQuotePosition + 1] == ' ')
|
||
|
{
|
||
|
if (endQuotePosition == -1)
|
||
|
{
|
||
|
// This is an unterminated quoted word, just return the entire word including the start quote
|
||
|
// TODO: should we do something else here?
|
||
|
return (_ptr, _cmd.Length, 0);
|
||
|
}
|
||
|
|
||
|
return (_ptr + 1, endQuotePosition, 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Not a quoted word, just find the next space and return as appropriate
|
||
|
var wordEnd = _cmd.IndexOf(' ', _ptr + 1);
|
||
|
return wordEnd != -1 ? (_ptr, wordEnd, 1) : (_ptr, _cmd.Length, 0);
|
||
|
}
|
||
|
}
|
||
|
}
|