blob: cf8077b13c0546edd968b85001915bf44b170e9c [file] [log] [blame]
/*
* Copyright (c) 2020 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
#pragma once
#include <app/data-model/Nullable.h>
#include <commands/clusters/ComplexArgument.h>
#include <commands/clusters/CustomArgument.h>
#include <inet/InetInterface.h>
#include <lib/core/Optional.h>
#include <lib/support/Span.h>
#include <lib/support/logging/CHIPLogging.h>
#include <atomic>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <vector>
class Command;
template <typename T, typename... Args>
std::unique_ptr<Command> make_unique(Args &&... args)
{
return std::unique_ptr<Command>(new T(std::forward<Args>(args)...));
}
struct movable_initializer_list
{
movable_initializer_list(std::unique_ptr<Command> && in) : item(std::move(in)) {}
operator std::unique_ptr<Command>() const && { return std::move(item); }
mutable std::unique_ptr<Command> item;
};
typedef std::initializer_list<movable_initializer_list> commands_list;
enum ArgumentType
{
Number_uint8,
Number_uint16,
Number_uint32,
Number_uint64,
Number_int8,
Number_int16,
Number_int32,
Number_int64,
Float,
Double,
Bool,
String,
CharString,
OctetString,
Address,
Complex,
Custom,
VectorBool,
Vector16,
Vector32,
VectorCustom,
VectorString, // comma separated string items
};
struct Argument
{
const char * name;
ArgumentType type;
int64_t min;
uint64_t max;
void * value;
uint8_t flags;
const char * desc;
enum
{
kOptional = (1 << 0),
kNullable = (1 << 1),
};
bool isOptional() const { return flags & kOptional; }
bool isNullable() const { return flags & kNullable; }
};
struct ReadOnlyGlobalCommandArgument
{
const char * name;
const char * value;
const char * desc;
};
class Command
{
public:
struct AddressWithInterface
{
::chip::Inet::IPAddress address;
::chip::Inet::InterfaceId interfaceId;
};
Command(const char * commandName, const char * helpText = nullptr) : mName(commandName), mHelpText(helpText) {}
virtual ~Command() {}
const char * GetName(void) const { return mName; }
const char * GetHelpText() const { return mHelpText; }
const char * GetReadOnlyGlobalCommandArgument(void) const;
const char * GetAttribute(void) const;
const char * GetEvent(void) const;
const char * GetArgumentName(size_t index) const;
const char * GetArgumentDescription(size_t index) const;
bool GetArgumentIsOptional(size_t index) const { return mArgs[index].isOptional(); }
size_t GetArgumentsCount(void) const { return mArgs.size(); }
bool InitArguments(int argc, char ** argv);
void AddArgument(const char * name, const char * value, const char * desc = "");
/**
* @brief
* Add a char string command argument
*
* @param name The name that will be displayed in the command help
* @param value A pointer to a `char *` where the argv value will be stored
* @param flags
* @param desc The description of the argument that will be displayed in the command help
* @returns The number of arguments currently added to the command
*/
size_t AddArgument(const char * name, char ** value, const char * desc = "", uint8_t flags = 0);
/**
* Add an octet string command argument
*/
size_t AddArgument(const char * name, chip::ByteSpan * value, const char * desc = "", uint8_t flags = 0);
size_t AddArgument(const char * name, chip::Span<const char> * value, const char * desc = "", uint8_t flags = 0);
size_t AddArgument(const char * name, AddressWithInterface * out, const char * desc = "", uint8_t flags = 0);
// Optional Complex arguments are not currently supported via the <chip::Optional> class.
// Instead, they must be explicitly specified as optional using kOptional in the flags parameter,
// and the base TypedComplexArgument<T> class is referenced.
size_t AddArgument(const char * name, ComplexArgument * value, const char * desc = "", uint8_t flags = 0);
size_t AddArgument(const char * name, CustomArgument * value, const char * desc = "");
size_t AddArgument(const char * name, int64_t min, uint64_t max, bool * out, const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<void *>(out), Bool, desc, flags);
}
size_t AddArgument(const char * name, int64_t min, uint64_t max, int8_t * out, const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<void *>(out), Number_int8, desc, flags);
}
size_t AddArgument(const char * name, int64_t min, uint64_t max, int16_t * out, const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<void *>(out), Number_int16, desc, flags);
}
size_t AddArgument(const char * name, int64_t min, uint64_t max, int32_t * out, const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<void *>(out), Number_int32, desc, flags);
}
size_t AddArgument(const char * name, int64_t min, uint64_t max, int64_t * out, const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<void *>(out), Number_int64, desc, flags);
}
size_t AddArgument(const char * name, int64_t min, uint64_t max, uint8_t * out, const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<void *>(out), Number_uint8, desc, flags);
}
size_t AddArgument(const char * name, int64_t min, uint64_t max, uint16_t * out, const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<void *>(out), Number_uint16, desc, flags);
}
size_t AddArgument(const char * name, int64_t min, uint64_t max, uint32_t * out, const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<void *>(out), Number_uint32, desc, flags);
}
size_t AddArgument(const char * name, int64_t min, uint64_t max, uint64_t * out, const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<void *>(out), Number_uint64, desc, flags);
}
size_t AddArgument(const char * name, float min, float max, float * out, const char * desc = "", uint8_t flags = 0);
size_t AddArgument(const char * name, double min, double max, double * out, const char * desc = "", uint8_t flags = 0);
size_t AddArgument(const char * name, int64_t min, uint64_t max, std::vector<uint16_t> * value, const char * desc = "");
size_t AddArgument(const char * name, int64_t min, uint64_t max, std::vector<uint32_t> * value, const char * desc = "");
size_t AddArgument(const char * name, std::vector<CustomArgument *> * value, const char * desc = "");
size_t AddArgument(const char * name, int64_t min, uint64_t max, chip::Optional<std::vector<bool>> * value,
const char * desc = "");
size_t AddArgument(const char * name, int64_t min, uint64_t max, chip::Optional<std::vector<uint32_t>> * value,
const char * desc = "");
template <typename T, typename = std::enable_if_t<std::is_enum<T>::value>>
size_t AddArgument(const char * name, int64_t min, uint64_t max, T * out, const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<std::underlying_type_t<T> *>(out), desc, flags);
}
template <typename T>
size_t AddArgument(const char * name, int64_t min, uint64_t max, chip::BitFlags<T> * out, const char * desc = "",
uint8_t flags = 0)
{
// This is a terrible hack that relies on BitFlags only having the one
// mValue member.
return AddArgument(name, min, max, reinterpret_cast<T *>(out), desc, flags);
}
template <typename T>
size_t AddArgument(const char * name, int64_t min, uint64_t max, chip::BitMask<T> * out, const char * desc = "",
uint8_t flags = 0)
{
// This is a terrible hack that relies on BitMask only having the one
// mValue member.
return AddArgument(name, min, max, reinterpret_cast<T *>(out), desc, flags);
}
template <typename T>
size_t AddArgument(const char * name, chip::Optional<T> * value, const char * desc = "")
{
return AddArgument(name, reinterpret_cast<T *>(value), desc, Argument::kOptional);
}
template <typename T>
size_t AddArgument(const char * name, int64_t min, uint64_t max, chip::Optional<T> * value, const char * desc = "")
{
return AddArgument(name, min, max, reinterpret_cast<T *>(value), desc, Argument::kOptional);
}
template <typename T>
size_t AddArgument(const char * name, chip::app::DataModel::Nullable<T> * value, const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, reinterpret_cast<T *>(value), desc, flags | Argument::kNullable);
}
template <typename T>
size_t AddArgument(const char * name, int64_t min, uint64_t max, chip::app::DataModel::Nullable<T> * value,
const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<T *>(value), desc, flags | Argument::kNullable);
}
size_t AddArgument(const char * name, float min, float max, chip::app::DataModel::Nullable<float> * value,
const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<float *>(value), desc, flags | Argument::kNullable);
}
size_t AddArgument(const char * name, double min, double max, chip::app::DataModel::Nullable<double> * value,
const char * desc = "", uint8_t flags = 0)
{
return AddArgument(name, min, max, reinterpret_cast<double *>(value), desc, flags | Argument::kNullable);
}
size_t AddArgument(const char * name, std::vector<std::string> * value, const char * desc);
size_t AddArgument(const char * name, chip::Optional<std::vector<std::string>> * value, const char * desc);
void ResetArguments();
virtual CHIP_ERROR Run() = 0;
bool IsInteractive() { return mIsInteractive; }
CHIP_ERROR RunAsInteractive(const chip::Optional<char *> & interactiveStorageDirectory)
{
mStorageDirectory = interactiveStorageDirectory;
mIsInteractive = true;
return Run();
}
const chip::Optional<char *> & GetStorageDirectory() const { return mStorageDirectory; }
protected:
// mStorageDirectory lives here so we can just set it in RunAsInteractive.
chip::Optional<char *> mStorageDirectory;
private:
bool InitArgument(size_t argIndex, char * argValue);
size_t AddArgument(const char * name, int64_t min, uint64_t max, void * out, ArgumentType type, const char * desc,
uint8_t flags);
size_t AddArgument(const char * name, int64_t min, uint64_t max, void * out, const char * desc, uint8_t flags);
/**
* Add the Argument to our list. This preserves the property that all
* optional arguments come at the end of the list.
*/
size_t AddArgumentToList(Argument && argument);
const char * mName = nullptr;
const char * mHelpText = nullptr;
bool mIsInteractive = false;
chip::Optional<ReadOnlyGlobalCommandArgument> mReadOnlyGlobalCommandArgument;
std::vector<Argument> mArgs;
};