Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

93 lines
2.4 KiB

/*
Part of the TERMINAL MODULE
Copyright (C) 2020 by Maxim Prokhorov <prokhorov dot max at outlook dot com>
Heavily inspired by the Embedis design:
- https://github.com/thingSoC/embedis
*/
#include <Arduino.h>
#include "terminal_commands.h"
#include <memory>
namespace terminal {
std::unordered_map<String, Terminal::CommandFunc,
parsing::LowercaseFnv1Hash<String>,
parsing::LowercaseEquals<String>> Terminal::commands;
void Terminal::addCommand(const String& name, CommandFunc func) {
if (!func) return;
commands.emplace(std::make_pair(name, func));
}
size_t Terminal::commandsSize() {
return commands.size();
}
std::vector<String> Terminal::commandNames() {
std::vector<String> out;
out.reserve(commands.size());
for (auto& command : commands) {
out.push_back(command.first);
}
return out;
}
Terminal::Result Terminal::processLine() {
// Arduino stream API returns either `char` >= 0 or -1 on error
int c = -1;
while ((c = stream.read()) >= 0) {
if (buffer.size() >= (buffer_size - 1)) {
buffer.clear();
return Result::BufferOverflow;
}
buffer.push_back(c);
if (c == '\n') {
// in case we see \r\n, offset minus one and overwrite \r
auto end = buffer.end() - 1;
if (*(end - 1) == '\r') {
--end;
}
*end = '\0';
// parser should pick out at least one arg (command)
auto cmdline = parsing::parse_commandline(buffer.data());
buffer.clear();
if (cmdline.argc >= 1) {
auto command = commands.find(cmdline.argv[0]);
if (command == commands.end()) return Result::CommandNotFound;
(*command).second(CommandContext{std::move(cmdline.argv), cmdline.argc, stream});
return Result::Command;
}
}
}
// we need to notify about the fixable things
if (buffer.size() && (c < 0)) {
return Result::Pending;
} else if (!buffer.size() && (c < 0)) {
return Result::NoInput;
// ... and some unexpected conditions
} else {
return Result::Error;
}
}
bool Terminal::defaultProcessFunc(Result result) {
return (result != Result::Error) && (result != Result::NoInput);
}
void Terminal::process(ProcessFunc func) {
while (func(processLine())) {
}
}
} // ns terminal