21#include <magic_enum.hpp>
25#include <wx/stdstream.h>
26#include <wx/wfstream.h>
42 const nlohmann::json& instance,
43 const std::string& message )
47 wxString::Format( wxS(
"JSON error: at %s, value:\n%s\n%s" ),
48 ptr.to_string(), instance.dump(), message ) );
58 type = magic_enum::enum_cast<PLUGIN_RUNTIME_TYPE>( aJson.at(
"type" ).get<std::string>(),
59 magic_enum::case_insensitive )
95 if( !aConfigFile.IsFileReadable() )
98 wxLogTrace(
traceApi,
"Plugin: parsing config file" );
100 wxFFileInputStream fp( aConfigFile.GetFullPath(), wxT(
"rt" ) );
101 wxStdInputStream fstream( fp );
107 js = nlohmann::json::parse( fstream,
nullptr,
113 wxLogTrace(
traceApi,
"Plugin: exception during parse" );
118 aValidator.
Validate( js, handler, nlohmann::json_uri(
"#/definitions/Plugin" ) );
121 wxLogTrace(
traceApi,
"Plugin: schema validation successful" );
126 identifier = js.at(
"identifier" ).get<wxString>();
127 name = js.at(
"name" ).get<wxString>();
128 description = js.at(
"description" ).get<wxString>();
130 if( !
runtime.FromJson( js.at(
"runtime" ) ) )
132 wxLogTrace(
traceApi,
"Plugin: error parsing runtime section" );
138 wxLogTrace(
traceApi,
"Plugin: exception while parsing required keys" );
144 wxLogTrace(
traceApi, wxString::Format(
"Plugin: identifier %s does not meet requirements",
153 const nlohmann::json& actionsJs = js.at(
"actions" );
155 if( actionsJs.is_array() )
157 for(
const nlohmann::json& actionJs : actionsJs )
159 if( std::optional<PLUGIN_ACTION> a =
parent.createActionFromJson( actionJs ) )
161 a->identifier = wxString::Format(
"%s.%s",
identifier, a->identifier );
162 wxLogTrace(
traceApi, wxString::Format(
"Plugin: loaded action %s",
171 wxLogTrace(
traceApi,
"Plugin: exception while parsing actions" );
200 wxRegEx identifierRegex( wxS(
"[\\w\\d]{2,}\\.[\\w\\d]+\\.[\\w\\d]+" ) );
201 return identifierRegex.Matches( aIdentifier );
255 action.
identifier = aJson.at(
"identifier" ).get<wxString>();
256 wxLogTrace(
traceApi, wxString::Format(
"Plugin: load action %s", action.
identifier ) );
257 action.
name = aJson.at(
"name" ).get<wxString>();
258 action.
description = aJson.at(
"description" ).get<wxString>();
259 action.
entrypoint = aJson.at(
"entrypoint" ).get<wxString>();
260 action.
show_button = aJson.contains(
"show-button" ) && aJson.at(
"show-button" ).get<
bool>();
264 wxLogTrace(
traceApi,
"Plugin: exception while parsing action required keys" );
270 if( !f.IsRelative() )
272 wxLogTrace(
traceApi, wxString::Format(
"Plugin: action contains abs path %s; skipping",
277 f.Normalize( wxPATH_NORM_ABSOLUTE,
m_configFile.GetPath() );
279 if( !f.IsFileReadable() )
281 wxLogTrace(
traceApi, wxString::Format(
"WARNING: action entrypoint %s is not readable",
285 if( aJson.contains(
"args" ) && aJson.at(
"args" ).is_array() )
287 for(
const nlohmann::json& argJs : aJson.at(
"args" ) )
291 action.
args.emplace_back( argJs.get<wxString>() );
295 wxLogTrace(
traceApi,
"Plugin: exception while parsing action args" );
301 if( aJson.contains(
"scopes" ) && aJson.at(
"scopes" ).is_array() )
303 for(
const nlohmann::json& scopeJs : aJson.at(
"scopes" ) )
307 action.
scopes.insert( magic_enum::enum_cast<PLUGIN_ACTION_SCOPE>(
308 scopeJs.get<std::string>(), magic_enum::case_insensitive )
313 wxLogTrace(
traceApi,
"Plugin: exception while parsing action scopes" );
320 [&](
const std::string& aKey, wxBitmapBundle& aDest )
322 if( aJson.contains( aKey ) && aJson.at( aKey ).is_array() )
324 wxVector<wxBitmap> bitmaps;
326 for(
const nlohmann::json& iconJs : aJson.at( aKey ) )
332 iconFile = iconJs.get<wxString>();
339 iconFile.Normalize( wxPATH_NORM_ABSOLUTE,
m_configFile.GetPath() );
342 wxString::Format(
"Plugin: action %s: loading icon %s",
343 action.
identifier, iconFile.GetFullPath() ) );
346 if( !iconFile.IsFileReadable() )
348 wxLogTrace(
traceApi,
"Plugin: icon file could not be read" );
354 bmp.LoadFile( iconFile.GetFullPath(), wxBITMAP_TYPE_PNG );
357 bitmaps.push_back( bmp );
359 wxLogTrace(
traceApi,
"Plugin: icon file not a valid bitmap" );
362 aDest = wxBitmapBundle::FromBitmaps( bitmaps );
366 handleBitmap(
"icons-light", action.
icon_light );
367 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
friend struct API_PLUGIN_CONFIG
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.
wxBitmapBundle icon_light
std::set< PLUGIN_ACTION_SCOPE > scopes
std::vector< wxString > args
bool FromJson(const nlohmann::json &aJson)