21#include <magic_enum.hpp>
22#include <nlohmann/json.hpp>
25#include <wx/stdstream.h>
26#include <wx/wfstream.h>
42 void error(
const nlohmann::json::json_pointer& ptr,
const nlohmann::json& instance,
43 const std::string& message )
override
47 wxString::Format( wxS(
"JSON error: at %s, value:\n%s\n%s" ),
48 ptr.to_string(), instance.dump(), message ) );
62 type = magic_enum::enum_cast<PLUGIN_RUNTIME_TYPE>( aJson.at(
"type" ).get<std::string>(),
63 magic_enum::case_insensitive )
64 .value_or( PLUGIN_RUNTIME_TYPE::INVALID );
73 return type != PLUGIN_RUNTIME_TYPE::INVALID;
99 if( !aConfigFile.IsFileReadable() )
102 wxLogTrace(
traceApi,
"Plugin: parsing config file" );
104 wxFFileInputStream fp( aConfigFile.GetFullPath(), wxT(
"rt" ) );
105 wxStdInputStream fstream( fp );
111 js = nlohmann::json::parse( fstream,
nullptr,
117 wxLogTrace(
traceApi,
"Plugin: exception during parse" );
122 aValidator.
Validate( js, handler, nlohmann::json_uri(
"#/definitions/Plugin" ) );
125 wxLogTrace(
traceApi,
"Plugin: schema validation successful" );
130 identifier = js.at(
"identifier" ).get<wxString>();
131 name = js.at(
"name" ).get<wxString>();
132 description = js.at(
"description" ).get<wxString>();
136 wxLogTrace(
traceApi,
"Plugin: error parsing runtime section" );
142 wxLogTrace(
traceApi,
"Plugin: exception while parsing required keys" );
148 wxLogTrace(
traceApi, wxString::Format(
"Plugin: identifier %s does not meet requirements",
157 const nlohmann::json& actionsJs = js.at(
"actions" );
159 if( actionsJs.is_array() )
161 for(
const nlohmann::json& actionJs : actionsJs )
165 a->identifier = wxString::Format(
"%s.%s",
identifier, a->identifier );
166 wxLogTrace(
traceApi, wxString::Format(
"Plugin: loaded action %s",
175 wxLogTrace(
traceApi,
"Plugin: exception while parsing actions" );
183 m_configFile( aConfigFile ),
203 wxRegEx identifierRegex( wxS(
"[\\w\\d]{2,}\\.[\\w\\d]+\\.[\\w\\d]+" ) );
204 return identifierRegex.Matches( aIdentifier );
258 action.
identifier = aJson.at(
"identifier" ).get<wxString>();
259 wxLogTrace(
traceApi, wxString::Format(
"Plugin: load action %s", action.
identifier ) );
260 action.
name = aJson.at(
"name" ).get<wxString>();
261 action.
description = aJson.at(
"description" ).get<wxString>();
262 action.
entrypoint = aJson.at(
"entrypoint" ).get<wxString>();
263 action.
show_button = aJson.contains(
"show-button" ) && aJson.at(
"show-button" ).get<
bool>();
267 wxLogTrace(
traceApi,
"Plugin: exception while parsing action required keys" );
273 if( !f.IsRelative() )
275 wxLogTrace(
traceApi, wxString::Format(
"Plugin: action contains abs path %s; skipping",
280 f.Normalize( wxPATH_NORM_ABSOLUTE,
m_configFile.GetPath() );
282 if( !f.IsFileReadable() )
284 wxLogTrace(
traceApi, wxString::Format(
"WARNING: action entrypoint %s is not readable",
288 if( aJson.contains(
"args" ) && aJson.at(
"args" ).is_array() )
290 for(
const nlohmann::json& argJs : aJson.at(
"args" ) )
294 action.
args.emplace_back( argJs.get<wxString>() );
298 wxLogTrace(
traceApi,
"Plugin: exception while parsing action args" );
304 if( aJson.contains(
"scopes" ) && aJson.at(
"scopes" ).is_array() )
306 for(
const nlohmann::json& scopeJs : aJson.at(
"scopes" ) )
310 action.
scopes.insert( magic_enum::enum_cast<PLUGIN_ACTION_SCOPE>(
311 scopeJs.get<std::string>(), magic_enum::case_insensitive )
312 .value_or( PLUGIN_ACTION_SCOPE::INVALID ) );
316 wxLogTrace(
traceApi,
"Plugin: exception while parsing action scopes" );
323 [&](
const std::string& aKey, wxBitmapBundle& aDest )
325 if( aJson.contains( aKey ) && aJson.at( aKey ).is_array() )
327 wxVector<wxBitmap> bitmaps;
329 for(
const nlohmann::json& iconJs : aJson.at( aKey ) )
335 iconFile = iconJs.get<wxString>();
342 iconFile.Normalize( wxPATH_NORM_ABSOLUTE,
m_configFile.GetPath() );
345 wxString::Format(
"Plugin: action %s: loading icon %s",
346 action.
identifier, iconFile.GetFullPath() ) );
349 if( !iconFile.IsFileReadable() )
351 wxLogTrace(
traceApi,
"Plugin: icon file could not be read" );
357 bmp.LoadFile( iconFile.GetFullPath(), wxBITMAP_TYPE_PNG );
360 bitmaps.push_back( bmp );
362 wxLogTrace(
traceApi,
"Plugin: icon file not a valid bitmap" );
365 aDest = wxBitmapBundle::FromBitmaps( bitmaps );
369 handleBitmap(
"icons-light", action.
icon_light );
370 handleBitmap(
"icons-dark", action.
icon_dark );
A plugin that is invoked by KiCad and runs as an external process; communicating with KiCad via the I...
const PLUGIN_RUNTIME & Runtime() const
const wxString & Name() const
const std::vector< PLUGIN_ACTION > & Actions() const
wxString ActionSettingsKey(const PLUGIN_ACTION &aAction) const
const wxString & Identifier() const
std::unique_ptr< API_PLUGIN_CONFIG > m_config
wxString BasePath() const
static bool IsValidIdentifier(const wxString &aIdentifier)
const wxString & Description() const
API_PLUGIN(const wxFileName &aConfigFile, const JSON_SCHEMA_VALIDATOR &aValidator)
std::optional< PLUGIN_ACTION > createActionFromJson(const nlohmann::json &aJson)
nlohmann::json Validate(const nlohmann::json &aJson, nlohmann::json_schema::error_handler &aErrorHandler, const nlohmann::json_uri &aInitialUri=nlohmann::json_uri("#")) const
void error(const nlohmann::json::json_pointer &ptr, const nlohmann::json &instance, const std::string &message) override
const wxChar *const traceApi
Flag to enable debug output related to the IPC API and its plugin system.
API_PLUGIN_CONFIG(API_PLUGIN &aParent, const wxFileName &aConfigFile, const JSON_SCHEMA_VALIDATOR &aValidator)
std::vector< PLUGIN_ACTION > actions
An action performed by a plugin via the IPC API (not to be confused with ACTION_PLUGIN,...
wxBitmapBundle icon_light
std::set< PLUGIN_ACTION_SCOPE > scopes
std::vector< wxString > args
bool FromJson(const nlohmann::json &aJson)