32#include <google/protobuf/empty.pb.h>
42using namespace kiapi::common::commands;
44using google::protobuf::Empty;
85 GetVersionResponse reply;
90 reply.mutable_version()->set_major( std::get<0>( version ) );
91 reply.mutable_version()->set_minor( std::get<1>( version ) );
92 reply.mutable_version()->set_patch( std::get<2>( version ) );
101 wxFileName fn( wxEmptyString, wxString::FromUTF8( aCtx.
Request.binary_name() ) );
103 fn.SetExt( wxT(
"exe" ) );
108 reply.set_path(
path.ToUTF8() );
116 if( !aAllowEmpty && ( aProject.name().empty() || aProject.path().empty() ) )
119 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
120 e.set_error_message(
"a project name and path must be specified" );
121 return tl::unexpected( e );
126 if( aProject.name().compare( prj.
GetProjectName().ToUTF8() ) != 0 )
129 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
130 e.set_error_message( fmt::format(
"the requested project {} is not open", aProject.name() ) );
131 return tl::unexpected( e );
134 if( aProject.path().compare( prj.
GetProjectPath().ToUTF8() ) != 0 )
137 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
138 e.set_error_message( fmt::format(
"the requested project {} is not open at path {}", aProject.name(),
140 return tl::unexpected( e );
150 return tl::unexpected(
result.error() );
152 NetClassesResponse reply;
154 std::shared_ptr<NET_SETTINGS>& netSettings =
157 google::protobuf::Any
any;
159 netSettings->GetDefaultNetclass()->Serialize(
any );
160 any.UnpackTo( reply.add_net_classes() );
162 for(
const auto& netClass : netSettings->GetNetclasses() | std::views::values )
164 netClass->Serialize(
any );
165 any.UnpackTo( reply.add_net_classes() );
175 return tl::unexpected(
result.error() );
177 std::shared_ptr<NET_SETTINGS>& netSettings =
180 if( aCtx.
Request.merge_mode() == MapMergeMode::MMM_REPLACE )
181 netSettings->ClearNetclasses();
183 auto netClasses = netSettings->GetNetclasses();
184 google::protobuf::Any
any;
186 for(
const auto& ncProto : aCtx.
Request.net_classes() )
188 any.PackFrom( ncProto );
189 wxString
name = wxString::FromUTF8( ncProto.name() );
191 bool deserialized =
false;
193 if(
name == wxT(
"Default" ) )
195 deserialized = netSettings->GetDefaultNetclass()->Deserialize(
any );
199 if( !netClasses.contains(
name ) )
200 netClasses.insert( {
name, std::make_shared<NETCLASS>(
name,
false ) } );
202 deserialized = netClasses[
name]->Deserialize(
any );
208 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
209 e.set_error_message( fmt::format(
"could not unpack netclass '{}'",
name.ToUTF8().data() ) );
210 return tl::unexpected( e );
214 netSettings->SetNetclasses( netClasses );
225 return tl::unexpected(
result.error() );
229 NetClassAssignmentsResponse reply;
231 for(
const auto& [netName, netclassNames] : netSettings->GetNetclassLabelAssignments() )
233 project::NetClassAssignment* assignment = reply.add_assignments();
234 assignment->set_net( netName.ToUTF8() );
236 for(
const wxString& netclassName : netclassNames )
237 assignment->add_netclasses( netclassName.ToUTF8() );
240 for(
const auto& [matcher, netclassName] : netSettings->GetNetclassPatternAssignments() )
242 project::NetClassPatternAssignment* pattern = reply.add_pattern_assignments();
243 pattern->set_pattern( matcher->GetPattern().ToUTF8() );
244 pattern->set_netclass( netclassName.ToUTF8() );
255 return tl::unexpected(
result.error() );
259 std::set<wxString, std::less<>> knownNetclasses;
262 for(
const wxString&
name : netSettings->GetNetclasses() | std::views::keys )
263 knownNetclasses.insert(
name );
265 auto checkNetclassName = [&](
const std::string& aName,
const char* aKind ) -> std::optional<ApiResponseStatus>
267 if( knownNetclasses.contains( wxString::FromUTF8( aName ) ) )
271 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
272 e.set_error_message( fmt::format(
"unknown netclass '{}' in {} assignment", aName, aKind ) );
276 if( aCtx.
Request.merge_mode() == MapMergeMode::MMM_REPLACE )
278 netSettings->ClearNetclassLabelAssignments();
279 netSettings->ClearNetclassPatternAssignments();
282 for(
const project::NetClassAssignment& assignment : aCtx.
Request.assignments() )
284 if( assignment.net().empty() )
287 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
288 e.set_error_message(
"net name cannot be empty in a netclass assignment" );
289 return tl::unexpected( e );
292 for(
const std::string& netclassName : assignment.netclasses() )
294 if( std::optional<ApiResponseStatus> err = checkNetclassName( netclassName,
"net" ) )
295 return tl::unexpected( *err );
298 std::set<wxString> netclasses;
300 for(
const std::string& netclassName : assignment.netclasses() )
301 netclasses.insert( wxString::FromUTF8( netclassName ) );
303 if( netclasses.empty() )
304 netSettings->ClearNetclassLabelAssignment( wxString::FromUTF8( assignment.net() ) );
306 netSettings->SetNetclassLabelAssignment( wxString::FromUTF8( assignment.net() ), netclasses );
309 for(
const project::NetClassPatternAssignment& pattern : aCtx.
Request.pattern_assignments() )
311 if( pattern.pattern().empty() )
314 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
315 e.set_error_message(
"pattern cannot be empty in a netclass pattern assignment" );
316 return tl::unexpected( e );
319 if( !pattern.netclass().empty() )
321 if( std::optional<ApiResponseStatus> err = checkNetclassName( pattern.netclass(),
"pattern" ) )
322 return tl::unexpected( *err );
325 std::vector<std::pair<std::unique_ptr<EDA_COMBINED_MATCHER>, wxString>> kept;
327 for(
auto& existing : netSettings->GetNetclassPatternAssignments() )
329 if( existing.first->GetPattern() != wxString::FromUTF8( pattern.pattern() ) )
330 kept.emplace_back( std::move( existing ) );
333 netSettings->SetNetclassPatternAssignments( std::move( kept ) );
335 if( !pattern.netclass().empty() )
337 netSettings->SetNetclassPatternAssignment( wxString::FromUTF8( pattern.pattern() ),
338 wxString::FromUTF8( pattern.netclass() ) );
342 netSettings->ClearAllCaches();
359 google::protobuf::Any
any;
365 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
366 e.set_error_message(
"Could not decode text in GetTextExtents message" );
367 return tl::unexpected( e );
370 types::Box2 response;
378 response.mutable_position()->set_x_nm( bbox.
GetPosition().
x );
379 response.mutable_position()->set_y_nm( bbox.
GetPosition().
y );
380 response.mutable_size()->set_x_nm( bbox.
GetSize().
x );
381 response.mutable_size()->set_y_nm( bbox.
GetSize().
y );
390 GetTextAsShapesResponse reply;
392 for(
const TextOrTextBox& textMsg : aCtx.
Request.text() )
395 const Text* textPtr = &textMsg.text();
397 if( textMsg.has_textbox() )
399 dummyText.set_text( textMsg.textbox().text() );
400 dummyText.mutable_attributes()->CopyFrom( textMsg.textbox().attributes() );
401 textPtr = &dummyText;
405 google::protobuf::Any
any;
406 any.PackFrom( *textPtr );
411 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
412 e.set_error_message(
"Could not decode text in GetTextAsShapes message" );
413 return tl::unexpected( e );
416 std::shared_ptr<SHAPE_COMPOUND> shapes =
text.GetEffectiveTextShape(
false );
418 TextWithShapes* entry = reply.add_text_with_shapes();
419 entry->mutable_text()->CopyFrom( textMsg );
421 for(
SHAPE* subshape : shapes->Shapes() )
425 GraphicShape* shapeMsg = entry->mutable_shapes()->add_shapes();
426 any.UnpackTo( shapeMsg );
429 if( textMsg.has_textbox() && textMsg.textbox().border_enabled() )
431 GraphicShape* border = entry->mutable_shapes()->add_shapes();
432 int width = textMsg.textbox().attributes().stroke_width().value_nm();
433 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
438 PackVector2( *border->mutable_segment()->mutable_start(), tl );
442 border = entry->mutable_shapes()->add_shapes();
443 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
445 PackVector2( *border->mutable_segment()->mutable_end(), br );
448 border = entry->mutable_shapes()->add_shapes();
449 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
450 PackVector2( *border->mutable_segment()->mutable_start(), br );
454 border = entry->mutable_shapes()->add_shapes();
455 border->mutable_attributes()->mutable_stroke()->mutable_width()->set_value_nm( width );
457 PackVector2( *border->mutable_segment()->mutable_end(), tl );
468 if( !aCtx.
Request.has_document() || aCtx.
Request.document().type() != DOCTYPE_PROJECT )
471 e.set_status( ApiStatusCode::AS_UNHANDLED );
473 return tl::unexpected( e );
476 ExpandTextVariablesResponse reply;
479 for(
const std::string& textMsg : aCtx.
Request.text() )
483 if( aCtx.
Request.expand_env_vars() )
486 reply.add_text(
result.ToUTF8() );
496 wxString identifier = wxString::FromUTF8( aCtx.
Request.identifier() );
498 if( identifier.IsEmpty() )
501 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
502 e.set_error_message(
"plugin identifier is missing" );
503 return tl::unexpected( e );
509 e.set_status( ApiStatusCode::AS_BAD_REQUEST );
510 e.set_error_message(
"plugin identifier is invalid" );
511 return tl::unexpected( e );
515 path.AppendDir(
"plugins" );
520 path.AppendDir( identifier );
522 StringResponse reply;
523 reply.set_response(
path.GetPath() );
531 if( !aCtx.
Request.has_document() || aCtx.
Request.document().type() != DOCTYPE_PROJECT )
534 e.set_status( ApiStatusCode::AS_UNHANDLED );
536 return tl::unexpected( e );
542 return tl::unexpected(
result.error() );
550 e.set_status( ApiStatusCode::AS_NOT_READY );
551 e.set_error_message(
"no valid project is loaded, cannot get text variables" );
552 return tl::unexpected( e );
555 const std::map<wxString, wxString>& vars =
project.GetTextVars();
557 project::TextVariables reply;
558 auto map = reply.mutable_variables();
560 for(
const auto& [key, value] : vars )
561 ( *map )[ std::string( key.ToUTF8() ) ] = value.ToUTF8();
570 if( !aCtx.
Request.has_document() || aCtx.
Request.document().type() != DOCTYPE_PROJECT )
573 e.set_status( ApiStatusCode::AS_UNHANDLED );
575 return tl::unexpected( e );
581 return tl::unexpected(
result.error() );
589 e.set_status( ApiStatusCode::AS_NOT_READY );
590 e.set_error_message(
"no valid project is loaded, cannot set text variables" );
591 return tl::unexpected( e );
594 const project::TextVariables& newVars = aCtx.
Request.variables();
595 std::map<wxString, wxString>& vars =
project.GetTextVars();
597 if( aCtx.
Request.merge_mode() == MapMergeMode::MMM_REPLACE )
600 for(
const auto& [key, value] : newVars.variables() )
601 vars[wxString::FromUTF8( key )] = wxString::FromUTF8( value );
603 if( !
Pgm().GetSettingsManager().SaveProject() )
606 e.set_status( ApiStatusCode::AS_INTERNAL_ERROR );
607 e.set_error_message(
"failed to save project text variables" );
608 return tl::unexpected( e );
621 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
622 e.set_error_message(
"OpenDocument is not available in this KiCad mode" );
623 return tl::unexpected( e );
636 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
637 e.set_error_message(
"CloseDocument is not available in this KiCad mode" );
638 return tl::unexpected( e );
651 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
652 e.set_error_message(
"CreateDocument is not available in this KiCad mode" );
653 return tl::unexpected( e );
665 e.set_status( ApiStatusCode::AS_UNIMPLEMENTED );
666 e.set_error_message(
"CloseAllDocuments is not available in this KiCad mode" );
667 return tl::unexpected( e );
676 GetPathsResponse reply;
678 auto addPath = [&]( types::PathType aType,
const wxString& aPath )
680 PathEntry* entry = reply.add_paths();
681 entry->set_type( aType );
682 entry->set_path( aPath.ToUTF8() );
tl::expected< T, ApiResponseStatus > HANDLER_RESULT
constexpr EDA_IU_SCALE pcbIUScale
const std::tuple< int, int, int > & GetMajorMinorPatchTuple()
Get the build version numbers as a tuple.
wxString GetBuildVersion()
Get the full KiCad version string.
HANDLER_RESULT< Empty > handlePing(const HANDLER_CONTEXT< commands::Ping > &aCtx)
HANDLER_RESULT< types::Box2 > handleGetTextExtents(const HANDLER_CONTEXT< commands::GetTextExtents > &aCtx)
CLOSE_ALL_DOCUMENTS_HANDLER m_closeAllDocumentsHandler
HANDLER_RESULT< commands::GetTextAsShapesResponse > handleGetTextAsShapes(const HANDLER_CONTEXT< commands::GetTextAsShapes > &aCtx)
HANDLER_RESULT< commands::GetVersionResponse > handleGetVersion(const HANDLER_CONTEXT< commands::GetVersion > &aCtx)
CREATE_DOCUMENT_HANDLER m_createDocumentHandler
HANDLER_RESULT< Empty > handleCloseAllDocuments(const HANDLER_CONTEXT< commands::CloseAllDocuments > &aCtx)
HANDLER_RESULT< Empty > handleSetNetClassAssignments(const HANDLER_CONTEXT< commands::SetNetClassAssignments > &aCtx)
HANDLER_RESULT< commands::OpenDocumentResponse > handleCreateDocument(const HANDLER_CONTEXT< commands::CreateDocument > &aCtx)
HANDLER_RESULT< commands::NetClassAssignmentsResponse > handleGetNetClassAssignments(const HANDLER_CONTEXT< commands::GetNetClassAssignments > &aCtx)
HANDLER_RESULT< commands::PathResponse > handleGetKiCadBinaryPath(const HANDLER_CONTEXT< commands::GetKiCadBinaryPath > &aCtx)
HANDLER_RESULT< commands::NetClassesResponse > handleGetNetClasses(const HANDLER_CONTEXT< commands::GetNetClasses > &aCtx)
CLOSE_DOCUMENT_HANDLER m_closeDocumentHandler
OPEN_DOCUMENT_HANDLER m_openDocumentHandler
HANDLER_RESULT< Empty > handleCloseDocument(const HANDLER_CONTEXT< commands::CloseDocument > &aCtx)
HANDLER_RESULT< Empty > handleSetNetClasses(const HANDLER_CONTEXT< commands::SetNetClasses > &aCtx)
static tl::expected< bool, ApiResponseStatus > validateProject(const ProjectSpecifier &aProject, bool aAllowEmpty=false)
HANDLER_RESULT< commands::StringResponse > handleGetPluginSettingsPath(const HANDLER_CONTEXT< commands::GetPluginSettingsPath > &aCtx)
HANDLER_RESULT< commands::OpenDocumentResponse > handleOpenDocument(const HANDLER_CONTEXT< commands::OpenDocument > &aCtx)
HANDLER_RESULT< commands::ExpandTextVariablesResponse > handleExpandTextVariables(const HANDLER_CONTEXT< commands::ExpandTextVariables > &aCtx)
HANDLER_RESULT< Empty > handleSetTextVariables(const HANDLER_CONTEXT< commands::SetTextVariables > &aCtx)
HANDLER_RESULT< commands::GetPathsResponse > handleGetPaths(const HANDLER_CONTEXT< commands::GetPaths > &aCtx)
HANDLER_RESULT< project::TextVariables > handleGetTextVariables(const HANDLER_CONTEXT< commands::GetTextVariables > &aCtx)
void registerHandler(HANDLER_RESULT< ResponseType >(HandlerType::*aHandler)(const HANDLER_CONTEXT< RequestType > &))
Registers an API command handler for the given message types.
void requestNetSettingsNotification()
static bool IsValidIdentifier(const wxString &aIdentifier)
constexpr const Vec & GetPosition() const
const BOX2< Vec > GetBoundingBoxRotated(const VECTOR2I &aRotCenter, const EDA_ANGLE &aAngle) const
Useful to calculate bounding box of rotated items, when rotation is not cardinal.
constexpr const SizeVec & GetSize() const
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
static const char Default[]
the name of the default NETCLASS
static wxString GetUserPluginsPath()
Gets the user path for plugins.
static wxString GetStockSymbolsPath()
Gets the stock (install) symbols path.
static wxString GetUserTemplatesPath()
Gets the user path for custom templates.
static bool EnsurePathExists(const wxString &aPath, bool aPathToFile=false)
Attempts to create a given path if it does not exist.
static wxString GetStock3dmodelsPath()
Gets the stock (install) 3dmodels path.
static wxString GetStockTemplatesPath()
Gets the stock (install) templates path.
static wxString GetUserSettingsPath()
Return the user configuration path used to store KiCad's configuration files.
static wxString GetStockDesignBlocksPath()
Gets the stock (install) footprints path.
static wxString GetStockFootprintsPath()
Gets the stock (install) footprints path.
virtual SETTINGS_MANAGER & GetSettingsManager() const
std::shared_ptr< NET_SETTINGS > m_NetSettings
Net settings for this project (owned here)
Container for project specific data.
virtual const wxString GetProjectPath() const
Return the full path of the project.
virtual const wxString GetProjectName() const
Return the short name of the project.
virtual PROJECT_FILE & GetProjectFile() const
PROJECT & Prj() const
A helper while we are not MDI-capable – return the one and only project.
An abstract shape on 2D plane.
A type-safe container of any type.
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
wxString ExpandTextVars(const wxString &aSource, const PROJECT *aProject, RESOLUTION_CONTEXT aContext)
static std::string ToStdString(const wxString &aStr)
wxString FindKicadFile(const wxString &shortname)
Search the executable file shortname in KiCad binary path and return full file name if found or short...
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput, const EDA_IU_SCALE &aScale)
PGM_BASE & Pgm()
The global program "get" accessor.
RequestMessageType Request
wxString result
Test unit parsing edge cases and error handling.
VECTOR2< int32_t > VECTOR2I