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 ) );
50 wxString
location = wxString::FromUTF8( ptr.to_string() );
58 m_errorMessage << wxString::Format(
_(
"invalid plugin configuration at '%s': %s" ),
59 location, wxString::FromUTF8( message ) );
67 type = magic_enum::enum_cast<PLUGIN_RUNTIME_TYPE>( aJson.at(
"type" ).get<std::string>(),
68 magic_enum::case_insensitive )
71 catch( std::exception& e )
73 return tl::unexpected( wxString::Format(
_(
"invalid plugin runtime: %s" ), e.what() ) );
103 if( !aConfigFile.IsFileReadable() )
105 error_message = _(
"could not read plugin configuration file" );
109 wxLogTrace(
traceApi,
"Plugin: parsing config file" );
111 wxFFileInputStream fp( aConfigFile.GetFullPath(), wxT(
"rt" ) );
112 wxStdInputStream fstream( fp );
118 js = nlohmann::json::parse( fstream,
nullptr,
122 catch(
const std::exception& e )
124 wxLogTrace(
traceApi,
"Plugin: exception during parse" );
125 error_message = wxString::Format(
_(
"plugin configuration file error: %s" ),
126 wxString::FromUTF8( e.what() ) );
131 aValidator.
Validate( js, handler, nlohmann::json_uri(
"#/definitions/Plugin" ) );
134 wxLogTrace(
traceApi,
"Plugin: schema validation successful" );
141 identifier = js.at(
"identifier" ).get<wxString>();
142 name = js.at(
"name" ).get<wxString>();
143 description = js.at(
"description" ).get<wxString>();
145 if( !
runtime.FromJson( js.at(
"runtime" ) ).or_else(
146 [
this](
const wxString& aError )
148 wxLogTrace( traceApi,
"Plugin %s: %s", identifier, aError );
149 error_message = aError;
155 catch(
const std::exception& e )
157 wxLogTrace(
traceApi,
"Plugin: exception while parsing required keys" );
158 error_message = wxString::Format(
_(
"missing or invalid required keys: %s" ),
159 wxString::FromUTF8( e.what() ) );
165 wxLogTrace(
traceApi, wxString::Format(
"Plugin: identifier %s does not meet requirements",
175 const nlohmann::json& actionsJs = js.at(
"actions" );
177 if( actionsJs.is_array() )
179 for(
const nlohmann::json& actionJs : actionsJs )
181 if( std::optional<PLUGIN_ACTION> a =
parent.createActionFromJson( actionJs ) )
183 a->identifier = wxString::Format(
"%s.%s",
identifier, a->identifier );
184 wxLogTrace(
traceApi, wxString::Format(
"Plugin: loaded action %s",
191 catch(
const std::exception& e )
193 wxLogTrace(
traceApi,
"Plugin: exception while parsing actions" );
194 error_message = wxString::Format(
_(
"actions section is invalid: %s" ),
195 wxString::FromUTF8( e.what() ) );
231 wxRegEx identifierRegex( wxS(
"[\\w\\d]{2,}\\.[\\w\\d]+\\.[\\w\\d]+" ) );
232 return identifierRegex.Matches( aIdentifier );
286 action.
identifier = aJson.at(
"identifier" ).get<wxString>();
287 wxLogTrace(
traceApi, wxString::Format(
"Plugin: load action %s", action.
identifier ) );
288 action.
name = aJson.at(
"name" ).get<wxString>();
289 action.
description = aJson.at(
"description" ).get<wxString>();
290 action.
entrypoint = aJson.at(
"entrypoint" ).get<wxString>();
291 action.
show_button = aJson.contains(
"show-button" ) && aJson.at(
"show-button" ).get<
bool>();
295 wxLogTrace(
traceApi,
"Plugin: exception while parsing action required keys" );
301 if( !f.IsRelative() )
303 wxLogTrace(
traceApi, wxString::Format(
"Plugin: action contains abs path %s; skipping",
308 f.Normalize( wxPATH_NORM_ABSOLUTE,
m_configFile.GetPath() );
310 if( !f.IsFileReadable() )
312 wxLogTrace(
traceApi, wxString::Format(
"WARNING: action entrypoint %s is not readable",
316 if( aJson.contains(
"args" ) && aJson.at(
"args" ).is_array() )
318 for(
const nlohmann::json& argJs : aJson.at(
"args" ) )
322 action.
args.emplace_back( argJs.get<wxString>() );
326 wxLogTrace(
traceApi,
"Plugin: exception while parsing action args" );
332 if( aJson.contains(
"scopes" ) && aJson.at(
"scopes" ).is_array() )
334 for(
const nlohmann::json& scopeJs : aJson.at(
"scopes" ) )
338 action.
scopes.insert( magic_enum::enum_cast<PLUGIN_ACTION_SCOPE>(
339 scopeJs.get<std::string>(), magic_enum::case_insensitive )
344 wxLogTrace(
traceApi,
"Plugin: exception while parsing action scopes" );
351 [&](
const std::string& aKey, wxBitmapBundle& aDest )
353 if( aJson.contains( aKey ) && aJson.at( aKey ).is_array() )
355 wxVector<wxBitmap> bitmaps;
357 for(
const nlohmann::json& iconJs : aJson.at( aKey ) )
363 iconFile = iconJs.get<wxString>();
370 iconFile.Normalize( wxPATH_NORM_ABSOLUTE,
m_configFile.GetPath() );
373 wxString::Format(
"Plugin: action %s: loading icon %s",
374 action.
identifier, iconFile.GetFullPath() ) );
377 if( !iconFile.IsFileReadable() )
379 wxLogTrace(
traceApi,
"Plugin: icon file could not be read" );
385 bmp.LoadFile( iconFile.GetFullPath(), wxBITMAP_TYPE_PNG );
388 bitmaps.push_back( bmp );
390 wxLogTrace(
traceApi,
"Plugin: icon file not a valid bitmap" );
393 aDest = wxBitmapBundle::FromBitmaps( bitmaps );
397 handleBitmap(
"icons-light", action.
icon_light );
398 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)
const wxString & ErrorMessage() const
nlohmann::json Validate(const nlohmann::json &aJson, nlohmann::json_schema::error_handler &aErrorHandler, const nlohmann::json_uri &aInitialUri=nlohmann::json_uri("#")) const
const wxString & ErrorMessage() 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
tl::expected< bool, wxString > FromJson(const nlohmann::json &aJson)